当前位置:   article > 正文

STM32/GD32学习指南-踩坑之(五)串口收发数据的三种方式:UART接收中断、UART+DMA空闲中断、UART+DMA超时中断,接收不定长数据,纯干货,有史以来最详细的讲解,附源码_gd32 uart串口接收数据

gd32 uart串口接收数据

一、 串口收发功能介绍

        常用的串口收发数据的方式一共两种,一种是不使用DMA,直接串口中断收发数据,另外一种是通过串口+DMA收发数据。

1. 串口接收数据

        对于使用DMA的串口数据收发,一般常用的还可以分为串口接收超时中断和串口空闲中断,所以细分的话,常用的主要有以下三种方式的串口数据接收使用方法:

(1)直接串口中断接收数据:

        也就是串口data寄存器非空即触发中断,中断标志为:USART_INT_RBNE;该种方法每接收一个字节的数据就会触发一次串口接收中断,当串口接收数据量小或是系统性能要求不高的情况下可以使用,如果串口接收数据量很大,频繁进入串口接收中断,会影响系统处理的性能,或者造成串口接收丢数的问题。

(2)串口空闲中断+DMA接收不定长数据:

        该种方式使用DMA(直接内存存取器)接收串口数据,中断标志为:USART_INT_IDLE;该种方式在串口RX处于空闲状态时,会触发中断,也就是一帧数据接收完成后,串口RX不再收到数据,即可触发空闲中断,此时可以将接收到的数据提取出来,由于使用DMA接收,所以可以接收不定长数据,并且数据接收过程中不需要CPU的参与,只是在数据接收完的时候,会触发一次空闲中断才涉及到CPU参与,可以大大的减少CPU的负荷,并且对于所有支持DMA的串口都可以使用串口空闲中断+DMA的方式接收数据。

(3)串口超时中断+DMA接收不定长数据(实测比空闲中断好用):

        超时中断的方式,中断标志为:USART_INT_RT;与上面的空闲中断其实非常类似,只不过空闲中断是硬件自带的,触发空闲中断的间隔也是固定的(1.5个字节,具体换算成时间的话还要涉及到时钟以及波特率的配置等),空闲间隔的判断不可以修改;而超时中断的超时时间是可以自己配置的,比如配置成10个字节的超时时间,那么收到一帧数据后的10个字节的时间内没有再收到新的数据,就会触发超时中断,此时可以通过DMA将接收的不定长数据取出。

        注意:GD32的芯片(我用的GD32 F470),只有USART支持超时中断,UART不支持超时中断,因为UART没有对应的超时RT寄存器,但是UART支持空闲中断。

2. 串口发送数据:

(1)直接串口发送数据:

        直接串口发送数据,只需要将数据写入对应串口的DATA寄存器即可,缺点的话是每次只能发送一个字节,发送不定长数据的话需要自己写个循环,比较占用CPU;优点的是操作比较简单,不容易出错

(2)串口+DMA发送数据:

        通过DMA的方式发送数据,需要将发送数据的buffer与DMA绑定,效率更高,可以降低CPU的占用。

二、 串口接收详细介绍(代码包含三种接收方法)

以下代码都是经过验证的,实际项目中在使用,稳定性良好,供大家参考

 1. Uart_hdl.h文件定义每个串口对应的GPIO以及DMA通道参还有函数声明,从上往下依次是:

(1)是否使能DMA接收和发送的枚举

(2)每个串口对应的GPIO引脚、时钟等

(3)每个串口对应的数据地址、DMA通道、DMA时钟的参数

(4)存储接收数据的环形buffer结构体

(5)DMA数据收发状态参数结构体(包含环形buffer结构体存储数据)

