一、 串口收发功能介绍
常用的串口收发数据的方式一共两种,一种是不使用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◕∀◕)ノ