0


基于stm32的UART高效接收DMA+IDLE编程示例

目录

基于stm32的UART高效接收DMA+IDLE编程示例

本文目标:基于stm32_h5的freertos编程示例

按照本文的描述,应该可以在对应的硬件上通实验并举一反三。

先决条件:拥有C语言基础,装有编译和集成的开发环境,比如:Keil uVision5

使用外设:USART1、USART1、GPIO、SysTick

HAL库版本:STM32H5xx HAL Driver version number 1.1.0

STMCubeMX版本:6.10.0

Keil uVision5版本:V5.38.0.0

实验目的

记录项目学习,学习在项目中进行的UART编程,体验串口的高效接收,设计一个实验,实现串口的接收。

场景使用原理图

在我的应用场景中,原理图的内容如下:

在这里插入图片描述

我将J4的接口的进行接线,这样就可以设计出一个串口发送,一个串口进行接收的实验。

UART的三种编程方式

结合 UART 硬件结构,有 3 种编程方法:

① 查询方式:

​ 要发送数据时,先把数据写入 TDR 寄存器,然后判断 TDR 为空再返回。当然也可以先判断 TDR 为空,再写入。要读取数据时,先判断 RDR 非空,再读取 RDR 得到数据。

② 中断方式:

​ 使用中断方式,效率更高,并且可以在接收数据时避免数据丢失。要发送数据时,使能“TXE”中断(发送寄存器空中断)。在 TXE 中断处理函数里,从程序的发送 buffer 里取出一个数据,写入 TDR。等再次发生 TXE 中断时,再从程序的发送buffer 里取出下一个数据写入 TDR。

对于接收数据,在一开始就使能“RXNE”中断(接收寄存器非空)。这样,UART 接收到一个数据就会触发中断,在中断程序里读取 RDR 得到数据,存入程序的接收 buffer。当程序向读取串口数据时,它直接读取接收 buffer 即可。这里涉及的“发送 buffer”、“接收 buffer”,特别适合使用“环形 buffer”。

③ DMA 方式:

​ 使用中断方式时,在传输、接收数据时,会发生中断,还需要 CPU 执行中断处理函数。有另外一种方法:DMA(Direct Memory Access),它可以直接在 2 个设备之间传递数据,无需 CPU 参与。

在这里插入图片描述

设置好 DMA(源、目的、地址增减方向、每次读取数据的长度、读取次数)后,DMA 就会自动地在 SRAM 和 UART 之间传递数据:

① 发送时:DMA 从 SRAM 得到数据,写入 UART 的 TDR 寄存器

② 接收时:DMA 从 UART 的 RDR 寄存器得到数据,写到 SRAM 去

③ 指定的数据传输完毕后,触发 DMA 中断;在数据传输过程中,没有中断,CPU 无需处理。

涉及使用的HAL库API如下:

  1. //查询方式://发送:
  2. HAL_UART_Transmit
  3. //接收:
  4. HAL_UART_Receive
  5. //中断方式://发送:
  6. HAL_UART_Transmit_IT
  7. HAL_UART_TxCpltCallback
  8. //接收:
  9. HAL_UART_Receive_IT
  10. HAL_UART_RxCpltCallback
  11. //DMA方式://发送:
  12. HAL_UART_Transmit_DMA
  13. HAL_UART_TxHalfCpltCallback
  14. HAL_UART_TxCpltCallback
  15. //接收:
  16. HAL_UART_Receive_DMA
  17. HAL_UART_RxHalfCpltCallback
  18. HAL_UART_RxCpltCallback
  19. // 错误
  20. HAL_UART_ErrorCallback
  21. HAL_UART_ErrorCallback

IDLE

IDLE,空闲的定义是:总线上在一个字节的时间内没有再接收到数据。UART 的 IDLE 中断何时发生?RxD 引脚一开始就是空闲的啊,难道 IDLE 中断一直产生?不是的。当我们使能 IDLE 中断后,它并不会立刻产生,而是:至少收到 1 个数据后,发现在一个字节的时间里,都没有接收到新数据,才会产生 IDLE 中断。我们使用 DMA 接收数据时,确实可以提高 CPU 的效率,但是“无法预知要接收多少数据”,而我们想尽快处理接收到的数据。怎么办?比如我想读取 100 字节的数据,但是接收到 60 字节后对方就不再发送数据了,怎么办?我们怎么判断数据传输中止了?可以使用IDLE 中断。在这种情况下,DMA 传输结束的条件有 3:

① 接收完指定数量的数据了,比如收到了 100 字节的数据了,HAL_UART_RxCpltCallback被调用