(6)一些串口配置和收发的函数声明

  1. /*!
  2. \file Uart_hdl.h
  3. \brief firmware functions to manage Uart
  4. \version 2024-01-10, V1.0.0
  5. \author tbj
  6. */
  7. #include "systick.h"
  8. #include "gd32f4xx.h"
  9. #include <stdio.h>
  10. #include <string.h>
  11. #ifndef UART_HAL_H
  12. #define UART_HAL_H
  13. #ifdef __cplusplus
  14. extern "C" {
  15. #endif
  16. typedef enum
  17. {
  18. DMA_TX_EB,
  19. DMA_TX_NEB,
  20. } UART_DMA_TX_STU;
  21. typedef enum
  22. {
  23. DMA_RX_EB,
  24. DMA_RX_NEB
  25. } UART_DMA_RX_STU;
  26. //串口引脚配置
  27. #define COMn 8U
  28. //usart0
  29. #define COM0 USART0
  30. #define COM0_CLK RCU_USART0
  31. #define COM0_TX_PIN GPIO_PIN_9
  32. #define COM0_RX_PIN GPIO_PIN_10
  33. #define COM0_TX_GPIO_PORT GPIOA
  34. #define COM0_RX_GPIO_PORT GPIOA
  35. #define COM0_TX_GPIO_CLK RCU_GPIOA
  36. #define COM0_RX_GPIO_CLK RCU_GPIOA
  37. #define COM0_AF GPIO_AF_7
  38. //usart1
  39. #define COM1 USART1
  40. #define COM1_CLK RCU_USART1
  41. #define COM1_TX_PIN GPIO_PIN_5
  42. #define COM1_RX_PIN GPIO_PIN_6
  43. #define COM1_TX_GPIO_PORT GPIOD
  44. #define COM1_RX_GPIO_PORT GPIOD
  45. #define COM1_TX_GPIO_CLK RCU_GPIOD
  46. #define COM1_RX_GPIO_CLK RCU_GPIOD
  47. #define COM1_AF GPIO_AF_7
  48. //usart2
  49. #define COM2 USART2
  50. #define COM2_CLK RCU_USART2
  51. #define COM2_TX_PIN GPIO_PIN_8
  52. #define COM2_RX_PIN GPIO_PIN_9
  53. #define COM2_TX_GPIO_PORT GPIOD
  54. #define COM2_RX_GPIO_PORT GPIOD
  55. #define COM2_TX_GPIO_CLK RCU_GPIOD
  56. #define COM2_RX_GPIO_CLK RCU_GPIOD
  57. #define COM2_AF GPIO_AF_7
  58. //uart3
  59. #define COM3 UART3
  60. #define COM3_CLK RCU_UART3
  61. #define COM3_TX_PIN GPIO_PIN_0
  62. #define COM3_RX_PIN GPIO_PIN_1
  63. #define COM3_TX_GPIO_PORT GPIOA
  64. #define COM3_RX_GPIO_PORT GPIOA
  65. #define COM3_TX_GPIO_CLK RCU_GPIOA
  66. #define COM3_RX_GPIO_CLK RCU_GPIOA
  67. #define COM3_AF GPIO_AF_8
  68. //uart4
  69. #define COM4 UART4
  70. #define COM4_CLK RCU_UART4
  71. #define COM4_TX_PIN GPIO_PIN_12
  72. #define COM4_RX_PIN GPIO_PIN_2
  73. #define COM4_TX_GPIO_PORT GPIOC
  74. #define COM4_RX_GPIO_PORT GPIOD
  75. #define COM4_TX_GPIO_CLK RCU_GPIOC
  76. #define COM4_RX_GPIO_CLK RCU_GPIOD
  77. #define COM4_AF GPIO_AF_8
  78. //usart5
  79. #define COM5 USART5
  80. #define COM5_CLK RCU_USART5
  81. #define COM5_TX_PIN GPIO_PIN_6
  82. #define COM5_RX_PIN GPIO_PIN_7
  83. #define COM5_TX_GPIO_PORT GPIOC
  84. #define COM5_RX_GPIO_PORT GPIOC
  85. #define COM5_TX_GPIO_CLK RCU_GPIOC
  86. #define COM5_RX_GPIO_CLK RCU_GPIOC
  87. #define COM5_AF GPIO_AF_8
  88. //uart6
  89. #define COM6 UART6
  90. #define COM6_CLK RCU_UART6
  91. #define COM6_TX_PIN GPIO_PIN_8
  92. #define COM6_RX_PIN GPIO_PIN_7
  93. #define COM6_TX_GPIO_PORT GPIOE
  94. #define COM6_RX_GPIO_PORT GPIOE
  95. #define COM6_TX_GPIO_CLK RCU_GPIOE
  96. #define COM6_RX_GPIO_CLK RCU_GPIOE
  97. #define COM6_AF GPIO_AF_8
  98. //uart7
  99. #define COM7 UART7
  100. #define COM7_CLK RCU_UART7
  101. #define COM7_TX_PIN GPIO_PIN_1
  102. #define COM7_RX_PIN GPIO_PIN_0
  103. #define COM7_TX_GPIO_PORT GPIOE
  104. #define COM7_RX_GPIO_PORT GPIOE
  105. #define COM7_TX_GPIO_CLK RCU_GPIOE
  106. #define COM7_RX_GPIO_CLK RCU_GPIOE
  107. #define COM7_AF GPIO_AF_8
  108. //****************************************************//
  109. //UART DMA相关
  110. //USART0:
  111. #define USART0_DATA_ADDR ((uint32_t)0x40011004)
  112. #define USART0_DMA_ADDR DMA1
  113. #define USART0_DMA_RCU RCU_DMA1
  114. #define USART0_DMA_TX_CHANNEL DMA_CH7
  115. #define USART0_DMA_RX_CHANNEL DMA_CH5//DMA_CH2
  116. #define USART0_DMA_SUBPERI DMA_SUBPERI4
  117. #define USART0_DMA_MEMORY DMA_MEMORY_1
  118. //USART1:
  119. #define USART1_DATA_ADDR ((uint32_t)0x40004404)
  120. #define USART1_DMA_ADDR DMA0
  121. #define USART1_DMA_RCU RCU_DMA0
  122. #define USART1_DMA_TX_CHANNEL DMA_CH6
  123. #define USART1_DMA_RX_CHANNEL DMA_CH5
  124. #define USART1_DMA_SUBPERI DMA_SUBPERI4
  125. #define USART1_DMA_MEMORY DMA_MEMORY_0
  126. //USART2:-与串口6的DMA通道冲突,串口2和串口6只能选其中一个使用DMA
  127. #define USART2_DATA_ADDR ((uint32_t)0x40004804)
  128. #define USART2_DMA_ADDR DMA0
  129. #define USART2_DMA_RCU RCU_DMA0
  130. #define USART2_DMA_TX_CHANNEL DMA_CH3
  131. #define USART2_DMA_RX_CHANNEL DMA_CH1
  132. #define USART2_DMA_SUBPERI DMA_SUBPERI4
  133. #define USART2_DMA_MEMORY DMA_MEMORY_0
  134. //UART3:
  135. #define UART3_DATA_ADDR ((uint32_t)0x40004C04)
  136. #define UART3_DMA_ADDR DMA0
  137. #define UART3_DMA_RCU RCU_DMA0
  138. #define UART3_DMA_TX_CHANNEL DMA_CH4
  139. #define UART3_DMA_RX_CHANNEL DMA_CH2
  140. #define UART3_DMA_SUBPERI DMA_SUBPERI4
  141. #define UART3_DMA_MEMORY DMA_MEMORY_0
  142. //UART4:
  143. #define UART4_DATA_ADDR ((uint32_t)0x40005004)
  144. #define UART4_DMA_ADDR DMA0
  145. #define UART4_DMA_RCU RCU_DMA0
  146. #define UART4_DMA_TX_CHANNEL DMA_CH7
  147. #define UART4_DMA_RX_CHANNEL DMA_CH0
  148. #define UART4_DMA_SUBPERI DMA_SUBPERI4
  149. #define UART4_DMA_MEMORY DMA_MEMORY_0
  150. //USART5:
  151. #define USART5_DATA_ADDR ((uint32_t)0x40011404)
  152. #define USART5_DMA_ADDR DMA1
  153. #define USART5_DMA_RCU RCU_DMA1
  154. #define USART5_DMA_TX_CHANNEL DMA_CH6
  155. #define USART5_DMA_RX_CHANNEL DMA_CH1
  156. #define USART5_DMA_SUBPERI DMA_SUBPERI5
  157. #define USART5_DMA_MEMORY DMA_MEMORY_1
  158. //UART6:-与串口2的DMA通道冲突,串口2和串口6只能选其中一个使用DMA
  159. #define UART6_DATA_ADDR ((uint32_t)0x40007804)
  160. #define UART6_DMA_ADDR DMA0
  161. #define UART6_DMA_RCU RCU_DMA0
  162. #define UART6_DMA_TX_CHANNEL DMA_CH1
  163. #define UART6_DMA_RX_CHANNEL DMA_CH3
  164. #define UART6_DMA_SUBPERI DMA_SUBPERI5
  165. #define UART6_DMA_MEMORY DMA_MEMORY_0
  166. //UART7:-与串口4的DMA通道冲突,串口4和串口7只能选其中一个使用DMA
  167. #define UART7_DATA_ADDR ((uint32_t)0x40007C04)
  168. #define UART7_DMA_ADDR DMA0
  169. #define UART7_DMA_RCU RCU_DMA0
  170. #define UART7_DMA_TX_CHANNEL DMA_CH0
  171. #define UART7_DMA_RX_CHANNEL DMA_CH6
  172. #define UART7_DMA_SUBPERI DMA_SUBPERI5
  173. #define UART7_DMA_MEMORY DMA_MEMORY_0
  174. //****************************************************//
  175. #define RINGBUFF_LEN 5120
  176. //环形buffer结构体
  177. typedef struct
  178. {
  179. uint32_t Head;
  180. uint32_t Tail;
  181. uint32_t Length;
  182. uint8_t Ring_Buff[RINGBUFF_LEN];
  183. } RingBuff_t;
  184. #define BUFFER_SIZE 5120
  185. //串口DMA收发状态和数据长度
  186. typedef struct{
  187. uint8_t uart_dma_rx_state; //串口是否使用DMA接收
  188. uint8_t uart_dma_tx_state; //串口是否使用DMA发送
  189. uint16_t uart_dma_rx_len; //串口使用DMA接收时,超时中断后接收的数据长度
  190. RingBuff_t uart_rx_buffer; //串口接收数据缓存区,是一个环形buffer,使用DMA接收时,按正常缓存区使用,不适用DMA接收时,按照环形buffer功能使用
  191. uint8_t uart_tx_buffer[BUFFER_SIZE];//串口使用DMA发送时的发送缓存区
  192. }uart_state;
  193. //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓普通串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
  194. //串口初始化
  195. void uart_init(uint8_t uart_id, uint32_t baudval, uint8_t nvic_irq_pre_priority, uint8_t nvic_irq_sub_priority,
  196. UART_DMA_TX_STU dma_tx_stu, UART_DMA_RX_STU dma_rx_stu);
  197. //串口发送数据-每次发送一字节
  198. void uart_send_byte(uint8_t uart_id, uint8_t ch);
  199. //串口发送数据-每次buf
  200. void uart_send_buf(uint8_t uart_id, uint8_t *ch, uint16_t len);
  201. //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓DMA串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
  202. //初始化串口DMA tx配置
  203. void usart_dma_tx_init(uint8_t uart_id);
  204. //初始化串口DMA rx配置
  205. void usart_dma_rx_init(uint8_t uart_id);
  206. //DMA串口发送数据
  207. void uart_dma_send(uint8_t uart_id, uint8_t *tx_buffer, uint32_t len);
  208. //获取DMA接收数据的长度
  209. unsigned int get_uart_dma_recv_data_size(uint8_t uart_id);
  210. //初始化串口相关状态、DMA、buffer等
  211. void init_uart_state(uint8_t uart_id);
  212. //重新配置DMA rx,为了重新接收数据
  213. void uart_dma_rx_refcg(uint8_t uart_id);
  214. #ifdef __cplusplus
  215. }
  216. #endif
  217. #endif /* UART_HAL_H */

2. Uart_hdl.c文件主要对Uart_hdl.h文件中声明的函数进行实现,主要包括串口初始化、DMA初始化、串口发送等功能函数,具体如下:

(1)首先定义每个串口状态结构体的对象usart0_state-usart7_state(包含存储数据的buffer在里面),同时定义一个指针数组(uart_dma_state_ptr)指向对应的串口状态对象,方便后续对其操作,然后定义了每个串口是否使用DMA接收数据的状态数组uart_rx_dma_en_stu;

(2)定义串口相应配置参数的若干个数组,主要包括串口号、串口对应GPIO号以及Bank时钟、串口中断号、复用情况等等;

(3)定义串口对应DMA通道以及时钟、地址等参数的若干个数组,这些数组都是为了后续方便统一操作配置多个串口使用;

