Gitee
https://gitee.com/WangXi_Chn/RttOs_ModuleLib
开发环境
CubeMX
MDK5 IDE
STM32F407IGH6TR 芯片
大疆开发板 C型
大疆M3508直流无刷电机
大疆C620无刷电机调速器
RT-Thread操作系统
面向对象模块接口设计
硬件条件
- 确保Can线供电电平达到5V(不可仅使用JTAG接口供电3.3V)
- Can线接口连接正确
-
核心思想
将一条Can线上的所有3508电机视为一个电机群
- 最大支持驱动8个不同ID的电机(符合电机电调参数手册)
- 隐藏固定的ID,减少驱动工作量,使能一个电机仅需打开开关,未打开的电机不使能
- 通过掩码思想,标记已打开的电机,并仅驱动打开的电机
- 设计电机工作模式:角度环/速度环,支持切换
- 每个电机绑定两个不同的PID分别控制角度和速度,根据电机模式分别使能
- 模块化思想
源文件
Module_DjiC610620Group.h
```c /*- Copyright (c) 2020 - ~, HIT_HERO Team *
- 2508MOTOR_CAN MODULE HEAD FILE
- Used in RT-Thread Operate System *
- Change Logs:
- Date Author Notes Mail
- 2020-08-08 WangXi first version WangXi_chn@foxmail.com *
- Note:
- DJI 3508 motor and C620 motor controler driver and control framework
- All paramemters shall be subject to the motor rotor control
- And the parameters of the shaft shall be converted by yourself if needed
- The positive direction is clockwise facing the rotor */
ifndef MODULE_DJIC610620GROUP_H
define MODULE_DJIC610620GROUP_H
include
include
include
include “Math_PID.h”
/ Definition —————————————————————————————/ enum DjiC610620ID { DjiC610620ID_1 = 0, DjiC610620ID_2 = 1, DjiC610620ID_3 = 2, DjiC610620ID_4 = 3, DjiC610620ID_5 = 4, DjiC610620ID_6 = 5, DjiC610620ID_7 = 6, DjiC610620ID_8 = 7, };
enum DjiC610620MODE { DjiC610620MODE_SPEED = 0, DjiC610620MODE_ANGLE = 1, };
/ Single motor label —————————————————————————————/ struct _MODULE_DjiC610620 { / Property / rt_uint8_t Enable; rt_uint8_t Mode;
MATH_PID PID_Speed;
MATH_PID PID_Angle;
/* Value */
rt_int16_t Value_motor_AimCurrent;
rt_int16_t Value_motor_AimRPM;
rt_int16_t Value_motor_AimAngle;
rt_int16_t Value_motor_RealCurrent;
rt_int16_t Value_motor_RealRPM;
rt_int16_t Value_motor_RealAngle;
rt_uint8_t Value_motor_Temperature;
rt_int8_t Value_motor_InitFlag;
rt_int16_t Value_motor_OffsetAngle;
rt_int16_t Value_motor_LastAngle;
rt_int32_t Value_motor_TotalAngle; /* after the conversion (°) */
rt_int16_t Value_motor_AngleCnt;
/* Method */
void (*Method_Init)(struct _MODULE_DjiC610620 *module);
}; typedef struct _MODULE_DjiC610620 MODULE_DjiC610620;
/ Motor group label —————————————————————————————/ struct _MODULE_DjiC610620GROUP { / Property / char * Property_CanDevName; MODULE_DjiC610620 Value_module_DjiC610620[7];
/* Value */
struct rt_can_msg Value_can_msgLow;
struct rt_can_msg Value_can_msgHigh;
struct rt_can_msg Value_can_mrg;
rt_device_t Value_can_dev;
rt_uint8_t Value_motorUsedMask;
/* Method */
void (*Method_Init) (struct _MODULE_DjiC610620GROUP *module);
void (*Method_Send) (struct _MODULE_DjiC610620GROUP *module);
void (*Method_Feed) (struct _MODULE_DjiC610620GROUP *module);
rt_err_t (*Method_Handle) (rt_device_t dev,rt_size_t size);
}; typedef struct _MODULE_DjiC610620GROUP MODULE_DjiC610620GROUP;
/ Glodal Method / rt_err_t Module_DjiC610620Group_Config(MODULE_DjiC610620GROUP *Dev_DjiC610620Group);
endif
/** (C) COPYRIGHT 2020 WANGXI **END OF FILE**/
<a name="bYc1E"></a>
## Module_DjiC610620Group.c
```c
/*
* Copyright (c) 2020 - ~, HIT_HERO Team
*
* 2508MOTOR_CAN MODULE SOUCE FILE
* Used in RT-Thread Operate System
*
* Change Logs:
* Date Author Notes Mail
* 2020-08-08 WangXi first version WangXi_chn@foxmail.com
*
* Note:
* DJI 3508 motor and C620 motor controler driver and control framework
* All paramemters shall be subject to the motor rotor control
* And the parameters of the shaft shall be converted by yourself if needed
* The positive direction is clockwise facing the rotor
*/
#include "Module_DjiC610620Group.h"
/* User Code Begin*/
/* User Code End */
/* Static Method */
static void Module_DjiC610620GroupInit(MODULE_DjiC610620GROUP *module);
static void Module_DjiC610620GroupSend(MODULE_DjiC610620GROUP *module);
static void Module_DjiC610620GroupFeed(MODULE_DjiC610620GROUP *module);
static rt_err_t Module_DjiC610620GroupHandle(rt_device_t dev, rt_size_t size);
static rt_err_t Module_DjiC610620_Config(MODULE_DjiC610620 *Dev_DjiC610620);
static void Module_DjiC610620Init(MODULE_DjiC610620 *module);
static struct rt_semaphore can3508Motor_sem;
/* Global Method */
rt_err_t Module_DjiC610620Group_Config(MODULE_DjiC610620GROUP *Dev_DjiC610620Group)
{
if( Dev_DjiC610620Group->Method_Init ==NULL &&
Dev_DjiC610620Group->Method_Feed ==NULL &&
Dev_DjiC610620Group->Method_Send ==NULL &&
Dev_DjiC610620Group->Method_Handle ==NULL
){
/* Link the Method */
Dev_DjiC610620Group->Method_Init = Module_DjiC610620GroupInit;
Dev_DjiC610620Group->Method_Feed = Module_DjiC610620GroupFeed;
Dev_DjiC610620Group->Method_Send = Module_DjiC610620GroupSend;
Dev_DjiC610620Group->Method_Handle = Module_DjiC610620GroupHandle;
}
else{
rt_kprintf("Warning: Module Motor 3508 is Configed twice\n");
return RT_ERROR;
}
/* Device Init */
Dev_DjiC610620Group->Method_Init(Dev_DjiC610620Group);
/* Motor Config */
for(int i=0;i<8;i++)
{
if(Dev_DjiC610620Group->Value_module_DjiC610620[i].Enable != 0)
{
Dev_DjiC610620Group->Value_motorUsedMask |= 0x01<<i;
Module_DjiC610620_Config(&(Dev_DjiC610620Group->Value_module_DjiC610620[i]));
}
}
return RT_EOK;
}
static void Module_DjiC610620GroupInit(MODULE_DjiC610620GROUP *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(&can3508Motor_sem, "can3508Motor_sem", 0, RT_IPC_FLAG_FIFO);
module->Value_can_msgLow.id = 0x200;
module->Value_can_msgLow.ide = RT_CAN_STDID;
module->Value_can_msgLow.rtr = RT_CAN_DTR;
module->Value_can_msgLow.len = 8;
module->Value_can_msgHigh.id = 0x1FF;
module->Value_can_msgHigh.ide = RT_CAN_STDID;
module->Value_can_msgHigh.rtr = RT_CAN_DTR;
module->Value_can_msgHigh.len = 8;
module->Value_can_mrg.hdr = -1;
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_DjiC610620_Config(MODULE_DjiC610620 *Dev_DjiC610620)
{
if( Dev_DjiC610620->Method_Init ==NULL
){
/* Link the Method */
Dev_DjiC610620->Method_Init = Module_DjiC610620Init;
}
else{
rt_kprintf("Warning: Module Motor 3508 is Configed twice\n");
return RT_ERROR;
}
/* Device Init */
Dev_DjiC610620->Method_Init(Dev_DjiC610620);
/* Module PID controler Init */
if(Dev_DjiC610620->Mode == DjiC610620MODE_SPEED)
{
Module_PID_Config(&(Dev_DjiC610620->PID_Speed));
}
else if(Dev_DjiC610620->Mode == DjiC610620MODE_ANGLE)
{
Module_PID_Config(&(Dev_DjiC610620->PID_Speed));
Module_PID_Config(&(Dev_DjiC610620->PID_Angle));
}
return RT_EOK;
}
/* Static Method */
static void Module_DjiC610620Init(MODULE_DjiC610620 *module)
{
module->Value_motor_AimAngle = 0;
module->Value_motor_AimCurrent = 0;
module->Value_motor_AimRPM = 0;
if(module->ENCODER == ROTARYENCODER)
{
module->Encoder_dev = rt_device_find(module->Encoder_DevName);
if (module->Encoder_dev == RT_NULL){
rt_kprintf("pulse encoder sample run failed! can't find %s device!\n", module->Encoder_DevName);
return;
}
rt_err_t ret = rt_device_open(module->Encoder_dev, RT_DEVICE_OFLAG_RDONLY);
if (ret != RT_EOK){
rt_kprintf("open %s device failed!\n", module->Encoder_DevName);
return;
}
rt_pin_mode(GET_PIN(D,12), PIN_MODE_INPUT_PULLUP);
rt_pin_mode(GET_PIN(D,13), PIN_MODE_INPUT_PULLUP);
}
}
static void Module_DjiC610620GroupSend(MODULE_DjiC610620GROUP *module)
{
rt_size_t size;
/* Calculate the final current value of motor */
for(int i=0;i<8;i++)
{
if((module->Value_motorUsedMask & (0x01<<i))!=0x00)
{
module->Value_module_DjiC610620[i].Value_motor_AimCurrent =
module->Value_module_DjiC610620[i].PID_Speed.Value_output;
}
}
/* make up the all can fram */
if((module->Value_motorUsedMask & 0x0F) != 0)
{
for(int i=1;i<=4;i++)
{
module->Value_can_msgLow.data[i*2-1] = module->Value_module_DjiC610620[i-1].Value_motor_AimCurrent & 0xFF;
module->Value_can_msgLow.data[i*2-2] = module->Value_module_DjiC610620[i-1].Value_motor_AimCurrent >> 8;
}
size = rt_device_write(module->Value_can_dev, 0, &(module->Value_can_msgLow), sizeof(module->Value_can_msgLow));
if (size == 0)
rt_kprintf("can dev write data failed!\n");
size = 0;
}
rt_thread_mdelay(1);
if((module->Value_motorUsedMask & 0xF0) != 0)
{
for(int i=5;i<=8;i++)
{
module->Value_can_msgHigh.data[i*2-9] = module->Value_module_DjiC610620[i-1].Value_motor_AimCurrent & 0xFF;
module->Value_can_msgHigh.data[i*2-10] = module->Value_module_DjiC610620[i-1].Value_motor_AimCurrent >> 8;
}
size = rt_device_write(module->Value_can_dev, 0, &(module->Value_can_msgHigh), sizeof(module->Value_can_msgHigh));
if (size == 0)
rt_kprintf("can dev write data failed!\n");
size = 0;
}
}
static rt_err_t Module_DjiC610620GroupHandle(rt_device_t dev, rt_size_t size)
{
rt_sem_release(&can3508Motor_sem);
return RT_EOK;
}
static void Module_DjiC610620GroupFeed(MODULE_DjiC610620GROUP *module)
{
rt_sem_take(&can3508Motor_sem, RT_WAITING_FOREVER);
/* Get true value of the motor */
rt_uint8_t index = 1;
rt_device_read(module->Value_can_dev, 0, &(module->Value_can_mrg), sizeof(module->Value_can_mrg));
index = (module->Value_can_mrg.id & 0x00F) - 1;
module->Value_module_DjiC610620[index].Value_motor_RealAngle =
((module->Value_can_mrg.data[0]<<8) | module->Value_can_mrg.data[1]);
module->Value_module_DjiC610620[index].Value_motor_RealRPM =
((module->Value_can_mrg.data[2]<<8) | module->Value_can_mrg.data[3]);
module->Value_module_DjiC610620[index].Value_motor_RealCurrent =
((module->Value_can_mrg.data[4]<<8) | module->Value_can_mrg.data[5]);
module->Value_module_DjiC610620[index].Value_motor_Temperature =
module->Value_can_mrg.data[6];
/* Get the offset angle */
if(module->Value_module_DjiC610620[index].Value_motor_InitFlag == 0)
{
module->Value_module_DjiC610620[index].Value_motor_OffsetAngle =
module->Value_module_DjiC610620[index].Value_motor_RealAngle;
module->Value_module_DjiC610620[index].Value_motor_InitFlag = 1;
return;
}
if(module->Value_module_DjiC610620[index].Mode == DjiC610620MODE_SPEED)
{
/* Update PID output */
module->Value_module_DjiC610620[index].PID_Speed.Method_Update(
&(module->Value_module_DjiC610620[index].PID_Speed),
module->Value_module_DjiC610620[index].Value_motor_AimRPM,
module->Value_module_DjiC610620[index].Value_motor_RealRPM);
}
else if(module->Value_module_DjiC610620[index].Mode == DjiC610620MODE_ANGLE)
{
/* Deal with the data of motor's angle */
if(module->Value_module_DjiC610620[index].ENCODER == DjiC610620SELF)
{
/* Use DjiC610/C620 it self encoder */
module->Value_module_DjiC610620[index].Value_motor_RealAngle -=
module->Value_module_DjiC610620[index].Value_motor_OffsetAngle;
if(module->Value_module_DjiC610620[index].Value_motor_RealAngle -
module->Value_module_DjiC610620[index].Value_motor_LastAngle > 4096)
module->Value_module_DjiC610620[index].Value_motor_AngleCnt --;
else if (module->Value_module_DjiC610620[index].Value_motor_RealAngle -
module->Value_module_DjiC610620[index].Value_motor_LastAngle < -4096)
module->Value_module_DjiC610620[index].Value_motor_AngleCnt ++;
module->Value_module_DjiC610620[index].Value_motor_LastAngle =
module->Value_module_DjiC610620[index].Value_motor_RealAngle;
module->Value_module_DjiC610620[index].Value_motor_TotalAngle =
( module->Value_module_DjiC610620[index].Value_motor_AngleCnt * 8192 +
module->Value_module_DjiC610620[index].Value_motor_RealAngle)*360/8191;
}
else if(module->Value_module_DjiC610620[index].ENCODER == ROTARYENCODER)
{
/* Not use DjiC610/C620 it self encoder but use extra rotary encoder*/
//"module->Value_module_DjiC610620[index].Value_motor_TotalAngle" get from the out side
}
/* Update PID output */
module->Value_module_DjiC610620[index].PID_Angle.Method_Update(
&(module->Value_module_DjiC610620[index].PID_Angle),
module->Value_module_DjiC610620[index].Value_motor_AimAngle,
module->Value_module_DjiC610620[index].Value_motor_TotalAngle);
module->Value_module_DjiC610620[index].Value_motor_AimRPM =
module->Value_module_DjiC610620[index].PID_Angle.Value_output;
module->Value_module_DjiC610620[index].PID_Speed.Method_Update(
&(module->Value_module_DjiC610620[index].PID_Speed),
module->Value_module_DjiC610620[index].Value_motor_AimRPM,
module->Value_module_DjiC610620[index].Value_motor_RealRPM);
}
}
/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/
Math_PID.h
/*
* Copyright (c) 2020 - ~, HIT_HERO Team
*
* PID MATH MODULE HEAD FILE
* Used in RT-Thread Operate System
*
* Change Logs:
* Date Author Notes Mail
* 2020-08-05 WangXi first version WangXi_chn@foxmail.com
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#ifndef _MATH_PID_H_
#define _MATH_PID_H_
struct _MATH_PID
{
/* Property */
float Property_Kp;
float Property_Ki;
float Property_Kd;
float Property_dt;
float Property_integralMax;
float Property_integralErrMax;
float Property_AimMax;
float Property_OutputMax;
/* Value */
float Value_Aim;
float Value_Actual;
float Value_err;
float Value_err_last;
float Value_integral;
float Value_output;
/* Method */
void (*Method_Init)(struct _MATH_PID *module);
void (*Method_Update)(struct _MATH_PID *module,float Aim, float Feedback);
};
typedef struct _MATH_PID MATH_PID;
rt_err_t Module_PID_Config(MATH_PID *module);
#endif
/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/
Math_PID.c
/*
* Copyright (c) 2020 - ~, HIT_HERO Team
*
* PID MATH MODULE SOUCE FILE
* Used in RT-Thread Operate System
*
* Change Logs:
* Date Author Notes Mail
* 2020-08-05 WangXi first version WangXi_chn@foxmail.com
*/
#include "Math_PID.h"
#include <math.h>
static void Module_PIDInit(MATH_PID *module);
static void Module_PIDUpdate(MATH_PID *module,float Aim, float Feedback);
/* Global Method */
rt_err_t Module_PID_Config(MATH_PID *module)
{
if( module->Method_Init ==NULL &&
module->Method_Update ==NULL
){
/* Link the Method */
module->Method_Init = Module_PIDInit;
module->Method_Update = Module_PIDUpdate;
}
else{
rt_kprintf("Warning: Module Motor 3508 is Configed twice\n");
return RT_ERROR;
}
/* Device Init */
module->Method_Init(module);
return RT_EOK;
}
static void Module_PIDInit(MATH_PID *module)
{
module->Value_Actual = 0;
module->Value_Aim = 0;
module->Value_err = 0;
module->Value_err_last = 0;
module->Value_integral = 0;
module->Value_output = 0;
}
static void Module_PIDUpdate(MATH_PID *module,float Aim, float Feedback)
{
float P,I,D;
module->Value_Aim = Aim;
module->Value_Actual = Feedback;
/* Culculate P */
if(module->Value_Aim > module->Property_AimMax)
module->Value_Aim = module->Property_AimMax;
else if(module->Value_Aim < -module->Property_AimMax)
module->Value_Aim = -module->Property_AimMax;
module->Value_err = module->Value_Aim - module->Value_Actual;
P = module->Value_err * module->Property_Kp;
/* Culculate I */
if(fabs(module->Value_err) < module->Property_integralErrMax)
{
module->Value_integral += module->Value_err * module->Property_Ki * module->Property_dt;
if(module->Value_integral > module->Property_integralMax)
module->Value_integral = module->Property_integralMax;
else if(module->Value_integral < -module->Property_integralMax)
module->Value_integral = -module->Property_integralMax;
I = module->Value_integral;
}
/* Culculate D */
D = module->Property_Kd * (module->Value_err - module->Value_err_last) / module->Property_dt;
/* Culculate Output */
module->Value_output = P + I + D;
if(module->Value_output > module->Property_OutputMax)
module->Value_output = module->Property_OutputMax;
else if(module->Value_output < -module->Property_OutputMax)
module->Value_output = -module->Property_OutputMax;
}
/************************ (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_Beep.h"
#include "Module_Key.h"
#include "Module_DjiC610620Group.h"
/* Module param list ---------------------------------------------------------------------------------------- */
MODULE_LED dev_led_state =
{
.pin = GET_PIN(H, 10),
.LED_TIME_CYCLE = 4000,
.LED_TIME_OUTPUT = 200
};
MODULE_DjiC610620GROUP dev_DjiC610620group =
{
.Property_CanDevName = "can1",
.Value_module_DjiC610620[DjiC610620ID_1] =
{
.Enable = 1,
.Mode = DjiC610620MODE_SPEED,
.ENCODER = DjiC610620SELF,
.PID_Speed =
{
.Property_Kp = 6, .Property_Ki = 2, .Property_Kd = 0, .Property_dt = 0.005,
.Property_integralMax = 500, .Property_AimMax = 9000, .Property_OutputMax =16300,
.Property_integralErrMax = 500,
}
},
.Value_module_DjiC610620[DjiC610620ID_2] =
{
.Enable = 1,
.Mode = DjiC610620MODE_SPEED,
.ENCODER = DjiC610620SELF,
.PID_Speed =
{
.Property_Kp = 6, .Property_Ki = 2, .Property_Kd = 0, .Property_dt = 0.005,
.Property_integralMax = 500, .Property_AimMax = 9000, .Property_OutputMax =16300,
.Property_integralErrMax = 500,
}
},
.Value_module_DjiC610620[DjiC610620ID_3] =
{
.Enable = 1,
.Mode = DjiC610620MODE_ANGLE,
.ENCODER = ROTARYENCODER,
.Encoder_DevName = "pulse1",
.PID_Speed =
{
.Property_Kp = 6, .Property_Ki = 1, .Property_Kd = 0, .Property_dt = 0.005,
.Property_integralMax = 2000, .Property_AimMax = 9000, .Property_OutputMax =16300,
.Property_integralErrMax = 1000,
},
.PID_Angle =
{
.Property_Kp = 3, .Property_Ki = 0.5, .Property_Kd = 0, .Property_dt = 0.005,
.Property_integralMax = 2000, .Property_AimMax = 60000, .Property_OutputMax =1000,
.Property_integralErrMax = 10000,
},
},
};
/* 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)
{
/* Communicate with motor group */
dev_DjiC610620group.Method_Feed(&dev_DjiC610620group);
rt_thread_mdelay(1);
//dev_DjiC610620group.Method_Send(&dev_DjiC610620group);
}
}
int main(void)
{
/* Config module ---------------------------------------------------------------------------------------- */
Module_Led_Config(&dev_led_state);
dev_led_state.Set(&dev_led_state,9);
Module_DjiC610620Group_Config(&dev_DjiC610620group);
/* 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 - 6, 20);
if (motor_thread != RT_NULL){
rt_thread_startup(motor_thread);
}
}
/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/