赞
踩
一、 串口收发功能介绍
常用的串口收发数据的方式一共两种,一种是不使用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)一些串口配置和收发的函数声明
- /*!
- \file Uart_hdl.h
- \brief firmware functions to manage Uart
- \version 2024-01-10, V1.0.0
- \author tbj
- */
-
- #include "systick.h"
- #include "gd32f4xx.h"
- #include <stdio.h>
- #include <string.h>
-
- #ifndef UART_HAL_H
- #define UART_HAL_H
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- typedef enum
- {
- DMA_TX_EB,
- DMA_TX_NEB,
- } UART_DMA_TX_STU;
-
- typedef enum
- {
- DMA_RX_EB,
- DMA_RX_NEB
- } UART_DMA_RX_STU;
-
- //串口引脚配置
- #define COMn 8U
-
- //usart0
- #define COM0 USART0
- #define COM0_CLK RCU_USART0
-
- #define COM0_TX_PIN GPIO_PIN_9
- #define COM0_RX_PIN GPIO_PIN_10
-
- #define COM0_TX_GPIO_PORT GPIOA
- #define COM0_RX_GPIO_PORT GPIOA
- #define COM0_TX_GPIO_CLK RCU_GPIOA
- #define COM0_RX_GPIO_CLK RCU_GPIOA
- #define COM0_AF GPIO_AF_7
-
- //usart1
- #define COM1 USART1
- #define COM1_CLK RCU_USART1
-
- #define COM1_TX_PIN GPIO_PIN_5
- #define COM1_RX_PIN GPIO_PIN_6
-
- #define COM1_TX_GPIO_PORT GPIOD
- #define COM1_RX_GPIO_PORT GPIOD
- #define COM1_TX_GPIO_CLK RCU_GPIOD
- #define COM1_RX_GPIO_CLK RCU_GPIOD
- #define COM1_AF GPIO_AF_7
-
- //usart2
- #define COM2 USART2
- #define COM2_CLK RCU_USART2
-
- #define COM2_TX_PIN GPIO_PIN_8
- #define COM2_RX_PIN GPIO_PIN_9
-
- #define COM2_TX_GPIO_PORT GPIOD
- #define COM2_RX_GPIO_PORT GPIOD
- #define COM2_TX_GPIO_CLK RCU_GPIOD
- #define COM2_RX_GPIO_CLK RCU_GPIOD
- #define COM2_AF GPIO_AF_7
-
- //uart3
- #define COM3 UART3
- #define COM3_CLK RCU_UART3
-
- #define COM3_TX_PIN GPIO_PIN_0
- #define COM3_RX_PIN GPIO_PIN_1
-
- #define COM3_TX_GPIO_PORT GPIOA
- #define COM3_RX_GPIO_PORT GPIOA
- #define COM3_TX_GPIO_CLK RCU_GPIOA
- #define COM3_RX_GPIO_CLK RCU_GPIOA
- #define COM3_AF GPIO_AF_8
-
- //uart4
- #define COM4 UART4
- #define COM4_CLK RCU_UART4
-
- #define COM4_TX_PIN GPIO_PIN_12
- #define COM4_RX_PIN GPIO_PIN_2
-
- #define COM4_TX_GPIO_PORT GPIOC
- #define COM4_RX_GPIO_PORT GPIOD
- #define COM4_TX_GPIO_CLK RCU_GPIOC
- #define COM4_RX_GPIO_CLK RCU_GPIOD
- #define COM4_AF GPIO_AF_8
-
- //usart5
- #define COM5 USART5
- #define COM5_CLK RCU_USART5
-
- #define COM5_TX_PIN GPIO_PIN_6
- #define COM5_RX_PIN GPIO_PIN_7
-
- #define COM5_TX_GPIO_PORT GPIOC
- #define COM5_RX_GPIO_PORT GPIOC
- #define COM5_TX_GPIO_CLK RCU_GPIOC
- #define COM5_RX_GPIO_CLK RCU_GPIOC
- #define COM5_AF GPIO_AF_8
-
- //uart6
- #define COM6 UART6
- #define COM6_CLK RCU_UART6
-
- #define COM6_TX_PIN GPIO_PIN_8
- #define COM6_RX_PIN GPIO_PIN_7
-
- #define COM6_TX_GPIO_PORT GPIOE
- #define COM6_RX_GPIO_PORT GPIOE
- #define COM6_TX_GPIO_CLK RCU_GPIOE
- #define COM6_RX_GPIO_CLK RCU_GPIOE
- #define COM6_AF GPIO_AF_8
-
- //uart7
- #define COM7 UART7
- #define COM7_CLK RCU_UART7
-
- #define COM7_TX_PIN GPIO_PIN_1
- #define COM7_RX_PIN GPIO_PIN_0
-
- #define COM7_TX_GPIO_PORT GPIOE
- #define COM7_RX_GPIO_PORT GPIOE
- #define COM7_TX_GPIO_CLK RCU_GPIOE
- #define COM7_RX_GPIO_CLK RCU_GPIOE
- #define COM7_AF GPIO_AF_8
- //****************************************************//
- //UART DMA相关
- //USART0:
- #define USART0_DATA_ADDR ((uint32_t)0x40011004)
- #define USART0_DMA_ADDR DMA1
- #define USART0_DMA_RCU RCU_DMA1
- #define USART0_DMA_TX_CHANNEL DMA_CH7
- #define USART0_DMA_RX_CHANNEL DMA_CH5//DMA_CH2
- #define USART0_DMA_SUBPERI DMA_SUBPERI4
- #define USART0_DMA_MEMORY DMA_MEMORY_1
- //USART1:
- #define USART1_DATA_ADDR ((uint32_t)0x40004404)
- #define USART1_DMA_ADDR DMA0
- #define USART1_DMA_RCU RCU_DMA0
- #define USART1_DMA_TX_CHANNEL DMA_CH6
- #define USART1_DMA_RX_CHANNEL DMA_CH5
- #define USART1_DMA_SUBPERI DMA_SUBPERI4
- #define USART1_DMA_MEMORY DMA_MEMORY_0
- //USART2:-与串口6的DMA通道冲突,串口2和串口6只能选其中一个使用DMA
- #define USART2_DATA_ADDR ((uint32_t)0x40004804)
- #define USART2_DMA_ADDR DMA0
- #define USART2_DMA_RCU RCU_DMA0
- #define USART2_DMA_TX_CHANNEL DMA_CH3
- #define USART2_DMA_RX_CHANNEL DMA_CH1
- #define USART2_DMA_SUBPERI DMA_SUBPERI4
- #define USART2_DMA_MEMORY DMA_MEMORY_0
- //UART3:
- #define UART3_DATA_ADDR ((uint32_t)0x40004C04)
- #define UART3_DMA_ADDR DMA0
- #define UART3_DMA_RCU RCU_DMA0
- #define UART3_DMA_TX_CHANNEL DMA_CH4
- #define UART3_DMA_RX_CHANNEL DMA_CH2
- #define UART3_DMA_SUBPERI DMA_SUBPERI4
- #define UART3_DMA_MEMORY DMA_MEMORY_0
- //UART4:
- #define UART4_DATA_ADDR ((uint32_t)0x40005004)
- #define UART4_DMA_ADDR DMA0
- #define UART4_DMA_RCU RCU_DMA0
- #define UART4_DMA_TX_CHANNEL DMA_CH7
- #define UART4_DMA_RX_CHANNEL DMA_CH0
- #define UART4_DMA_SUBPERI DMA_SUBPERI4
- #define UART4_DMA_MEMORY DMA_MEMORY_0
- //USART5:
- #define USART5_DATA_ADDR ((uint32_t)0x40011404)
- #define USART5_DMA_ADDR DMA1
- #define USART5_DMA_RCU RCU_DMA1
- #define USART5_DMA_TX_CHANNEL DMA_CH6
- #define USART5_DMA_RX_CHANNEL DMA_CH1
- #define USART5_DMA_SUBPERI DMA_SUBPERI5
- #define USART5_DMA_MEMORY DMA_MEMORY_1
- //UART6:-与串口2的DMA通道冲突,串口2和串口6只能选其中一个使用DMA
- #define UART6_DATA_ADDR ((uint32_t)0x40007804)
- #define UART6_DMA_ADDR DMA0
- #define UART6_DMA_RCU RCU_DMA0
- #define UART6_DMA_TX_CHANNEL DMA_CH1
- #define UART6_DMA_RX_CHANNEL DMA_CH3
- #define UART6_DMA_SUBPERI DMA_SUBPERI5
- #define UART6_DMA_MEMORY DMA_MEMORY_0
- //UART7:-与串口4的DMA通道冲突,串口4和串口7只能选其中一个使用DMA
- #define UART7_DATA_ADDR ((uint32_t)0x40007C04)
- #define UART7_DMA_ADDR DMA0
- #define UART7_DMA_RCU RCU_DMA0
- #define UART7_DMA_TX_CHANNEL DMA_CH0
- #define UART7_DMA_RX_CHANNEL DMA_CH6
- #define UART7_DMA_SUBPERI DMA_SUBPERI5
- #define UART7_DMA_MEMORY DMA_MEMORY_0
- //****************************************************//
-
- #define RINGBUFF_LEN 5120
- //环形buffer结构体
- typedef struct
- {
- uint32_t Head;
- uint32_t Tail;
- uint32_t Length;
- uint8_t Ring_Buff[RINGBUFF_LEN];
- } RingBuff_t;
-
- #define BUFFER_SIZE 5120
- //串口DMA收发状态和数据长度
- typedef struct{
- uint8_t uart_dma_rx_state; //串口是否使用DMA接收
- uint8_t uart_dma_tx_state; //串口是否使用DMA发送
- uint16_t uart_dma_rx_len; //串口使用DMA接收时,超时中断后接收的数据长度
- RingBuff_t uart_rx_buffer; //串口接收数据缓存区,是一个环形buffer,使用DMA接收时,按正常缓存区使用,不适用DMA接收时,按照环形buffer功能使用
- uint8_t uart_tx_buffer[BUFFER_SIZE];//串口使用DMA发送时的发送缓存区
- }uart_state;
-
- //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓普通串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
- //串口初始化
- 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);
- //串口发送数据-每次发送一字节
- void uart_send_byte(uint8_t uart_id, uint8_t ch);
- //串口发送数据-每次buf
- void uart_send_buf(uint8_t uart_id, uint8_t *ch, uint16_t len);
-
- //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓DMA串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
- //初始化串口DMA tx配置
- void usart_dma_tx_init(uint8_t uart_id);
- //初始化串口DMA rx配置
- void usart_dma_rx_init(uint8_t uart_id);
- //DMA串口发送数据
- void uart_dma_send(uint8_t uart_id, uint8_t *tx_buffer, uint32_t len);
- //获取DMA接收数据的长度
- unsigned int get_uart_dma_recv_data_size(uint8_t uart_id);
- //初始化串口相关状态、DMA、buffer等
- void init_uart_state(uint8_t uart_id);
- //重新配置DMA rx,为了重新接收数据
- void uart_dma_rx_refcg(uint8_t uart_id);
-
- #ifdef __cplusplus
- }
- #endif
-
- #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等对象置零初始化;
- /*!
- \file Uart_hdl.c
- \brief firmware functions to manage Uart
- \version 2024-01-10, V1.0.0
- \author tbj
- */
-
- #include "Uart_hdl.h"
-
- //存储当前发送数据的串口基地址
- uint32_t curr_send_uart_addr = 0;
- //串口状态对象,包括DMA收发使用情况,串口收发缓存区等
- uart_state usart0_state, usart1_state, usart2_state, uart3_state, uart4_state, usart5_state, uart6_state, uart7_state;
- //串口状态对象指针数组(为了方便其他函数调用建立的)
- uart_state *uart_dma_state_ptr[COMn] = {&usart0_state, &usart1_state, &usart2_state, &uart3_state, &uart4_state, &usart5_state, &uart6_state, &uart7_state};
- //串口是否使用DMA通道收数据,用于串口中断函数判断使用
- 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};
-
- 串口相关参数列表
- //串口编号
- static uint32_t COM_NUM[COMn] = {USART0, USART1, USART2, UART3, UART4, USART5, UART6, UART7};
- //串口中断号
- static uint32_t COM_IRQn[COMn] = {USART0_IRQn, USART1_IRQn, USART2_IRQn, UART3_IRQn, UART4_IRQn, USART5_IRQn, UART6_IRQn, UART7_IRQn};
- //GPIO复用功能,GPIO_AF_7和GPIO_AF_8将GPIO复用为串口
- static uint32_t COM_AF[COMn] = {COM0_AF, COM1_AF, COM2_AF, COM3_AF, COM4_AF, COM5_AF, COM6_AF, COM7_AF};
- //串口TX对应的GPIO号
- 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};
- //串口RX对应的GPIO号
- 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};
- //串口TX对应的GPIO端口分组
- 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};
- //串口RX对应的GPIO端口分组
- 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};
- //串口对应时钟
- static rcu_periph_enum COM_CLK[COMn] = {COM0_CLK, COM1_CLK, COM2_CLK, COM3_CLK, COM4_CLK, COM5_CLK, COM6_CLK, COM7_CLK};
- //复用为串口TX的GPIO的时钟
- 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};
- //复用为串口RX的GPIO的时钟
- 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};
-
- 串口DMA相关参数列表
- //外设基地址-数据寄存器
- 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};
- //DMA基地址
- 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};
- //DMA时钟
- 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};
- //DMA TX通道
- 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};
- //DMA RX通道
- 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};
- //DMA总线通道
- 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};
- //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};
-
-
- //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓普通串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
- /**
- *功能:初始化串口相关参数
- *入参1:串口号
- *入参2:波特率
- *入参3:串口中断抢占优先级
- *入参4:串口中断响应优先级
- *入参5:串口DMA发送使能
- *入参6:串口DMA接收使能
- */
- 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)
- {
- //清空使用串口的状态和缓存区
- init_uart_state(uart_id);
-
- //使能GPIO时钟
- if(COM_TX_GPIO_CLK[uart_id] == COM_RX_GPIO_CLK[uart_id])
- {
- //TX和RX是同一组GPIO的时钟
- rcu_periph_clock_enable(COM_TX_GPIO_CLK[uart_id]);
- }else{
- //TX和RX不是同一组GPIO的时钟
- rcu_periph_clock_enable(COM_TX_GPIO_CLK[uart_id]);
- rcu_periph_clock_enable(COM_RX_GPIO_CLK[uart_id]);
- }
-
- //使能串口时钟
- rcu_periph_clock_enable(COM_CLK[uart_id]);
-
- //配置GPIO复用为串口TX
- gpio_af_set(COM_TX_GPIO_PORT[uart_id], COM_AF[uart_id], COM_TX_PIN[uart_id]);
-
- //配置GPIO复用为串口RX
- gpio_af_set(COM_RX_GPIO_PORT[uart_id], COM_AF[uart_id], COM_RX_PIN[uart_id]);
-
- //配置TX引脚模式,上下拉,速率等
- gpio_mode_set(COM_TX_GPIO_PORT[uart_id], GPIO_MODE_AF, GPIO_PUPD_PULLUP,COM_TX_PIN[uart_id]);
- gpio_output_options_set(COM_TX_GPIO_PORT[uart_id], GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,COM_TX_PIN[uart_id]);
-
- //配置RX引脚模式,上下拉,速率等
- gpio_mode_set(COM_RX_GPIO_PORT[uart_id], GPIO_MODE_AF, GPIO_PUPD_PULLUP,COM_RX_PIN[uart_id]);
- gpio_output_options_set(COM_RX_GPIO_PORT[uart_id], GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,COM_RX_PIN[uart_id]);
-
- //配置串口相关参数
- usart_deinit(COM_NUM[uart_id]);
- usart_baudrate_set(COM_NUM[uart_id], baudval);
- usart_receive_config(COM_NUM[uart_id], USART_RECEIVE_ENABLE);
- usart_transmit_config(COM_NUM[uart_id], USART_TRANSMIT_ENABLE);
- usart_enable(COM_NUM[uart_id]);
-
- //设置串口中断优先级
- nvic_irq_enable(COM_IRQn[uart_id], nvic_irq_pre_priority, nvic_irq_sub_priority);
-
- //根据入参,选择是否使用DMA
- if(dma_rx_stu == DMA_RX_EB){
- // 接收超时设置,100个波特率的比特位(10个字节的超时中断触发)
- usart_receiver_timeout_threshold_config(COM_NUM[uart_id], 100);
- //使用DMA收数的话,要使能串口接收超时中断
- //usart_interrupt_enable(COM_NUM[uart_id], USART_INT_RT);
-
- usart_interrupt_enable(COM_NUM[uart_id], USART_INT_IDLE);
- //使能串口超时功能
- usart_receiver_timeout_enable(COM_NUM[uart_id]);
- //初始化串口接收DMA功能
- usart_dma_rx_init(uart_id);
- //将对应串口接收使用DMA的情况进行更新,方便别的地方使用
- uart_rx_dma_en_stu[uart_id] = DMA_RX_EB;
- }else{
- //不使用DMA收数时,要使能串口非空中断
- usart_interrupt_enable(COM_NUM[uart_id], USART_INT_RBNE);
- //将对应串口接收使用DMA的情况进行更新,方便别的地方使用
- uart_rx_dma_en_stu[uart_id] = DMA_RX_NEB;
- }
-
- if(dma_tx_stu == DMA_TX_EB){
- //初始化串口发送DMA功能
- usart_dma_tx_init(uart_id);
- }
- }
-
-
- /**
- *功能:串口数据发送函数
- *入参1:串口号
- *入参2:要发送的字节数据
- */
- void uart_send_byte(uint8_t uart_id, uint8_t ch)
- {
- //串口发送数据
- curr_send_uart_addr = COM_NUM[uart_id];
- usart_data_transmit(COM_NUM[uart_id], ch);
- //增加定时器计时,超过一秒没有发送完,清除TC标志位,跳出函数
- timer_enable(TIMER1);
- while(usart_flag_get(COM_NUM[uart_id], USART_FLAG_TBE) == RESET);
- timer_disable(TIMER1);
- timer_counter_value_config(TIMER1, 0);
- }
-
- /**
- *功能:串口buf数据发送函数
- *入参1:串口号
- *入参2:要发送的字节数据
- */
- void uart_send_buf(uint8_t uart_id, uint8_t *ch, uint16_t len)
- {
- for(uint32_t i = 0; i < len; i++){
- uart_send_byte(uart_id, *(ch + i));
- }
- }
-
-
- /**
- *功能:printf函数重映射
- */
- int fputc(int ch, FILE *f)
- {
- while(usart_flag_get(COM1, USART_FLAG_TBE) == RESET ){;}
- usart_data_transmit(COM1, (uint8_t)ch);
- return ch;
- }
-
- //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓DMA串口收发↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓//
- /**
- *功能:初始化串口DMA tx配置
- *入参1:串口号
- *返回值:是否为复位指令
- */
- void usart_dma_tx_init(uint8_t uart_id)
- {
- dma_single_data_parameter_struct dma_init_struct;
-
- /* enable dma_addr */
- rcu_periph_clock_enable(COM_DMA_RCU[uart_id]);
-
- /* deinitialize DMA channe7(USART0 TX) */
- dma_deinit(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]);
- dma_init_struct.direction = DMA_MEMORY_TO_PERIPH; //存储器到外设方向
- dma_init_struct.memory0_addr = (uint32_t)uart_dma_state_ptr[uart_id]->uart_tx_buffer; //存储器地址
- dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //存储器地址自增
- dma_init_struct.number = 0; //传输数据个数,赋值BUFFER_SIZE时,上电初始化DMA会发送一堆0;
- dma_init_struct.periph_addr = COM_DATA_ADDR[uart_id]; //外设基地址-数据寄存器
- dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; //外设地址自增关闭-需要固定
- dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; //外设数据位宽8bit
- dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //设置优先级最高
- dma_single_data_mode_init(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], &dma_init_struct); //初始化对应DMA的对应通道
- dma_channel_subperipheral_select(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], COM_DMA_SUBPERI[uart_id]); //使能总得通道
- /* configure DMA mode */
- dma_circulation_disable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]); //关闭DMA循环模式配置
- usart_dma_transmit_config(COM_NUM[uart_id], USART_DENT_ENABLE); //DMA串口发送配置
-
- /* 使能 DMA 通道 */
- dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]);
- }
-
-
- /**
- *功能:初始化串口DMA rx配置
- *入参1:串口号
- *返回值:是否为复位指令
- */
- void usart_dma_rx_init(uint8_t uart_id){
-
- dma_single_data_parameter_struct dma_init_struct;
-
- /* enable dma_addr */
- rcu_periph_clock_enable(COM_DMA_RCU[uart_id]);
-
- /* deinitialize DMA channe7(USART0 TX) */
- dma_deinit(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
- dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; //外设到存储器
- dma_init_struct.memory0_addr = (uint32_t)uart_dma_state_ptr[uart_id]->uart_rx_buffer.Ring_Buff; //存储器地址
- dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //存储器地址自增
- dma_init_struct.number = RINGBUFF_LEN; //传输数据个数
- dma_init_struct.periph_addr = COM_DATA_ADDR[uart_id]; //外设基地址-数据寄存器
- dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; //外设地址自增关闭-需要固定
- dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; //外设数据位宽8bit
- dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //设置优先级最高
- dma_single_data_mode_init(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], &dma_init_struct); //初始化对应DMA的对应通道
- dma_channel_subperipheral_select(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], COM_DMA_SUBPERI[uart_id]); //使能总得通道
- /* configure DMA mode */
- dma_circulation_disable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]); //关闭DMA循环模式配置
- //dma_circulation_enable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]); //关闭DMA循环模式配置
-
- usart_dma_receive_config(COM_NUM[uart_id], USART_DENR_ENABLE); //DMA串口接收配置
-
- /* 使能 DMA 通道 */
- dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
- }
-
-
- /**
- *功能:DMA串口发送数据
- *入参1:存储数据的buffer指针
- *入参2:要发送的数据长度
- *返回值:是否为复位指令
- */
- void uart_dma_send(uint8_t uart_id, uint8_t *tx_buffer, uint32_t len)
- {
- dma_channel_disable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]);
- dma_flag_clear(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], DMA_FLAG_FTF); // 清除DMA传输完成标志位
- dma_memory_address_config(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], DMA_MEMORY_0, (uint32_t)(tx_buffer)); // 存储器地址
- dma_transfer_number_config(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], len);
- dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id]); // 使能DMA传输
-
- // 等待传输完成
- //增加定时器计时,超过一秒没有发送完,清除TC标志位,跳出函数
- timer_enable(TIMER1);
- while(dma_flag_get(COM_DMA_ADDR[uart_id], COM_DMA_TX_CHANNEL[uart_id], DMA_FLAG_FTF) == RESET);
- timer_disable(TIMER1);
- timer_counter_value_config(TIMER1, 0);
- }
-
-
- /**
- *功能:获取DMA接收数据的长度
- *入参1:串口号
- *返回值:当前DMA接收数据的长度
- */
- unsigned int get_uart_dma_recv_data_size(uint8_t uart_id)
- {
- /*
- dma_transfer_number_get(DMA_CH2);是获取当前指针计数值,
- 用内存缓冲区大小 - 此计数值 = 接收到的数据长度(这里单位为字节)。
- 需要说明下在读取数据长度的时候需要先把接收DMA关闭,读取完了或者是数据处理完了在打开接收DMA,防止在处理的过程中有数据到来而出错。
- */
- return (RINGBUFF_LEN - (dma_transfer_number_get(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id])));
- }
-
-
- /**
- *功能:重新配置DMA rx
- *入参1:串口号
- *返回值:是否为复位指令
- */
- void uart_dma_rx_refcg(uint8_t uart_id)
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
- dma_memory_address_config(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], DMA_MEMORY_0,
- (uint32_t)(uart_dma_state_ptr[uart_id]->uart_rx_buffer.Ring_Buff)); // 存储器地址
- dma_transfer_number_config(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id], RINGBUFF_LEN);
-
- uart_dma_state_ptr[uart_id]->uart_dma_rx_state = 0;
- uart_dma_state_ptr[uart_id]->uart_dma_rx_len = 0;
-
- // 使能通道
- dma_channel_enable(COM_DMA_ADDR[uart_id], COM_DMA_RX_CHANNEL[uart_id]);
- }
-
-
- /**
- *功能:初始化串口相关状态、DMA、buffer等
- *入参1:串口号
- */
- void init_uart_state(uint8_t uart_id){
- memset(uart_dma_state_ptr[uart_id], 0, sizeof(uart_state));
- }
-
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中断,触发中断来将发送完成标志位置位,防止串口发发送过程中出现以外卡死的问题;
- /*!
- \file gd32f4xx_it.c
- \brief interrupt service routines
- \version 2023-05-10, V1.0.0
- \author tbj
- */
-
- #include "gd32f4xx_it.h"
- #include "task_hdl.h"
-
-
- //*******************************串口中断相关*****************************//
- //串口DMA状态结构体,包含接收数据状态,buffer等
- extern uart_state *uart_dma_state_ptr;
- //DMA基地址
- extern uint32_t COM_DMA_ADDR[COMn];
- //DMA TX通道
- extern dma_channel_enum COM_DMA_TX_CHANNEL[COMn];
- //DMA RX通道
- extern dma_channel_enum COM_DMA_RX_CHANNEL[COMn];
- //串口是否使用DMA通道收数据,用于串口中断函数判断使用
- extern UART_DMA_RX_STU uart_rx_dma_en_stu[COMn];
- //存储当前发送数据的串口基地址
- extern uint32_t curr_send_uart_addr;
-
-
- /*!
- \brief this function handles NMI exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void NMI_Handler(void)
- {
- }
-
- /*!
- \brief this function handles HardFault exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void HardFault_Handler(void)
- {
- /* if Hard Fault exception occurs, go to infinite loop */
- while (1){
- }
- }
-
- /*!
- \brief this function handles MemManage exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void MemManage_Handler(void)
- {
- /* if Memory Manage exception occurs, go to infinite loop */
- while (1){
- }
- }
-
- /*!
- \brief this function handles BusFault exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void BusFault_Handler(void)
- {
- /* if Bus Fault exception occurs, go to infinite loop */
- while (1){
- }
- }
-
- /*!
- \brief this function handles UsageFault exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void UsageFault_Handler(void)
- {
- /* if Usage Fault exception occurs, go to infinite loop */
- while (1){
- }
- }
-
- /*!
- \brief this function handles SVC exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void SVC_Handler(void)
- {
- }
-
- /*!
- \brief this function handles DebugMon exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void DebugMon_Handler(void)
- {
- }
-
- /*!
- \brief this function handles PendSV exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void PendSV_Handler(void)
- {
- }
-
- /*!
- \brief this function handles SysTick exception
- \param[in] none
- \param[out] none
- \retval none
- */
- void SysTick_Handler(void)
- {
- //延时计数使用
- delay_decrement();
- //系统开机计时
- add_system_time();
- //任务处理状态监测
- TaskTick();
- }
-
-
- /**
- *功能:串口0中断接收函数
- */
- void USART0_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[0] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(USART0, USART_INT_FLAG_RT) != RESET) &&
- (usart_flag_get(USART0, USART_FLAG_RT) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[0], COM_DMA_RX_CHANNEL[0]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[0], COM_DMA_RX_CHANNEL[0], DMA_FLAG_FTF);
-
- /* Clear receiver timeout flag */
- //usart_flag_clear(BLE_UART, USART_FLAG_RT);
- usart_interrupt_flag_clear(USART0,USART_INT_FLAG_RT);
- usart_data_receive(USART0); /* 清除接收完成标志位 */
-
- // 设置接收的数据长度
- uart_dma_state_ptr[0].uart_dma_rx_len = get_uart_dma_recv_data_size(0);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[0].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(USART0,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
- data = usart_data_receive(USART0);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[0].uart_rx_buffer);
-
- }
- }
- }
-
- /**
- *功能:串口1中断接收函数
- */
- void USART1_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[1] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(USART1, USART_INT_FLAG_RT) != RESET) &&
- (usart_flag_get(USART1, USART_FLAG_RT) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[1], COM_DMA_RX_CHANNEL[1]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[1], COM_DMA_RX_CHANNEL[1], DMA_FLAG_FTF);
-
- /* Clear receiver timeout flag */
- //usart_flag_clear(BLE_UART, USART_FLAG_RT);
- usart_interrupt_flag_clear(USART1,USART_INT_FLAG_RT);
- usart_data_receive(USART1); /* 清除接收完成标志位 */
-
- // 设置接收的数据长度
- uart_dma_state_ptr[1].uart_dma_rx_len = get_uart_dma_recv_data_size(1);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[1].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(USART1,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(USART1, USART_INT_FLAG_RBNE);
- data = usart_data_receive(USART1);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[1].uart_rx_buffer);
- }
- }
- }
-
-
- /**
- *功能:串口2中断接收函数
- */
- void USART2_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[2] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(USART2, USART_INT_FLAG_RT) != RESET) &&
- (usart_flag_get(USART2, USART_FLAG_RT) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[2], COM_DMA_RX_CHANNEL[2]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[2], COM_DMA_RX_CHANNEL[2], DMA_FLAG_FTF);
-
- /* Clear receiver timeout flag */
- //usart_flag_clear(BLE_UART, USART_FLAG_RT);
- usart_interrupt_flag_clear(USART2,USART_INT_FLAG_RT);
- usart_data_receive(USART2); /* 清除接收完成标志位 */
-
- // 设置接收的数据长度
- uart_dma_state_ptr[2].uart_dma_rx_len = get_uart_dma_recv_data_size(2);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[2].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(USART2,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(USART2, USART_INT_FLAG_RBNE);
- data = usart_data_receive(USART2);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[2].uart_rx_buffer);
- }
- }
- }
-
-
- /**
- *功能:串口3中断接收函数
- */
- void UART3_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[3] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(UART3, USART_INT_FLAG_IDLE) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[3], COM_DMA_RX_CHANNEL[3]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[3], COM_DMA_RX_CHANNEL[3], DMA_FLAG_FTF);
-
- //清除空闲中断标志位
- usart_data_receive(UART3);
-
- // 设置接收的数据长度
- uart_dma_state_ptr[3].uart_dma_rx_len = get_uart_dma_recv_data_size(3);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[3].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(UART3,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(UART3, USART_INT_FLAG_RBNE);
- data = usart_data_receive(UART3);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[3].uart_rx_buffer);
- }
- }
- }
-
-
- /**
- *功能:串口4中断接收函数
- */
- void UART4_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[4] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(UART4, USART_INT_FLAG_IDLE) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[4], COM_DMA_RX_CHANNEL[4]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[4], COM_DMA_RX_CHANNEL[4], DMA_FLAG_FTF);
-
- //清除空闲中断标志位
- usart_data_receive(UART4);
-
- // 设置接收的数据长度
- uart_dma_state_ptr[4].uart_dma_rx_len = get_uart_dma_recv_data_size(4);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[4].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(UART4,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(UART4, USART_INT_FLAG_RBNE);
- data = usart_data_receive(UART4);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[4].uart_rx_buffer);
- }
- }
- }
-
-
- /**
- *功能:串口5中断接收函数
- */
- void USART5_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[5] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(USART5, USART_INT_FLAG_RT) != RESET) &&
- (usart_flag_get(USART5, USART_FLAG_RT) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[5], COM_DMA_RX_CHANNEL[5]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[5], COM_DMA_RX_CHANNEL[5], DMA_FLAG_FTF);
-
- /* Clear receiver timeout flag */
- //usart_flag_clear(BLE_UART, USART_FLAG_RT);
- usart_interrupt_flag_clear(USART5,USART_INT_FLAG_RT);
- usart_data_receive(USART5); /* 清除接收完成标志位 */
-
- // 设置接收的数据长度
- uart_dma_state_ptr[5].uart_dma_rx_len = get_uart_dma_recv_data_size(5);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[5].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(USART5,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(USART5, USART_INT_FLAG_RBNE);
- data = usart_data_receive(USART5);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[5].uart_rx_buffer);
- }
- }
- }
-
-
- /**
- *功能:串口6中断接收函数
- */
- void UART6_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[6] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(UART6, USART_INT_FLAG_IDLE) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[6], COM_DMA_RX_CHANNEL[6]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[6], COM_DMA_RX_CHANNEL[6], DMA_FLAG_FTF);
-
- //清除空闲中断标志位
- usart_data_receive(UART6);
-
- // 设置接收的数据长度
- uart_dma_state_ptr[6].uart_dma_rx_len = get_uart_dma_recv_data_size(6);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[6].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(UART6,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(UART6, USART_INT_FLAG_RBNE);
- data = usart_data_receive(UART6);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[6].uart_rx_buffer);
- }
- }
- }
-
-
- /**
- *功能:串口7中断接收函数
- */
- void UART7_IRQHandler(void)
- {
- //判断是使用DMA收数还是正常收数
- if(uart_rx_dma_en_stu[7] == DMA_RX_EB){
- /* UART接收超时中断 */
- if ((usart_interrupt_flag_get(UART7, USART_INT_FLAG_IDLE) != RESET))
- {
- //关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据
- dma_channel_disable(COM_DMA_ADDR[7], COM_DMA_RX_CHANNEL[7]);
- //清除DMA传输完成标志位
- dma_flag_clear(COM_DMA_ADDR[7], COM_DMA_RX_CHANNEL[7], DMA_FLAG_FTF);
-
- //清除空闲中断标志位
- usart_data_receive(UART7);
-
- // 设置接收的数据长度
- uart_dma_state_ptr[7].uart_dma_rx_len = get_uart_dma_recv_data_size(7);
-
- /* 接收超时后,说明一帧数据接收完毕,置接收完成标志 */
- uart_dma_state_ptr[7].uart_dma_rx_state = 1;
- }
- }else{
- if( usart_interrupt_flag_get(UART7,USART_INT_FLAG_RBNE) != RESET)
- {
- uint8_t data;
- usart_interrupt_flag_clear(UART7, USART_INT_FLAG_RBNE);
- data = usart_data_receive(UART7);
- Write_RingBuff(&data, 1, &uart_dma_state_ptr[7].uart_rx_buffer);
- }
- }
- }
-
-
- /**
- *功能:timer1中断函数
- */
- void TIMER1_IRQHandler(void)
- {
- if ( timer_interrupt_flag_get(TIMER1 , TIMER_INT_UP) != RESET )
- {
- timer_interrupt_flag_clear(TIMER1 , TIMER_INT_UP);
- usart_flag_clear(curr_send_uart_addr, USART_FLAG_TC);
- timer_disable(TIMER1);
- }
- }
4. data_hdl.c文件中的方法将串口中断中接收的数据提取出来,并进行操作,例如转发,或是进一步进行数据帧格式的解析
(1)extern串口状态和DMA相关变量,用于方便这里操作和状态读取,同时定义提取数据后存放的buffer、前后两段时间接收数据的长度,用于判断数据是否接收完毕;
(2)写入和读取环形buffer数据的方
(3)在使用和不使用DMA的两种情况下,通过前后两次接收数据的长度是否还有变化,判断数据接收是否完毕,接收完毕的话,进行下一步的帧数据的解析或者是转发,没有接收完毕的话,等待下次的接收在进行判断;
- /*!
- \file data_hdl.c
- \brief data_hdl arm project
- \version 2024-01-09, V1.0.0
- \author tbj
- */
-
- #include "data_hdl.h"
-
- //串口DMA状态结构体,包含接收数据状态,buffer等
- extern uart_state *uart_dma_state_ptr;
- //串口是否使用DMA通道收数据,用于串口中断函数判断使用
- extern UART_DMA_RX_STU uart_rx_dma_en_stu[COMn];
- //存放提取帧格式后的串口接收数据
- uint8_t extc_data_buff[RINGBUFF_LEN] = {0};
- //当前任务时刻串口接收到的数据长度-对应8个串口
- uint32_t curr_recv_count[8] = {0};
- //上一任务时刻串口接收到的数据长度-对应8个串口
- uint32_t last_recv_count[8] = {0};
-
- /**
- *功能:初始化环形buffer
- *入参1:buffer指针
- */
- void RingBuff_Init(RingBuff_t *ringBuff)
- {
- //初始化相关信息
- ringBuff->Head = 0;
- ringBuff->Tail = 0;
- ringBuff->Length = 0;
- }
-
-
- /**
- *功能:数据写入环形缓冲区
- *入参1:要写入的数据
- *入参2:buffer指针
- *返回值:buffer是否已满
- */
- uint8_t Write_RingBuff(uint8_t *data, uint16_t dataLen, RingBuff_t *ringBuff)
- {
- if(ringBuff->Length >= RINGBUFF_LEN) //判断缓冲区是否已满
- {
- //如果buffer爆掉了,清空buffer,进行重新初始化
- memset(ringBuff, 0, RINGBUFF_LEN);
- RingBuff_Init(ringBuff);
- return 1;
- }
-
- if(ringBuff->Tail + dataLen > RINGBUFF_LEN){
- memcpy(ringBuff->Ring_Buff + ringBuff->Tail, data, RINGBUFF_LEN - ringBuff->Tail);
- memcpy(ringBuff->Ring_Buff, data + RINGBUFF_LEN - ringBuff->Tail, dataLen - (RINGBUFF_LEN - ringBuff->Tail));
- }else{
- memcpy(ringBuff->Ring_Buff + ringBuff->Tail, data, dataLen);
- }
-
- ringBuff->Tail = ( ringBuff->Tail + dataLen ) % RINGBUFF_LEN;//防止越界非法访问
- ringBuff->Length = ringBuff->Length + dataLen;
-
- return 0;
- }
-
-
- /**
- *功能:读取缓存区整帧数据
- *入参1:存放提取数据的指针
- *入参2:环形区buffer指针
- *返回值:是否成功提取数据
- */
- uint8_t Read_RingBuff(uint8_t *rData, RingBuff_t *ringBuff)
- {
- if(ringBuff->Length == 0)//判断非空
- {
- return 1;
- }
-
- if(ringBuff->Head < ringBuff->Tail){
- memcpy(rData, ringBuff->Ring_Buff + ringBuff->Head, ringBuff->Length);
- memset(ringBuff->Ring_Buff + ringBuff->Head, 0, ringBuff->Length);
- }else{
- memcpy(rData, ringBuff->Ring_Buff + ringBuff->Head, RINGBUFF_LEN - ringBuff->Head);
- memcpy(rData + RINGBUFF_LEN - ringBuff->Head, ringBuff->Ring_Buff, ringBuff->Length - (RINGBUFF_LEN - ringBuff->Head));
- memset(ringBuff->Ring_Buff + ringBuff->Head, 0, RINGBUFF_LEN - ringBuff->Head);
- memset(ringBuff->Ring_Buff, 0, ringBuff->Length - (RINGBUFF_LEN - ringBuff->Head));
- }
-
- ringBuff->Head = (ringBuff->Head + ringBuff->Length) % RINGBUFF_LEN;//防止越界非法访问
- ringBuff->Length = 0;
-
- return 0;
- }
-
-
- /**
- *功能:提取串口中断接收的数据到缓存区,进行下一步操作
- *入参1:串口号
- *返回值:只用于没接收完时的函数结束返回,无特定意义
- */
- int uart_recv_data_hdl(uint8_t uart_id){
-
- //串口使用DMA
- if(uart_rx_dma_en_stu[uart_id] == DMA_RX_EB){
- //记录上次接收数据长度
- last_recv_count[uart_id] = curr_recv_count[uart_id];
- //记录当前接收数据的长度,由于使用DMA超时中断,只有发生超时中断时才会有接收数据长度,否则长度一直是0
- curr_recv_count[uart_id] = uart_dma_state_ptr[uart_id].uart_dma_rx_len;
- if(last_recv_count[uart_id] == curr_recv_count[uart_id] && curr_recv_count[uart_id] != 0){ //接收完毕
- //将数据从串口接收buf中提取出来
- memset(extc_data_buff, 0, RINGBUFF_LEN);
- memcpy(extc_data_buff, uart_dma_state_ptr[uart_id].uart_rx_buffer.Ring_Buff, curr_recv_count[uart_id]);
- //重置串口DMA接收中断配置,重新进行接收
- uart_dma_rx_refcg(uart_id);
- //判断提取数据的帧格式,根据串口号,判断是提取十六进制还是字符串
- if(uart_id == 0 || uart_id == 1 || uart_id == 2 || uart_id == 3 || uart_id == 5 || uart_id == 7){
- //*************To Do Something**************//
- }else if(uart_id == 6){
- //*************To Do Something**************//
- }else if(uart_id == 4){
- //*************To Do Something**************//
- }else{
- return 0;
- }
- //清空记录接收数据长度的字段
- curr_recv_count[uart_id] = 0;
- last_recv_count[uart_id] = 0;
- }else{ //还在接收
- return 0;
- }
- }else{ //串口不使用DMA
- last_recv_count[uart_id] = curr_recv_count[uart_id];
- curr_recv_count[uart_id] = uart_dma_state_ptr[uart_id].uart_rx_buffer.Length;
- if(last_recv_count[uart_id] == curr_recv_count[uart_id] && curr_recv_count[uart_id] != 0){ //接收完毕
- memset(extc_data_buff, 0, RINGBUFF_LEN);
- Read_RingBuff(extc_data_buff, &uart_dma_state_ptr[uart_id].uart_rx_buffer);
- //判断提取数据的帧格式,根据串口号,判断是提取十六进制还是字符串
- if(uart_id == 0 || uart_id == 1 || uart_id == 2 || uart_id == 3 || uart_id == 5 || uart_id == 7){
- //*************To Do Something**************//
- }else if(uart_id == 6){
- //*************To Do Something**************//
- }else if(uart_id == 4){
- //*************To Do Something**************//
- }else{
- return 0;
- }
- curr_recv_count[uart_id] = 0;
- last_recv_count[uart_id] = 0;
- }else{ //还在接收
- return 0;
- }
- }
- return 0;
- }
-
-
5. 串口应用
(1)先初始化串口相关的配置,包括GPIO复用、串口参数配置、DMA配置等
(2)循环执行串口接收数据处理函数来处理数据(如果出现串口接收数据不正常,建议新建一个任务,每隔几秒循环依次,将串口数据处理函数uart_recv_data_hdl放在任务中每隔几秒执行一次,因为while中循环执行过快,可能出现问题)
- /*!
- \brief main function
- \param[in] none
- \param[out] none
- \retval none
- */
- int main(void)
- {
-
- /* configure systick */
- systick_config();
-
- //防止串口发送卡死的定时器初始化
- timer1_config();
-
- //串口资源初始化
- uart_init(0, 460800U, 1, 2, DMA_TX_EB, DMA_RX_EB);
- uart_init(1, 460800U, 1, 1, DMA_TX_EB, DMA_RX_EB);
- uart_init(2, 460800U, 1, 1, DMA_TX_EB, DMA_RX_EB);
- uart_init(3, 115200U, 1, 2, DMA_TX_EB, DMA_RX_EB);
- uart_init(4, 115200U, 1, 1, DMA_TX_EB, DMA_RX_EB);
- uart_init(5, 115200U, 1, 2, DMA_TX_EB, DMA_RX_EB);
- uart_init(6, 460800U, 1, 0, DMA_TX_NEB, DMA_RX_NEB);
- uart_init(7, 460800U, 1, 1, DMA_TX_NEB, DMA_RX_NEB);
-
-
- while(1){
- //循环执行串口数据接收处理任务
- uart_recv_data_hdl(0);
- uart_recv_data_hdl(1);
- uart_recv_data_hdl(2);
- uart_recv_data_hdl(3);
- uart_recv_data_hdl(4);
- uart_recv_data_hdl(5);
- uart_recv_data_hdl(6);
- uart_recv_data_hdl(7);
- }
- }
创作不易,希望大家点赞、收藏、关注哦!!!ヾ(o◕∀◕)ノ
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。