(4)初始化串口相关参数的方法:void uart_init(uint8_t uart_id, uint32_t baudval, uint8_t nvic_irq_pre_priority, uint8_t nvic_irq_sub_priority, 
                                UART_DMA_TX_STU dma_tx_stu, UART_DMA_RX_STU dma_rx_stu)

通过传入串口号,对不同的串口进行初始化配置,里面包含了GPIO复用串口的相关配置、串口波特率、中断使能、优先级、串口收发使能等配置

同时判断当前初始化的串口是否使用DMA收发,如果使用DMA收发,则同时进行DMA收发的配置操作,其中判断的0,1,2,5串口为USART,可使用串口超时中断和空闲中断,其余的为UART,只能使用空闲中断;

(5)串口单字节数据发送方法:uart_send_byte(uint8_t uart_id, uint8_t ch),函数中的定时器timer1主要是用来防止串口发送数据过程中发生卡死的现象,无法跳出while循环,等待2s后如果未发送完,触发定时器中断,手动将发送完成标志位置位;

(6)串口不定长数据发送方法:void uart_send_buf(uint8_t uart_id, uint8_t *ch, uint16_t len)

(7)printf打印输出重映射方法:int fputc(int ch, FILE *f),可将printf函数打印信息通过串口发出;

(8)串口DMA发送配置方法:void usart_dma_tx_init(uint8_t uart_id),主要配置DMA数据发送方向、发送存储器(发送数据存储的buffer,即之前定义的环形buffer)、DMA使能等;

(9)串口DMA接收配置方法:void usart_dma_rx_init(uint8_t uart_id),主要配置DMA数据接收方向、接收存储器(接收数据存储的buffer,即之前定义的环形buffer)、DMA使能等;

(10)串口DMA数据发送方法:void uart_dma_send(uint8_t uart_id, uint8_t *tx_buffer, uint32_t len),主要将发送数据buffer绑定至DMA,并使能DMA发送,然后等待发送完成,这里面的timer1与(5)中的timer1功能一致;

(11)获取DMA接收数据长度的方法:unsigned int get_uart_dma_recv_data_size(uint8_t uart_id),返回当前串口DMA接收数据的长度

(12)串口DMA接收Rx重配置方法:void uart_dma_rx_refcg(uint8_t uart_id),每当接收完一包数据并且提取出来后,需要重新进入接收状态,这时候就需要重置DMA的一些配置

