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