在高级语言中,I/O 流输入(input)操作一般都要求指定要读取的数据的最大长度(字节数)。当接收到至少1字节、最多所指定的字节数时,函数返回。
STM32 串口接收数据时,HAL API 要求指定数据长度。但无论轮询、中断或是DMA方式,都必须完整地接收到这么多字节,程序流程才继续。如何接收变长消息,我想不到特别好的实现方式。一种方式是,轮询加超时。另一种方式是,设计消息协议,使消息头为定长,且消息头内包含消息体的长度。但是,如果通讯异常,导致消息数据错误或丢失,那么,还是缺少“提前返回”的机制。
相对来说,轮询加超时的方式似乎更好些。效率低,但是是可靠的。我也不确定。
DMA是STM32内的一个硬件模块,它独立于CPU在外围设备和内存之间进行数据传输,解放了CPU。每个型号的STM32 MCU有1-2个DMA,每个DMA有一定数量的Channel。每个Channel两端分别绑定到外围设备和内存。每个Channel可与哪种外围设备绑定,这是STM32设计时固定下来的,要查询参考手册得知。
Nucleo-F303RE 的 USART2 支持DMA。使用 DMA模式发送数据,要启用 DMA Channel的中断和USART2的中断。数据发送完成时,HAL会触发USART2 的中断进而调用中断回调函数。概况起来:
- 调用 HAL_UART_Transmit_DMA() 函数发送数据
- 实现 HAL_UART_TxCpltCallback() 回调函数。当数据发送完成后,此函数被HAL调用
下面的例程使用 DMA 方式依次从串口发送3条消息。App_loop() 在main() 函数的主循环中被调用。当串口数据发送完成时,txDone 标志被置1,此时将闪烁 LED(blink()),并发送下一条消息:
static void blink(); static const char * msgArr[] = { "We still can find a way\n", // "Because nothing lasts for ever\n", // "Event the cold November rain\n" }; static int msgIndex = 0; volatile uint8_t txDone = 1; void App_loop() { if (txDone) { blink(); txDone = 0; const char * msg = msgArr[msgIndex]; HAL_UART_Transmit_DMA(&huart2, (uint8_t *) msg, strlen(msg)); msgIndex = (1 + msgIndex) % 3; } } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { txDone = 1; }
从Cube HAL的角度来说,就这么多。