(13)初始化串口状态对象方法:void init_uart_state(uint8_t uart_id),在初始化串口配置前,先将串口串口、接收、发送buffer等对象置零初始化;

  1. /*!
  2. \file Uart_hdl.c
  3. \brief firmware functions to manage Uart
  4. \version 2024-01-10, V1.0.0
  5. \author tbj
  6. */
  7. #include "Uart_hdl.h"
  8. //存储当前发送数据的串口基地址
  9. uint32_t curr_send_uart_addr = 0;
  10. //串口状态对象,包括DMA收发使用情况,串口收发缓存区等
  11. uart_state usart0_state, usart1_state, usart2_state, uart3_state, uart4_state, usart5_state, uart6_state, uart7_state;
  12. //串口状态对象指针数组(为了方便其他函数调用建立的)
  13. uart_state *uart_dma_state_ptr[COMn] = {&usart0_state, &usart1_state, &usart2_state, &uart3_state, &uart4_state, &usart5_state, &uart6_state, &uart7_state};
  14. //串口是否使用DMA通道收数据,用于串口中断函数判断使用
  15. UART_DMA_RX_STU uart_rx_dma_en_stu [COMn] = {DMA_RX_NEB, DMA_RX_NEB, DMA_RX_NEB, DMA_RX_NEB, DMA_RX_NEB, DMA_RX_NEB, DMA_RX_NEB, DMA_RX_NEB};
  16. 串口相关参数列表
  17. //串口编号
  18. static uint32_t COM_NUM[COMn] = {USART0, USART1, USART2, UART3, UART4, USART5, UART6, UART7};
  19. //串口中断号
  20. static uint32_t COM_IRQn[COMn] = {USART0_IRQn, USART1_IRQn, USART2_IRQn, UART3_IRQn, UART4_IRQn, USART5_IRQn, UART6_IRQn, UART7_IRQn};
  21. //GPIO复用功能,GPIO_AF_7和GPIO_AF_8将GPIO复用为串口
  22. static uint32_t COM_AF[COMn] = {COM0_AF, COM1_AF, COM2_AF, COM3_AF, COM4_AF, COM5_AF, COM6_AF, COM7_AF};
  23. //串口TX对应的GPIO号
  24. static uint32_t COM_TX_PIN[COMn] = {COM0_TX_PIN, COM1_TX_PIN, COM2_TX_PIN, COM3_TX_PIN, COM4_TX_PIN, COM5_TX_PIN, COM6_TX_PIN, COM7_TX_PIN};
  25. //串口RX对应的GPIO号
  26. static uint32_t COM_RX_PIN[COMn] = {COM0_RX_PIN, COM1_RX_PIN, COM2_RX_PIN, COM3_RX_PIN, COM4_RX_PIN, COM5_RX_PIN, COM6_RX_PIN, COM7_RX_PIN};
  27. //串口TX对应的GPIO端口分组
  28. static uint32_t COM_TX_GPIO_PORT[COMn] = {COM0_TX_GPIO_PORT, COM1_TX_GPIO_PORT, COM2_TX_GPIO_PORT, COM3_TX_GPIO_PORT, COM4_TX_GPIO_PORT, COM5_TX_GPIO_PORT, COM6_TX_GPIO_PORT, COM7_TX_GPIO_PORT};
  29. //串口RX对应的GPIO端口分组
  30. static uint32_t COM_RX_GPIO_PORT[COMn] = {COM0_RX_GPIO_PORT, COM1_RX_GPIO_PORT, COM2_RX_GPIO_PORT, COM3_RX_GPIO_PORT, COM4_RX_GPIO_PORT, COM5_RX_GPIO_PORT, COM6_RX_GPIO_PORT, COM7_RX_GPIO_PORT};
  31. //串口对应时钟
  32. static rcu_periph_enum COM_CLK[COMn] = {COM0_CLK, COM1_CLK, COM2_CLK, COM3_CLK, COM4_CLK, COM5_CLK, COM6_CLK, COM7_CLK};
  33. //复用为串口TX的GPIO的时钟
  34. static rcu_periph_enum COM_TX_GPIO_CLK[COMn] = {COM0_TX_GPIO_CLK, COM1_TX_GPIO_CLK, COM2_TX_GPIO_CLK, COM3_TX_GPIO_CLK, COM4_TX_GPIO_CLK, COM5_TX_GPIO_CLK, COM6_TX_GPIO_CLK, COM7_TX_GPIO_CLK};
  35. //复用为串口RX的GPIO的时钟
  36. static rcu_periph_enum COM_RX_GPIO_CLK[COMn] = {COM0_RX_GPIO_CLK, COM1_RX_GPIO_CLK, COM2_RX_GPIO_CLK, COM3_RX_GPIO_CLK, COM4_RX_GPIO_CLK, COM5_RX_GPIO_CLK, COM6_RX_GPIO_CLK, COM7_RX_GPIO_CLK};
  37. 串口DMA相关参数列表
  38. //外设基地址-数据寄存器
  39. static uint32_t COM_DATA_ADDR[COMn] = {USART0_DATA_ADDR, USART1_DATA_ADDR, USART2_DATA_ADDR, UART3_DATA_ADDR, UART4_DATA_ADDR, USART5_DATA_ADDR, UART6_DATA_ADDR, UART7_DATA_ADDR};
  40. //DMA基地址
  41. uint32_t COM_DMA_ADDR[COMn] = {USART0_DMA_ADDR, USART1_DMA_ADDR, USART2_DMA_ADDR, UART3_DMA_ADDR, UART4_DMA_ADDR, USART5_DMA_ADDR, UART6_DMA_ADDR, UART7_DMA_ADDR};
  42. //DMA时钟
  43. static rcu_periph_enum COM_DMA_RCU[COMn] = {USART0_DMA_RCU, USART1_DMA_RCU, USART2_DMA_RCU, UART3_DMA_RCU, UART4_DMA_RCU, USART5_DMA_RCU, UART6_DMA_RCU, UART7_DMA_RCU};
  44. //DMA TX通道
  45. dma_channel_enum COM_DMA_TX_CHANNEL[COMn] = {USART0_DMA_TX_CHANNEL, USART1_DMA_TX_CHANNEL, USART2_DMA_TX_CHANNEL, UART3_DMA_TX_CHANNEL, UART4_DMA_TX_CHANNEL, USART5_DMA_TX_CHANNEL, UART6_DMA_TX_CHANNEL, UART7_DMA_TX_CHANNEL};
  46. //DMA RX通道
  47. dma_channel_enum COM_DMA_RX_CHANNEL[COMn] = {USART0_DMA_RX_CHANNEL, USART1_DMA_RX_CHANNEL, USART2_DMA_RX_CHANNEL, UART3_DMA_RX_CHANNEL, UART4_DMA_RX_CHANNEL, USART5_DMA_RX_CHANNEL, UART6_DMA_RX_CHANNEL, UART7_DMA_RX_CHANNEL};
  48. //DMA总线通道
  49. static dma_subperipheral_enum COM_DMA_SUBPERI[COMn] = {USART0_DMA_SUBPERI, USART1_DMA_SUBPERI, USART2_DMA_SUBPERI, UART3_DMA_SUBPERI, UART4_DMA_SUBPERI, USART5_DMA_SUBPERI, UART6_DMA_SUBPERI, UART7_DMA_SUBPERI};
  50. //static uint32_t COM_DMA_MEMORY[COMn] = {USART0_DMA_MEMORY, USART1_DMA_MEMORY, USART2_DMA_MEMORY, UART3_DMA_MEMORY, UART4_DMA_MEMORY, USART5_DMA_MEMORY, UART6_DMA_MEMORY, UART7_DMA_MEMORY};
  51. //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓普通串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
  52. /**
  53. *功能:初始化串口相关参数
  54. *入参1:串口号
  55. *入参2:波特率
  56. *入参3:串口中断抢占优先级
  57. *入参4:串口中断响应优先级
  58. *入参5:串口DMA发送使能
  59. *入参6:串口DMA接收使能
  60. */
  61. void uart_init(uint8_t uart_id, uint32_t baudval, uint8_t nvic_irq_pre_priority, uint8_t nvic_irq_sub_priority,
  62. UART_DMA_TX_STU dma_tx_stu, UART_DMA_RX_STU dma_rx_stu)
  63. {
  64. //清空使用串口的状态和缓存区
  65. init_uart_state(uart_id);
  66. //使能GPIO时钟
  67. if(COM_TX_GPIO_CLK[uart_id] == COM_RX_GPIO_CLK[uart_id])
  68. {
  69. //TX和RX是同一组GPIO的时钟
  70. rcu_periph_clock_enable(COM_TX_GPIO_CLK[uart_id]);
  71. }else{
  72. //TX和RX不是同一组GPIO的时钟
  73. rcu_periph_clock_enable(COM_TX_GPIO_CLK[uart_id]);
  74. rcu_periph_clock_enable(COM_RX_GPIO_CLK[uart_id]);
  75. }
  76. //使能串口时钟
  77. rcu_periph_clock_enable(COM_CLK[uart_id]);
  78. //配置GPIO复用为串口TX
  79. gpio_af_set(COM_TX_GPIO_PORT[uart_id], COM_AF[uart_id], COM_TX_PIN[uart_id]);
  80. //配置GPIO复用为串口RX
  81. gpio_af_set(COM_RX_GPIO_PORT[uart_id], COM_AF[uart_id], COM_RX_PIN[uart_id]);
  82. //配置TX引脚模式,上下拉,速率等
  83. gpio_mode_set(COM_TX_GPIO_PORT[uart_id], GPIO_MODE_AF, GPIO_PUPD_PULLUP,COM_TX_PIN[uart_id]);
  84. gpio_output_options_set(COM_TX_GPIO_PORT[uart_id], GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,COM_TX_PIN[uart_id]);
  85. //配置RX引脚模式,上下拉,速率等
  86. gpio_mode_set(COM_RX_GPIO_PORT[uart_id], GPIO_MODE_AF, GPIO_PUPD_PULLUP,COM_RX_PIN[uart_id]);
  87. gpio_output_options_set(COM_RX_GPIO_PORT[uart_id], GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,COM_RX_PIN[uart_id]);
  88. //配置串口相关参数
  89. usart_deinit(COM_NUM[uart_id]);
  90. usart_baudrate_set(COM_NUM[uart_id], baudval);
  91. usart_receive_config(COM_NUM[uart_id], USART_RECEIVE_ENABLE);
  92. usart_transmit_config(COM_NUM[uart_id], USART_TRANSMIT_ENABLE);
  93. usart_enable(COM_NUM[uart_id]);
  94. //设置串口中断优先级
  95. nvic_irq_enable(COM_IRQn[uart_id], nvic_irq_pre_priority, nvic_irq_sub_priority);
  96. //根据入参,选择是否使用DMA
  97. if(dma_rx_stu == DMA_RX_EB){
  98. // 接收超时设置,100个波特率的比特位(10个字节的超时中断触发)
  99. usart_receiver_timeout_threshold_config(COM_NUM[uart_id], 100);
  100. //使用DMA收数的话,要使能串口接收超时中断
  101. //usart_interrupt_enable(COM_NUM[uart_id], USART_INT_RT);
  102. usart_interrupt_enable(COM_NUM[uart_id], USART_INT_IDLE);
  103. //使能串口超时功能
  104. usart_receiver_timeout_enable(COM_NUM[uart_id]);
  105. //初始化串口接收DMA功能
  106. usart_dma_rx_init(uart_id);
  107. //将对应串口接收使用DMA的情况进行更新,方便别的地方使用
  108. uart_rx_dma_en_stu[uart_id] = DMA_RX_EB;
  109. }else{
  110. //不使用DMA收数时,要使能串口非空中断
  111. usart_interrupt_enable(COM_NUM[uart_id], USART_INT_RBNE);
  112. //将对应串口接收使用DMA的情况进行更新,方便别的地方使用
  113. uart_rx_dma_en_stu[uart_id] = DMA_RX_NEB;
  114. }
  115. if(dma_tx_stu == DMA_TX_EB){
  116. //初始化串口发送DMA功能
  117. usart_dma_tx_init(uart_id);
  118. }
  119. }
  120. /**
  121. *功能:串口数据发送函数
  122. *入参1:串口号
  123. *入参2:要发送的字节数据
  124. */
  125. void uart_send_byte(uint8_t uart_id, uint8_t ch)
  126. {
  127. //串口发送数据
  128. curr_send_uart_addr = COM_NUM[uart_id];
  129. usart_data_transmit(COM_NUM[uart_id], ch);
  130. //增加定时器计时,超过一秒没有发送完,清除TC标志位,跳出函数
  131. timer_enable(TIMER1);
  132. while(usart_flag_get(COM_NUM[uart_id], USART_FLAG_TBE) == RESET);
  133. timer_disable(TIMER1);
  134. timer_counter_value_config(TIMER1, 0);
  135. }
  136. /**
  137. *功能:串口buf数据发送函数
  138. *入参1:串口号
  139. *入参2:要发送的字节数据
  140. */
  141. void uart_send_buf(uint8_t uart_id, uint8_t *ch, uint16_t len)
  142. {
  143. for(uint32_t i = 0; i < len; i++){
  144. uart_send_byte(uart_id, *(ch + i));
  145. }
  146. }
  147. /**
  148. *功能:printf函数重映射
  149. */
  150. int fputc(int ch, FILE *f)
  151. {
  152. while(usart_flag_get(COM1, USART_FLAG_TBE) == RESET ){;}
  153. usart_data_transmit(COM1, (uint8_t)ch);
  154. return ch;
  155. }
  156. //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓DMA串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
  157. /**
  158. *功能:初始化串口DMA tx配置
  159. *入参1:串口号
  160. *返回值:是否为复位指令
  161. */
  162. void usart_dma_tx_init(uint8_t uart_id)
  163. {
  164. dma_single_data_parameter_struct dma_init_struct;
  165. /* enable dma_addr */
  166. rcu_periph_clock_enable(COM_DMA_RCU[uart_id]);
  167. /* deinitialize DMA channe7(USART0 TX) */
  168. dma_deinit(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]);
  169. dma_init_struct.direction = DMA_MEMORY_TO_PERIPH; //存储器到外设方向
  170. dma_init_struct.memory0_addr = (uint32_t)uart_dma_state_ptr[uart_id]->uart_tx_buffer; //存储器地址
  171. dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //存储器地址自增
  172. dma_init_struct.number = 0; //传输数据个数,赋值BUFFER_SIZE时,上电初始化DMA会发送一堆0;
  173. dma_init_struct.periph_addr = COM_DATA_ADDR[uart_id]; //外设基地址-数据寄存器
  174. dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; //外设地址自增关闭-需要固定
  175. dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; //外设数据位宽8bit
  176. dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //设置优先级最高
  177. dma_single_data_mode_init(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], &dma_init_struct); //初始化对应DMA的对应通道
  178. dma_channel_subperipheral_select(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], COM_DMA_SUBPERI[uart_id]); //使能总得通道
  179. /* configure DMA mode */
  180. dma_circulation_disable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]); //关闭DMA循环模式配置
  181. usart_dma_transmit_config(COM_NUM[uart_id], USART_DENT_ENABLE); //DMA串口发送配置
  182. /* 使能 DMA 通道 */
  183. dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]);
  184. }
  185. /**
  186. *功能:初始化串口DMA rx配置
  187. *入参1:串口号
  188. *返回值:是否为复位指令
  189. */
  190. void usart_dma_rx_init(uint8_t uart_id){
  191. dma_single_data_parameter_struct dma_init_struct;
  192. /* enable dma_addr */
  193. rcu_periph_clock_enable(COM_DMA_RCU[uart_id]);
  194. /* deinitialize DMA channe7(USART0 TX) */
  195. dma_deinit(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
  196. dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; //外设到存储器
  197. dma_init_struct.memory0_addr = (uint32_t)uart_dma_state_ptr[uart_id]->uart_rx_buffer.Ring_Buff; //存储器地址
  198. dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //存储器地址自增
  199. dma_init_struct.number = RINGBUFF_LEN; //传输数据个数
  200. dma_init_struct.periph_addr = COM_DATA_ADDR[uart_id]; //外设基地址-数据寄存器
  201. dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; //外设地址自增关闭-需要固定
  202. dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; //外设数据位宽8bit
  203. dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //设置优先级最高
  204. dma_single_data_mode_init(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], &dma_init_struct); //初始化对应DMA的对应通道
  205. dma_channel_subperipheral_select(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], COM_DMA_SUBPERI[uart_id]); //使能总得通道
  206. /* configure DMA mode */
  207. dma_circulation_disable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]); //关闭DMA循环模式配置
  208. //dma_circulation_enable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]); //关闭DMA循环模式配置
  209. usart_dma_receive_config(COM_NUM[uart_id], USART_DENR_ENABLE); //DMA串口接收配置
  210. /* 使能 DMA 通道 */
  211. dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
  212. }
  213. /**
  214. *功能:DMA串口发送数据
  215. *入参1:存储数据的buffer指针
  216. *入参2:要发送的数据长度
  217. *返回值:是否为复位指令
  218. */
  219. void uart_dma_send(uint8_t uart_id, uint8_t *tx_buffer, uint32_t len)
  220. {
  221. dma_channel_disable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]);
  222. dma_flag_clear(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], DMA_FLAG_FTF); // 清除DMA传输完成标志位
  223. dma_memory_address_config(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], DMA_MEMORY_0, (uint32_t)(tx_buffer)); // 存储器地址
  224. dma_transfer_number_config(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], len);
  225. dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]); // 使能DMA传输
  226. // 等待传输完成
  227. //增加定时器计时,超过一秒没有发送完,清除TC标志位,跳出函数
  228. timer_enable(TIMER1);
  229. while(dma_flag_get(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], DMA_FLAG_FTF) == RESET);
  230. timer_disable(TIMER1);
  231. timer_counter_value_config(TIMER1, 0);
  232. }
  233. /**
  234. *功能:获取DMA接收数据的长度
  235. *入参1:串口号
  236. *返回值:当前DMA接收数据的长度
  237. */
  238. unsigned int get_uart_dma_recv_data_size(uint8_t uart_id)
  239. {
  240. /*
  241. dma_transfer_number_get(DMA_CH2);是获取当前指针计数值,
  242. 用内存缓冲区大小 - 此计数值 = 接收到的数据长度(这里单位为字节)。
  243. 需要说明下在读取数据长度的时候需要先把接收DMA关闭,读取完了或者是数据处理完了在打开接收DMA,防止在处理的过程中有数据到来而出错。
  244. */
  245. return (RINGBUFF_LEN - (dma_transfer_number_get(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id])));
  246. }
  247. /**
  248. *功能:重新配置DMA rx
  249. *入参1:串口号
  250. *返回值:是否为复位指令
  251. */
  252. void uart_dma_rx_refcg(uint8_t uart_id)
  253. {
  254. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  255. dma_channel_disable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
  256. dma_memory_address_config(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], DMA_MEMORY_0,
  257. (uint32_t)(uart_dma_state_ptr[uart_id]->uart_rx_buffer.Ring_Buff)); // 存储器地址
  258. dma_transfer_number_config(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], RINGBUFF_LEN);
  259. uart_dma_state_ptr[uart_id]->uart_dma_rx_state = 0;
  260. uart_dma_state_ptr[uart_id]->uart_dma_rx_len = 0;
  261. // 使能通道
  262. dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
  263. }
  264. /**
  265. *功能:初始化串口相关状态、DMA、buffer等
  266. *入参1:串口号
  267. */
  268. void init_uart_state(uint8_t uart_id){
  269. memset(uart_dma_state_ptr[uart_id], 0, sizeof(uart_state));
  270. }

