STM32使用DMA+环形缓冲区解析MAVLink 2时丢包问题排查
STM32 UART读取Pixhawk4遥测丢包问题修复方案
问题根源
你用大小为1的缓冲区是丢包的核心原因:单字节缓冲区会导致UART/DMA中断触发过于频繁,CPU来不及处理后续 incoming 数据,要么DMA传输时覆盖未处理的字节,要么中断响应不及时直接丢帧。
具体修复步骤
1. 替换单字节缓冲区为环形缓冲区
改用环形缓冲区可以批量缓存数据,减少中断触发频率,避免数据丢失。建议缓冲区大小至少覆盖Mavlink单包最大长度(Mavlink2最大包长255字节,选256字节足够):
#define UART_RX_BUF_SIZE 256 uint8_t uart_rx_buf[UART_RX_BUF_SIZE]; // 环形缓冲区读写指针(volatile确保编译器不优化) volatile uint16_t rx_read_ptr = 0; volatile uint16_t rx_write_ptr = 0;
2. 调整DMA配置为循环模式+半满/全满中断
将DMA设为循环模式,让它持续填充缓冲区,同时开启半满和全满中断,批量处理数据:
// 启动DMA循环接收,缓冲区长度设为UART_RX_BUF_SIZE HAL_UART_Receive_DMA(&huart1, uart_rx_buf, UART_RX_BUF_SIZE); // 开启DMA半满(HT)和全满(TC)中断 __HAL_DMA_ENABLE_IT(&hdma_usart1_rx, DMA_IT_HT | DMA_IT_TC);
3. 优化中断回调函数
在半满/全满中断回调里更新环形缓冲区的写指针,确保数据被正确缓存:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 缓冲区全满,更新写指针到下一个起始位置 rx_write_ptr = (rx_write_ptr + UART_RX_BUF_SIZE) % UART_RX_BUF_SIZE; } } void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 缓冲区半满,更新写指针到半满位置 rx_write_ptr = (rx_write_ptr + UART_RX_BUF_SIZE/2) % UART_RX_BUF_SIZE; } }
4. 适配环形缓冲区的Mavlink解析逻辑
从环形缓冲区中逐个读取字节解析,完成一包就转发,避免数据堆积:
void parse_and_forward_mavlink(void) { mavlink_message_t msg; mavlink_status_t status; // 只要缓冲区有未处理数据,就持续解析 while (rx_read_ptr != rx_write_ptr) { uint8_t curr_byte = uart_rx_buf[rx_read_ptr]; // 更新读指针(环形取模) rx_read_ptr = (rx_read_ptr + 1) % UART_RX_BUF_SIZE; // 解析Mavlink数据包,成功解析后转发 if (mavlink_parse_char(MAVLINK_COMM_0, curr_byte, &msg, &status)) { // 转发到目标设备,这里用huart2示例 HAL_UART_Transmit(&huart2, (uint8_t*)&msg, msg.len + MAVLINK_NUM_NON_PAYLOAD_BYTES, HAL_MAX_DELAY); } } }
5. 主循环处理解析任务
把主循环的continue替换成解析函数调用,确保数据被及时处理:
while (1) { parse_and_forward_mavlink(); // 如果有其他低优先级任务,可以放在这里执行 }
额外注意事项
- 确保UART波特率和Pixhawk配置一致,波特率不匹配也会导致丢包
- 如果用HAL库,注意DMA中断优先级要高于其他低优先级任务,避免中断被抢占
- 环形缓冲区的读写指针操作要保证原子性,必要时关中断(如果在多任务环境下)
内容的提问来源于stack exchange,提问作者João Ferreira
相关产品推荐
相关产品推荐

