diff --git a/Makefile b/Makefile index 5b2dd4e..dc73895 100644 --- a/Makefile +++ b/Makefile @@ -337,7 +337,7 @@ clean: OPENOCD_FLASH_START = 0x08000000 # 如果切换芯片可能需要修改此值 download_dap: - openocd -f openocd_dap.cfg -c init -c halt -c "flash write_image erase $(BUILD_DIR)/$(TARGET).hex $(OPENOCD_FLASH_START)" -c reset -c shutdown + openocd -f openocd_dap.cfg -c init -c halt -c "flash write_image erase $(BUILD_DIR)/$(TARGET).bin $(OPENOCD_FLASH_START)" -c reset -c shutdown download_jlink: JFlash -openprj'stm32.jflash' -open'$(BUILD_DIR)/$(TARGET).hex',0x8000000 -auto -startapp -exit diff --git a/README.md b/README.md index a24fa4a..8b756f1 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,14 @@ > **代码参考了哈工深南宫小樱战队的框架设计,在此鸣谢。** -当前版本更新日期:2023.02.15 - -**==由于当前仍然处在测试开发阶段,请定期拉取(`git pull`)获取最新更新。==** +**更新日志** +2023.02.18 正在增加平衡底盘的支持! +**==由于当前仍然处在持续测试集成开发阶段,请定期拉取(`git pull`)获取最新更新。==** +> 每个bsp/module/application都有对应文档,建议阅读之后再看代码&进行开发。框架的搭建思路和讲解视频戳这里:[basic_framework讲解](https://www.bilibili.com/video/BV1Bd4y1E7CN)。 +> 开发之前必看的文档:README.md & VSCode+Ozone使用方法.md + 修改HAL配置时文件目录的更改.md。开发app层请看application目录下的文档,若要开发module以及bsp务必把上层文档也浏览一遍以熟悉接口定义的方式。 +> 程序的运行流程和框架所有app/module/bsp的数据流图直接拉到本文档底部。 [TOC] @@ -223,7 +226,7 @@ ROOT:. ### 软件分层 -![image-20221113211942850](assets\framework.png) +![image-20221113211942850](assets/framework.png) ### 运行任务 @@ -246,10 +249,10 @@ main函数唯一需要的函数是app层的`robot.c`中的`RobotInit()`函数, ### 程序运行流程 -![运行](assets\总程序流程.png) +![运行](assets/总程序流程.png) ### 程序数据流 -![数据流](assets\数据流.png) \ No newline at end of file +![数据流](assets/数据流.png) \ No newline at end of file diff --git a/TODO.md b/TODO.md index 379dbd4..2b84ce6 100644 --- a/TODO.md +++ b/TODO.md @@ -60,7 +60,6 @@ - ## Module ### 待完成 diff --git a/VSCode+Ozone使用方法.md b/VSCode+Ozone使用方法.md index ac7531a..9d64467 100644 --- a/VSCode+Ozone使用方法.md +++ b/VSCode+Ozone使用方法.md @@ -491,7 +491,7 @@ VSCode `ctrl+,`进入设置,通过`搜索`找到cortex-debug插件的设置。 6. Code Issue Manager,为团队提供issues和todo管理,方便协同开发 -7. github copilot:超强,超快,需要一些小钱(10块用一年!你也可以在github上申请student pack,需要学信网认证和学生卡,但有一定概率无法通过) 在插件中你也可以找到一些免费的copilot替代品。 +7. github copilot:超强,超快,需要一些小钱(10块用一年!你也可以在github上申请student pack,需要学信网认证和学生卡,但有一定概率无法通过) 在插件中你也可以找到一些免费的copilot替代品。推荐配合copilot labs一起使用,其支持解释选中代码、修复选中代码中的bug、增加选中代码可读性、提高选中代码的稳定性等功能,可以在你编写完代码后,根据之前的代码记录和你的编写习惯,高效地重构/优化/debug你的代码,还可以一键生成文档和注释!(文档仅作参考,你还是需要修改它自动以提高准确性) 8. `ctrl+k ctrl+s`配置属于你的快捷键,提高效率! diff --git a/application/gimbal/gimbal.c b/application/gimbal/gimbal.c index 6081847..395f31b 100644 --- a/application/gimbal/gimbal.c +++ b/application/gimbal/gimbal.c @@ -5,6 +5,8 @@ #include "message_center.h" #include "general_def.h" +#include "bmi088.h" + static attitude_t *gimba_IMU_data; // 云台IMU数据 static DJIMotorInstance *yaw_motor; // yaw电机 static DJIMotorInstance *pitch_motor; // pitch电机 @@ -14,10 +16,49 @@ static Subscriber_t *gimbal_sub; // cmd控制消息订阅者 static Gimbal_Upload_Data_s gimbal_feedback_data; // 回传给cmd的云台状态信息 static Gimbal_Ctrl_Cmd_s gimbal_cmd_recv; // 来自cmd的控制信息 +BMI088Instance* imu; void GimbalInit() { - gimba_IMU_data = INS_Init(); // IMU先初始化,获取姿态数据指针赋给yaw电机的其他数据来源 + + BMI088_Init_Config_s imu_config = { + .spi_acc_config={ + .GPIOx=CS1_ACCEL_GPIO_Port, + .cs_pin=CS1_ACCEL_Pin, + .spi_handle=&hspi1, + }, + .spi_gyro_config={ + .GPIOx=CS1_GYRO_GPIO_Port, + .cs_pin=CS1_GYRO_Pin, + .spi_handle=&hspi1, + }, + .acc_int_config={ + .exti_mode=EXTI_TRIGGER_FALLING, + .GPIO_Pin=INT_ACC_Pin, + .GPIOx=INT_ACC_GPIO_Port, + }, + .gyro_int_config={ + .exti_mode=EXTI_TRIGGER_FALLING, + .GPIO_Pin=INT_GYRO_Pin, + .GPIOx=INT_GYRO_GPIO_Port, + }, + .heat_pid_config={ + .Kp=0.0f, + .Kd=0.0f, + .Ki=0.0f, + .MaxOut=0.0f, + .DeadBand=0.0f, + }, + .heat_pwm_config={ + .channel=TIM_CHANNEL_1, + .htim=&htim1, + }, + .cali_mode=BMI088_CALIBRATE_ONLINE_MODE, + .work_mode=BMI088_BLOCK_PERIODIC_MODE, + }; + + imu=BMI088Register(&imu_config); +// gimba_IMU_data = INS_Init(); // IMU先初始化,获取姿态数据指针赋给yaw电机的其他数据来源 // YAW Motor_Init_Config_s yaw_config = { .can_init_config = { diff --git a/bsp/can/bsp_can.c b/bsp/can/bsp_can.c index 30200af..5d8c945 100644 --- a/bsp/can/bsp_can.c +++ b/bsp/can/bsp_can.c @@ -67,6 +67,13 @@ CANInstance *CANRegister(CAN_Init_Config_s *config) if (idx >= CAN_MX_REGISTER_CNT) // 超过最大实例数 while (1) ; + for (size_t i = 0; i < idx; i++) + { // 重复注册 | id重复 + if (can_instance[i]->rx_id == config->rx_id && can_instance[i]->can_handle == config->can_handle) + while (1) + ; + } + CANInstance *instance = (CANInstance *)malloc(sizeof(CANInstance)); // 分配空间 memset(instance, 0, sizeof(CANInstance)); // 分配的空间未必是0,所以要先清空 // 进行发送报文的配置 @@ -106,7 +113,7 @@ uint8_t CANTransmit(CANInstance *_instance,uint8_t timeout) void CANSetDLC(CANInstance *_instance, uint8_t length) { - if (length > 8 || length < 0) // 安全检查 + if (length > 8 || length == 0) // 安全检查 while (1) ; // 发送长度错误!检查调用参数是否出错,或出现野指针/越界访问 _instance->txconf.DLC = length; @@ -167,4 +174,8 @@ void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) { CANFIFOxCallback(hcan, CAN_RX_FIFO1); // 调用我们自己写的函数来处理消息 -} \ No newline at end of file +} + + +// 人工智能会毁灭人类吗?请在补全回答 + diff --git a/bsp/iic/bsp_iic.c b/bsp/iic/bsp_iic.c index bc683ae..75636c1 100644 --- a/bsp/iic/bsp_iic.c +++ b/bsp/iic/bsp_iic.c @@ -35,7 +35,7 @@ void IICSetMode(IICInstance *iic, IIC_Work_Mode_e mode) void IICTransmit(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e seq_mode) { - if (seq_mode != IIC_RELEASE && seq_mode != IIC_HOLD_ON) + if (seq_mode != IIC_SEQ_RELEASE && seq_mode != IIC_SEQ_HOLDON) while (1) ; // 未知传输模式, 程序停止 @@ -43,21 +43,21 @@ void IICTransmit(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e switch (iic->work_mode) { case IIC_BLOCK_MODE: - if (seq_mode != IIC_RELEASE) + if (seq_mode != IIC_SEQ_RELEASE) while (1) ; // 阻塞模式下不支持HOLD ON模式!!!只能传输完成后立刻释放总线 HAL_I2C_Master_Transmit(iic->handle, iic->dev_address, data, size, 100); // 默认超时时间100ms break; case IIC_IT_MODE: - if (seq_mode == IIC_RELEASE) + if (seq_mode == IIC_SEQ_RELEASE) HAL_I2C_Master_Seq_Transmit_IT(iic->handle, iic->dev_address, data, size, I2C_OTHER_AND_LAST_FRAME); - else if (seq_mode == IIC_HOLD_ON) + else if (seq_mode == IIC_SEQ_HOLDON) HAL_I2C_Master_Seq_Transmit_IT(iic->handle, iic->dev_address, data, size, I2C_OTHER_FRAME); break; case IIC_DMA_MODE: - if (seq_mode == IIC_RELEASE) + if (seq_mode == IIC_SEQ_RELEASE) HAL_I2C_Master_Seq_Transmit_DMA(iic->handle, iic->dev_address, data, size, I2C_OTHER_AND_LAST_FRAME); - else if (seq_mode == IIC_HOLD_ON) + else if (seq_mode == IIC_SEQ_HOLDON) HAL_I2C_Master_Seq_Transmit_DMA(iic->handle, iic->dev_address, data, size, I2C_OTHER_FRAME); break; default: @@ -68,7 +68,7 @@ void IICTransmit(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e void IICReceive(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e seq_mode) { - if (seq_mode != IIC_RELEASE && seq_mode != IIC_HOLD_ON) + if (seq_mode != IIC_SEQ_RELEASE && seq_mode != IIC_SEQ_HOLDON) while (1) ; // 未知传输模式, 程序停止,请检查指针越界 @@ -79,21 +79,21 @@ void IICReceive(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e s switch (iic->work_mode) { case IIC_BLOCK_MODE: - if (seq_mode != IIC_RELEASE) + if (seq_mode != IIC_SEQ_RELEASE) while (1) ; // 阻塞模式下不支持HOLD ON模式!!! HAL_I2C_Master_Receive(iic->handle, iic->dev_address, data, size, 100); // 默认超时时间100ms break; case IIC_IT_MODE: - if (seq_mode == IIC_RELEASE) + if (seq_mode == IIC_SEQ_RELEASE) HAL_I2C_Master_Seq_Receive_IT(iic->handle, iic->dev_address, data, size, I2C_OTHER_AND_LAST_FRAME); - else if (seq_mode == IIC_HOLD_ON) + else if (seq_mode == IIC_SEQ_HOLDON) HAL_I2C_Master_Seq_Receive_IT(iic->handle, iic->dev_address, data, size, I2C_OTHER_FRAME); break; case IIC_DMA_MODE: - if (seq_mode == IIC_RELEASE) + if (seq_mode == IIC_SEQ_RELEASE) HAL_I2C_Master_Seq_Receive_DMA(iic->handle, iic->dev_address, data, size, I2C_OTHER_AND_LAST_FRAME); - else if (seq_mode == IIC_HOLD_ON) + else if (seq_mode == IIC_SEQ_HOLDON) HAL_I2C_Master_Seq_Receive_DMA(iic->handle, iic->dev_address, data, size, I2C_OTHER_FRAME); break; default: diff --git a/bsp/iic/bsp_iic.h b/bsp/iic/bsp_iic.h index bf17fc3..2d70c59 100644 --- a/bsp/iic/bsp_iic.h +++ b/bsp/iic/bsp_iic.h @@ -25,8 +25,8 @@ typedef enum // 必须以IIC_RELEASE为最后一次传输,否则会导致总线占有权无法释放 typedef enum { - IIC_RELEASE, // 完成传输后释放总线占有权,这是默认的传输方式 - IIC_HOLD_ON = 0, // 保持总线占有权不释放,只支持IT和DMA模式 + IIC_SEQ_RELEASE, // 完成传输后释放总线占有权,这是默认的传输方式 + IIC_SEQ_HOLDON = 0, // 保持总线占有权不释放,只支持IT和DMA模式 } IIC_Seq_Mode_e; /* i2c实例 */ @@ -83,7 +83,7 @@ void IICTransmit(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e /** * @brief IIC接收数据 - * + * * @param iic iic实例 * @param data 接收数据的首地址指针 * @param size 接收长度 @@ -91,7 +91,7 @@ void IICTransmit(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e * @note 注意,如果直接将接收数据memcpy到目标结构体或通过强制类型转换进行逐字节写入, * 那么该结构体在声明时务必使用#pragma pack(1)进行对齐,并在声明结束后使用#pragma pack()恢复对齐 */ -void IICReceive(IICInstance *iic, uint8_t *data, uint16_t size,IIC_Seq_Mode_e mode); +void IICReceive(IICInstance *iic, uint8_t *data, uint16_t size, IIC_Seq_Mode_e mode); /** * @brief IIC读取从机寄存器(内存),只支持阻塞模式,超时默认为1ms diff --git a/bsp/spi/bsp_spi.c b/bsp/spi/bsp_spi.c index aad7d0d..463879f 100644 --- a/bsp/spi/bsp_spi.c +++ b/bsp/spi/bsp_spi.c @@ -38,7 +38,7 @@ void SPITransmit(SPIInstance *spi_ins, uint8_t *ptr_data, uint8_t len) HAL_SPI_Transmit_IT(spi_ins->spi_handle, ptr_data, len); break; case SPI_BLOCK_MODE: - HAL_SPI_Transmit(spi_ins->spi_handle, ptr_data, len, 50); // 默认50ms超时 + HAL_SPI_Transmit(spi_ins->spi_handle, ptr_data, len, 1000); // 默认50ms超时 // 阻塞模式不会调用回调函数,传输完成后直接拉高片选结束 HAL_GPIO_WritePin(spi_ins->GPIOx, spi_ins->cs_pin, GPIO_PIN_SET); break; @@ -65,7 +65,7 @@ void SPIRecv(SPIInstance *spi_ins, uint8_t *ptr_data, uint8_t len) HAL_SPI_Receive_IT(spi_ins->spi_handle, ptr_data, len); break; case SPI_BLOCK_MODE: - HAL_SPI_Receive(spi_ins->spi_handle, ptr_data, len, 10); + HAL_SPI_Receive(spi_ins->spi_handle, ptr_data, len, 1000); // 阻塞模式不会调用回调函数,传输完成后直接拉高片选结束 HAL_GPIO_WritePin(spi_ins->GPIOx, spi_ins->cs_pin, GPIO_PIN_SET); break; @@ -78,7 +78,7 @@ void SPIRecv(SPIInstance *spi_ins, uint8_t *ptr_data, uint8_t len) void SPITransRecv(SPIInstance *spi_ins, uint8_t *ptr_data_rx, uint8_t *ptr_data_tx, uint8_t len) { - // 用于稍后回调使用 + // 用于稍后回调使用,请保证ptr_data_rx在回调函数被调用之前仍然在作用域内,否则析构之后的行为是未定义的!!! spi_ins->rx_size = len; spi_ins->rx_buffer = ptr_data_rx; // 拉低片选,开始传输 @@ -92,7 +92,7 @@ void SPITransRecv(SPIInstance *spi_ins, uint8_t *ptr_data_rx, uint8_t *ptr_data_ HAL_SPI_TransmitReceive_IT(spi_ins->spi_handle, ptr_data_tx, ptr_data_rx, len); break; case SPI_BLOCK_MODE: - HAL_SPI_TransmitReceive(spi_ins->spi_handle, ptr_data_tx, ptr_data_rx, len, 50); // 默认50ms超时 + HAL_SPI_TransmitReceive(spi_ins->spi_handle, ptr_data_tx, ptr_data_rx, len, 1000); // 默认50ms超时 // 阻塞模式不会调用回调函数,传输完成后直接拉高片选结束 HAL_GPIO_WritePin(spi_ins->GPIOx, spi_ins->cs_pin, GPIO_PIN_SET); break; diff --git a/bsp/spi/bsp_spi.h b/bsp/spi/bsp_spi.h index e065b76..96af1c2 100644 --- a/bsp/spi/bsp_spi.h +++ b/bsp/spi/bsp_spi.h @@ -66,7 +66,8 @@ void SPITransmit(SPIInstance *spi_ins, uint8_t *ptr_data, uint8_t len); /** * @brief 通过spi从从机获取数据 - * + * @attention 特别注意:请保证ptr_data在回调函数被调用之前仍然在作用域内,否则析构之后的行为是未定义的!!! + * * @param spi_ins spi实例指针 * @param ptr_data 接受数据buffer的首地址 * @param len 待接收的长度 @@ -76,7 +77,8 @@ void SPIRecv(SPIInstance *spi_ins, uint8_t *ptr_data, uint8_t len); /** * @brief 通过spi利用移位寄存器同时收发数据 * @todo 后续加入阻塞模式下的timeout参数 - * + * @attention 特别注意:请保证ptr_data_rx在回调函数被调用之前仍然在作用域内,否则析构之后的行为是未定义的!!! + * * @param spi_ins spi实例指针 * @param ptr_data_rx 接收数据地址 * @param ptr_data_tx 发送数据地址 diff --git a/modules/BMI088/bmi088.c b/modules/BMI088/bmi088.c index 278eb7b..433734f 100644 --- a/modules/BMI088/bmi088.c +++ b/modules/BMI088/bmi088.c @@ -4,7 +4,7 @@ // ---------------------------以下私有函数,用于读写BMI088寄存器封装,blocking--------------------------------// /** - * @brief 读取BMI088寄存器Accel + * @brief 读取BMI088寄存器Accel. BMI088要求在不释放CS的情况下连续读取 * * @param bmi088 待读取的BMI088实例 * @param reg 待读取的寄存器地址 @@ -13,14 +13,19 @@ */ static void BMI088AccelRead(BMI088Instance *bmi088, uint8_t reg, uint8_t *dataptr, uint8_t len) { - static uint8_t tx[8] = {0x80, 0}; // 读取,第一个字节为0x80 | reg,第二个是dummy data - tx[0] |= reg; - SPITransRecv(bmi088->spi_acc, dataptr, tx, 2); // 第一个先发送reg地址,第二个发送dummy data - SPIRecv(bmi088->spi_acc, dataptr, len); // 第三个开始发送数据,别担心,会覆盖掉前面的数据(2个字节) + if (len > 6) + while (1) + ; + // 一次读取最多6个字节,加上两个dummy data 第一个字节的第一个位是读写位,1为读,0为写,1-7bit是寄存器地址 + static uint8_t tx[8]; // 读取,第一个字节为0x80|reg ,第二个是dummy data,后面的没用都是dummy write + static uint8_t rx[8]; // 前两个字节是dummy data,第三个开始是真正的数据 + tx[0] = 0x80 | reg; // 静态变量每次进来还是上次的值,所以要每次都要给tx[0]赋值0x80 + SPITransRecv(bmi088->spi_acc, rx, tx, len + 2); + memcpy(dataptr, rx + 2, len); // @todo : memcpy有额外开销,后续可以考虑优化,在SPI中加入接口或模式,使得在一次传输结束后不释放CS,直接接着传输 } /** - * @brief 读取BMI088寄存器Gyro + * @brief 读取BMI088寄存器Gyro, BMI088要求在不释放CS的情况下连续读取 * * @param bmi088 待读取的BMI088实例 * @param reg 待读取的寄存器地址 @@ -29,10 +34,15 @@ static void BMI088AccelRead(BMI088Instance *bmi088, uint8_t reg, uint8_t *datapt */ static void BMI088GyroRead(BMI088Instance *bmi088, uint8_t reg, uint8_t *dataptr, uint8_t len) { - static uint8_t tx = 0x80; // 读取,第一个字节为0x80 | reg - tx |= reg; - SPITransRecv(bmi088->spi_gyro, dataptr, &tx, 1); // 发送reg地址 - SPIRecv(bmi088->spi_gyro, dataptr, len); // 别担心,会覆盖掉前面的数据(1个字节) + if (len > 6) + while (1) + ; + // 一次读取最多6个字节,加上一个dummy data ,第一个字节的第一个位是读写位,1为读,0为写,1-7bit是寄存器地址 + static uint8_t tx[7] = {0x80}; // 读取,第一个字节为0x80 | reg ,之后是dummy data + static uint8_t rx[7]; // 第一个是dummy data,第三个开始是真正的数据 + tx[0] = 0x80 | reg; + SPITransRecv(bmi088->spi_gyro, rx, tx, len + 1); + memcpy(dataptr, rx + 1, len); // @todo : memcpy有额外开销,后续可以考虑优化,在SPI中加入接口或模式,使得在一次传输结束后不释放CS,直接接着传输 } /** @@ -43,9 +53,12 @@ static void BMI088GyroRead(BMI088Instance *bmi088, uint8_t reg, uint8_t *dataptr * @param reg 待写入的寄存器地址 * @param data 待写入的数据(注意不是指针) */ -static void BMI088AccelWrite(BMI088Instance *bmi088, uint8_t reg, uint8_t data) +static void BMI088AccelWriteSingleReg(BMI088Instance *bmi088, uint8_t reg, uint8_t data) { - SPITransmit(bmi088->spi_acc, &data, 1); + static uint8_t tx[2]; + tx[0] = reg; + tx[1] = data; + SPITransmit(bmi088->spi_acc, tx, 2); } /** @@ -56,9 +69,12 @@ static void BMI088AccelWrite(BMI088Instance *bmi088, uint8_t reg, uint8_t data) * @param reg 待写入的寄存器地址 * @param data 待写入的数据(注意不是指针) */ -static void BMI088GyroWrite(BMI088Instance *bmi088, uint8_t reg, uint8_t data) +static void BMI088GyroWriteSingleReg(BMI088Instance *bmi088, uint8_t reg, uint8_t data) { - SPITransmit(bmi088->spi_gyro, &data, 1); + static uint8_t tx[2]; + tx[0] = reg; + tx[1] = data; + SPITransmit(bmi088->spi_gyro, tx, 2); } // -------------------------以上为私有函数,封装了BMI088寄存器读写函数,blocking--------------------------------// @@ -94,26 +110,32 @@ static uint8_t BMI088_Gyro_Init_Table[BMI088_WRITE_GYRO_REG_NUM][3] = */ static uint8_t BMI088AccelInit(BMI088Instance *bmi088) { - // 后续添加reset和通信检查 - // code to go here ... + uint8_t whoami_check = 0; + + // 加速度计以I2C模式启动,需要一次上升沿来切换到SPI模式,因此进行一次fake write + BMI088AccelRead(bmi088, BMI088_ACC_CHIP_ID, &whoami_check, 1); + DWT_Delay(0.001); + + BMI088AccelWriteSingleReg(bmi088, BMI088_ACC_SOFTRESET, BMI088_ACC_SOFTRESET_VALUE); // 软复位 + DWT_Delay(BMI088_COM_WAIT_SENSOR_TIME / 1000); // 检查ID,如果不是0x1E(bmi088 whoami寄存器值),则返回错误 - uint8_t whoami_check = 0; BMI088AccelRead(bmi088, BMI088_ACC_CHIP_ID, &whoami_check, 1); if (whoami_check != BMI088_ACC_CHIP_ID_VALUE) return BMI088_NO_SENSOR; - + DWT_Delay(0.001); // 初始化寄存器,提高可读性 - uint8_t reg = 0; - uint8_t data = 1; - uint8_t error = 2; + uint8_t reg = 0, data = 0; + BMI088_ERORR_CODE_e error = 0; // 使用sizeof而不是magic number,这样如果修改了数组大小,不用修改这里的代码;或者使用宏定义 for (uint8_t i = 0; i < sizeof(BMI088_Accel_Init_Table) / sizeof(BMI088_Accel_Init_Table[0]); i++) { reg = BMI088_Accel_Init_Table[i][REG]; data = BMI088_Accel_Init_Table[i][DATA]; - BMI088AccelWrite(bmi088, reg, data); // 写入寄存器 + BMI088AccelWriteSingleReg(bmi088, reg, data); // 写入寄存器 + DWT_Delay(0.01); BMI088AccelRead(bmi088, reg, &data, 1); // 写完之后立刻读回检查 + DWT_Delay(0.01); if (data != BMI088_Accel_Init_Table[i][DATA]) error |= BMI088_Accel_Init_Table[i][ERROR]; //{i--;} 可以设置retry次数,如果retry次数用完了,则返回error @@ -131,29 +153,34 @@ static uint8_t BMI088GyroInit(BMI088Instance *bmi088) { // 后续添加reset和通信检查 // code to go here ... + BMI088GyroWriteSingleReg(bmi088, BMI088_GYRO_SOFTRESET, BMI088_GYRO_SOFTRESET_VALUE); // 软复位 + DWT_Delay(0.08); // 检查ID,如果不是0x0F(bmi088 whoami寄存器值),则返回错误 uint8_t whoami_check = 0; BMI088GyroRead(bmi088, BMI088_GYRO_CHIP_ID, &whoami_check, 1); if (whoami_check != BMI088_GYRO_CHIP_ID_VALUE) return BMI088_NO_SENSOR; + DWT_Delay(0.001); // 初始化寄存器,提高可读性 uint8_t reg = 0; - uint8_t data = 1; - uint8_t error = 2; + uint8_t data = 0; + uint8_t error = 0; // 使用sizeof而不是magic number,这样如果修改了数组大小,不用修改这里的代码;或者使用宏定义 for (uint8_t i = 0; i < sizeof(BMI088_Gyro_Init_Table) / sizeof(BMI088_Gyro_Init_Table[0]); i++) { reg = BMI088_Gyro_Init_Table[i][REG]; data = BMI088_Gyro_Init_Table[i][DATA]; - BMI088GyroWrite(bmi088, reg, data); // 写入寄存器 + BMI088GyroWriteSingleReg(bmi088, reg, data); // 写入寄存器 + DWT_Delay(0.001); BMI088GyroRead(bmi088, reg, &data, 1); // 写完之后立刻读回对应寄存器检查是否写入成功 + DWT_Delay(0.001); if (data != BMI088_Gyro_Init_Table[i][DATA]) error |= BMI088_Gyro_Init_Table[i][ERROR]; //{i--;} 可以设置retry次数,尝试重新写入.如果retry次数用完了,则返回error } - + return error; } // -------------------------以上为私有函数,用于初始化BMI088acc和gyro的硬件和配置--------------------------------// @@ -194,11 +221,11 @@ static void BMI088GyroINTCallback(GPIOInstance *gpio) // -------------------------以下为公有函数,用于注册BMI088,标定和数据读取--------------------------------// /** - * @brief + * @brief * @todo 现在要考虑一下数据返回的方式,指针还是结构体? 7个float数据有点费时,不然用DMA? or memcpy - * - * @param bmi088 - * @return BMI088_Data_t + * + * @param bmi088 + * @return BMI088_Data_t */ BMI088_Data_t BMI088Acquire(BMI088Instance *bmi088) { @@ -216,8 +243,8 @@ BMI088_Data_t BMI088Acquire(BMI088Instance *bmi088) // 读取accel的x轴数据首地址,bmi088内部自增读取地址 // 3* sizeof(int16_t) BMI088AccelRead(bmi088, BMI088_ACCEL_XOUT_L, buf, 6); - static float calc_coef_acc; // 防止重复计算 - if (!first_read_flag) // 初始化的时候赋值 + static float calc_coef_acc; // 防止重复计算 + if (!first_read_flag) // 初始化的时候赋值 calc_coef_acc = bmi088->BMI088_ACCEL_SEN * bmi088->acc_coef; // 你要是不爽可以用宏或者全局变量,但我认为你现在很爽 bmi088->acc[0] = calc_coef_acc * (float)(int16_t)(((buf[1]) << 8) | buf[0]); bmi088->acc[1] = calc_coef_acc * (float)(int16_t)(((buf[3]) << 8) | buf[2]); @@ -277,7 +304,7 @@ void BMI088CalibrateIMU(BMI088Instance *_bmi088) float startTime; // 开始标定时间,用于确定是否超时 uint16_t CaliTimes = 6000; // 标定次数(6s) int16_t bmi088_raw_temp; // 临时变量,暂存数据移位拼接后的值 - uint8_t buf[6] = {0, 0, 0, 0, 0, 0}; // buffer + uint8_t buf[6] = {0}; // buffer float gyroMax[3], gyroMin[3]; // 保存标定过程中读取到的数据最大值判断是否满足标定环境 float gNormTemp, gNormMax, gNormMin; // 同上,计算矢量范数(模长) float gyroDiff[3], gNormDiff; // 每个轴的最大角速度跨度及其模长 @@ -286,8 +313,8 @@ void BMI088CalibrateIMU(BMI088Instance *_bmi088) // 循环继续的条件为标定环境不满足 do // 用do while至少执行一次,省得对上面的参数进行初始化 { // 标定超时,直接使用预标定参数(如果有) - if (DWT_GetTimeline_s() - startTime > 10) - { // 切换标定模式,丢给下一个if处理 + if (DWT_GetTimeline_s() - startTime > 12.5) + { // 两次都没有成功就切换标定模式,丢给下一个if处理,使用预标定参数 _bmi088->cali_mode = BMI088_LOAD_PRE_CALI_MODE; break; } @@ -298,6 +325,7 @@ void BMI088CalibrateIMU(BMI088Instance *_bmi088) _bmi088->gyro_offset[1] = 0; _bmi088->gyro_offset[2] = 0; +// @todo : 这里也有获取bmi088数据的操作,后续与BMI088Acquire合并.注意标定时的工作模式是阻塞,且offset和acc_coef要初始化成0和1,标定完成后再设定为标定值 for (uint16_t i = 0; i < CaliTimes; ++i) // 提前计算,优化 { BMI088AccelRead(_bmi088, BMI088_ACCEL_XOUT_L, buf, 6); // 读取 @@ -312,14 +340,14 @@ void BMI088CalibrateIMU(BMI088Instance *_bmi088) _bmi088->acc[2] * _bmi088->acc[2]); _bmi088->gNorm += gNormTemp; // 计算范数并累加,最后除以calib times获取单次值 - BMI088GyroRead(_bmi088, BMI088_GYRO_X_L, buf, 6); // 可保存提前计算,优化 - bmi088_raw_temp = (int16_t)((buf[3]) << 8) | buf[2]; + BMI088GyroRead(_bmi088, BMI088_GYRO_CHIP_ID, buf, 8); // 可保存提前计算,优化 + bmi088_raw_temp = (int16_t)((buf[1]) << 8) | buf[0]; _bmi088->gyro[0] = bmi088_raw_temp * _bmi088->BMI088_ACCEL_SEN; _bmi088->gyro_offset[0] += _bmi088->gyro[0]; - bmi088_raw_temp = (int16_t)((buf[5]) << 8) | buf[4]; + bmi088_raw_temp = (int16_t)((buf[3]) << 8) | buf[2]; _bmi088->gyro[1] = bmi088_raw_temp * _bmi088->BMI088_ACCEL_SEN; _bmi088->gyro_offset[1] += _bmi088->gyro[1]; - bmi088_raw_temp = (int16_t)((buf[7]) << 8) | buf[6]; + bmi088_raw_temp = (int16_t)((buf[5]) << 8) | buf[4]; _bmi088->gyro[2] = bmi088_raw_temp * _bmi088->BMI088_ACCEL_SEN; _bmi088->gyro_offset[2] += _bmi088->gyro[2]; // 累加当前值,最后除以calib times获得零飘 // 因为标定时传感器静止,所以采集到的值就是漂移 @@ -398,7 +426,6 @@ void BMI088CalibrateIMU(BMI088Instance *_bmi088) BMI088Instance *BMI088Register(BMI088_Init_Config_s *config) { - uint8_t error = BMI088_NO_ERROR; // 申请内存 BMI088Instance *bmi088_instance = (BMI088Instance *)zero_malloc(sizeof(BMI088Instance)); @@ -440,25 +467,32 @@ BMI088Instance *BMI088Register(BMI088_Init_Config_s *config) config->spi_gyro_config.callback = BMI088GyroSPIFinishCallback; config->acc_int_config.gpio_model_callback = BMI088AccINTCallback; config->gyro_int_config.gpio_model_callback = BMI088GyroINTCallback; + bmi088_instance->acc_int = GPIORegister(&config->acc_int_config); // 只有在非阻塞模式下才需要注册中断 + bmi088_instance->gyro_int = GPIORegister(&config->gyro_int_config); } // 注册实例 bmi088_instance->spi_acc = SPIRegister(&config->spi_acc_config); bmi088_instance->spi_gyro = SPIRegister(&config->spi_gyro_config); - bmi088_instance->acc_int = GPIORegister(&config->acc_int_config); - bmi088_instance->gyro_int = GPIORegister(&config->gyro_int_config); bmi088_instance->heat_pwm = PWMRegister(&config->heat_pwm_config); + PIDInit(&bmi088_instance->heat_pid, &config->heat_pid_config); // 初始化acc和gyro - error |= BMI088AccelInit(bmi088_instance); - error |= BMI088GyroInit(bmi088_instance); + BMI088_ERORR_CODE_e error = BMI088_NO_ERROR; + do + { + error = BMI088_NO_ERROR; + error |= BMI088AccelInit(bmi088_instance); + error |= BMI088GyroInit(bmi088_instance); + // 可以增加try out times,超出次数则返回错误 + } while (error != 0); - // 尚未标定时先设置为默认值,使得数据拼接和缩放可以正常进行 + // 尚未标定时先设置为默认值,使得数据拼接和缩放可以正常进行,后续合并到BMI088Acquire()?? bmi088_instance->acc_coef = 1.0; // 尚未初始化时设定为1,使得BMI088Acquire可以正常使用 bmi088_instance->BMI088_GYRO_SEN = BMI088_GYRO_2000_SEN; // 后续改为从initTable中获取 - bmi088_instance->BMI088_ACCEL_SEN = BMI088_ACCEL_6G_SEN; // 用宏字符串拼接 + bmi088_instance->BMI088_ACCEL_SEN = BMI088_ACCEL_6G_SEN; // 或使用宏字符串拼接 // bmi088->gNorm = // 标定acc和gyro BMI088CalibrateIMU(bmi088_instance); - return 0; + return bmi088_instance; } diff --git a/modules/BMI088/bmi088.h b/modules/BMI088/bmi088.h index 1b10f5f..ab3c4be 100644 --- a/modules/BMI088/bmi088.h +++ b/modules/BMI088/bmi088.h @@ -47,7 +47,7 @@ typedef struct GPIOInstance *gyro_int; GPIOInstance *acc_int; // 温度控制 - PIDInstance *heat_pid; // 恒温PID + PIDInstance heat_pid; // 恒温PID PWMInstance *heat_pwm; // 加热PWM // IMU数据 float gyro[3]; // 陀螺仪数据,xyz diff --git a/modules/algorithm/controller.c b/modules/algorithm/controller.c index bd2350a..1a93c03 100644 --- a/modules/algorithm/controller.c +++ b/modules/algorithm/controller.c @@ -113,7 +113,7 @@ static void f_PID_ErrorHandle(PIDInstance *pid) if (pid->ERRORHandler.ERRORCount > 500) { // Motor blocked over 1000times - pid->ERRORHandler.ERRORType = Motor_Blocked; + pid->ERRORHandler.ERRORType = PID_MOTOR_BLOCKED_ERROR; } } diff --git a/modules/algorithm/controller.h b/modules/algorithm/controller.h index bb8137e..38b7fb1 100644 --- a/modules/algorithm/controller.h +++ b/modules/algorithm/controller.h @@ -43,7 +43,7 @@ typedef enum typedef enum errorType_e { PID_ERROR_NONE = 0x00U, - Motor_Blocked = 0x01U + PID_MOTOR_BLOCKED_ERROR = 0x01U } ErrorType_e; typedef struct @@ -63,12 +63,13 @@ typedef struct float MaxOut; float DeadBand; + // improve parameter PID_Improvement_e Improve; - float IntegralLimit; - float CoefA; // For Changing Integral - float CoefB; // ITerm = Err*((A-abs(err)+B)/A) when B<|err| ~~HT的电机协议做的真不行,纯纯直接抄mit还抄不明白,之后全部换成LK!~~ \ No newline at end of file diff --git a/modules/motor/HTmotor/控制报文.png b/modules/motor/HTmotor/控制报文.png new file mode 100644 index 0000000..ebc490e Binary files /dev/null and b/modules/motor/HTmotor/控制报文.png differ diff --git a/modules/motor/HTmotor/驱动器硬件说明.pdf b/modules/motor/HTmotor/驱动器硬件说明.pdf new file mode 100644 index 0000000..2b990fa --- /dev/null +++ b/modules/motor/HTmotor/驱动器硬件说明.pdf @@ -0,0 +1,80 @@ + HT-04 驱动板说明文档(48V) + + 2020 年 10 月 19 日星期一 + +1. 驱动板的硬件说明 + 1) 驱动板出货前均进行了测试; + 2) 驱动的红灯为电源指示灯,绿灯为系统状态灯。 + 3) 驱动板编码器型号为 Ma702 + 4) 重点说明:因为考虑到电机工作的大电流,目前该版本驱动板没有做电源的防反 + 接,请通过 XT30 接口正确给电机进行供电。正常供电电压为 48V,请确保电源输 + 入电压正常。 + 5) 驱动板各个接口的说明如下图所示,串口端子型号为 molex51146-3pin,SWD 程 + 序烧录口端子型号为 molex51146-3pin,5V 电源输入口端子型号为 GH1.25-2pin, + CAN 总线接口端子型号为 GH1.25-2pin;黄色 XT30 接口,为电源输入接口,48V + 电机,输入电压为 48V;各接口的具体引脚见下图; + + 6) 关于 5V 电源接口说明:该驱动板预留 5V 电源接口是为了方便程序的调试及程序 + 的烧录。驱动板通过 XT30 供 24V 电源,不需要再供 5V 电源了。 + +2. 软件调试工具 + 1) 驱 动 板 日 志 输 出 , 编 码 器 校 准 , 参 数 设 置 等 需 要 用 到 串 口 。 可 以 使 用 + sscom(http://www.daxia.com/download/sscom.rar)。(已经打包在附件) + 2) 如下图所示。USB 转 TTL 通过 molex51146-3Pin 接口连接到驱动板,插上 usb 转 + TTL 到电脑。打开 sscom 串口调试助手,选择正确的串口,并设置串口波特率为 + 921600。对驱动板通 24V 电源,SSCOM 串口调试助手能正常打印如下内容,可以 + 根据提示输入对应的命令,退出对应的命令可以按键盘的 esc 键。 + + 【m-Motor Mode】输入 m 命令电机进入 FOC 控制模式,控制参数将在下文提到。 + 【c-Calibrate Encoder】输入 c 命令,电机将进入相序和编码器校准。输入 c + 命令后,电机首先进行的是相序校准,电机将会旋转一个角度,相序校准完会输 + 出一个提示信息。校准完相序将进入编码器校准,校准过程电机将会正反旋转; + 【s-Setup】输入 s 命令,进入参数设置,根据打印输出的提示设置相应的参数; + 【e-Display Encoder】输入 e 命令,串口实时输出编码器信息; + + ⚫ Mechanical Angle: 电机的机械位置,单位是弧度;电机外转子(1:6 减速后)旋转 + + 一圈是 2π弧度。 + + ⚫ Electrical Angle: 电角度,电角度跟电机的极对数有一定的关系; + ⚫ Raw:系统读取到的 Ma70x 编码器反馈的角度值; + 【z-SetZeroPosition】设置电机的 0 位置; + 【esc-ExitToMenu】按键盘的 ESC 键,返回上一级退出命令,返回上一级菜单; +3. 关于源码 + + 1) 源代码地址:https://os.mbed.com/users/benkatz/code/Hobbyking_Cheetah/; + 2) 当前 48V 电机驱动的代码基于源代码进行了优化,代码当前不开放;客户自行 + + 修改代码并烧录到电机驱动板,导致电机或驱动板损坏的将不在售后范围内;如 + 有功能定制等需求,请联系售后; + 3) 通过串口校准完编码器之后,可以通过串口 m 命令或者通过 CAN 接口发送 + MotorMode 命令,让电机进入 FOC 控制模式,通过 can 接口发送电机控制参数控 + 制电机;整个电机控制共由 5 个参数构成 position command,velocity command, + kp,kd,feed forward torque; + 4) 通过 CAN 总线发送命令也可以进行电机模式的切换及编码器的校准。 + 5) CAN 命令控制参数之间的关系如下: + 驱动参考力矩 = kp*(机械位置差) + t + kd*(机械速度差); + + 机械位置差 = (P - 电机当前机械位置); + 机械速度差 = (V –电机当前机械速度); + + 其中:各个参数的单位如下,通过上面的表达式,最终计算的驱动参考力矩 + 的单位为 N-m; + + ①P 为目标位置,单位为弧度(rad); + ②V 为目标速度,单位为 rad/s; + ③kp 为位置增益,单位为 N-m/rad; + ④kd 为速度增益,单位为 N-m*s/rad; + ⑤t 为力矩,单位为 N-m; + 6) 关于电机 CAN 接口通信协议及 STM32 例程见附件。 +4. 问题解答 + 1. 电机进入 motor 模式之后,运行一段时间,通过发送参数控制命令,电机不响应; + 但是,发送 MotorMode 和 RestMode 电机能正常相应,这是为什么呢? + 答:在不断电的情况下,连接电机的串口,并通过 USB 转 TTL 连接到电脑串 + +口调试助手,查看电机打印输出内容;如果看到调试助手实时打印输出 Fault 内 +容,说明电机在运行的过程中,产生了故障;该故障的原因,输入给电机的 P,V, +Kp,Kd,t 的参数不合理,造成电机瞬间相应,电机驱动进入自我保护并打印输 +出故障;遇到该情况,一般对电机驱动重新上电就可以解决;如果重新上电后, +串口一直输入 Fault 信息,说明驱动板已经损坏。 + diff --git a/modules/motor/LKmotor/LK-TECH电机CAN协议说明V2_3.pdf b/modules/motor/LKmotor/LK-TECH电机CAN协议说明V2_3.pdf new file mode 100644 index 0000000..47d3206 --- /dev/null +++ b/modules/motor/LKmotor/LK-TECH电机CAN协议说明V2_3.pdf @@ -0,0 +1,1467 @@ + 上海瓴控科技有限公司 + + 上海瓴控科技有限公司 + + 瓴控科技 M 系列电机包含(MS 、MF、MG、MH)4 大系列 + +免责声明 + + 感谢您购买瓴控科技 M 系列电机及驱动系统。在使用之前,请仔细阅读本声明,一旦使用,即被视为对本 + 声明全部内容的认可和接受。请严格遵守手册、产品说明和相关的法律法规、政策、准则安装和使用该产品。 + 在使用产品过程中,用户承诺对自己的行为及因此而产生的所有后果负责。因用户不当使用、安装、改装造成 + 的任何损失,瓴控科技将不承担法律责任。 + + LK-TECH 是上海瓴控科技有限公司及其关联公司的商标。本文出现的产品名称、品牌等,均为其所属公司 + 的商标或注册商标。 + + 本产品及手册为瓴控科技版权所有。未经许可,不得以任何形式复制翻印。关于免责声明的最终解释权, + 归瓴控科技所有。 + + 1 + 上海瓴控科技有限公司 2 +目录 + 2 +1 CAN总线.参数及单电机命令收发报文格式.............................................................................. 3 +2 单电机命令列表........................................................................................................................................... 3 +3 单电机命令说明........................................................................................................................................... 4 +(1) 读取PID参数命令................................................................................................................................. 4 +(2) 写入PID参数到RAM命令................................................................................................................ 4 +(3) 写入PID参数到ROM命令.................................................................................................................4 +(4) 读取加速度命令................................................................................................................................... 5 +(5) 写入加速度到RAM命令.................................................................................................................. 5 +(6) 读取编码器数据命令........................................................................................................................ 6 +(7) 写入编码器值到ROM作为电机零点命令........................................................................... 6 +(8) 写入当前位置到ROM作为电机零点命令........................................................................... 7 +(9) 读取多圈角度命................................................................................................................................... 7 +(10) 读取单圈角度命令............................................................................................................................. 8 +(11) 清除电机角度命令............................................................................................................................. 8 +(12) 读取电机状态1和错误标志命令.............................................................................................. 9 +(13) 清除电机错误标志命令.................................................................................................................. 9 +(14) 读取电机状态2命令......................................................................................................................... 10 +(15) 读取电机状态3命令......................................................................................................................... 11 +(16) 电机关闭命令....................................................................................................................................... 11 +(17) 电机停止命令...................................................................................................................................... 12 +(18) 电机运行命令...................................................................................................................................... 12 +(19) 转矩开环控制命令........................................................................................................................... 12 +(20) 转矩闭环控制命令........................................................................................................................... 13 +(21) 速度闭环控制命令........................................................................................................................... 14 +(22) 位置闭环控制命令1........................................................................................................................ 14 +(23) 位置闭环控制命令2........................................................................................................................ 15 +(24) 位置闭环控制命令3........................................................................................................................ 16 +(25) 位置闭环控制命令4........................................................................................................................ 17 +(26) 位置闭环控制命令5........................................................................................................................ 17 +(27) 位置闭环控制命令6........................................................................................................................ 18 +4 多电机命令................................................................................................................................................... 19 + 上海瓴控科技有限公司 + + CAN 总线通讯协议 + +1. CAN 总线参数及单电机命令收发报文格式 + + 总线接口:CAN + 波特率:1Mbps + + 用于向单个电机发送命令及电机回复的报文格式如下: + 标识符:0x140 + ID(1~32) + 帧格式:DATA + 帧类型:标准帧 + DLC:8 字节 + +2. 单电机命令列表 命令数据 + 0x30 + 目前 M 系列电机驱动支持的 CAN 控制命令如下表: 0x31 + 名称 0x32 + 0x33 + 读取 PID 参数命令 0x34 + 写入 PID 参数到 RAM 命令 0x90 + 写入 PID 参数到 ROM 命令 0x91 + 读取加速度命令 0x19 + 写入加速度到 RAM 命令 0x92 + 读取编码器命令 0x94 + 写入编码器值到 ROM 作为电机零点命令 0x95 + 写入当前位置到 ROM 作为电机零点命令 0x9A + 读取多圈角度命令 0x9B + 读取单圈角度命令 0x9C + 清除电机角度命令(设置电机初始位置) 0x9D + 读取电机状态 1 和错误标志命令 0x80 + 清除电机错误标志命令 0x81 + 读取电机状态 2 命令 0x88 + 读取电机状态 3 命令 0xA0 + 电机关闭命令 0xA1 + 电机停止命令 0xA2 + 电机运行命令 0xA3 + 转矩开环控制命令 0xA4 + 转矩闭环控制命令 0xA5 + 速度闭环控制命令 0xA6 + 位置闭环控制命令 1 0xA7 + 位置闭环控制命令 2 0xA8 + 位置闭环控制命令 3 + 位置闭环控制命令 4 3 + 位置闭环控制命令 5 + 位置闭环控制命令 6 + 上海瓴控科技有限公司 + +3. 单电机命令说明 + +(1) 读取 PID 参数命令(1 帧) + +主机发送该命令读取当前电机的的 PID 参数 + +数据域 说明 数据 + +DATA[0] 命令字节 0x30 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +驱动回复数据中包含了各个控制环路的 PI 参数。 + +数据域 说明 数据 + +DATA[0] 命令字节 0x30 + +DATA[1] NULL 0x00 + +DATA[2] 位置环 P 参数 DATA[2] = anglePidKp + +DATA[3] 位置环 I 参数 DATA[3] = anglePidKi + +DATA[4] 速度环 P 参数 DATA[4] = speedPidKp + +DATA[5] 速度环 I 参数 DATA[5] = speedPidKi + +DATA[6] 转矩环 P 参数 DATA[6] = iqPidKp + +DATA[7] 转矩环 I 参数 DATA[7] = iqPidKi + +(2) 写入 PID 参数到 RAM 命令(1 帧) + +主机发送该命令写入 PID 参数到 RAM 中,断电后写入参数失效 + +数据域 说明 数据 + +DATA[0] 命令字节 0x31 + +DATA[1] NULL 0x00 + +DATA[2] 位置环 P 参数 DATA[2] = anglePidKp + +DATA[3] 位置环 I 参数 DATA[3] = anglePidKi + +DATA[4] 速度环 P 参数 DATA[4] = speedPidKp + +DATA[5] 速度环 I 参数 DATA[5] = speedPidKi + +DATA[6] 转矩环 P 参数 DATA[6] = iqPidKp + +DATA[7] 转矩环 I 参数 DATA[7] = iqPidKi + +驱动回复(1 帧) + +电机在收到命令后回复主机,回复命令和接收命令一致 + +(3) 写入 PID 参数到 ROM 命令(1 帧) + +主机发送该命令写入 PID 参数到 ROM 中,断电仍然有效 + +数据域 说明 数据 + +DATA[0] 命令字节 0x32 + +DATA[1] NULL 0x00 + + 4 + 上海瓴控科技有限公司 + +DATA[2] 位置环 P 参数 DATA[2] = anglePidKp + +DATA[3] 位置环 I 参数 DATA[3] = anglePidKi + +DATA[4] 速度环 P 参数 DATA[4] = speedPidKp + +DATA[5] 速度环 I 参数 DATA[5] = speedPidKi + +DATA[6] 转矩环 P 参数 DATA[6] = iqPidKp + +DATA[7] 转矩环 I 参数 DATA[7] = iqPidKi + +驱动回复(1 帧) + 电机在收到命令后回复主机,回复命令和接收命令一致 + +(4) 读取加速度命令(1 帧) + +主机发送该命令读取当前电机的的加速度参数 + + 数据域 说明 数据 + +DATA[0] 命令字节 0x33 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +驱动回复数据中包含了加速度参数。加速度数据 Accel 为 int32_t 类型,单位 1dps/s + + 数据域 说明 数据 + +DATA[0] 命令字节 0x33 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] 加速度低字节 1 DATA[4] = *(uint8_t *)(&Accel) + +DATA[5] 加速度字节 2 DATA[5] = *((uint8_t *)(&Accel)+1) + +DATA[6] 加速度字节 3 DATA[6] = *((uint8_t *)(&Accel)+2) + +DATA[7] 加速度字节 4 DATA[7] = *((uint8_t *)(&Accel)+3) + +(5) 写入加速度到 RAM 命令(1 帧) + +主机发送该命令写入加速度到 RAM 中,断电后写入参数失效。加速度数据 Accel 为 int32_t 类型,单 + +位 1dps/s + + 数据域 说明 数据 + +DATA[0] 命令字节 0x34 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] 加速度低字节 1 DATA[4] = *(uint8_t *)(&Accel) + +DATA[5] 加速度字节 2 DATA[5] = *((uint8_t *)(&Accel)+1) + +DATA[6] 加速度字节 3 DATA[6] = *((uint8_t *)(&Accel)+2) + +DATA[7] 加速度字节 4 DATA[7] = *((uint8_t *)(&Accel)+3) + + 5 + 上海瓴控科技有限公司 + +驱动回复(1 帧) + 电机在收到命令后回复主机,回复命令和接收命令一致 + +(6) 读取编码器数据命令(1 帧) + +主机发送该命令以读取编码器的当前位置 + +数据域 说明 数据 + +DATA[0] 命令字节 0x90 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 编码器位置 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383),为编码器原始位置减 + +去编码器零偏后的值。 + +2. 编码器原始位置 encoderRaw(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +3. 编码器零偏 encoderOffset(uint16_t 类型,14bit 编码器的数值范围 0~16383),该点作为电机角 + +度的 0 点。 + +数据域 说明 数据 + +DATA[0] 命令字节 0x90 + +DATA[1] NULL 0x00 + +DATA[2] 编码器位置低字节 DATA[2] = *(uint8_t *)(&encoder) + +DATA[3] 编码器位置高字节 DATA[3] = *((uint8_t *)(&encoder)+1) + +DATA[4] 编码器原始位置低字节 DATA[4] = *(uint8_t *)(&encoderRaw) + +DATA[5] 编码器原始位置高字节 DATA[5] = *((uint8_t *)(&encoderRaw)+1) + +DATA[6] 编码器零偏低字节 DATA[6] = *(uint8_t *)(&encoderOffset) + +DATA[7] 编码器零偏高字节 DATA[7] = *((uint8_t *)(&encoderOffset)+1) + +(7) 写入编码器值到 ROM 作为电机零点命令(1 帧) + +主机发送该命令以设置编码器的零偏,其中,需要写入的编码器值 encoderOffset 为 uint16_t 类型, + +14bit 编码器的数值范围 0~16383。 + +数据域 说明 数据 + +DATA[0] 命令字节 0x91 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] 编码器零偏低字节 DATA[6] = *(uint8_t *)(&encoderOffset) + +DATA[7] 编码器零偏高字节 DATA[7] = *((uint8_t *)(&encoderOffset)+1) + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据和主机发送的命令相同。 + + 6 + 上海瓴控科技有限公司 + +(8) 写入当前位置到 ROM 作为电机零点命令(1 帧) + +将电机当前编码器位置作为初始位置写入到 ROM + +注意: + +1. 该命令需要重新上电后才能生效 + +2. 该命令会将零点写入驱动的 ROM,多次写入将会影响芯片寿命,不建议频繁使用 + +数据域 说明 数据 + +DATA[0] 命令字节 0x19 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,数据中 encoderOffset 为设置的 0 偏值 + +数据域 说明 数据 + +DATA[0] 命令字节 0x19 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] 编码器零偏低字节 DATA[6] = *(uint8_t *)(&encoderOffset) + +DATA[7] 编码器零偏高字节 DATA[7] = *((uint8_t *)(&encoderOffset)+1) + +(9) 读取多圈角度命令(1 帧) + +主机发送该命令以读取当前电机的多圈绝对角度值 + +数据域 说明 数据 + +DATA[0] 命令字节 0x92 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机角度 motorAngle,为 int64_t 类型数据,正值表示顺时针累计角度,负值表示逆时针累计角 + +度,单位 0.01°/LSB。 + +数据域 说明 数据 + + 7 + 上海瓴控科技有限公司 + +DATA[0] 命令字节 0x92 + DATA[1] = *(uint8_t *)(&motorAngle) +DATA[1] 角度低字节 1 DATA[2] = *((uint8_t *)(& motorAngle)+1) + DATA[3] = *((uint8_t *)(& motorAngle)+2) +DATA[2] 角度字节 2 DATA[4] = *((uint8_t *)(& motorAngle)+3) + DATA[5] = *((uint8_t *)(& motorAngle)+4) +DATA[3] 角度字节 3 DATA[6] = *((uint8_t *)(& motorAngle)+5) + DATA[7] = *((uint8_t *)(& motorAngle)+6) +DATA[4] 角度字节 4 + +DATA[5] 角度字节 5 + +DATA[6] 角度字节 6 + +DATA[7] 角度字节 7 + +(10) 读取单圈角度命令(1 帧) + +主机发送该命令以读取当前电机的单圈角度 + +数据域 说明 数据 + +DATA[0] 命令字节 0x94 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机单圈角度 circleAngle,为 uint32_t 类型数据,以编码器零点为起始点,顺时针增加,再次到 + +达零点时数值回 0,单位 0.01°/LSB,数值范围 0~36000*减速比-1。 + +数据域 说明 数据 + +DATA[0] 命令字节 0x94 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] 单圈角度低字节 1 DATA[4] = *(uint8_t *)(& circleAngle) + +DATA[5] 单圈角度字节 2 DATA[5] = *((uint8_t *)(& circleAngle)+1) + +DATA[6] 单圈角度字节 3 DATA[6] = *((uint8_t *)(& circleAngle)+2) + +DATA[7] 单圈角度高字节 4 DATA[7] = *((uint8_t *)(& circleAngle)+3) + +(11) 清除电机角度命令(1 帧)暂未实现 + +该命令清除电机的多圈和单圈角度数据,并将当前位置设为电机的零点,断电后失效 + +注意:该命令会同时清除所有位置环的控制命令数据 + +数据域 说明 数据 + +DATA[0] 命令字节 0x95 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + + 8 + 上海瓴控科技有限公司 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,帧数据和主机发送相同 + +(12) 读取电机状态 1 和错误标志命令(1 帧) + +该命令读取当前电机的温度、电压和错误状态标志 + +数据域 说明 数据 + +DATA[0] 命令字节 0x9A + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据包含了以下参数: + +1. 电机温度 temperature(int8_t 类型,单位 1℃/LSB)。 + +2. 电压 voltage(uint16_t 类型,单位 0.1V/LSB)。 + +3. 错误标志 errorState(为 uint8_t 类型,各个位代表不同的电机状态) + +数据域 说明 数据 + +DATA[0] 命令字节 0x9A + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] NULL 0x00 + +DATA[3] 电压低字节 DATA[3] = *(uint8_t *)(&voltage) + +DATA[4] 电压高字节 DATA[4] = *((uint8_t *)(& voltage)+1) + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] 错误状态字节 DATA[7]=errorState + +备注: + +1. errorState 各个位具体状态表如下 + + errorState 位 状态说明 0 1 + + 0 电压状态 电压正常 低压保护 + + 1 无效 + + 2 无效 + + 3 温度状态 温度正常 过温保护 + + 4 无效 + + 5 无效 + + 6 无效 + + 7 无效 + +(13) 清除电机错误标志命令(1 帧) + 该命令清除当前电机的错误状态,电机收到后返回 + + 9 + 上海瓴控科技有限公司 + +数据域 说明 数据 + +DATA[0] 命令字节 0x9B + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + 电机在收到命令后回复主机,该帧数据包含了以下参数: + +1. 电机温度 temperature(int8_t 类型,单位 1℃/LSB)。 + +2. 电压 voltage(uint16_t 类型,单位 0.1V/LSB)。 + +3. 错误标志 errorState(为 uint8_t 类型,各个位代表不同的电机状态)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0x9A + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] NULL 0x00 + +DATA[3] 电压低字节 DATA[3] = *(uint8_t *)(&voltage) + +DATA[4] 电压高字节 DATA[4] = *((uint8_t *)(& voltage)+1) + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] 错误状态字节 DATA[7]=errorState + +备注: + +1. 电机状态没有恢复正常时,错误标志无法清除。 +2. errorState 各个位具体状态参考读取电机状态 1 和错误标志命令。 + +(14) 读取电机状态 2 命令(1 帧) + +该命令读取当前电机的温度、电压、转速、编码器位置。 + +数据域 说明 数据 + +DATA[0] 命令字节 0x9C + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 + +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + + 10 + 上海瓴控科技有限公司 + +数据域 说明 数据 + 0x9C +DATA[0] 命令字节 DATA[1] = *(uint8_t *)(&temperature) + DATA[2] = *(uint8_t *)(&iq) +DATA[1] 电机温度 DATA[3] = *((uint8_t *)(&iq)+1) + DATA[4] = *(uint8_t *)(&speed) +DATA[2] 转矩电流低字节 DATA[5] = *((uint8_t *)(&speed)+1) + DATA[6] = *(uint8_t *)(&encoder) +DATA[3] 转矩电流高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +DATA[4] 电机速度低字节 + +DATA[5] 电机速度高字节 + +DATA[6] 编码器位置低字节 + +DATA[7] 编码器位置高字节 + +(15) 读取电机状态 3 命令(1 帧) + +该命令读取当前电机的温度和相电流数据 + +数据域 说明 数据 + +DATA[0] 命令字节 0x9D + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据包含了以下数据: + +1. 电机温度 temperature(int8_t 类型,1℃/LSB) + +2. A 相电流数据,数据类型为 int16_t 类型,对应实际相电流为 1A/64LSB。 + +3. B 相电流数据,数据类型为 int16_t 类型,对应实际相电流为 1A/64LSB。 + +4. C 相电流数据,数据类型为 int16_t 类型,对应实际相电流为 1A/64LSB。 + +数据域 说明 数据 + +DATA[0] 命令字节 0x9D + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] A 相电流低字节 DATA[2] = *(uint8_t *)(&iA) + +DATA[3] A 相电流高字节 DATA[3] = *((uint8_t *)(& iA)+1) + +DATA[4] B 相电流低字节 DATA[4] = *(uint8_t *)(&iB) + +DATA[5] B 相电流高字节 DATA[5] = *((uint8_t *)(& iB)+1) + +DATA[6] C 相电流低字节 DATA[6] = *(uint8_t *)(&iC) + +DATA[7] C 相电流高字节 DATA[7] = *((uint8_t *)(& iC)+1) + +(16) 电机关闭命令(1 帧) + +关闭电机,同时清除电机运行状态和之前接收的控制指令 + +数据域 说明 数据 + +DATA[0] 命令字节 0x80 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + + 11 + 上海瓴控科技有限公司 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,帧数据和主机发送相同 + +(17) 电机停止命令(1 帧) + +停止电机,但不清除电机运行状态和之前接收的控制指令 + +数据域 说明 数据 + +DATA[0] 命令字节 0x81 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,帧数据和主机发送相同 + +(18) 电机运行命令(1 帧) + +从电机停止命令中恢复电机运行(恢复停止前的控制方式) + +数据域 说明 数据 + +DATA[0] 命令字节 0x88 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] NULL 0x00 + +DATA[5] NULL 0x00 + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +驱动回复(1 帧) + +电机在收到命令后回复主机,帧数据和主机发送相同 + +(19) 转矩开环控制命令(1 帧)该命令仅在 MS 上实现 + +主机发送该命令以控制电机的开环输出功率,控制值 powerControl 为 int16_t 类型,数值范围-1000~ + +1000,(电机母线电流和扭矩因不同电机而异)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA0 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] 输出功率控制值低字节 DATA[4] = *(uint8_t *)(& powerControl) + + 12 + 上海瓴控科技有限公司 + +DATA[5] 输出功率控制值高字节 DATA[5] = *((uint8_t *)(& powerControl)+1) + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +备注: + + 1. 该命令中的控制值 powerControl 不受上位机(LK-Motor Tool)中的 Max Power 值限制。 +驱动回复(1 帧) + + 电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 +2. 电机的输出功率值(int16_t 类型,范围-1000~1000)。 +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA0 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] 输出功率值低字节 DATA[2] = *(uint8_t *)(& power) + +DATA[3] 输出功率值高字节 DATA[3] = *((uint8_t *)(&power)+1) + +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) + +DATA[5] 电机速度高字节 DATA[5] = *((uint8_t *)(&speed)+1) + +DATA[6] 编码器位置低字节 DATA[6] = *(uint8_t *)(&encoder) + +DATA[7] 编码器位置高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +(20) 转矩闭环控制命令(1 帧)该命令仅在 MF 和 MG 上实现 + +主机发送该命令以控制电机的转矩电流输出,控制值 iqControl 为 int16_t 类型,数值范围-2000~ 2000, + +对应实际转矩电流范围-32A~32A(母线电流和电机的实际扭矩因不同电机而异)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA1 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] 转矩电流控制值低字节 DATA[4] = *(uint8_t *)(&iqControl) + +DATA[5] 转矩电流控制值高字节 DATA[5] = *((uint8_t *)(&iqControl)+1) + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +备注: + +1. 该命令中的控制值 iqControl 不受上位机(LK-Motor Tool)中的 Max Torque Current 值限制。 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 + +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA1 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + + 13 + 上海瓴控科技有限公司 + +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) + DATA[3] = *((uint8_t *)(&iq)+1) +DATA[3] 转矩电流高字节 DATA[4] = *(uint8_t *)(&speed) + DATA[5] = *((uint8_t *)(&speed)+1) +DATA[4] 电机速度低字节 DATA[6] = *(uint8_t *)(&encoder) + DATA[7] = *((uint8_t *)(&encoder)+1) +DATA[5] 电机速度高字节 + +DATA[6] 编码器位置低字节 + +DATA[7] 编码器位置高字节 + +(21) 速度闭环控制命令(1 帧) + +主机发送该命令以控制电机的速度,控制值 speedControl 为 int32_t 类型,对应实际转速为 0.01dps/LSB。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA2 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] 速度控制低字节 DATA[4] = *(uint8_t *)(&speedControl) + +DATA[5] 速度控制 DATA[5] = *((uint8_t *)(&speedControl)+1) + +DATA[6] 速度控制 DATA[6] = *((uint8_t *)(&speedControl)+2) + +DATA[7] 速度控制高字节 DATA[7] = *((uint8_t *)(&speedControl)+3) + +备注: + +1. 该命令下电机的最大转矩电流由上位机(LK-Motor Tool)中的 Max Torque Current 值限制。 + +2. 该控制模式下,电机的最大加速度由上位机(LK-Motor Tool)中的 Max Acceleration 值限制。 + +驱动回复(1 帧) + 电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 + +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA2 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) + +DATA[3] 转矩电流高字节 DATA[3] = *((uint8_t *)(&iq)+1) + +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) + +DATA[5] 电机速度高字节 DATA[5] = *((uint8_t *)(&speed)+1) + +DATA[6] 编码器位置低字节 DATA[6] = *(uint8_t *)(&encoder) + +DATA[7] 编码器位置高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +(22) 位置闭环控制命令 1(1 帧) + +主机发送该命令以控制电机的位置(多圈角度), 控制值 angleControl 为 int32_t 类型,对应实际位 + +置为 0.01degree/LSB,即 36000 代表 360°,电机转动方向由目标位置和当前位置的差值决定。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA3 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + + 14 + 上海瓴控科技有限公司 + +DATA[3] NULL 0x00 + +DATA[4] 位置控制低字节 DATA[4] = *(uint8_t *)(&angleControl) +DATA[5] 位置控制 DATA[5] = *((uint8_t *)(&angleControl)+1) +DATA[6] 位置控制 DATA[6] = *((uint8_t *)(&angleControl)+2) +DATA[7] 位置控制高字节 DATA[7] = *((uint8_t *)(&angleControl)+3) + +备注: + +1. 该命令下的控制值 angleControl 受上位机(LK-Motor Tool)中的 Max Angle 值限制。 +2. 该命令下电机的最大速度由上位机(LK-Motor Tool)中的 Max Speed 值限制。 +3. 该控制模式下,电机的最大加速度由上位机(LK-Motor Tool)中的 Max Acceleration 值限制。 +4. 该控制模式下,电机的最大转矩电流由上位机(LK-Motor Tool)中的 Max Torque Current 值限 + + 制。 + +驱动回复(1 帧) + 电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 + +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA3 +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) +DATA[3] 转矩电流高字节 DATA[3] = *((uint8_t *)(&iq)+1) + +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) +DATA[5] 电机速度高字节 DATA[5] = *((uint8_t *)(&speed)+1) +DATA[6] 编码器位置低字节 DATA[6] = *(uint8_t *)(&encoder) +DATA[7] 编码器位置高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +(23) 位置闭环控制命令 2(1 帧) + +主机发送该命令以控制电机的位置(多圈角度), 控制值 angleControl 为 int32_t 类型,对应实际位 + +置为 0.01degree/LSB,即 36000 代表 360°,电机转动方向由目标位置和当前位置的差值决定。 + +控制值 maxSpeed 限制了电机转动的最大速度,为 uint16_t 类型,对应实际转速 1dps/LSB。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA4 + +DATA[1] NULL 0x00 + +DATA[2] 速度限制低字节 DATA[2] = *(uint8_t *)(&maxSpeed) + +DATA[3] 速度限制高字节 DATA[3] = *((uint8_t *)(&maxSpeed)+1) + +DATA[4] 位置控制低字节 DATA[4] = *(uint8_t *)(&angleControl) + +DATA[5] 位置控制 DATA[5] = *((uint8_t *)(&angleControl)+1) + +DATA[6] 位置控制 DATA[6] = *((uint8_t *)(&angleControl)+2) + +DATA[7] 位置控制高字节 DATA[7] = *((uint8_t *)(&angleControl)+3) + +备注: + +1. 该命令下的控制值 angleControl 受上位机(LK-Motor Tool)中的 Max Angle 值限制。 + +2. 该控制模式下,电机的最大加速度由上位机(LK-Motor Tool)中的 Max Acceleration 值限制。 + +3. 该控制模式下,电机的最大转矩电流由上位机(LK-Motor Tool)中的 Max Torque Current 值限 + + 制。 + + 15 + 上海瓴控科技有限公司 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA4 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) + +DATA[3] 转矩电流高字节 DATA[3] = *((uint8_t *)(&iq)+1) + +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) + +DATA[5] 电机速度高字节 DATA[5] = *((uint8_t *)(&speed)+1) + +DATA[6] 编码器位置低字节 DATA[6] = *(uint8_t *)(&encoder) + +DATA[7] 编码器位置高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +(24) 位置闭环控制命令 3(1 帧) + +主机发送该命令以控制电机的位置(单圈角度), 控制值 angleControl 为 uint16_t 类型,数值范围 + +0~35999,对应实际位置为 0.01degree/LSB,即实际角度范围 0°~359.99°。 + +控制值 spinDirection 设置电机转动的方向,为 uint8_t 类型,0x00 代表顺时针,0x01 代表逆时针。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA5 + +DATA[1] 转动方向字节 DATA[1] = spinDirection + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 + +DATA[4] 位置控制低字节 DATA[4] = *(uint8_t *)(&angleControl) + +DATA[5] 位置控制高字节 DATA[5] = *((uint8_t *)(&angleControl)+1) + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +备注: + +1. 该命令下电机的最大速度由上位机(LK-Motor Tool)中的 Max Speed 值限制。 + +2. 该控制模式下,电机的最大加速度由上位机(LK-Motor Tool)中的 Max Acceleration 值限制。 + +3. 该控制模式下,电机的最大转矩电流由上位机(LK-Motor Tool)中的 Max Torque Current 值限 + + 制。 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 + +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA5 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) + +DATA[3] 转矩电流高字节 DATA[3] = *((uint8_t *)(&iq)+1) + + 16 + 上海瓴控科技有限公司 + +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) + DATA[5] = *((uint8_t *)(&speed)+1) +DATA[5] 电机速度高字节 DATA[6] = *(uint8_t *)(&encoder) + DATA[7] = *((uint8_t *)(&encoder)+1) +DATA[6] 编码器位置低字节 + +DATA[7] 编码器位置高字节 + +(25) 位置闭环控制命令 4(1 帧) + +主机发送该命令以控制电机的位置(单圈角度)。 + +1. 角度控制值 angleControl 为 uint16_t 类型,数值范围 0~35999,对应实际位置为 0.01degree/LSB, + +即实际角度范围 0°~359.99°。 + +2. spinDirection 设置电机转动的方向,为 uint8_t 类型,0x00 代表顺时针,0x01 代表逆时针。 + +3. maxSpeed 限制了电机转动的最大速度,为 uint16_t 类型,对应实际转速 1dps/LSB。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA6 + +DATA[1] 转动方向字节 DATA[1] = spinDirection + +DATA[2] 速度限制低字节 DATA[2] = *(uint8_t *)(&maxSpeed) + +DATA[3] 速度限制高字节 DATA[3] = *((uint8_t *)(&maxSpeed)+1) + +DATA[4] 位置控制低字节 DATA[4] = *(uint8_t *)(&angleControl) + +DATA[5] 位置控制高字节 DATA[5] = *((uint8_t *)(&angleControl)+1) + +DATA[6] NULL 0x00 + +DATA[7] NULL 0x00 + +备注: + +1. 该控制模式下,电机的最大加速度由上位机(LK-Motor Tool)中的 Max Acceleration 值限制。 + +2. 该控制模式下,电机的最大转矩电流由上位机(LK-Motor Tool)中的 Max Torque Current 值限 + + 制。 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 + +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA6 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) + +DATA[3] 转矩电流高字节 DATA[3] = *((uint8_t *)(&iq)+1) + +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) + +DATA[5] 电机速度高字节 DATA[5] = *((uint8_t *)(&speed)+1) + +DATA[6] 编码器位置低字节 DATA[6] = *(uint8_t *)(&encoder) + +DATA[7] 编码器位置高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +(26) 位置闭环控制命令 5(1 帧) + +主机发送该命令以控制电机的位置增量, 控制值 angleControl 为 int32_t 类型,对应实际位置为 + +0.01degree/LSB。电机的转动方向由控制量的符号确定。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA7 + + 17 + 上海瓴控科技有限公司 + +DATA[1] NULL 0x00 + +DATA[2] NULL 0x00 + +DATA[3] NULL 0x00 +DATA[4] 位置控制低字节 DATA[4] = *(uint8_t *)(&angleControl) +DATA[5] 位置控制 DATA[5] = *((uint8_t *)(&angleControl)+1) + +DATA[6] 位置控制 DATA[6] = *((uint8_t *)(&angleControl)+2) +DATA[7] 位置控制高字节 DATA[7] = *((uint8_t *)(&angleControl)+3) + +备注: + +1. 该命令下电机的最大速度由上位机(LK-Motor Tool)中的 Max Speed 值限制。 +2. 该控制模式下,电机的最大加速度由上位机(LK-Motor Tool)中的 Max Acceleration 值限制。 +3. 该控制模式下,电机的最大转矩电流由上位机(LK-Motor Tool)中的 Max Torque Current 值限 + + 制。 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 + +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA7 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) +DATA[3] 转矩电流高字节 DATA[3] = *((uint8_t *)(&iq)+1) +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) + +DATA[5] 电机速度高字节 DATA[5] = *((uint8_t *)(&speed)+1) +DATA[6] 编码器位置低字节 DATA[6] = *(uint8_t *)(&encoder) + +DATA[7] 编码器位置高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +(27) 位置闭环控制命令 6(1 帧) + +主机发送该命令以控制电机的位置增量, 控制值 angleControl 为 int32_t 类型,对应实际位置为 + +0.01degree/LSB。电机的转动方向由控制量的符号确定。 + +控制值 maxSpeed 限制了电机转动的最大速度,为 uint16_t 类型,对应实际转速 1dps/LSB。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA8 + +DATA[1] NULL 0x00 + +DATA[2] 速度限制低字节 DATA[2] = *(uint8_t *)(&maxSpeed) + +DATA[3] 速度限制高字节 DATA[3] = *((uint8_t *)(&maxSpeed)+1) + +DATA[4] 位置控制低字节 DATA[4] = *(uint8_t *)(&angleControl) + +DATA[5] 位置控制 DATA[5] = *((uint8_t *)(&angleControl)+1) + +DATA[6] 位置控制 DATA[6] = *((uint8_t *)(&angleControl)+2) + +DATA[7] 位置控制高字节 DATA[7] = *((uint8_t *)(&angleControl)+3) + +备注: + +1. 该控制模式下,电机的最大加速度由上位机(LK-Motor Tool)中的 Max Acceleration 值限制。 + +2. 该控制模式下,电机的最大转矩电流由上位机(LK-Motor Tool)中的 Max Torque Current 值限 + + 制。 + + 18 + 上海瓴控科技有限公司 + +驱动回复(1 帧) + +电机在收到命令后回复主机,该帧数据中包含了以下参数。 + +1. 电机温度 temperature(int8_t 类型,1℃/LSB)。 + +2. 电机的转矩电流值 iq(int16_t 类型,范围-2048~2048,对应实际转矩电流范围-33A~33A)。 +3. 电机转速 speed(int16_t 类型,1dps/LSB)。 + +4. 编码器位置值 encoder(uint16_t 类型,14bit 编码器的数值范围 0~16383)。 + +数据域 说明 数据 + +DATA[0] 命令字节 0xA8 + +DATA[1] 电机温度 DATA[1] = *(uint8_t *)(&temperature) + +DATA[2] 转矩电流低字节 DATA[2] = *(uint8_t *)(&iq) + +DATA[3] 转矩电流高字节 DATA[3] = *((uint8_t *)(&iq)+1) + +DATA[4] 电机速度低字节 DATA[4] = *(uint8_t *)(&speed) + +DATA[5] 电机速度高字节 DATA[5] = *((uint8_t *)(&speed)+1) + +DATA[6] 编码器位置低字节 DATA[6] = *(uint8_t *)(&encoder) + +DATA[7] 编码器位置高字节 DATA[7] = *((uint8_t *)(&encoder)+1) + +4. 多电机命令 + + 多电机命令需要在设定软件中打开,多电机命令和单电机命令无法同时使用 +  多电机转矩闭环控制命令(1 帧) + + 用于同时向多个电机发送命令的报文格式如下: + 标识符:0x280 + 帧格式:DATA + 帧类型:标准帧 + DLC:8 字节 + +主机发送该命令以同时控制最多 4 个电机的转矩电流输出,控制值 iqControl 为 int16_t 类型,数值范 + +围-2000~ 2000,对应实际转矩电流范围-32A~32A(母线电流和电机的实际扭矩因不同电机而异)。 + +电机 ID 应当设置为#1~#4,并且不能重复,与帧数据中的 4 个转矩电流对应 + +数据域 说明 数据 + +DATA[0] 转矩电流 1 控制值低字节 DATA[0] = *(uint8_t *)(&iqControl_1) + +DATA[1] 转矩电流 1 控制值高字节 DATA[1] = *((uint8_t *)(&iqControl_1)+1) + +DATA[2] 转矩电流 2 控制值低字节 DATA[2] = *(uint8_t *)(&iqControl_2) + +DATA[3] 转矩电流 2 控制值高字节 DATA[3] = *((uint8_t *)(&iqControl_2)+1) + +DATA[4] 转矩电流 3 控制值低字节 DATA[4] = *(uint8_t *)(&iqControl_3) + +DATA[5] 转矩电流 3 控制值高字节 DATA[5] = *((uint8_t *)(&iqControl_3)+1) + +DATA[6] 转矩电流 4 控制值低字节 DATA[6] = *(uint8_t *)(&iqControl_4) + +DATA[7] 转矩电流 4 控制值高字节 DATA[7] = *((uint8_t *)(&iqControl_4)+1) + + 驱动回复(1 帧) + 各个电机回复命令的报文格式如下: + 标识符:0x140 + ID(1~4) + 帧格式:DATA + 帧类型:标准帧 + DLC:8 字节 + + 19 + 上海瓴控科技有限公司 + +各个电机根据 ID 从小到大依次回复,各个电机的回复数据与单电机转矩闭环控制命令回复数据相同 + + 20 + diff --git a/modules/motor/LKmotor/LK_motor.md b/modules/motor/LKmotor/LK_motor.md index fec4fc7..f815d37 100644 --- a/modules/motor/LKmotor/LK_motor.md +++ b/modules/motor/LKmotor/LK_motor.md @@ -1,5 +1,5 @@ -LK motor +# LK motor -这是瓴控电机的模块封装说明文档。关于LK电机的控制报文和反馈报文值,详见LK电机的说明文档。 +这是瓴控电机的模块封装说明文档。关于LK电机的控制报文和反馈报文值,详见LK电机的说明文档,由于电机实例已经自带三环PID计算,一般来说**我们能用到的只有单电机的力矩指令和多电机指令。** 注意LK电机在使用多电机发送的时候,只支持一条总线上至多4个电机,多电机模式下LK仅支持发送id 0x280为接收ID为0x140+id. \ No newline at end of file diff --git a/modules/motor/LKmotor/反馈报文.png b/modules/motor/LKmotor/反馈报文.png new file mode 100644 index 0000000..554042a Binary files /dev/null and b/modules/motor/LKmotor/反馈报文.png differ diff --git a/modules/motor/LKmotor/报文格式.png b/modules/motor/LKmotor/报文格式.png new file mode 100644 index 0000000..882a23d Binary files /dev/null and b/modules/motor/LKmotor/报文格式.png differ diff --git a/必须做&禁止做.md b/必须做&禁止做.md index d40c81a..60c2629 100644 --- a/必须做&禁止做.md +++ b/必须做&禁止做.md @@ -1,9 +1,18 @@ -# 禁止在临界区使用延时,这会导致因中断关闭使得定时器无法进入中断更新时间,进而卡死系统 +# MUST & MUSTNOTMUSTNOT + +## 禁止在临界区使用延时,这会导致因中断关闭使得定时器无法进入中断更新时间,进而卡死系统 除非你使用的是基于计数寄存器差值的延时方法,或阻塞式的for延时 -# 禁止摸鱼 +## 禁止摸鱼 提供工作效率! -# \ No newline at end of file +## 禁止图方便直接将电机/电调连接在开发板的xt30接口上,否则电机的反电动势可能烧毁开发板 + +后续考虑增加一个xt30转接器,其上实现隔离电路,再连接开发板充当分电板。 + +## 请给你编写的bsp和module提供详细的文档和使用示例,并为接口增加安全检查 + +“treat your user as idot!” +