3. 中断处理函数:"gd32f4xx_it.c",主要实现了串口接收中断以及定时器中断

(1)extern相关的一些变量,均为Uart_hdl.c中定义的一些串口配置相关的数组,用于方便在中断处理函数中操作;

(2)串口0、1、2、5为USART,判断的中断标志为超时中断,即USART_INT_FLAG_RT(当然也可以使用空闲中断USART_INT_FLAG_IDLE);串口3、4、6、7为UART,不支持超时中断,所以判断的是空闲中断标志,即USART_INT_FLAG_IDLE;

(3)每个串口中断函数中,还分为了两种情况,一种是使用DMA接收数据,一种是不使用DMA直接触发串口接收中断来接收数据;其中使用DMA的方式接收数据时,是在中断函数种将接收数据标志位置位,具体接收数据已经自动存储到DMA初始化时绑定的接收buffer中,而直接串口中断接收的数据,每次中断接收一个字节数据,直接在中断函数调用Write_RingBuff方法中存储到环形buffer中;

(4)timer1中断,触发中断来将发送完成标志位置位,防止串口发发送过程中出现以外卡死的问题;

  1. /*!
  2. \file gd32f4xx_it.c
  3. \brief interrupt service routines
  4. \version 2023-05-10, V1.0.0
  5. \author tbj
  6. */
  7. #include "gd32f4xx_it.h"
  8. #include "task_hdl.h"
  9. //*******************************串口中断相关*****************************//
  10. //串口DMA状态结构体,包含接收数据状态,buffer等
  11. extern uart_state *uart_dma_state_ptr;
  12. //DMA基地址
  13. extern uint32_t COM_DMA_ADDR[COMn];
  14. //DMA TX通道
  15. extern dma_channel_enum COM_DMA_TX_CHANNEL[COMn];
  16. //DMA RX通道
  17. extern dma_channel_enum COM_DMA_RX_CHANNEL[COMn];
  18. //串口是否使用DMA通道收数据,用于串口中断函数判断使用
  19. extern UART_DMA_RX_STU uart_rx_dma_en_stu[COMn];
  20. //存储当前发送数据的串口基地址
  21. extern uint32_t curr_send_uart_addr;
  22. /*!
  23. \brief this function handles NMI exception
  24. \param[in] none
  25. \param[out] none
  26. \retval none
  27. */
  28. void NMI_Handler(void)
  29. {
  30. }
  31. /*!
  32. \brief this function handles HardFault exception
  33. \param[in] none
  34. \param[out] none
  35. \retval none
  36. */
  37. void HardFault_Handler(void)
  38. {
  39. /* if Hard Fault exception occurs, go to infinite loop */
  40. while (1){
  41. }
  42. }
  43. /*!
  44. \brief this function handles MemManage exception
  45. \param[in] none
  46. \param[out] none
  47. \retval none
  48. */
  49. void MemManage_Handler(void)
  50. {
  51. /* if Memory Manage exception occurs, go to infinite loop */
  52. while (1){
  53. }
  54. }
  55. /*!
  56. \brief this function handles BusFault exception
  57. \param[in] none
  58. \param[out] none
  59. \retval none
  60. */
  61. void BusFault_Handler(void)
  62. {
  63. /* if Bus Fault exception occurs, go to infinite loop */
  64. while (1){
  65. }
  66. }
  67. /*!
  68. \brief this function handles UsageFault exception
  69. \param[in] none
  70. \param[out] none
  71. \retval none
  72. */
  73. void UsageFault_Handler(void)
  74. {
  75. /* if Usage Fault exception occurs, go to infinite loop */
  76. while (1){
  77. }
  78. }
  79. /*!
  80. \brief this function handles SVC exception
  81. \param[in] none
  82. \param[out] none
  83. \retval none
  84. */
  85. void SVC_Handler(void)
  86. {
  87. }
  88. /*!
  89. \brief this function handles DebugMon exception
  90. \param[in] none
  91. \param[out] none
  92. \retval none
  93. */
  94. void DebugMon_Handler(void)
  95. {
  96. }
  97. /*!
  98. \brief this function handles PendSV exception
  99. \param[in] none
  100. \param[out] none
  101. \retval none
  102. */
  103. void PendSV_Handler(void)
  104. {
  105. }
  106. /*!
  107. \brief this function handles SysTick exception
  108. \param[in] none
  109. \param[out] none
  110. \retval none
  111. */
  112. void SysTick_Handler(void)
  113. {
  114. //延时计数使用
  115. delay_decrement();
  116. //系统开机计时
  117. add_system_time();
  118. //任务处理状态监测
  119. TaskTick();
  120. }
  121. /**
  122. *功能:串口0中断接收函数
  123. */
  124. void USART0_IRQHandler(void)
  125. {
  126. //判断是使用DMA收数还是正常收数
  127. if(uart_rx_dma_en_stu[0] == DMA_RX_EB){
  128. /* UART接收超时中断 */
  129. if ((usart_interrupt_flag_get(USART0, USART_INT_FLAG_RT) != RESET) &&
  130. (usart_flag_get(USART0, USART_FLAG_RT) != RESET))
  131. {
  132. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  133. dma_channel_disable(COM_DMA_ADDR[0], COM_DMA_RX_CHANNEL[0]);
  134. //清除DMA传输完成标志位
  135. dma_flag_clear(COM_DMA_ADDR[0], COM_DMA_RX_CHANNEL[0], DMA_FLAG_FTF);
  136. /* Clear receiver timeout flag */
  137. //usart_flag_clear(BLE_UART, USART_FLAG_RT);
  138. usart_interrupt_flag_clear(USART0,USART_INT_FLAG_RT);
  139. usart_data_receive(USART0); /* 清除接收完成标志位 */
  140. // 设置接收的数据长度
  141. uart_dma_state_ptr[0].uart_dma_rx_len = get_uart_dma_recv_data_size(0);
  142. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  143. uart_dma_state_ptr[0].uart_dma_rx_state = 1;
  144. }
  145. }else{
  146. if( usart_interrupt_flag_get(USART0,USART_INT_FLAG_RBNE) != RESET)
  147. {
  148. uint8_t data;
  149. usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
  150. data = usart_data_receive(USART0);
  151. Write_RingBuff(&data, 1, &uart_dma_state_ptr[0].uart_rx_buffer);
  152. }
  153. }
  154. }
  155. /**
  156. *功能:串口1中断接收函数
  157. */
  158. void USART1_IRQHandler(void)
  159. {
  160. //判断是使用DMA收数还是正常收数
  161. if(uart_rx_dma_en_stu[1] == DMA_RX_EB){
  162. /* UART接收超时中断 */
  163. if ((usart_interrupt_flag_get(USART1, USART_INT_FLAG_RT) != RESET) &&
  164. (usart_flag_get(USART1, USART_FLAG_RT) != RESET))
  165. {
  166. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  167. dma_channel_disable(COM_DMA_ADDR[1], COM_DMA_RX_CHANNEL[1]);
  168. //清除DMA传输完成标志位
  169. dma_flag_clear(COM_DMA_ADDR[1], COM_DMA_RX_CHANNEL[1], DMA_FLAG_FTF);
  170. /* Clear receiver timeout flag */
  171. //usart_flag_clear(BLE_UART, USART_FLAG_RT);
  172. usart_interrupt_flag_clear(USART1,USART_INT_FLAG_RT);
  173. usart_data_receive(USART1); /* 清除接收完成标志位 */
  174. // 设置接收的数据长度
  175. uart_dma_state_ptr[1].uart_dma_rx_len = get_uart_dma_recv_data_size(1);
  176. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  177. uart_dma_state_ptr[1].uart_dma_rx_state = 1;
  178. }
  179. }else{
  180. if( usart_interrupt_flag_get(USART1,USART_INT_FLAG_RBNE) != RESET)
  181. {
  182. uint8_t data;
  183. usart_interrupt_flag_clear(USART1, USART_INT_FLAG_RBNE);
  184. data = usart_data_receive(USART1);
  185. Write_RingBuff(&data, 1, &uart_dma_state_ptr[1].uart_rx_buffer);
  186. }
  187. }
  188. }
  189. /**
  190. *功能:串口2中断接收函数
  191. */
  192. void USART2_IRQHandler(void)
  193. {
  194. //判断是使用DMA收数还是正常收数
  195. if(uart_rx_dma_en_stu[2] == DMA_RX_EB){
  196. /* UART接收超时中断 */
  197. if ((usart_interrupt_flag_get(USART2, USART_INT_FLAG_RT) != RESET) &&
  198. (usart_flag_get(USART2, USART_FLAG_RT) != RESET))
  199. {
  200. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  201. dma_channel_disable(COM_DMA_ADDR[2], COM_DMA_RX_CHANNEL[2]);
  202. //清除DMA传输完成标志位
  203. dma_flag_clear(COM_DMA_ADDR[2], COM_DMA_RX_CHANNEL[2], DMA_FLAG_FTF);
  204. /* Clear receiver timeout flag */
  205. //usart_flag_clear(BLE_UART, USART_FLAG_RT);
  206. usart_interrupt_flag_clear(USART2,USART_INT_FLAG_RT);
  207. usart_data_receive(USART2); /* 清除接收完成标志位 */
  208. // 设置接收的数据长度
  209. uart_dma_state_ptr[2].uart_dma_rx_len = get_uart_dma_recv_data_size(2);
  210. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  211. uart_dma_state_ptr[2].uart_dma_rx_state = 1;
  212. }
  213. }else{
  214. if( usart_interrupt_flag_get(USART2,USART_INT_FLAG_RBNE) != RESET)
  215. {
  216. uint8_t data;
  217. usart_interrupt_flag_clear(USART2, USART_INT_FLAG_RBNE);
  218. data = usart_data_receive(USART2);
  219. Write_RingBuff(&data, 1, &uart_dma_state_ptr[2].uart_rx_buffer);
  220. }
  221. }
  222. }
  223. /**
  224. *功能:串口3中断接收函数
  225. */
  226. void UART3_IRQHandler(void)
  227. {
  228. //判断是使用DMA收数还是正常收数
  229. if(uart_rx_dma_en_stu[3] == DMA_RX_EB){
  230. /* UART接收超时中断 */
  231. if ((usart_interrupt_flag_get(UART3, USART_INT_FLAG_IDLE) != RESET))
  232. {
  233. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  234. dma_channel_disable(COM_DMA_ADDR[3], COM_DMA_RX_CHANNEL[3]);
  235. //清除DMA传输完成标志位
  236. dma_flag_clear(COM_DMA_ADDR[3], COM_DMA_RX_CHANNEL[3], DMA_FLAG_FTF);
  237. //清除空闲中断标志位
  238. usart_data_receive(UART3);
  239. // 设置接收的数据长度
  240. uart_dma_state_ptr[3].uart_dma_rx_len = get_uart_dma_recv_data_size(3);
  241. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  242. uart_dma_state_ptr[3].uart_dma_rx_state = 1;
  243. }
  244. }else{
  245. if( usart_interrupt_flag_get(UART3,USART_INT_FLAG_RBNE) != RESET)
  246. {
  247. uint8_t data;
  248. usart_interrupt_flag_clear(UART3, USART_INT_FLAG_RBNE);
  249. data = usart_data_receive(UART3);
  250. Write_RingBuff(&data, 1, &uart_dma_state_ptr[3].uart_rx_buffer);
  251. }
  252. }
  253. }
  254. /**
  255. *功能:串口4中断接收函数
  256. */
  257. void UART4_IRQHandler(void)
  258. {
  259. //判断是使用DMA收数还是正常收数
  260. if(uart_rx_dma_en_stu[4] == DMA_RX_EB){
  261. /* UART接收超时中断 */
  262. if ((usart_interrupt_flag_get(UART4, USART_INT_FLAG_IDLE) != RESET))
  263. {
  264. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  265. dma_channel_disable(COM_DMA_ADDR[4], COM_DMA_RX_CHANNEL[4]);
  266. //清除DMA传输完成标志位
  267. dma_flag_clear(COM_DMA_ADDR[4], COM_DMA_RX_CHANNEL[4], DMA_FLAG_FTF);
  268. //清除空闲中断标志位
  269. usart_data_receive(UART4);
  270. // 设置接收的数据长度
  271. uart_dma_state_ptr[4].uart_dma_rx_len = get_uart_dma_recv_data_size(4);
  272. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  273. uart_dma_state_ptr[4].uart_dma_rx_state = 1;
  274. }
  275. }else{
  276. if( usart_interrupt_flag_get(UART4,USART_INT_FLAG_RBNE) != RESET)
  277. {
  278. uint8_t data;
  279. usart_interrupt_flag_clear(UART4, USART_INT_FLAG_RBNE);
  280. data = usart_data_receive(UART4);
  281. Write_RingBuff(&data, 1, &uart_dma_state_ptr[4].uart_rx_buffer);
  282. }
  283. }
  284. }
  285. /**
  286. *功能:串口5中断接收函数
  287. */
  288. void USART5_IRQHandler(void)
  289. {
  290. //判断是使用DMA收数还是正常收数
  291. if(uart_rx_dma_en_stu[5] == DMA_RX_EB){
  292. /* UART接收超时中断 */
  293. if ((usart_interrupt_flag_get(USART5, USART_INT_FLAG_RT) != RESET) &&
  294. (usart_flag_get(USART5, USART_FLAG_RT) != RESET))
  295. {
  296. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  297. dma_channel_disable(COM_DMA_ADDR[5], COM_DMA_RX_CHANNEL[5]);
  298. //清除DMA传输完成标志位
  299. dma_flag_clear(COM_DMA_ADDR[5], COM_DMA_RX_CHANNEL[5], DMA_FLAG_FTF);
  300. /* Clear receiver timeout flag */
  301. //usart_flag_clear(BLE_UART, USART_FLAG_RT);
  302. usart_interrupt_flag_clear(USART5,USART_INT_FLAG_RT);
  303. usart_data_receive(USART5); /* 清除接收完成标志位 */
  304. // 设置接收的数据长度
  305. uart_dma_state_ptr[5].uart_dma_rx_len = get_uart_dma_recv_data_size(5);
  306. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  307. uart_dma_state_ptr[5].uart_dma_rx_state = 1;
  308. }
  309. }else{
  310. if( usart_interrupt_flag_get(USART5,USART_INT_FLAG_RBNE) != RESET)
  311. {
  312. uint8_t data;
  313. usart_interrupt_flag_clear(USART5, USART_INT_FLAG_RBNE);
  314. data = usart_data_receive(USART5);
  315. Write_RingBuff(&data, 1, &uart_dma_state_ptr[5].uart_rx_buffer);
  316. }
  317. }
  318. }
  319. /**
  320. *功能:串口6中断接收函数
  321. */
  322. void UART6_IRQHandler(void)
  323. {
  324. //判断是使用DMA收数还是正常收数
  325. if(uart_rx_dma_en_stu[6] == DMA_RX_EB){
  326. /* UART接收超时中断 */
  327. if ((usart_interrupt_flag_get(UART6, USART_INT_FLAG_IDLE) != RESET))
  328. {
  329. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  330. dma_channel_disable(COM_DMA_ADDR[6], COM_DMA_RX_CHANNEL[6]);
  331. //清除DMA传输完成标志位
  332. dma_flag_clear(COM_DMA_ADDR[6], COM_DMA_RX_CHANNEL[6], DMA_FLAG_FTF);
  333. //清除空闲中断标志位
  334. usart_data_receive(UART6);
  335. // 设置接收的数据长度
  336. uart_dma_state_ptr[6].uart_dma_rx_len = get_uart_dma_recv_data_size(6);
  337. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  338. uart_dma_state_ptr[6].uart_dma_rx_state = 1;
  339. }
  340. }else{
  341. if( usart_interrupt_flag_get(UART6,USART_INT_FLAG_RBNE) != RESET)
  342. {
  343. uint8_t data;
  344. usart_interrupt_flag_clear(UART6, USART_INT_FLAG_RBNE);
  345. data = usart_data_receive(UART6);
  346. Write_RingBuff(&data, 1, &uart_dma_state_ptr[6].uart_rx_buffer);
  347. }
  348. }
  349. }
  350. /**
  351. *功能:串口7中断接收函数
  352. */
  353. void UART7_IRQHandler(void)
  354. {
  355. //判断是使用DMA收数还是正常收数
  356. if(uart_rx_dma_en_stu[7] == DMA_RX_EB){
  357. /* UART接收超时中断 */
  358. if ((usart_interrupt_flag_get(UART7, USART_INT_FLAG_IDLE) != RESET))
  359. {
  360. //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
  361. dma_channel_disable(COM_DMA_ADDR[7], COM_DMA_RX_CHANNEL[7]);
  362. //清除DMA传输完成标志位
  363. dma_flag_clear(COM_DMA_ADDR[7], COM_DMA_RX_CHANNEL[7], DMA_FLAG_FTF);
  364. //清除空闲中断标志位
  365. usart_data_receive(UART7);
  366. // 设置接收的数据长度
  367. uart_dma_state_ptr[7].uart_dma_rx_len = get_uart_dma_recv_data_size(7);
  368. /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
  369. uart_dma_state_ptr[7].uart_dma_rx_state = 1;
  370. }
  371. }else{
  372. if( usart_interrupt_flag_get(UART7,USART_INT_FLAG_RBNE) != RESET)
  373. {
  374. uint8_t data;
  375. usart_interrupt_flag_clear(UART7, USART_INT_FLAG_RBNE);
  376. data = usart_data_receive(UART7);
  377. Write_RingBuff(&data, 1, &uart_dma_state_ptr[7].uart_rx_buffer);
  378. }
  379. }
  380. }
  381. /**
  382. *功能:timer1中断函数
  383. */
  384. void TIMER1_IRQHandler(void)
  385. {
  386. if ( timer_interrupt_flag_get(TIMER1 , TIMER_INT_UP) != RESET )
  387. {
  388. timer_interrupt_flag_clear(TIMER1 , TIMER_INT_UP);
  389. usart_flag_clear(curr_send_uart_addr, USART_FLAG_TC);
  390. timer_disable(TIMER1);
  391. }
  392. }

