串口通讯(DMA模式)

在高级语言中,I/O 流输入(input)操作一般都要求指定要读取的数据的最大长度(字节数)。当接收到至少1字节、最多所指定的字节数时,函数返回。

STM32 串口接收数据时,HAL API 要求指定数据长度。但无论轮询、中断或是DMA方式,都必须完整地接收到这么多字节,程序流程才继续。如何接收变长消息,我想不到特别好的实现方式。一种方式是,轮询加超时。另一种方式是,设计消息协议,使消息头为定长,且消息头内包含消息体的长度。但是,如果通讯异常,导致消息数据错误或丢失,那么,还是缺少“提前返回”的机制。

相对来说,轮询加超时的方式似乎更好些。效率低,但是是可靠的。我也不确定。

DMA是STM32内的一个硬件模块,它独立于CPU在外围设备和内存之间进行数据传输,解放了CPU。每个型号的STM32MCU有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的角度来说,就这么多。

相关推荐