初试FreeRTOS:创建上位机接收数据驱动4个舵机任务,如裸机般无感
解析函数上位机数据协议协议格式 (LD150舵机)[0x55][0x55][ID][长度][命令][数据...][校验和] 2字节 1字节 1字节 1字节 N字节 1字节帧头: 0x55 0x55ID: 舵机ID (1-4) 或 0xFE (广播)数据: 每组5字节 ID time_low time_high pos_low pos_high位置: 0-1000 对应 0-240° (转换为 0-180°)定义全局量// 全局解析结果volatile 确保中断安全 volatile int g_servo_s1 0; volatile int g_servo_s2 0; volatile int g_servo_s3 0; volatile int g_servo_s4 0; volatile int g_delay_ms 1000; volatile bool g_new_command_ready false;解析函数实现/* 解析4个舵机二进制协议 */ void parse_servo_frame(const uint8_t *data, uint16_t len) { if (len SERVO_FRAME_MIN_LEN) return; // 检查帧头 0x55 0x55 if (data[0] ! 0x55 || data[1] ! 0x55) return; // 检查广播ID或有效ID uint8_t servo_id data[2]; if (servo_id ! SERVO_BROADCAST_ID (servo_id 1 || servo_id 4)) return; // 检查数据长度 uint8_t data_len data[3]; uint8_t expected_len 4 data_len 1; // header(2) id(1) len(1) data(n) checksum(1) if (len expected_len) return; // 检查命令 (0x03 位置写命令) if (data[4] ! SERVO_CMD_POS_WRITE) return; // 验证校验和 uint8_t calc_sum calc_checksum(data, expected_len - 1); if (calc_sum ! data[expected_len - 1]) { // 校验和错误 return; } // 解析舵机数据 (每个舵机5字节: id time_low time_high pos_low pos_high) uint8_t *frame_data (uint8_t*)data[5]; uint8_t servo_count data_len / 5; for (uint8_t i 0; i servo_count; i) { uint8_t id frame_data[i * 5]; // 位置: pos pos_high * 256 pos_low, 范围0-1000对应0-240度 uint16_t pos frame_data[i * 5 3] | (frame_data[i * 5 4] 8); // 转换为角度 (0-240度 - 0-180度) uint16_t angle (pos * 180) / 240; if (angle 180) angle 180; // 更新对应舵机 switch (id) { case 1: g_servo_s1 angle; break; case 2: g_servo_s2 angle; break; case 3: g_servo_s3 angle; break; case 4: g_servo_s4 angle; break; } } // 解析运动时间 (用于DELAY) if (servo_count 0) { uint16_t move_time frame_data[1] | (frame_data[2] 8); g_delay_ms move_time; // 单位ms } g_new_command_ready true; }串口通信函数先配置好串口4开DMA接收和IDLE中断配置的代码不是重点就不放上来了。void UART4_IRQHandler(void) { /* USER CODE BEGIN UART4_IRQn 0 */ uint32_t flag 0; uint32_t temp; flag __HAL_UART_GET_FLAG(huart4, UART_FLAG_IDLE); if(flag ! RESET) { __HAL_UART_CLEAR_IDLEFLAG(huart4); HAL_UART_DMAStop(huart4); temp __HAL_DMA_GET_COUNTER(hdma_uart4_rx); uint16_t rx_len UART4_RX_BUFFER_SIZE - temp; parse_uart4_command((char*)uart4_rx_buffer, rx_len); HAL_UART_Receive_DMA(huart4, uart4_rx_buffer, UART4_RX_BUFFER_SIZE); } /* USER CODE END UART4_IRQn 0 */ HAL_UART_IRQHandler(huart4); /* USER CODE BEGIN UART4_IRQn 1 */ /* USER CODE END UART4_IRQn 1 */ }在parse_uart4_command中调用parse_servo_frame。void parse_uart4_command(char *data, uint16_t len) { if (len 0 || len UART4_RX_BUFFER_SIZE) return; // 解析LD150二进制协议 (帧头 0x55 0x55) if (len SERVO_FRAME_MIN_LEN (uint8_t)data[0] 0x55 (uint8_t)data[1] 0x55) { parse_servo_frame((const uint8_t*)data, len); } }FreeRTOS集成对于不熟悉FreeRTOS的开发者来说重头戏来了。首先解释下领域信息要实现周期性精确调度就必须用到以下函数void vTaskDelayUntil(TickType_tpxPreviousWakeTime, TickType_t xTimeIncrement)。pxPreviousWakeTime指向一个变量该变量保存任务最后一次解除阻塞的时间。第一次使用前该变量必须初始化为当前时间。xTimeIncrement周期循环时间。当时间等于 (pxPreviousWakeTime xTimeIncrement) 时任务解除阻塞。因此还需要pdMS_TO_TICKS(),将毫秒转换为FreeRTOS能理解的节拍数作为xTimeIncrementxTaskGetTickCount()获取自系统启动以来的时钟节拍数作为pxPreviousWakeTime。这样代码的逻辑就清晰起来了。初始化调用pdMS_TO_TICKS和xTaskGetTickCount。之后在循环中用g_new_command_ready标志判断parse_servo_frame解析有没有成功有的话就Servo_SetPulse驱动舵机。由于舵机对应的通道和目标角度都是全局且在其他位置定义的所以代码可以用一行表示很简洁。驱动任务结束后再把g_new_command_ready置0。若g_new_command_ready为假则直接跳过执行舵机驱动等待下一次延时。/* 舵机控制命令任务 - 处理UART4收到的舵机指令 */ void vTaskServoControl(void *pvParameters) { TickType_t xLastWakeTime; const TickType_t xFrequency pdMS_TO_TICKS(10); // 10ms检查一次 xLastWakeTime xTaskGetTickCount(); for(;;) { // 检查是否有新命令 if (g_new_command_ready) { // 解析LD150舵机二进制协议帧 // 位置范围: 0-1000 (对应0-240度) // 注意: Servo_Channel枚举从0开始 Servo_SetPulse(SERVO_CH1, g_servo_s1); Servo_SetPulse(SERVO_CH2, g_servo_s2); Servo_SetPulse(SERVO_CH3, g_servo_s3); Servo_SetPulse(SERVO_CH4, g_servo_s4); // 清除标志 g_new_command_ready false; } vTaskDelayUntil(xLastWakeTime, xFrequency); } }数据来源链路UART4 DMAIDLE中断 ↓ parse_uart4_command() ↓ (检测0x55 0x55帧头) parse_servo_frame() ↓ (解析ID、位置、校验和) g_servo_s1~s5 position ↓ g_new_command_ready true ↓ vTaskServoControl() 读取并更新PWM
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2487079.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!