4. data_hdl.c文件中的方法将串口中断中接收的数据提取出来,并进行操作,例如转发,或是进一步进行数据帧格式的解析

(1)extern串口状态和DMA相关变量,用于方便这里操作和状态读取,同时定义提取数据后存放的buffer、前后两段时间接收数据的长度,用于判断数据是否接收完毕;

(2)写入和读取环形buffer数据的方

(3)在使用和不使用DMA的两种情况下,通过前后两次接收数据的长度是否还有变化,判断数据接收是否完毕,接收完毕的话,进行下一步的帧数据的解析或者是转发,没有接收完毕的话,等待下次的接收在进行判断;

  1. /*!
  2. \file data_hdl.c
  3. \brief data_hdl arm project
  4. \version 2024-01-09, V1.0.0
  5. \author tbj
  6. */
  7. #include "data_hdl.h"
  8. //串口DMA状态结构体,包含接收数据状态,buffer等
  9. extern uart_state *uart_dma_state_ptr;
  10. //串口是否使用DMA通道收数据,用于串口中断函数判断使用
  11. extern UART_DMA_RX_STU uart_rx_dma_en_stu[COMn];
  12. //存放提取帧格式后的串口接收数据
  13. uint8_t extc_data_buff[RINGBUFF_LEN] = {0};
  14. //当前任务时刻串口接收到的数据长度-对应8个串口
  15. uint32_t curr_recv_count[8] = {0};
  16. //上一任务时刻串口接收到的数据长度-对应8个串口
  17. uint32_t last_recv_count[8] = {0};
  18. /**
  19. *功能:初始化环形buffer
  20. *入参1:buffer指针
  21. */
  22. void RingBuff_Init(RingBuff_t *ringBuff)
  23. {
  24. //初始化相关信息
  25. ringBuff->Head = 0;
  26. ringBuff->Tail = 0;
  27. ringBuff->Length = 0;
  28. }
  29. /**
  30. *功能:数据写入环形缓冲区
  31. *入参1:要写入的数据
  32. *入参2:buffer指针
  33. *返回值:buffer是否已满
  34. */
  35. uint8_t Write_RingBuff(uint8_t *data, uint16_t dataLen, RingBuff_t *ringBuff)
  36. {
  37. if(ringBuff->Length >= RINGBUFF_LEN) //判断缓冲区是否已满
  38. {
  39. //如果buffer爆掉了,清空buffer,进行重新初始化
  40. memset(ringBuff, 0, RINGBUFF_LEN);
  41. RingBuff_Init(ringBuff);
  42. return 1;
  43. }
  44. if(ringBuff->Tail + dataLen > RINGBUFF_LEN){
  45. memcpy(ringBuff->Ring_Buff + ringBuff->Tail, data, RINGBUFF_LEN - ringBuff->Tail);
  46. memcpy(ringBuff->Ring_Buff, data + RINGBUFF_LEN - ringBuff->Tail, dataLen - (RINGBUFF_LEN - ringBuff->Tail));
  47. }else{
  48. memcpy(ringBuff->Ring_Buff + ringBuff->Tail, data, dataLen);
  49. }
  50. ringBuff->Tail = ( ringBuff->Tail + dataLen ) % RINGBUFF_LEN;//防止越界非法访问
  51. ringBuff->Length = ringBuff->Length + dataLen;
  52. return 0;
  53. }
  54. /**
  55. *功能:读取缓存区整帧数据
  56. *入参1:存放提取数据的指针
  57. *入参2:环形区buffer指针
  58. *返回值:是否成功提取数据
  59. */
  60. uint8_t Read_RingBuff(uint8_t *rData, RingBuff_t *ringBuff)
  61. {
  62. if(ringBuff->Length == 0)//判断非空
  63. {
  64. return 1;
  65. }
  66. if(ringBuff->Head < ringBuff->Tail){
  67. memcpy(rData, ringBuff->Ring_Buff + ringBuff->Head, ringBuff->Length);
  68. memset(ringBuff->Ring_Buff + ringBuff->Head, 0, ringBuff->Length);
  69. }else{
  70. memcpy(rData, ringBuff->Ring_Buff + ringBuff->Head, RINGBUFF_LEN - ringBuff->Head);
  71. memcpy(rData + RINGBUFF_LEN - ringBuff->Head, ringBuff->Ring_Buff, ringBuff->Length - (RINGBUFF_LEN - ringBuff->Head));
  72. memset(ringBuff->Ring_Buff + ringBuff->Head, 0, RINGBUFF_LEN - ringBuff->Head);
  73. memset(ringBuff->Ring_Buff, 0, ringBuff->Length - (RINGBUFF_LEN - ringBuff->Head));
  74. }
  75. ringBuff->Head = (ringBuff->Head + ringBuff->Length) % RINGBUFF_LEN;//防止越界非法访问
  76. ringBuff->Length = 0;
  77. return 0;
  78. }
  79. /**
  80. *功能:提取串口中断接收的数据到缓存区,进行下一步操作
  81. *入参1:串口号
  82. *返回值:只用于没接收完时的函数结束返回,无特定意义
  83. */
  84. int uart_recv_data_hdl(uint8_t uart_id){
  85. //串口使用DMA
  86. if(uart_rx_dma_en_stu[uart_id] == DMA_RX_EB){
  87. //记录上次接收数据长度
  88. last_recv_count[uart_id] = curr_recv_count[uart_id];
  89. //记录当前接收数据的长度,由于使用DMA超时中断,只有发生超时中断时才会有接收数据长度,否则长度一直是0
  90. curr_recv_count[uart_id] = uart_dma_state_ptr[uart_id].uart_dma_rx_len;
  91. if(last_recv_count[uart_id] == curr_recv_count[uart_id] && curr_recv_count[uart_id] != 0){ //接收完毕
  92. //将数据从串口接收buf中提取出来
  93. memset(extc_data_buff, 0, RINGBUFF_LEN);
  94. memcpy(extc_data_buff, uart_dma_state_ptr[uart_id].uart_rx_buffer.Ring_Buff, curr_recv_count[uart_id]);
  95. //重置串口DMA接收中断配置,重新进行接收
  96. uart_dma_rx_refcg(uart_id);
  97. //判断提取数据的帧格式,根据串口号,判断是提取十六进制还是字符串
  98. if(uart_id == 0 || uart_id == 1 || uart_id == 2 || uart_id == 3 || uart_id == 5 || uart_id == 7){
  99. //*************To Do Something**************//
  100. }else if(uart_id == 6){
  101. //*************To Do Something**************//
  102. }else if(uart_id == 4){
  103. //*************To Do Something**************//
  104. }else{
  105. return 0;
  106. }
  107. //清空记录接收数据长度的字段
  108. curr_recv_count[uart_id] = 0;
  109. last_recv_count[uart_id] = 0;
  110. }else{ //还在接收
  111. return 0;
  112. }
  113. }else{ //串口不使用DMA
  114. last_recv_count[uart_id] = curr_recv_count[uart_id];
  115. curr_recv_count[uart_id] = uart_dma_state_ptr[uart_id].uart_rx_buffer.Length;
  116. if(last_recv_count[uart_id] == curr_recv_count[uart_id] && curr_recv_count[uart_id] != 0){ //接收完毕
  117. memset(extc_data_buff, 0, RINGBUFF_LEN);
  118. Read_RingBuff(extc_data_buff, &uart_dma_state_ptr[uart_id].uart_rx_buffer);
  119. //判断提取数据的帧格式,根据串口号,判断是提取十六进制还是字符串
  120. if(uart_id == 0 || uart_id == 1 || uart_id == 2 || uart_id == 3 || uart_id == 5 || uart_id == 7){
  121. //*************To Do Something**************//
  122. }else if(uart_id == 6){
  123. //*************To Do Something**************//
  124. }else if(uart_id == 4){
  125. //*************To Do Something**************//
  126. }else{
  127. return 0;
  128. }
  129. curr_recv_count[uart_id] = 0;
  130. last_recv_count[uart_id] = 0;
  131. }else{ //还在接收
  132. return 0;
  133. }
  134. }
  135. return 0;
  136. }

