修复BMI088初始化异常,测试通过请尽快迁移到新版本。增加了电机的协议说明。
This commit is contained in:
parent
a2a83f9fbf
commit
7bb141af06
2
Makefile
2
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
|
||||
|
|
15
README.md
15
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)
|
||||
![数据流](assets/数据流.png)
|
|
@ -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`配置属于你的快捷键,提高效率!
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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); // 调用我们自己写的函数来处理消息
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 人工智能会毁灭人类吗?请在补全回答
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 发送数据地址
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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|<A+B
|
||||
float Output_LPF_RC; // RC = 1/omegac
|
||||
float Derivative_LPF_RC;
|
||||
float IntegralLimit; // 积分限幅
|
||||
float CoefA; // 变速积分 For Changing Integral
|
||||
float CoefB; // 变速积分 ITerm = Err*((A-abs(err)+B)/A) when B<|err|<A+B
|
||||
float Output_LPF_RC; // 输出滤波器 RC = 1/omegac
|
||||
float Derivative_LPF_RC; // 微分滤波器系数
|
||||
|
||||
//-----------------------------------
|
||||
// for calculating
|
||||
|
@ -108,7 +109,7 @@ typedef struct // config parameter
|
|||
// improve parameter
|
||||
PID_Improvement_e Improve;
|
||||
float IntegralLimit; // 积分限幅
|
||||
float CoefA; // For Changing Integral
|
||||
float CoefA; // AB为变速积分参数,变速积分实际上就引入了积分分离
|
||||
float CoefB; // ITerm = Err*((A-abs(err)+B)/A) when B<|err|<A+B
|
||||
float Output_LPF_RC; // RC = 1/omegac
|
||||
float Derivative_LPF_RC;
|
||||
|
|
|
@ -26,5 +26,5 @@ IST8310_Init_Config_s ist8310_conf = {
|
|||
|
||||
IST8310Instance *asdf = IST8310Init(&ist8310_conf);
|
||||
|
||||
// 随后数据会被放到asdf.mag[i]中
|
||||
// 随后数据会被放到asdf.mag[i]中,每次数据准备好了就会触发int_ist引脚中断,继而启动iic通信读取数据并解析
|
||||
```
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
- 在通过串口设置电机的时候,注意发送指令不要追加回车\n,否则电机端不响应。可以使用sscom,其会将你的按键直接当作ascii字符发送出去。
|
||||
|
||||
- **此电机的CAN线序和RoboMaster开发板相反,注意单独制作CAN线**
|
||||
|
||||
- 电机控制和反馈报文:
|
||||
![控制报文](%E6%8E%A7%E5%88%B6%E6%8A%A5%E6%96%87.png)
|
||||
|
||||
注意,我们的代码实现不使用其电调协议的位置PD算法;自行实现了三环,并在最后给出电流参考值,发送给电调。因此command packed structure中的**pos cmd & vel cmd & Kp & Kd均为零**。报文均为小端,**低位在前**。
|
||||
|
||||
请注意发送和反馈数据的**单位**。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> ~~HT的电机协议做的真不行,纯纯直接抄mit还抄不明白,之后全部换成LK!~~
|
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
|
@ -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 信息,说明驱动板已经损坏。
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
LK motor
|
||||
# LK motor
|
||||
|
||||
这是瓴控电机的模块封装说明文档。关于LK电机的控制报文和反馈报文值,详见LK电机的说明文档。
|
||||
这是瓴控电机的模块封装说明文档。关于LK电机的控制报文和反馈报文值,详见LK电机的说明文档,由于电机实例已经自带三环PID计算,一般来说**我们能用到的只有单电机的力矩指令和多电机指令。**
|
||||
|
||||
注意LK电机在使用多电机发送的时候,只支持一条总线上至多4个电机,多电机模式下LK仅支持发送id 0x280为接收ID为0x140+id.
|
Binary file not shown.
After Width: | Height: | Size: 163 KiB |
Binary file not shown.
After Width: | Height: | Size: 276 KiB |
15
必须做&禁止做.md
15
必须做&禁止做.md
|
@ -1,9 +1,18 @@
|
|||
# 禁止在临界区使用延时,这会导致因中断关闭使得定时器无法进入中断更新时间,进而卡死系统
|
||||
# MUST & MUSTNOTMUSTNOT
|
||||
|
||||
## 禁止在临界区使用延时,这会导致因中断关闭使得定时器无法进入中断更新时间,进而卡死系统
|
||||
|
||||
除非你使用的是基于计数寄存器差值的延时方法,或阻塞式的for延时
|
||||
|
||||
# 禁止摸鱼
|
||||
## 禁止摸鱼
|
||||
|
||||
提供工作效率!
|
||||
|
||||
#
|
||||
## 禁止图方便直接将电机/电调连接在开发板的xt30接口上,否则电机的反电动势可能烧毁开发板
|
||||
|
||||
后续考虑增加一个xt30转接器,其上实现隔离电路,再连接开发板充当分电板。
|
||||
|
||||
## 请给你编写的bsp和module提供详细的文档和使用示例,并为接口增加安全检查
|
||||
|
||||
“treat your user as idot!”
|
||||
|
||||
|
|
Loading…
Reference in New Issue