mirror of
https://gitee.com/dlmu-cone/tronone-h7-scaffold
synced 2026-07-23 19:25:09 +08:00
add DM motor drv
This commit is contained in:
274
User_Code/module/motor/can_motors/dm_motor/dm_motor.c
Normal file
274
User_Code/module/motor/can_motors/dm_motor/dm_motor.c
Normal file
@@ -0,0 +1,274 @@
|
||||
#include "dm_motor.h"
|
||||
#include "bsp_log.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "daemon.h"
|
||||
#include "general_def.h"
|
||||
#include "memory.h"
|
||||
#include "motor_def.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "user_lib.h"
|
||||
|
||||
static uint8_t idx;
|
||||
static DMMotorInstance *dm_motor_instance[DM_MOTOR_CNT];
|
||||
static TaskHandle_t dm_task_handle[DM_MOTOR_CNT];
|
||||
/* 两个用于将uint值和float值进行映射的函数,在设定发送值和解析反馈值时使用 */
|
||||
static uint16_t float_to_uint(float x, float x_min, float x_max, uint8_t bits)
|
||||
{
|
||||
float span = x_max - x_min;
|
||||
float offset = x_min;
|
||||
return (uint16_t)((x - offset) * ((float)((1 << bits) - 1)) / span);
|
||||
}
|
||||
static float uint_to_float(int x_int, float x_min, float x_max, int bits)
|
||||
{
|
||||
float span = x_max - x_min;
|
||||
float offset = x_min;
|
||||
return ((float)x_int) * span / ((float)((1 << bits) - 1)) + offset;
|
||||
}
|
||||
|
||||
static void DMMotorSetMode(DMMotor_Mode_e cmd, DMMotorInstance *motor)
|
||||
{
|
||||
memset(motor->motor_can_instance->tx_buff, 0xff, 7); // 发送电机指令的时候前面7bytes都是0xff
|
||||
motor->motor_can_instance->tx_buff[7] = (uint8_t)cmd; // 最后一位是命令id
|
||||
CANTransmit(motor->motor_can_instance, 1);
|
||||
}
|
||||
|
||||
static void DMMotorDecode(FDCANInstance *motor_can)
|
||||
{
|
||||
uint16_t tmp; // 用于暂存解析值,稍后转换成float数据,避免多次创建临时变量
|
||||
uint8_t *rxbuff = motor_can->rx_buff;
|
||||
DMMotorInstance *motor = (DMMotorInstance *)motor_can->id;
|
||||
DM_Motor_Measure_s *measure = &(motor->measure); // 将can实例中保存的id转换成电机实例的指针
|
||||
|
||||
DaemonReload(motor->motor_daemon);
|
||||
|
||||
measure->last_position = measure->position;
|
||||
|
||||
// 区分一控四模式和MIT模式的反馈解析
|
||||
if (motor->ctrl_mode == DM_CTRL_ONE_TO_FOUR)
|
||||
{
|
||||
// 标识符为0x300+电机ID时的解析逻辑
|
||||
// D[0], D[1] 为位置高/低8位,范围0-8191对应一圈位置
|
||||
tmp = (uint16_t)((rxbuff[0] << 8) | rxbuff[1]);
|
||||
measure->position = (float)tmp;
|
||||
|
||||
// D[2], D[3] 为速度高/低8位,单位rpm放大一百倍
|
||||
int16_t vel_tmp = (int16_t)((rxbuff[2] << 8) | rxbuff[3]);
|
||||
measure->velocity = (float)vel_tmp / 100.0f;
|
||||
|
||||
// D[4], D[5] 为扭矩电流高/低8位,单位mA
|
||||
int16_t torq_tmp = (int16_t)((rxbuff[4] << 8) | rxbuff[5]);
|
||||
measure->torque = (float)torq_tmp / 1000.0f;
|
||||
|
||||
// D[6] 为电机线圈温度
|
||||
measure->T_Mos = (float)rxbuff[6];
|
||||
|
||||
// D[7] 为错误状态
|
||||
measure->state = rxbuff[7];
|
||||
}
|
||||
else
|
||||
{
|
||||
// 原MIT反馈解析逻辑
|
||||
tmp = (uint16_t)((rxbuff[1] << 8) | rxbuff[2]);
|
||||
measure->position = uint_to_float(tmp, DM_P_MIN, DM_P_MAX, 16);
|
||||
|
||||
tmp = (uint16_t)((rxbuff[3] << 4) | rxbuff[4] >> 4);
|
||||
measure->velocity = uint_to_float(tmp, DM_V_MIN, DM_V_MAX, 12);
|
||||
|
||||
tmp = (uint16_t)(((rxbuff[4] & 0x0f) << 8) | rxbuff[5]);
|
||||
measure->torque = uint_to_float(tmp, DM_T_MIN, DM_T_MAX, 12);
|
||||
|
||||
measure->T_Mos = (float)rxbuff[6];
|
||||
measure->T_Rotor = (float)rxbuff[7];
|
||||
}
|
||||
}
|
||||
|
||||
static void DMMotorLostCallback(void *motor_ptr)
|
||||
{
|
||||
}
|
||||
void DMMotorCaliEncoder(DMMotorInstance *motor)
|
||||
{
|
||||
DMMotorSetMode(DM_CMD_ZERO_POSITION, motor);
|
||||
DWT_Delay(0.1);
|
||||
}
|
||||
DMMotorInstance *DMMotorInit(Motor_Init_Config_s *config)
|
||||
{
|
||||
DMMotorInstance *motor = (DMMotorInstance *)malloc(sizeof(DMMotorInstance));
|
||||
memset(motor, 0, sizeof(DMMotorInstance));
|
||||
|
||||
// 默认初始化为MIT模式
|
||||
motor->ctrl_mode = DM_CTRL_MIT;
|
||||
|
||||
motor->motor_settings = config->controller_setting_init_config;
|
||||
PIDInit(&motor->current_PID, &config->controller_param_init_config.current_PID);
|
||||
PIDInit(&motor->speed_PID, &config->controller_param_init_config.speed_PID);
|
||||
PIDInit(&motor->angle_PID, &config->controller_param_init_config.angle_PID);
|
||||
motor->other_angle_feedback_ptr = config->controller_param_init_config.other_angle_feedback_ptr;
|
||||
motor->other_speed_feedback_ptr = config->controller_param_init_config.other_speed_feedback_ptr;
|
||||
|
||||
config->fdcan_init_config.can_module_callback = DMMotorDecode;
|
||||
config->fdcan_init_config.id = motor;
|
||||
motor->motor_can_instance = CANRegister(&config->fdcan_init_config);
|
||||
|
||||
Daemon_Init_Config_s conf = {
|
||||
.callback = DMMotorLostCallback,
|
||||
.owner_id = motor,
|
||||
.reload_count = 10,
|
||||
};
|
||||
motor->motor_daemon = DaemonRegister(&conf);
|
||||
|
||||
DMMotorEnable(motor);
|
||||
DMMotorSetMode(DM_CMD_MOTOR_MODE, motor);
|
||||
DWT_Delay(0.1);
|
||||
DMMotorCaliEncoder(motor);
|
||||
DWT_Delay(0.1);
|
||||
dm_motor_instance[idx++] = motor;
|
||||
return motor;
|
||||
}
|
||||
|
||||
void DMMotorSetRef(DMMotorInstance *motor, float ref)
|
||||
{
|
||||
motor->pid_ref = ref;
|
||||
}
|
||||
|
||||
void DMMotorEnable(DMMotorInstance *motor)
|
||||
{
|
||||
motor->stop_flag = MOTOR_ENALBED;
|
||||
}
|
||||
|
||||
void DMMotorStop(DMMotorInstance *motor)//不使用使能模式是因为需要收到反馈
|
||||
{
|
||||
motor->stop_flag = MOTOR_STOP;
|
||||
}
|
||||
|
||||
void DMMotorOuterLoop(DMMotorInstance *motor, Closeloop_Type_e type)
|
||||
{
|
||||
motor->motor_settings.outer_loop_type = type;
|
||||
}
|
||||
|
||||
void DMMotorSetCtrlMode(DMMotorInstance *motor, DMMotor_Ctrl_Mode_e mode)
|
||||
{
|
||||
motor->ctrl_mode = mode;
|
||||
}
|
||||
|
||||
// 一控四下发指令:支持1帧控制4个电机,由外部统一调用,不要在DMMotorTask中高频调用此函数
|
||||
void DMMotorSendOneToFourGroup(FDCANInstance *can_instance, uint8_t group, float i1, float i2, float i3, float i4)
|
||||
{
|
||||
// 根据电机ID组配置对应的报文ID:[1,4]为0x3FE, [5,8]为0x4FE
|
||||
uint32_t tx_id = (group == 1) ? 0x3FE : 0x4FE;
|
||||
|
||||
// 控制电流为标幺值,采用力位混控i_des相同的16位映射机制进行量化
|
||||
uint16_t cur1 = float_to_uint(i1, DM_T_MIN, DM_T_MAX, 16);
|
||||
uint16_t cur2 = float_to_uint(i2, DM_T_MIN, DM_T_MAX, 16);
|
||||
uint16_t cur3 = float_to_uint(i3, DM_T_MIN, DM_T_MAX, 16);
|
||||
uint16_t cur4 = float_to_uint(i4, DM_T_MIN, DM_T_MAX, 16);
|
||||
|
||||
uint32_t old_id = can_instance->tx_id;
|
||||
can_instance->tx_id = tx_id;
|
||||
|
||||
// 数据段填充:先低8位,再高8位
|
||||
can_instance->tx_buff[0] = (uint8_t)(cur1 & 0xFF);
|
||||
can_instance->tx_buff[1] = (uint8_t)(cur1 >> 8);
|
||||
can_instance->tx_buff[2] = (uint8_t)(cur2 & 0xFF);
|
||||
can_instance->tx_buff[3] = (uint8_t)(cur2 >> 8);
|
||||
can_instance->tx_buff[4] = (uint8_t)(cur3 & 0xFF);
|
||||
can_instance->tx_buff[5] = (uint8_t)(cur3 >> 8);
|
||||
can_instance->tx_buff[6] = (uint8_t)(cur4 & 0xFF);
|
||||
can_instance->tx_buff[7] = (uint8_t)(cur4 >> 8);
|
||||
|
||||
CANTransmit(can_instance, 1);
|
||||
|
||||
can_instance->tx_id = old_id; // 恢复旧有配置
|
||||
}
|
||||
|
||||
// 一控四模式下特殊清零指令
|
||||
void DMMotorSetZeroOneToFour(FDCANInstance *can_instance, uint16_t target_can_id)
|
||||
{
|
||||
uint32_t old_id = can_instance->tx_id;
|
||||
can_instance->tx_id = 0x7FF; // 零点设置特殊指令报文ID
|
||||
|
||||
// 依序填入CANID和固定魔法字
|
||||
can_instance->tx_buff[0] = (uint8_t)(target_can_id & 0xFF);
|
||||
can_instance->tx_buff[1] = (uint8_t)(target_can_id >> 8);
|
||||
can_instance->tx_buff[2] = 0x55;
|
||||
can_instance->tx_buff[3] = 0x50;
|
||||
can_instance->tx_buff[4] = 0x00;
|
||||
can_instance->tx_buff[5] = 0x00;
|
||||
can_instance->tx_buff[6] = 0x00;
|
||||
can_instance->tx_buff[7] = 0x00;
|
||||
|
||||
CANTransmit(can_instance, 1);
|
||||
can_instance->tx_id = old_id;
|
||||
}
|
||||
|
||||
|
||||
//@Todo: MIT模式目前只实现了力控,更多位控PID等请自行添加
|
||||
void DMMotorTask(void *argument)
|
||||
{
|
||||
float pid_ref, set;
|
||||
DMMotorInstance *motor = (DMMotorInstance *)argument;
|
||||
Motor_Control_Setting_s *setting = &motor->motor_settings;
|
||||
DMMotor_Send_s motor_send_mailbox;
|
||||
while (1)
|
||||
{
|
||||
// 若当前实例设为了一控四模式,不应由单独的电机Task发送报文
|
||||
// 需要在用户外部的任务里定期调用 DMMotorSendOneToFourGroup()
|
||||
if (motor->ctrl_mode == DM_CTRL_ONE_TO_FOUR)
|
||||
{
|
||||
osDelay(2);
|
||||
continue;
|
||||
}
|
||||
|
||||
pid_ref = motor->pid_ref;
|
||||
|
||||
set = pid_ref;
|
||||
if (setting->motor_reverse_flag == MOTOR_DIRECTION_REVERSE)
|
||||
set *= -1;
|
||||
|
||||
LIMIT_MIN_MAX(set, DM_T_MIN, DM_T_MAX);
|
||||
motor_send_mailbox.position_des = float_to_uint(0, DM_P_MIN, DM_P_MAX, 16);
|
||||
motor_send_mailbox.velocity_des = float_to_uint(0, DM_V_MIN, DM_V_MAX, 12);
|
||||
motor_send_mailbox.torque_des = float_to_uint(pid_ref, DM_T_MIN, DM_T_MAX, 12);
|
||||
motor_send_mailbox.Kp = 0;
|
||||
motor_send_mailbox.Kd = 0;
|
||||
|
||||
if(motor->stop_flag == MOTOR_STOP)
|
||||
motor_send_mailbox.torque_des = float_to_uint(0, DM_T_MIN, DM_T_MAX, 12);
|
||||
|
||||
motor->motor_can_instance->tx_buff[0] = (uint8_t)(motor_send_mailbox.position_des >> 8);
|
||||
motor->motor_can_instance->tx_buff[1] = (uint8_t)(motor_send_mailbox.position_des);
|
||||
motor->motor_can_instance->tx_buff[2] = (uint8_t)(motor_send_mailbox.velocity_des >> 4);
|
||||
motor->motor_can_instance->tx_buff[3] = (uint8_t)(((motor_send_mailbox.velocity_des & 0xF) << 4) | (motor_send_mailbox.Kp >> 8));
|
||||
motor->motor_can_instance->tx_buff[4] = (uint8_t)(motor_send_mailbox.Kp);
|
||||
motor->motor_can_instance->tx_buff[5] = (uint8_t)(motor_send_mailbox.Kd >> 4);
|
||||
motor->motor_can_instance->tx_buff[6] = (uint8_t)(((motor_send_mailbox.Kd & 0xF) << 4) | (motor_send_mailbox.torque_des >> 8));
|
||||
motor->motor_can_instance->tx_buff[7] = (uint8_t)(motor_send_mailbox.torque_des);
|
||||
|
||||
CANTransmit(motor->motor_can_instance, 1);
|
||||
|
||||
osDelay(2);
|
||||
}
|
||||
}
|
||||
|
||||
void DMMotorControlInit()
|
||||
{
|
||||
// 遍历所有电机实例,创建任务
|
||||
if (!idx)
|
||||
return;
|
||||
|
||||
// 注意:CMSIS-RTOS V2的osThreadDef不支持动态生成的名称
|
||||
// 我们需要为每个电机创建独立的线程定义或使用不同的方法
|
||||
|
||||
// 方案1:使用循环和预定义的线程定义(如果电机数量固定)
|
||||
// 这里改为直接使用FreeRTOS原生API创建线程,更加灵活可靠
|
||||
|
||||
for (size_t i = 0; i < idx; i++)
|
||||
{
|
||||
// 使用FreeRTOS原生API创建线程
|
||||
// 参数:线程函数、线程名称、堆栈大小、参数、优先级、线程句柄
|
||||
if (xTaskCreate(DMMotorTask, "DMMotorTask", 128, dm_motor_instance[i], osPriorityNormal, &dm_task_handle[i]) != pdPASS)
|
||||
{
|
||||
LOGERROR("[DM_Motor] Failed to create motor thread for motor %d", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
96
User_Code/module/motor/can_motors/dm_motor/dm_motor.h
Normal file
96
User_Code/module/motor/can_motors/dm_motor/dm_motor.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef DM_MOTOR_H
|
||||
#define DM_MOTOR_H
|
||||
#include <stdint.h>
|
||||
#include "bsp_fdcan.h"
|
||||
#include "pid.h"
|
||||
#include "motor_def.h"
|
||||
#include "daemon.h"
|
||||
|
||||
#define DM_MOTOR_CNT 4
|
||||
|
||||
#define DM_P_MIN (-12.5f)
|
||||
#define DM_P_MAX 12.5f
|
||||
#define DM_V_MIN (-45.0f)
|
||||
#define DM_V_MAX 45.0f
|
||||
#define DM_T_MIN (-18.0f)
|
||||
#define DM_T_MAX 18.0f
|
||||
|
||||
// 新增:电机控制模式枚举
|
||||
typedef enum {
|
||||
DM_CTRL_MIT = 0,//MIT模式,默认的单电机控制模式
|
||||
DM_CTRL_ONE_TO_FOUR = 1, // 一控四模式
|
||||
} DMMotor_Ctrl_Mode_e;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t id;
|
||||
uint8_t state;
|
||||
float velocity;
|
||||
float last_position;
|
||||
float position;
|
||||
float torque;
|
||||
float T_Mos;
|
||||
float T_Rotor;
|
||||
int32_t total_round;
|
||||
}DM_Motor_Measure_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t position_des;
|
||||
uint16_t velocity_des;
|
||||
uint16_t torque_des;
|
||||
uint16_t Kp;
|
||||
uint16_t Kd;
|
||||
}DMMotor_Send_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DM_Motor_Measure_s measure;
|
||||
Motor_Control_Setting_s motor_settings;
|
||||
PIDInstance current_PID;
|
||||
PIDInstance speed_PID;
|
||||
PIDInstance angle_PID;
|
||||
float *other_angle_feedback_ptr;
|
||||
float *other_speed_feedback_ptr;
|
||||
float *speed_feedforward_ptr;
|
||||
float *current_feedforward_ptr;
|
||||
float pid_ref;
|
||||
Motor_Working_Type_e stop_flag;
|
||||
FDCANInstance *motor_can_instance;
|
||||
Daemon_Instance* motor_daemon;
|
||||
uint32_t lost_cnt;
|
||||
|
||||
// 新增:当前电机的控制模式标志
|
||||
DMMotor_Ctrl_Mode_e ctrl_mode;
|
||||
}DMMotorInstance;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DM_CMD_MOTOR_MODE = 0xfc, // 使能,会响应指令
|
||||
DM_CMD_RESET_MODE = 0xfd, // 停止
|
||||
DM_CMD_ZERO_POSITION = 0xfe, // 将当前的位置设置为编码器零位
|
||||
DM_CMD_CLEAR_ERROR = 0xfb // 清除电机过热错误
|
||||
}DMMotor_Mode_e;
|
||||
|
||||
DMMotorInstance *DMMotorInit(Motor_Init_Config_s *config);
|
||||
|
||||
void DMMotorSetRef(DMMotorInstance *motor, float ref);
|
||||
|
||||
void DMMotorOuterLoop(DMMotorInstance *motor,Closeloop_Type_e closeloop_type);
|
||||
|
||||
void DMMotorEnable(DMMotorInstance *motor);
|
||||
|
||||
void DMMotorStop(DMMotorInstance *motor);
|
||||
void DMMotorCaliEncoder(DMMotorInstance *motor);
|
||||
void DMMotorControlInit();
|
||||
|
||||
// 新增:设置电机控制模式
|
||||
void DMMotorSetCtrlMode(DMMotorInstance *motor, DMMotor_Ctrl_Mode_e mode);
|
||||
|
||||
// 新增:一控四模式下,单控制帧发送4个电机的电流
|
||||
void DMMotorSendOneToFourGroup(FDCANInstance *can_instance, uint8_t group, float i1, float i2, float i3, float i4);
|
||||
|
||||
// 新增:一控四模式下的零点设置
|
||||
void DMMotorSetZeroOneToFour(FDCANInstance *can_instance, uint16_t target_can_id);
|
||||
|
||||
#endif // !DMMOTOR
|
||||
@@ -1 +1,94 @@
|
||||
# 达妙电机
|
||||
# 达妙电机
|
||||
|
||||
在 `gimbal.c` 中进行达妙电机的初始化配置并使用“一拖四”模式发送指令,你可以按照以下结构来组织代码。
|
||||
|
||||
### 1. 初始化配置与指定ID
|
||||
|
||||
在初始化阶段,你需要先配置好 `Motor_Init_Config_s`,并通过刚才新增的 `DMMotorSetCtrlMode` 函数将实例切换为一拖四模式。电机的 **ID** 是在 `can_init_config.tx_id` 中指定的。
|
||||
|
||||
```c
|
||||
#include "DMmotor.h"
|
||||
|
||||
// 1. 定义电机配置结构体 (以ID为1的电机为例)
|
||||
static Motor_Init_Config_s gimbal_dm_config_id1 = {
|
||||
.can_init_config = {
|
||||
.can_handle = &hcan1, // 指定使用的CAN外设句柄,例如 hcan1 或 hcan2
|
||||
.tx_id = 0x01, // 【指定电机ID】这里填入电机的实际ID,如 1
|
||||
},
|
||||
.controller_param_init_config = {
|
||||
// 虽然一控四主要是直接下发电流,但为了结构完整性或外环计算,可配置相关PID
|
||||
.current_PID = {
|
||||
.Kp = 6.0f,
|
||||
.Ki = 0.0f,
|
||||
.Kd = 0.495f,
|
||||
.MaxOut = 45.0f,
|
||||
},
|
||||
},
|
||||
.controller_setting_init_config = {
|
||||
.motor_reverse_flag = MOTOR_DIRECTION_NORMAL,
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 声明电机实例指针
|
||||
DMMotorInstance *gimbal_motor_1;
|
||||
// 如果同一条总线上有另外三个电机,你需要分别为它们声明实例并配置 tx_id = 2, 3, 4
|
||||
|
||||
void Gimbal_Init(void)
|
||||
{
|
||||
// 3. 调用Init函数完成底层初始化与实例分配
|
||||
gimbal_motor_1 = DMMotorInit(&gimbal_dm_config_id1);
|
||||
|
||||
// 4. 【关键步骤】将该电机控制模式切换为一控四模式
|
||||
DMMotorSetCtrlMode(gimbal_motor_1, DM_CTRL_ONE_TO_FOUR);
|
||||
|
||||
// (同理,对ID为2、3、4的电机执行相同的Init和SetCtrlMode操作)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 2. 使用一拖四模式发送指令
|
||||
|
||||
在控制任务(例如 FreeRTOS 的 `Gimbal_Task`)中,你不再需要让每个电机单独发送报文,而是通过刚才新增的 `DMMotorSendOneToFourGroup` 函数**统一打包下发**。
|
||||
|
||||
```c
|
||||
void Gimbal_Task(void const * argument)
|
||||
{
|
||||
// 假设通过你的控制器(如LQR或MPC等)计算得出了4个电机的目标电流
|
||||
// 单位与你设定DM_T_MIN、DM_T_MAX的量纲一致
|
||||
float target_i1 = 1.5f;
|
||||
float target_i2 = -0.5f;
|
||||
float target_i3 = 2.0f;
|
||||
float target_i4 = 0.0f;
|
||||
|
||||
while(1)
|
||||
{
|
||||
// ... (各种控制算法计算过程) ...
|
||||
|
||||
// 调用一控四发送函数打包下发控制帧
|
||||
// 参数1: can_instance -> 传入挂载在该CAN总线上的任一电机实例的CAN指针即可
|
||||
// 参数2: group -> 1 表示控制电机 ID[1~4] (对应报文 0x3FE)
|
||||
// 2 表示控制电机 ID[5~8] (对应报文 0x4FE)
|
||||
// 参数3~6: 分别对应这4个电机的电流值
|
||||
DMMotorSendOneToFourGroup(gimbal_motor_1->motor_can_instace, 1,
|
||||
target_i1, target_i2, target_i3, target_i4);
|
||||
|
||||
osDelay(2);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 3. 一拖四模式下的零点设置 (附加)
|
||||
|
||||
如果你在调试时需要将某台电机当前的位置设置为编码器零位,可以调用对应的零点校准函数,传入目标电机的ID:
|
||||
|
||||
```c
|
||||
void Gimbal_Set_Zero(void)
|
||||
{
|
||||
// 将总线上 ID = 1 的电机当前位置设为零点
|
||||
DMMotorSetZeroOneToFour(gimbal_motor_1->motor_can_instace, 0x01);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
按照这种方式组织 `gimbal.c`,底层的CAN发送与接收解析就会被彻底隔离开,既保证了多电机联合控制的同步性,又能极大节省 CAN 总线的带宽。
|
||||
Reference in New Issue
Block a user