在无人机上使用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稳定数据采集与传输。