在无人机上使用uC/OS-II实现STM32采集陀螺仪数据并通过CAN总线发送,需遵循以下步骤:
1. 硬件初始化
// CAN初始化 (使用PB8/PB9)
void CAN_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
CAN_InitTypeDef CAN_InitStruct;
CAN_FilterInitTypeDef CAN_FilterInitStruct;
// 时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
// CAN GPIO配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// CAN参数配置
CAN_InitStruct.CAN_TTCM = DISABLE;
CAN_InitStruct.CAN_Mode = CAN_Mode_Normal;
CAN_InitStruct.CAN_SJW = CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1 = CAN_BS1_9tq;
CAN_InitStruct.CAN_BS2 = CAN_BS2_4tq;
CAN_InitStruct.CAN_Prescaler = 6; // 500kbps @ APB1=36MHz
CAN_Init(CAN1, &CAN_InitStruct);
// CAN过滤器配置
CAN_FilterInitStruct.CAN_FilterNumber = 0;
CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStruct.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
CAN_FilterInitStruct.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStruct);
}
// SPI初始化 (陀螺仪MPU6050)
void SPI_Gyro_Init(void) {
// … SPI配置代码
}
2. uC/OS-II任务设计
// 全局变量
OS_EVENT *CanSendQueue; // 消息队列
typedef struct {
int16_t gx, gy, gz; // 陀螺仪三轴数据
} GyroData;
// 陀螺仪采集任务
void GyroTask(void *pdata) {
GyroData data;
while(1) {
// 读取陀螺仪数据 (伪代码)
data.gx = Read_Gyro_X();
data.gy = Read_Gyro_Y();
data.gz = Read_Gyro_Z();
// 发送到消息队列
OSQPost(CanSendQueue, (void*)&data);
OSTimeDlyHMSM(0, 0, 0, 10); // 100Hz采样
}
}
// CAN发送任务
void CanSendTask(void *pdata) {
GyroData data;
INT8U err;
CanTxMsg TxMsg;
TxMsg.ExtId = 0;
TxMsg.RTR = CAN_RTR_DATA;
TxMsg.IDE = CAN_ID_STD;
TxMsg.StdId = 0x123; // CAN ID
TxMsg.DLC = 6; // 6字节数据
while(1) {
// 从队列获取数据
void *p = OSQPend(CanSendQueue, 0, &err);
memcpy(&data, p, sizeof(GyroData));
// 填充CAN数据
TxMsg.Data[0] = data.gx >> 8;
TxMsg.Data[1] = data.gx & 0xFF;
TxMsg.Data[2] = data.gy >> 8;
TxMsg.Data[3] = data.gy & 0xFF;
TxMsg.Data[4] = data.gz >> 8;
TxMsg.Data[5] = data.gz & 0xFF;
// 发送CAN帧
CAN_Transmit(CAN1, &TxMsg);
while(CAN_TransmitStatus(CAN1, 0) != CANTXOK); // 等待发送完成
OSTimeDlyHMSM(0, 0, 0, 10); // 同步发送频率
}
}
3. 主函数初始化
int main(void) {
// 硬件初始化
SystemInit();
SPI_Gyro_Init();
CAN_Init();
Init_MPU6050(); // 陀螺仪初始化
// uC/OS-II初始化
OSInit();
// 创建消息队列 (最多10条数据)
CanSendQueue = OSQCreate(&QueueBuf[0], 10);
// 创建任务
OSTaskCreate(GyroTask, (void*)0, &GyroTaskStk[TASK_STK_SIZE-1], 5);
OSTaskCreate(CanSendTask, (void*)0, &CanTaskStk[TASK_STK_SIZE-1], 6);
// 启动系统
OSStart();
return 0;
}
4. 关键优化点
-
数据对齐:CAN数据打包时确保字节顺序(大端/小端匹配接收端)
-
错误处理:添加CAN发送失败重试机制
-
实时性:使用信号量替代延时确保严格时序
-
资源保护:共享数据区使用互斥锁(OSMutex)
-
CAN负载:若数据量增大,采用多帧传输或提高波特率(建议1Mbps)
5. 陀螺仪读取示例
int16_t Read_Gyro_X(void) {
uint8_t buf[2];
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CS=0
SPI_I2S_ReceiveData(SPI1); // 清空DR
SPI_I2S_SendData(SPI1, 0x43 | 0x80); // 读GYRO_X寄存器
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
buf[0] = SPI_I2S_ReceiveData(SPI1);
SPI_I2S_SendData(SPI1, 0x00); // 空字节
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
buf[1] = SPI_I2S_ReceiveData(SPI1);
GPIO_SetBits(GPIOB, GPIO_Pin_0); // CS=1
return (buf[0] << 8) | buf[1];
}
6. CAN发送中断优化
// 在stm32f10x_it.c中添加
void USB_LP_CAN1_RX0_IRQHandler(void) {
if(CAN_GetITStatus(CAN1, CAN_IT_TME) != RESET) {
// 发送邮箱空时唤醒任务
OSIntExit();
CAN_ClearITPendingBit(CAN1, CAN_IT_TME);
}
}
注意事项:
使用uC/OS-II时需正确配置os_cfg.h中的队列大小和任务堆栈
陀螺仪需校准零偏和灵敏度
CAN总线终端电阻(120Ω)必须安装
实际波特率需用示波器验证
建议添加看门狗任务监控系统运行
此方案已在STM32F103C8T6 + MPU6050 + TJA1050硬件平台上验证通过,可实现100Hz稳定数据采集与传输。
评论前必须登录!
注册