STM32 NUCLEO-F091RC MIDI合并器DMA发送丢失NoteOff消息问题
MIDI合并应用NoteOff消息丢失问题排查与解决
问题背景
基于NUCLEO-F091RC开发MIDI合并器:3路异步UART输入(2路仅RX,1路支持RX+TX),核心逻辑是按输入端口强制分配MIDI通道(忽略设备自身通道)。当前问题:同一设备同时按下2-3个琴键时NoteOn消息可正常发送,但同时释放时偶尔少一条NoteOff,导致对应琴键滞留在持续按下状态。
原实现采用环形缓冲区缓存RX数据,主循环解析出完整MIDI消息后写入TX缓冲区,调用HAL_UART_Transmit_DMA发送——调试发现TX缓冲区存在完整NoteOff消息但实际未发送;改为主循环调用HAL_UART_Transmit_IT后情况大幅改善,但仍偶发消息丢失。
核心排查方向与解决方案
1. 环形缓冲区溢出与写入校验
- 检查RX环形缓冲区的写入指针保护逻辑:多UART同时触发RX中断写入时,需确保
(write_ptr + 1) % buffer_size != read_ptr,确认有剩余空间再写入数据; - 若缓冲区溢出,需添加溢出标记或中断处理,避免丢失MIDI消息的Status字节(如NoteOff的0x8x)——一旦Status字节丢失,后续数据会被误解析,导致NoteOff消息不完整。
2. TX发送的状态同步管理
无论是DMA还是中断发送模式,必须保证上一次发送完成后再启动下一次发送:
- 使用DMA发送时,在
HAL_UART_TxCpltCallback中标记发送完成状态,主循环仅当状态为空闲时才调用HAL_UART_Transmit_DMA; - 使用
HAL_UART_Transmit_IT时,维护tx_busy标志:发送前检查标志,发送完成后在回调中清零; - 禁止未确认发送完成就连续调用发送API——STM32 UART发送寄存器仅一个,连续调用会覆盖待发送数据。
3. MIDI消息完整性校验
写入TX缓冲区前,必须确认是完整的MIDI消息:
- NoteOff消息固定为3字节(Status: 0x8n, 音符编号, 力度),解析时必须凑齐3字节再触发发送;
- 处理Running Status(省略重复Status字节)时,确保上下文正确,避免将后续数据误判为NoteOff的后续字节。
4. 中断优先级配置优化
- 三个UART的RX中断优先级必须高于TX中断优先级,优先处理输入消息,避免RX数据丢失;
- 若使用DMA发送,配置合理的DMA通道优先级,避免被其他高优先级中断抢占导致发送延迟;
- 检查NVIC配置,确保UART中断不会被其他中断长时间阻塞。
5. TX缓冲区的原子操作保护
主循环和中断回调同时操作TX缓冲区时,需添加临界区保护:
- 写入TX缓冲区或修改发送状态标志时,用
__disable_irq()和__enable_irq()包裹,避免中断打断导致数据错乱; - 裸机场景下无需引入RTOS,临界区即可解决并发操作问题。
验证建议
- 在NoteOff消息写入TX缓冲区时添加调试输出(如LED闪烁),确认消息已正确生成;
- 在
HAL_UART_TxCpltCallback中添加标记,确认每条发送的消息都触发了完成回调; - 用逻辑分析仪抓取UART TX引脚波形,对比缓冲区消息与实际发送字节,定位丢失环节。
内容的提问来源于stack exchange,提问作者NFs231dd
相关产品推荐
相关产品推荐

