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即可避免该问题