② 总线空闲了:HAL_UARTEx_RxEventCallback 被调用

③ 发生了错误:HAL_UART_ErrorCallback 被调用

使用 IDLE 状态来接收的函数有:

  1. //查询方式:
  2. //接收:
  3. HAL_UARTEx_ReceiveToIdle
  4. //回调函数:
  5. //根据返回参数 RxLen 判断是否接收完毕,还是因为空闲而返回
  6. //中断方式:
  7. //接收:
  8. HAL_UARTEx_ReceiveToIdle_IT
  9. //回调函数:
  10. 完毕:HAL_UART_RxCpltCallback
  11. 因为空闲而中止:
  12. HAL_UARTEx_RxEventCallback
  13. //DMA方式:
  14. //接收:
  15. HAL_UARTEx_ReceiveToIdle_DMA
  16. //回调函数:
  17. 传输一半:
  18. HAL_UART_RxHalfCpltCallback
  19. 完毕:
  20. HAL_UART_RxCpltCallback
  21. 因为空闲而中止:
  22. HAL_UARTEx_RxEventCallback
  23. // 错误
  24. HAL_UART_ErrorCallback

程序设计

① 使用 DMA+IDLE 中断的方式接收数据,它会把数据存入临时缓冲区;

② 在回调函数里:把临时缓冲器的数据写入队列,然后再次使能 DMA

③ APP读取队列:如果队列里没有数据则阻塞。

在这里插入图片描述

串口配置

打开配置工具,进行串口配置

在这里插入图片描述

在这里插入图片描述

配置中断

在这里插入图片描述

配置DMA

在这里插入图片描述

在这里插入图片描述

代码片段

按照上述的配置进行底层配置之后,我们写点代码,如下:

  1. xTaskCreate(
  2. CH1_UART2_TxTaskFunction,// 函数指针, 任务函数"ch1_uart2_tx_task",// 任务的名字200,// 栈大小,单位为word,200表示800字节NULL,// 调用任务函数时传入的参数
  3. osPriorityNormal,// 优先级NULL);// 任务句柄, 以后使用它来操作这个任务xTaskCreate(
  4. CH2_UART4_RxTaskFunction,// 函数指针, 任务函数"ch2_uart4_rx_task",// 任务的名字200,// 栈大小,单位为word,200表示800字节NULL,// 调用任务函数时传入的参数
  5. osPriorityNormal,// 优先级NULL);// 任务句柄, 以后使用它来操作这个任务

这里的创建的两个任务代码片段如下:

  1. static void CH1_UART2_TxTaskFunction( void *pvParameters )
  2. {
  3. uint8_t c = 0;
  4. while (1)
  5. {
  6. // send data
  7. HAL_UART_Transmit_DMA (&huart2, &c, 1);
  8. Wait_UART2_TxComplete(100);
  9. vTaskDelay(500);
  10. c++;
  11. }
  12. }
  13. static void CH2_UART4_RxTaskFunction( void *pvParameters )
  14. {
  15. uint8_t c = 0;
  16. int cnt = 0;
  17. char buf[100];
  18. HAL_StatusTypeDef err;
  19. UART4_Rx_Start();
  20. while (1)
  21. {
  22. // receive data
  23. err = UART4_GetData(&c);
  24. if(err == 0)
  25. {
  26. sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", c, cnt++);
  27. Draw_String(0, 0, buf, 0x0000ff00, 0);
  28. }
  29. else
  30. {
  31. HAL_UART_DMAStop(&huart4);
  32. }
  33. }
  34. }

其中开始发送和开始接收的代码片段:

  1. int UART4_GetData(uint8_t *pData)
  2. {
  3. xQueueReceive(g_xUART4_RX_Queue, pData, portMAX_DELAY);
  4. return 0;
  5. }
  6. void UART4_Rx_Start(void)
  7. {
  8. g_xUART4_RX_Queue = xQueueCreate(200, 1);
  9. HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
  10. }

编译、烧写、运行,可以看到开发板的 LED 不断闪烁,LCD 上不断的有数据过来,实验设计成功。

在这里插入图片描述

工程实验成功,后续将会继续记录项目中的实验,感谢关注。

本文中使用的测试工程

https://download.csdn.net/download/weixin_44317448/89195002


本文转载自: https://blog.csdn.net/weixin_44317448/article/details/138042887
版权归原作者 独处东汉 所有, 如有侵权,请联系我们删除。

“基于stm32的UART高效接收DMA+IDLE编程示例”的评论:

还没有评论