Gitee
https://gitee.com/WangXi_Chn/RttOs_ModuleLib
开发环境
CubeMX
MDK5 IDE
STM32F407IGH6TR 芯片
大疆开发板 C型
冯哈伯 3863 直流电机 带减速箱(38:2)
RoboModule直流伺服电机驱动器 RMDS-303
RoboModule电机配置上位机
RT-Thread操作系统
面向对象模块接口设计
硬件条件
- 需要一根RS232转USB线,电脑上位机配置电调
- 电机与Robomodule连接正确
-
配置电机
Robomodule内部集成电机控制算法,仅需通过上位机配置电机的PID参数等
- 单片机仅需根据协议发送指令即可驱动电机
Robomodule配置电机教程
http://www.robomodule.net/download.html
核心思想
- 同3508电机思路一样,视挂载在同一条Can线上的所有Robomodule电机为一个电机群
- 根据结构体成员的赋值实现电机使能
- 因为实际使用中,用到的3863电机不会超过8个,因此注释掉了多余的ID选项(可自行扩展)
- Can报文协议也遵循最多8电机编写(组号为0,电机号为1~8)(格式参照Robomodule教程)
Module_RobomoduleGroup.h
```c /*- Copyright (c) 2020 - ~, HIT_HERO Team *
- ROBOMODULE MODULE HEAD FILE
- Used in RT-Thread Operate System *
- Change Logs:
- Date Author Notes Mail
- 2020-08-22 WangXi first version WangXi_chn@foxmail.com
- Note:
- */
include
include
include
enum ROBOMODULE_GROUPID { ROBOMODULE_GROUPID_0 = 0, // ROBOMODULE_GROUPID_1 = 1, // ROBOMODULE_GROUPID_2 = 2, // ROBOMODULE_GROUPID_3 = 3, // ROBOMODULE_GROUPID_4 = 4, // ROBOMODULE_GROUPID_5 = 5, // ROBOMODULE_GROUPID_6 = 6, // ROBOMODULE_GROUPID_7 = 7, };
enum ROBOMODULE_NUMID { ROBOMODULE_NUMID_1 = 0, ROBOMODULE_NUMID_2 = 1, ROBOMODULE_NUMID_3 = 2, ROBOMODULE_NUMID_4 = 3, ROBOMODULE_NUMID_5 = 4, ROBOMODULE_NUMID_6 = 5, ROBOMODULE_NUMID_7 = 6, ROBOMODULE_NUMID_8 = 7, // ROBOMODULE_NUMID_9 = 9, // ROBOMODULE_NUMID_10 = 10, // ROBOMODULE_NUMID_11 = 11, // ROBOMODULE_NUMID_12 = 12, // ROBOMODULE_NUMID_13 = 13, // ROBOMODULE_NUMID_14 = 14, // ROBOMODULE_NUMID_15 = 15, };
enum ROBOMODULE_MODE { ROBOMODULE_MODE_OPENLOOP = 1, ROBOMODULE_MODE_CURRENT = 2, ROBOMODULE_MODE_SPEED = 3, ROBOMODULE_MODE_LOCATION = 4, ROBOMODULE_MODE_SPEEDLOCATION = 5, ROBOMODULE_MODE_CURRENTSPEED = 6, ROBOMODULE_MODE_CURRENLOCATION = 7, ROBOMODULE_MODE_CURRENSPEEDLOCATION = 8, };
/ Single module label —————————————————————————————/ struct _MODULE_ROBOMODULE { / Property / rt_uint8_t Property_Enable; enum ROBOMODULE_GROUPID Property_GroupID; enum ROBOMODULE_NUMID Property_NumID; enum ROBOMODULE_MODE Property_Mode;
/* Value */
rt_device_t Value_can_dev;
struct rt_can_msg Value_can_msg;
struct rt_can_msg Value_can_mrg;
rt_int16_t Value_motor_AimCurrent;
rt_int16_t Value_motor_AimRPM;
rt_int32_t Value_motor_AimAngle;
rt_int16_t Value_motor_RealCurrent;
rt_int16_t Value_motor_RealRPM;
rt_int32_t Value_motor_RealAngle;
/* Method */
void (*Method_Init)(struct _MODULE_ROBOMODULE *module);
}; typedef struct _MODULE_ROBOMODULE MODULE_ROBOMODULE;
/ Motor group label —————————————————————————————/ struct _MODULE_ROBOMODULEGROUP { / Property / char * Property_CanDevName;
/* Value */
MODULE_ROBOMODULE Value_robomodule[8];
rt_device_t Value_can_dev;
struct rt_can_msg Value_can_msg;
struct rt_can_msg Value_can_mrg;
rt_uint8_t Value_moduleUsedMask;
/* Method */
void (*Method_Init) (struct _MODULE_ROBOMODULEGROUP *module);
void (*Method_Send) (struct _MODULE_ROBOMODULEGROUP *module);
void (*Method_Feed) (struct _MODULE_ROBOMODULEGROUP *module);
rt_err_t (*Method_Handle) (rt_device_t dev,rt_size_t size);
}; typedef struct _MODULE_ROBOMODULEGROUP MODULE_ROBOMODULEGROUP;
/ Glodal Method / rt_err_t Module_RobomoduleGroup_Config(MODULE_ROBOMODULEGROUP *module);
<a name="nlcHm"></a>
## Module_RobomoduleGroup.c
```c
/*
* Copyright (c) 2020 - ~, HIT_HERO Team
*
* ROBOMODULE MODULE SOUCE FILE
* Used in RT-Thread Operate System
*
* Change Logs:
* Date Author Notes Mail
* 2020-08-22 WangXi first version WangXi_chn@foxmail.com
*
* Note:
*
*/
#include "Module_RobomoduleGroup.h"
/* User Code Begin*/
/* User Code End */
/* Static Method */
static void Module_RobomoduleGroupInit(MODULE_ROBOMODULEGROUP *module);
static void Module_RobomoduleGroupSend(MODULE_ROBOMODULEGROUP *module);
static void Module_RobomoduleGroupFeed(MODULE_ROBOMODULEGROUP *module);
static rt_err_t Module_RobomoduleGroupHandle(rt_device_t dev, rt_size_t size);
static rt_err_t Module_Robomodule_Config(MODULE_ROBOMODULE *module);
static void Module_RobomoduleInit(MODULE_ROBOMODULE *module);
static struct rt_semaphore canRobomodule_sem;
/* Global Method */
rt_err_t Module_RobomoduleGroup_Config(MODULE_ROBOMODULEGROUP *module)
{
if( module->Method_Init ==NULL &&
module->Method_Feed ==NULL &&
module->Method_Send ==NULL &&
module->Method_Handle ==NULL
){
/* Link the Method */
module->Method_Init = Module_RobomoduleGroupInit;
module->Method_Feed = Module_RobomoduleGroupFeed;
module->Method_Send = Module_RobomoduleGroupSend;
module->Method_Handle = Module_RobomoduleGroupHandle;
}
else{
rt_kprintf("Warning: Module Robomodule is Configed twice\n");
return RT_ERROR;
}
/* Device Init */
module->Method_Init(module);
/* Motor Config */
for(int i=0;i<8;i++)
{
if(module->Value_robomodule[i].Property_Enable!= 0)
{
module->Value_moduleUsedMask |= 0x01<<i;
module->Value_robomodule[i].Value_can_dev = module->Value_can_dev;
Module_Robomodule_Config(&(module->Value_robomodule[i]));
}
}
return RT_EOK;
}
static void Module_RobomoduleGroupInit(MODULE_ROBOMODULEGROUP *module)
{
rt_err_t res;
/* link can devices */
module->Value_can_dev = rt_device_find(module->Property_CanDevName);
if (!module->Value_can_dev)
rt_kprintf("find %s failed!\n", module->Property_CanDevName);
/* init semaphone */
rt_sem_init(&canRobomodule_sem, "canRobomodule_sem", 0, RT_IPC_FLAG_FIFO);
res = rt_device_open(module->Value_can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
RT_ASSERT(res == RT_EOK);
/* set up can baudrate */
rt_device_control(module->Value_can_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN1MBaud);
/* link call back function */
rt_device_set_rx_indicate(module->Value_can_dev, module->Method_Handle);
}
rt_err_t Module_Robomodule_Config(MODULE_ROBOMODULE *module)
{
if( module->Method_Init ==NULL
){
/* Link the Method */
module->Method_Init = Module_RobomoduleInit;
}
else{
rt_kprintf("Warning: Module Motor 3508 is Configed twice\n");
return RT_ERROR;
}
/* Device Init */
module->Method_Init(module);
return RT_EOK;
}
/* Static Method */
static void Module_RobomoduleInit(MODULE_ROBOMODULE *module)
{
/* Reset the motor */
module->Value_can_msg.id = (module->Property_GroupID<<8)|((module->Property_NumID+1)<<4);
module->Value_can_msg.id += 0x000;
module->Value_can_msg.ide = RT_CAN_STDID;
module->Value_can_msg.rtr = RT_CAN_DTR;
module->Value_can_msg.len = 8;
for(int i=0;i<8;i++)
module->Value_can_msg.data[i]=0x55;
rt_size_t size = rt_device_write(module->Value_can_dev, 0, &(module->Value_can_msg), sizeof(module->Value_can_msg));
if (size == 0)
rt_kprintf("can dev write data failed!\n");
rt_thread_mdelay(500);
/* Set the motor mode*/
module->Value_can_msg.id = (module->Property_GroupID<<8)|((module->Property_NumID+1)<<4);
module->Value_can_msg.id += 0x001;
module->Value_can_msg.ide = RT_CAN_STDID;
module->Value_can_msg.rtr = RT_CAN_DTR;
module->Value_can_msg.len = 8;
module->Value_can_msg.data[0]=module->Property_Mode;
for(int i=1;i<8;i++)
module->Value_can_msg.data[i]=0x55;
size = rt_device_write(module->Value_can_dev, 0, &(module->Value_can_msg), sizeof(module->Value_can_msg));
if (size == 0)
rt_kprintf("can dev write data failed!\n");
rt_thread_mdelay(500);
module->Value_motor_AimAngle = 0;
module->Value_motor_AimCurrent = 0;
module->Value_motor_AimRPM = 0;
}
static void Module_RobomoduleGroupSend(MODULE_ROBOMODULEGROUP *module)
{
rt_size_t size;
/* Send motor data */
for(int i=0;i<8;i++)
{
if((module->Value_moduleUsedMask & (0x01<<i))!=0x00)
{
switch(module->Value_robomodule[i].Property_Mode)
{
case ROBOMODULE_MODE_OPENLOOP:
break;
case ROBOMODULE_MODE_CURRENT:
break;
case ROBOMODULE_MODE_SPEED:
break;
case ROBOMODULE_MODE_LOCATION:
module->Value_can_msg.id
=(module->Value_robomodule[i].Property_GroupID<<8)
|((module->Value_robomodule[i].Property_NumID+1)<<4);
module->Value_can_msg.id += 0x005;
module->Value_can_msg.ide = RT_CAN_STDID;
module->Value_can_msg.rtr = RT_CAN_DTR;
module->Value_can_msg.len = 8;
module->Value_can_msg.data[0]= (unsigned char)(5000>>8)&0xFF;
module->Value_can_msg.data[1]= (unsigned char)(5000)&0xFF;
module->Value_can_msg.data[2]=0x55;
module->Value_can_msg.data[3]=0x55;
module->Value_can_msg.data[4]=
(unsigned char)((module->Value_robomodule[i].Value_motor_AimAngle>>24)&0xff);
module->Value_can_msg.data[5]=
(unsigned char)((module->Value_robomodule[i].Value_motor_AimAngle>>16)&0xff);
module->Value_can_msg.data[6]=
(unsigned char)((module->Value_robomodule[i].Value_motor_AimAngle>>8)&0xff);
module->Value_can_msg.data[7]=
(unsigned char)(module->Value_robomodule[i].Value_motor_AimAngle&0xff);
size = rt_device_write(module->Value_can_dev, 0,
&(module->Value_can_msg), sizeof(module->Value_can_msg));
if (size == 0)
rt_kprintf("robomodule can dev write data failed!\n");
break;
case ROBOMODULE_MODE_SPEEDLOCATION:
break;
case ROBOMODULE_MODE_CURRENTSPEED:
break;
case ROBOMODULE_MODE_CURRENLOCATION:
break;
case ROBOMODULE_MODE_CURRENSPEEDLOCATION:
break;
}
}
}
}
static rt_err_t Module_RobomoduleGroupHandle(rt_device_t dev, rt_size_t size)
{
rt_sem_release(&canRobomodule_sem);
return RT_EOK;
}
static void Module_RobomoduleGroupFeed(MODULE_ROBOMODULEGROUP *module)
{
rt_sem_take(&canRobomodule_sem, RT_WAITING_FOREVER);
/* Get true value of the motor */
rt_device_read(module->Value_can_dev, 0, &(module->Value_can_mrg), sizeof(module->Value_can_mrg));
}
/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/
使用
/*
* Copyright (c) 2020 - ~, HIT_HERO Team
*
* MAIN SOUCE FILE
* Used in RT-Thread Operate System
*
* Change Logs:
* Date Author Notes Mail
* 2020-08-08 WangXi first version WangXi_chn@foxmail.com
* 2020-08-13 WangXi second version WangXi_chn@foxmail.com
*
* Note:
* Used for friction gear shot Rugby
*
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include "Module_LED.h"
#include "Module_BlueToothHC06.h"
#include "Module_RobomoduleGroup.h"
/* Module param list ---------------------------------------------------------------------------------------- */
MODULE_LED dev_led_state =
{
.pin = GET_PIN(H, 10),
.LED_TIME_CYCLE = 4000,
.LED_TIME_OUTPUT = 200
};
MODULE_BLUETOOTHHC06 dev_communicate =
{
.Property_UartDevName = "uart6",
};
MODULE_ROBOMODULEGROUP dev_robomoduleGroup =
{
.Property_CanDevName = "can1",
.Value_robomodule[ROBOMODULE_NUMID_1] =
{
.Property_Enable = 1,
.Property_GroupID = ROBOMODULE_GROUPID_0,
.Property_NumID = ROBOMODULE_NUMID_1,
.Property_Mode = ROBOMODULE_MODE_LOCATION,
},
.Value_robomodule[ROBOMODULE_NUMID_2] =
{
.Property_Enable = 1,
.Property_GroupID = ROBOMODULE_GROUPID_0,
.Property_NumID = ROBOMODULE_NUMID_2,
.Property_Mode = ROBOMODULE_MODE_LOCATION,
},
};
/* thread entry list ---------------------------------------------------------------------------------------- */
static void led_shine_entry(void *parameter)
{
while (1)
{
rt_thread_mdelay(1);
dev_led_state.Handle(&dev_led_state);
}
}
static void motor_entry(void *parameter)
{
while (1)
{
rt_thread_mdelay(1);
dev_robomoduleGroup.Method_Send(&dev_robomoduleGroup);
}
}
static void bluetupdata_entry(void *parameter)
{
dev_communicate.Method_Feed(&dev_communicate);
}
static void bluetcontrol_entry(void *parameter)
{
static rt_uint32_t time_1000m = 0;
while(1)
{
rt_thread_mdelay(1);
time_1000m++;
switch(dev_communicate.Value_keyMask)
{
case (0x0001<<0):
dev_robomoduleGroup.Value_robomodule[ROBOMODULE_NUMID_1].Value_motor_AimAngle= 27500; //360°
dev_robomoduleGroup.Value_robomodule[ROBOMODULE_NUMID_2].Value_motor_AimAngle= -27500;
break;
case (0x0001<<1):
dev_robomoduleGroup.Value_robomodule[ROBOMODULE_NUMID_1].Value_motor_AimAngle=0;
dev_robomoduleGroup.Value_robomodule[ROBOMODULE_NUMID_2].Value_motor_AimAngle=0;
break;
case (0x0001<<2):
break;
case (0x0001<<3):
break;
case (0x0001<<4):
break;
case (0x0001<<5):
break;
case (0x0001<<6):
break;
case (0x0001<<7):
break;
case (0x0001<<8):
break;
case (0x0001<<9):
break;
case (0x0001<<10):
break;
case (0x0001<<11):
break;
}
dev_communicate.Value_keyMask = 0x0000;
if(time_1000m >= 1000)
{
time_1000m = 0;
rt_int16_t temp = dev_robomoduleGroup.Value_robomodule[ROBOMODULE_NUMID_1].Value_motor_AimAngle*360/27500;
dev_communicate.Method_Send(&dev_communicate,PARAM_SHOW_1,
temp);
}
}
}
int main(void)
{
/* Config module ---------------------------------------------------------------------------------------- */
Module_Led_Config(&dev_led_state);
dev_led_state.Set(&dev_led_state,9);
Module_RobomoduleGroup_Config(&dev_robomoduleGroup);
Module_BlueToothHC06_Config(&dev_communicate);
/* Enable task thread ---------------------------------------------------------------------------------------- */
/* system running shine led thread */
rt_thread_t led_thread = rt_thread_create("ledshine", led_shine_entry, RT_NULL,
512, RT_THREAD_PRIORITY_MAX - 3, 20);
if (led_thread != RT_NULL){
rt_thread_startup(led_thread);
}
/* motor control thread */
rt_thread_t motor_thread = rt_thread_create("motor", motor_entry, RT_NULL,
1024, RT_THREAD_PRIORITY_MAX - 2, 20);
if (motor_thread != RT_NULL){
rt_thread_startup(motor_thread);
}
/* blue tooth updata thread */
rt_thread_t bluetupdata_thread = rt_thread_create("bluetupdata", bluetupdata_entry, RT_NULL,
512, RT_THREAD_PRIORITY_MAX - 5, 20);
if (bluetupdata_thread != RT_NULL){
rt_thread_startup(bluetupdata_thread);
}
/* blue tooth updata thread */
rt_thread_t bluetcontrol_thread = rt_thread_create("bluetcontrol", bluetcontrol_entry, RT_NULL,
512, RT_THREAD_PRIORITY_MAX - 5, 20);
if (bluetcontrol_thread != RT_NULL){
rt_thread_startup(bluetcontrol_thread);
}
}
/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/
补充说明
- 目前模块支持电机的多种模式设置,但由于仅用到了位置模式,所以填充的发送位置模式报文的地方
- 如果需要其他模式,在结构体成员赋值时,配置模式,并填充对应分支即可
- 模块的初始化大致分为两步
- 电机群初始化:完成Can线的驱动,根据电机使能标记,选择性初始化电机
- 电机初始化:完成电机复位指令发送,电机模式选择指令发送
- 目前还未解读报文返回信息,仅是放置在了缓冲区内,用一个信号量指示接收到了报文
- 后期可根据需求解析报文
- 使用上与前面的3508电机群模块使用基本一致
- 声明结构体变量,并赋值,使能电机,设置模式,设置ID
- 在主函数完成电机群模块的初始化
- 在电机控制线程中调用电机群的发送报文方法
- 然后在任何一个位置中均可以设置电机群中某一个电机的转角
注意:如果需要同其他类型的Can设备(如3508电机群)公用同一Can线,注意会出现RTT的Can设备被多次初始化而被报错的情况,建议同种协议的设备唯一占用一趟Cans线,比如3508电机和2006电机使用Can1,Robomodule使用Can2即可避免该问题