5. 串口应用

(1)先初始化串口相关的配置,包括GPIO复用、串口参数配置、DMA配置等

(2)循环执行串口接收数据处理函数来处理数据(如果出现串口接收数据不正常,建议新建一个任务,每隔几秒循环依次,将串口数据处理函数uart_recv_data_hdl放在任务中每隔几秒执行一次,因为while中循环执行过快,可能出现问题)

  1. /*!
  2. \brief main function
  3. \param[in] none
  4. \param[out] none
  5. \retval none
  6. */
  7. int main(void)
  8. {
  9. /* configure systick */
  10. systick_config();
  11. //防止串口发送卡死的定时器初始化
  12. timer1_config();
  13. //串口资源初始化
  14. uart_init(0, 460800U, 1, 2, DMA_TX_EB, DMA_RX_EB);
  15. uart_init(1, 460800U, 1, 1, DMA_TX_EB, DMA_RX_EB);
  16. uart_init(2, 460800U, 1, 1, DMA_TX_EB, DMA_RX_EB);
  17. uart_init(3, 115200U, 1, 2, DMA_TX_EB, DMA_RX_EB);
  18. uart_init(4, 115200U, 1, 1, DMA_TX_EB, DMA_RX_EB);
  19. uart_init(5, 115200U, 1, 2, DMA_TX_EB, DMA_RX_EB);
  20. uart_init(6, 460800U, 1, 0, DMA_TX_NEB, DMA_RX_NEB);
  21. uart_init(7, 460800U, 1, 1, DMA_TX_NEB, DMA_RX_NEB);
  22. while(1){
  23. //循环执行串口数据接收处理任务
  24. uart_recv_data_hdl(0);
  25. uart_recv_data_hdl(1);
  26. uart_recv_data_hdl(2);
  27. uart_recv_data_hdl(3);
  28. uart_recv_data_hdl(4);
  29. uart_recv_data_hdl(5);
  30. uart_recv_data_hdl(6);
  31. uart_recv_data_hdl(7);
  32. }
  33. }

创作不易,希望大家点赞、收藏、关注哦!!!ヾ(o◕∀◕)ノ

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/649353
推荐阅读
  

闽ICP备14008679号