一、前言
实际项目应用中,对于串口通信速率很快的接收,在不知道多少字节为一帧数据时,处理不好会存在数据的丢失。对于一次性要发送很长的数据,一直让CPU去处理,会造成其他功能处理进入瘫痪。针对这类场景,编写了一份便于移植的应用层代码,对串口数据的收发,提供一个处理框架。
二、设计概述
这套源码属于应用层,驱动层需根据不同平台来实现,后面以stm32 + cubemx来实现驱动层。整个应用层有4个文件,分别是dev_uart.c、dev_uart.h、dataqueue.c和dataqueue.h。可实现多个串口的设备注册和对应帧数据的读取。
2.1 DMA接收不定长数据,完成分帧概述
需要对应芯片的串口和DMA,支持以下功能
- DMA循环模式(普通模式和循环模式区别参考文末链接2)
- DMA半传输中断
- DMA完成传输中断
- 串口空闲中断
2.2 DMA发送固定长度数据概述
三、功能效果
3.1 DMA方式接收不定长数据,然后通过DMA发送回显
可以看到截图有如下结果。
- 发送的字节数等于接收的字节数
- 1ms定时连续发送了2次不同的数据(后面数据多了个6),回显结果对比没有出现错误
- 不定长的数据接收和数据的fifo处理正常(总fifo设置为250字节,多次快速读写后正常)
3.2 DMA方式同时进行发送和接收
设计逻辑,接收到不定长数据后,统计接收长度。每10ms通过DMA发送10字节数据,发送10次后,回显接收到的长度和已发送的长度。 下图结果说明
- DMA发送的过程中,不会干扰DMA数据的接收,即不需要考虑发送和接收不能同时进行。(DMA发送和接收搬运数据是不同的硬件,只是CPU去下发指令和搬运数据时,发送和接收不能同时进行)
![在这里插入图片描述](https://img-blog.csdnimg.cn/7f6d2c67e67c4f6bb8cd17d71734f1c9.png
四、详细设计
4.1 数据结构
数据结构一共有2个,以下是串口设备的抽象,实现里面的指针和大小设置、驱动层调用相应的接口,即可实现以下功能
- 不丢数据的不定长数据接收,通过空闲中断分数据帧
- DMA方式发送和接收,大大减轻处理器的负担
- 应用层直接获取数据帧
typedef struct uart_device
{
uint8_t *name;
uint8_t name_size;
uint8_t *rx_frame_start;
uint8_t *dmarx_buf;
uint16_t rx_current_size;
uint16_t rx_max_size;
uint16_t dmarx_buf_size;
uint16_t last_dmarx_size;
struct data_queue fifo;
int (*recv_fifo_write)(struct data_queue *queue,uint8_t *data_ptr,uint16_t size);
int (*recv_fifo_read)(struct data_queue *queue,uint8_t *data_ptr,uint16_t *size,uint16_t max_size);
void (*recv_fifo_reset)(struct data_queue *queue);
void (*send)(uint8_t *data,uint16_t size);
int (*dmarx_remain_size)(void);
}uart_device_t;
以下是数据帧队列处理方式的数据结构,可直接使用或者用操作系统对应的消息队列去处理。 数据帧队列的实现主要思想是用2个环形缓存,一个记录数据的内容,一个记录数据帧的长度。
struct data_queue
{
uint8_t *Ring_Buff;
uint8_t magic;
uint8_t frame_r_index;
uint8_t frame_w_index;
uint8_t frame_max_num;
uint8_t frame_recv_num;
uint16_t *frame_size_fifo;
uint16_t Ring_Buff_Head;
uint16_t Ring_Buff_Tail;
uint16_t Ring_Buff_size;
};
4.2 应用层接口
应用层接口一共有2个,发送和接收。 发送如下,其中定义了一个确定传入的发送buff是互斥的宏,即发送结果出来前不对buff进行修改,裸机可以定义该宏。 如果用了操作系统,在很多任务都有调用发送接口,比较复杂的时候,要加上操作系统的互斥量来上锁和解锁,有序的对uart_tx_frame_buff内存访问。
void uart_dma_tx(struct uart_device *serial,uint8_t *data,uint16_t size)
{
#if defined(CONFIRM_SEND_BUFF_MUTUALLY_EXCLUSIV)
uart_send_lock();
for(int i = 0; i < size;++i)
uart_tx_frame_buff[i] = data[i];
serial->send(uart1_tx_frame_buff,size);
uart_send_unlock();
#else
serial->send(data,size);
#endif
}
接收如下,调用对应的接收fifo处理,在空闲中断的驱动里面,调用数据队列的写fifo数据,然后读取的时候就会用队列的方式返回写入的fifo数据。
int uart_dma_rx(struct uart_device *serial,uint8_t *data,uint16_t *size)
{
return serial->recv_fifo_read(&serial->fifo,data,size);
}
4.3 需实现的驱动接口
需要实现的驱动接口一共有5个或更多
void uart1_device_init(struct uart_device *serial);
void uart_dmarx_half_done_isr(struct uart_device *serial);
void uart_dmarx_done_isr(struct uart_device *serial);
void uart_dmarx_idle_isr(struct uart_device *serial);
void uart_dma_error_report(struct uart_device *serial,uint8_t *data,uint16_t size);
4.3.1 uart1_device_init是对应串口设备的初始化,参考以下代码
- 定义分配内存和大小,以及函数指针指向对应的函数地址。
- 在硬件驱动文件里面,传入uart1_device_init对应的struct uart_device的结构体,进行初始化
- 如该单片机需要几个串口设备,编写uart2_device_init驱动接口,然后在驱动文件里面,传入uart2_device_init对应的struct uart_device的结构体,进行初始化
#define UART1_RX_FRAME_BUF_SIZE 512
#define UART1_DMA_RX_BUF_SIZE 256
#define UART1_RX_FIFO_BUF_SIZE 256
#define UART1_RX_FRAME_MAX_NUM 10
#define UART1_NAME_MAX_SIZE 6
static uint8_t uart1_rx_frame_buff[UART1_RX_FRAME_BUF_SIZE];
static uint8_t uart1_dmarx_buff[UART1_DMA_RX_BUF_SIZE];
static uint8_t uart1_rx_ring_buff[UART1_RX_FIFO_BUF_SIZE];
static uint16_t uart1_frame_max[UART1_RX_FRAME_MAX_NUM];
static uint8_t uart1_name[UART1_NAME_MAX_SIZE] = {"uart1"};
void uart1_device_init(struct uart_device *serial)
{
serial->name = uart1_name;
serial->name_size = UART1_NAME_MAX_SIZE - 1;
serial->rx_frame_start = uart1_rx_frame_buff;
serial->dmarx_buf = uart1_dmarx_buff;
serial->dmarx_buf_size = UART1_DMA_RX_BUF_SIZE;
serial->last_dmarx_size = 0;
serial->rx_current_size = 0;
serial->rx_max_size = UART1_RX_FRAME_BUF_SIZE;
serial->send = UART1_Send;
serial->dmarx_remain_size = usart1_dma_rx_remain_size;
if(0==data_queue_init(&serial->fifo,uart1_rx_ring_buff,UART1_RX_FIFO_BUF_SIZE,uart1_frame_max,UART1_RX_FRAME_MAX_NUM))
{
serial->recv_fifo_write = data_queue_push;
serial->recv_fifo_read = data_queue_pop;
serial->recv_fifo_reset = data_queue_reset;
}
else
{
serial->recv_fifo_write = NULL;
serial->recv_fifo_read = NULL;
serial->recv_fifo_reset = NULL;
}
}
4.3.2 uart_dmarx_half_done_isr
当指定DMA的接收长度后,如30字节,在收到第15字节的时候,单片机就会有dma半传输中断信号,等MCU有空去处理时候,获取当前剩余要搬运的字节。假设MCU比较慢去响应中断,此时dma已经搬运了18个字节,那么会得出dma剩余要搬运12个字节,MCU快速将数据搬运到别的内存空间。
void uart_dmarx_half_done_isr(struct uart_device *serial)
{
uint16_t recv_total_size;
uint16_t recv_size;
recv_total_size = serial->dmarx_buf_size - serial->dmarx_remain_size();
recv_size = recv_total_size - serial->last_dmarx_size;
if( recv_size + serial->rx_current_size < serial->rx_max_size)
{
for(int i = 0;i < recv_size;++i)
serial->rx_frame_start[serial->rx_current_size++] = serial->dmarx_buf[serial->last_dmarx_size++];
}
}
在对应的驱动中断里面,获取中断标志,调用该API,实现举例如下
uart_device_t serial_1;
char debug_buff[50];
void DMA1_Channel5_IRQHandler(void)
{
if( __HAL_DMA_GET_FLAG(&hdma_usart1_rx,DMA_FLAG_HT5))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx,DMA_FLAG_HT5);
uart_dmarx_half_done_isr(&serial_1);
}
else if( __HAL_DMA_GET_FLAG(&hdma_usart1_rx,DMA_FLAG_TC5))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx,DMA_FLAG_TC5);
uart_dmarx_done_isr(&serial_1);
}
else if( __HAL_DMA_GET_FLAG(&hdma_usart1_rx,DMA_FLAG_TE5))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx,DMA_FLAG_TE5);
uint16_t len;
len = sprintf(debug_buff,"uart1 DMA recv errorCode:%d\r\n",hdma_usart1_rx.ErrorCode);
uart_dma_error_report(&serial_1,(uint8_t *)debug_buff,len);
}
HAL_DMA_IRQHandler(&hdma_usart1_rx);
}
4.3.3 uart_dmarx_done_isr
在DMA接收到指定长度后,会有一个dma接收完成中断,如果是dma循环模式,接收完成后,dma外设会将接收地址修改为一开始配置的接收起始地址,计数从0开始,相等与软件再配置一次dma普通模式接收。 这样即使处理器还没空去相应DMA的中断时候,DMA外设也会继续搬运数据,而不会丢失数据,结合半传输和完成传输的处理,让MCU有容错的时间去搬运数据。所以使用这种方式,能保证数据传输在很快的速率下,也不丢数据。
void uart_dmarx_done_isr(struct uart_device *serial)
{
uint16_t recv_size;
recv_size = serial->dmarx_buf_size - serial->last_dmarx_size;
if(recv_size + serial->rx_current_size < serial->rx_max_size)
{
for(int i = 0;i < recv_size;++i)
serial->rx_frame_start[serial->rx_current_size++] = serial->dmarx_buf[serial->last_dmarx_size++];
}
serial->last_dmarx_size = 0;
}
4.3.4 uart_dmarx_idle_isr
- 串口的空闲中断是实现分帧的标志,读取完dma接收的字节,再写入到数据队列中,即可完成分包。
- 如果串口空闲中断调试有bug,可尝试用硬件定时器去分包,如30毫秒,没有dma的中断促发,就认为接收完了一帧,有dma中断到来时,将定时器计时清零。
- 如果是有协议包头的数据通信,可不实现数据队列,直接在dma半传输完成、dma传输完成、空闲中断写入fifo即可,应用层去读取fifo查找包头解析数据。
void uart_dmarx_idle_isr(struct uart_device *serial)
{
uint16_t recv_total_size;
uint16_t recv_size;
recv_total_size = serial->dmarx_buf_size - serial->dmarx_remain_size();
recv_size = recv_total_size - serial->last_dmarx_size;
if( recv_size + serial->rx_current_size < serial->rx_max_size)
{
for(int i = 0;i < recv_size;++i)
serial->rx_frame_start[serial->rx_current_size++] = serial->dmarx_buf[serial->last_dmarx_size++];
}
if(serial->recv_fifo_write != NULL && serial->rx_current_size > 0) serial->recv_fifo_write(&serial->fifo,serial->rx_frame_start,serial->rx_current_size);
serial->rx_current_size= 0;
}
4.3.5 uart_dma_error_report
dma发送和接收传输的汇报接口,就是打印串口设备名称,和传输的汇报内容。 可以在dma的发送和接收里面,获取错误标志,进行添加信息打印。
void uart_dma_error_report(struct uart_device *serial,uint8_t *data,uint16_t size)
{
printf("%.*s Error Info :%.*s",serial->name_size,serial->name,size,data);
}
发送端的错误可以如下,接收端的错误在4.3.2部分有举例
uart_device_t serial_1;
char debug_buff[50];
void UART1_Send(uint8_t *pData, uint16_t Size)
{
if(HAL_OK != HAL_UART_Transmit_DMA(&huart1,pData,Size))
{
uint16_t len;
len = sprintf(debug_buff,"uart1 DMA send HAL errorCode:%d\r\n",hdma_usart1_tx.ErrorCode);
uart_dma_error_report(&serial_1,(uint8_t *)debug_buff,len);
}
}
void DMA1_Channel4_IRQHandler(void)
{
if( __HAL_DMA_GET_FLAG(&hdma_usart1_tx,DMA_FLAG_TE4))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx,DMA_FLAG_TE4);
uint16_t len;
len = sprintf(debug_buff,"uart1 DMA send errorCode:%d\r\n",hdma_usart1_tx.ErrorCode);
uart_dma_error_report(&serial_1,(uint8_t *)debug_buff,len);
}
HAL_DMA_IRQHandler(&hdma_usart1_tx);
}
4.4 发送实现逻辑
发送的实现逻辑,根据官方的SDK来设计的,阅读stm32的相关源码发现,HAL_UART_Transmit_DMA接口是配置dma的发送,结合初始化DMA通道的时候,置为普通模式,不配置为循环模式,就可实现调用一次就发送一次。HAL_UART_Transmit_DMA接口里面还开启了DMA半传输中断、DMA完成中断、DMA错误中断。因为传入的buff应用层设计为不可改变,所以对DMA半传输中断、DMA完成中断没有运用,可修改源码关闭这两个中断,只开错误中断。 DMA完成中断,可作为一个发送成功标志,告诉应用层发送成功了,可以对传入发送buff进行修改。
4.5 接收实现逻辑
接收实现的逻辑可参考链接1的文章,思路和文章作者一致,只是作了一些数据结构、驱动配合以及应用层上进行简化改动。 核心逻辑个人表诉如下
- DMA半传输中断,处理器有空去处理时,记录当前收到的数据大小,DMA完成中断到来时,总共要接收的字节减去上一次dma处理的数据大小,就是要拷贝的剩余数据,此时算完成了一次DMA的普通接收,DMA循环模式下,相当于无限进行DMA的普通接收,执行半中断和完成中断的触发。
- DMA接收完成中断处理,要对上一次dma处理的数据清零,其实就是将要拷贝数据的指针,指向dma的缓存接收buff的首地址,还一个作用是,让空闲和半传输中断回到最初的模式处理,不会出错统计要接收的字节大小。
- 空闲中断的触发的时候,说明串口没有数据输入了,此时获取dma要接收的总大小减去dma剩余要传输的大小的值,并减去上一次dma收到的大小,就是剩余要拷贝的字节内容。对于半传输和空闲中的处理,可能场景图示如下
![在这里插入图片描述](https://img-blog.csdnimg.cn/8684dce13e374bf38e284a42085b7c84.png
五、stm32芯片 +keil + cubemx配置例程
举例是stm32L431的芯片配置,对stm32的芯片配置都类似。
5.1 配置时钟
5.2 配置串口DMA接收
5.3 keil工程生成配置
5.4 驱动层适配
char debug_buff[50];
uart_device_t serial_1;
uart1_device_init(&serial_1);
HAL_UART_Receive_DMA(&huart1,serial_1.dmarx_buf,serial_1.dmarx_buf_size);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xff);
return ch;
}
void USART1_IRQHandler(void)
{
if( __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
uart_dmarx_idle_isr(&serial_1);
}
HAL_UART_IRQHandler(&huart1);
}
int usart1_dma_rx_remain_size(void)
{
return __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
}
void UART1_Send(uint8_t *pData, uint16_t Size)
{
if(HAL_OK != HAL_UART_Transmit_DMA(&huart1,pData,Size))
{
uint16_t len;
len = sprintf(debug_buff,"uart1 DMA send HAL errorCode:%d\r\n",hdma_usart1_tx.ErrorCode);
uart_dma_error_report(&serial_1,(uint8_t *)debug_buff,len);
}
}
void DMA1_Channel4_IRQHandler(void)
{
if( __HAL_DMA_GET_FLAG(&hdma_usart1_tx,DMA_FLAG_TE4))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx,DMA_FLAG_TE4);
uint16_t len;
len = sprintf(debug_buff,"uart1 DMA send errorCode:%d\r\n",hdma_usart1_tx.ErrorCode);
uart_dma_error_report(&serial_1,(uint8_t *)debug_buff,len);
}
HAL_DMA_IRQHandler(&hdma_usart1_tx);
}
void DMA1_Channel5_IRQHandler(void)
{
if( __HAL_DMA_GET_FLAG(&hdma_usart1_rx,DMA_FLAG_HT5))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx,DMA_FLAG_HT5);
uart_dmarx_half_done_isr(&serial_1);
}
else if( __HAL_DMA_GET_FLAG(&hdma_usart1_rx,DMA_FLAG_TC5))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx,DMA_FLAG_TC5);
uart_dmarx_done_isr(&serial_1);
}
else if( __HAL_DMA_GET_FLAG(&hdma_usart1_rx,DMA_FLAG_TE5))
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx,DMA_FLAG_TE5);
uint16_t len;
len = sprintf(debug_buff,"uart1 DMA recv errorCode:%d\r\n",hdma_usart1_rx.ErrorCode);
uart_dma_error_report(&serial_1,(uint8_t *)debug_buff,len);
}
HAL_DMA_IRQHandler(&hdma_usart1_rx);
}
5.5 应用层简单使用举例
在maic.c里面定义读取的帧的内存和记录帧大小的全局变量,如下
uint8_t recv_buff[1024];
uint16_t recv_size;
在main.c的whlie(1)添加如下代码,即可实现数据回显。
if(uart_dma_rx(&serial_1,recv_buff,&recv_size,sizeof(recv_buff)))
{
uart_dma_tx(&serial_1,recv_buff,10);
}
六、源码文件
6.1 dev_uart.c
#include <stddef.h>
#include <stdint.h>
#include "dev_uart.h"
#include "usart.h"
#ifdef UNCONFIRM_SEND_BUFF_MUTUALLY_EXCLUSIV
#define UART1_TX_BUF_SIZE 1024
static uint8_t uart_tx_frame_buff[UART1_TX_BUF_SIZE];
void uart_send_lock(void)
{
__disable_irq();
}
void uart_send_unlock(void)
{
__enable_irq();
}
#endif
#define UART1_RX_FRAME_BUF_SIZE 512
#define UART1_DMA_RX_BUF_SIZE 256
#define UART1_RX_FIFO_BUF_SIZE 256
#define UART1_RX_FRAME_MAX_NUM 10
#define UART1_NAME_MAX_SIZE 6
static uint8_t uart1_rx_frame_buff[UART1_RX_FRAME_BUF_SIZE];
static uint8_t uart1_dmarx_buff[UART1_DMA_RX_BUF_SIZE];
static uint8_t uart1_rx_ring_buff[UART1_RX_FIFO_BUF_SIZE];
static uint16_t uart1_frame_max[UART1_RX_FRAME_MAX_NUM];
static uint8_t uart1_name[UART1_NAME_MAX_SIZE] = {"uart1"};
void uart1_device_init(struct uart_device *serial)
{
serial->name = uart1_name;
serial->name_size = UART1_NAME_MAX_SIZE - 1;
serial->rx_frame_start = uart1_rx_frame_buff;
serial->dmarx_buf = uart1_dmarx_buff;
serial->dmarx_buf_size = UART1_DMA_RX_BUF_SIZE;
serial->last_dmarx_size = 0;
serial->rx_current_size = 0;
serial->rx_max_size = UART1_RX_FRAME_BUF_SIZE;
serial->send = UART1_Send;
serial->dmarx_remain_size = usart1_dma_rx_remain_size;
if(0==data_queue_init(&serial->fifo,uart1_rx_ring_buff,UART1_RX_FIFO_BUF_SIZE,uart1_frame_max,UART1_RX_FRAME_MAX_NUM))
{
serial->recv_fifo_write = data_queue_push;
serial->recv_fifo_read = data_queue_pop;
serial->recv_fifo_reset = data_queue_reset;
}
else
{
serial->recv_fifo_write = NULL;
serial->recv_fifo_read = NULL;
serial->recv_fifo_reset = NULL;
}
}
void uart_dmarx_half_done_isr(struct uart_device *serial)
{
uint16_t recv_total_size;
uint16_t recv_size;
recv_total_size = serial->dmarx_buf_size - serial->dmarx_remain_size();
recv_size = recv_total_size - serial->last_dmarx_size;
if( recv_size + serial->rx_current_size < serial->rx_max_size)
{
for(int i = 0;i < recv_size;++i)
serial->rx_frame_start[serial->rx_current_size++] = serial->dmarx_buf[serial->last_dmarx_size++];
}
}
void uart_dmarx_done_isr(struct uart_device *serial)
{
uint16_t recv_size;
recv_size = serial->dmarx_buf_size - serial->last_dmarx_size;
if(recv_size + serial->rx_current_size < serial->rx_max_size)
{
for(int i = 0;i < recv_size;++i)
serial->rx_frame_start[serial->rx_current_size++] = serial->dmarx_buf[serial->last_dmarx_size++];
}
serial->last_dmarx_size = 0;
}
void uart_dmarx_idle_isr(struct uart_device *serial)
{
uint16_t recv_total_size;
uint16_t recv_size;
recv_total_size = serial->dmarx_buf_size - serial->dmarx_remain_size();
recv_size = recv_total_size - serial->last_dmarx_size;
if( recv_size + serial->rx_current_size < serial->rx_max_size)
{
for(int i = 0;i < recv_size;++i)
serial->rx_frame_start[serial->rx_current_size++] = serial->dmarx_buf[serial->last_dmarx_size++];
}
if(serial->recv_fifo_write != NULL && serial->rx_current_size > 0) serial->recv_fifo_write(&serial->fifo,serial->rx_frame_start,serial->rx_current_size);
serial->rx_current_size= 0;
}
void uart_dma_tx(struct uart_device *serial,uint8_t *data,uint16_t size)
{
#if defined(UNCONFIRM_SEND_BUFF_MUTUALLY_EXCLUSIV)
uart_send_lock();
for(int i = 0; i < size;++i)
uart_tx_frame_buff[i] = data[i];
serial->send(uart_tx_frame_buff,size);
uart_send_unlock();
#else
serial->send(data,size);
#endif
}
int uart_dma_rx(struct uart_device *serial,uint8_t *data,uint16_t *size,uint16_t max_size)
{
return serial->recv_fifo_read(&serial->fifo,data,size,max_size);
}
void uart_dma_error_report(struct uart_device *serial,uint8_t *data,uint16_t size)
{
printf("%.*s Error Info :%.*s",serial->name_size,serial->name,size,data);
}
6.2 dev_uart.h
#ifndef _DEV_UART_H_
#define _DEV_UART_H_
#include <stdint.h>
#include "dataqueue.h"
typedef struct uart_device
{
uint8_t *name;
uint8_t name_size;
uint8_t *rx_frame_start;
uint8_t *dmarx_buf;
uint16_t rx_current_size;
uint16_t rx_max_size;
uint16_t dmarx_buf_size;
uint16_t last_dmarx_size;
struct data_queue fifo;
int (*recv_fifo_write)(struct data_queue *queue,uint8_t *data_ptr,uint16_t size);
int (*recv_fifo_read)(struct data_queue *queue,uint8_t *data_ptr,uint16_t *size,uint16_t max_size);
void (*recv_fifo_reset)(struct data_queue *queue);
void (*send)(uint8_t *data,uint16_t size);
int (*dmarx_remain_size)(void);
}uart_device_t;
void uart_dma_tx(struct uart_device *serial,uint8_t *data,uint16_t size);
int uart_dma_rx(struct uart_device *serial,uint8_t *data,uint16_t *size,uint16_t max_size);
void uart1_device_init(struct uart_device *serial);
void uart_dmarx_half_done_isr(struct uart_device *serial);
void uart_dmarx_done_isr(struct uart_device *serial);
void uart_dmarx_idle_isr(struct uart_device *serial);
void uart_dma_error_report(struct uart_device *serial,uint8_t *data,uint16_t size);
#endif
6.3 dataqueue.c
#include "dataqueue.h"
#include <stddef.h>
#include <stdio.h>
#define INITOK 0x55
void data_queue_lock(void)
{
__disable_irq();
}
void data_queue_unlock(void)
{
__enable_irq();
}
int data_queue_init(struct data_queue *queue,uint8_t *ring_buff_ptr,uint16_t ring_buff_size,uint16_t *frame_size_ptr,uint16_t frame_max_num)
{
if(queue == NULL || ring_buff_ptr == NULL || frame_size_ptr == NULL) return -1;
queue->Ring_Buff = ring_buff_ptr;
queue->frame_r_index = 0;
queue->frame_w_index = 0;
queue->frame_max_num = frame_max_num;
queue->frame_recv_num = 0;
queue->frame_size_fifo = frame_size_ptr;
queue->Ring_Buff_Head = 0;
queue->Ring_Buff_Tail = 0;
queue->Ring_Buff_size = ring_buff_size;
queue->magic = INITOK;
return 0;
}
int data_queue_push(struct data_queue *queue,uint8_t *data_ptr,uint16_t size)
{
if(queue->magic != INITOK) return 0;
if(queue->frame_recv_num >= queue->frame_max_num || size > queue->Ring_Buff_size)
{
printf("Error: data queue is full!\r\n");
return 0;
}
uint8_t *p = data_ptr;
uint16_t i;
data_queue_lock();
queue->frame_size_fifo[queue->frame_w_index] = size;
if(++queue->frame_w_index >= queue->frame_max_num) queue->frame_w_index = 0;
if(queue->Ring_Buff_Tail + size < queue->Ring_Buff_size)
{
for(i = 0; i < size; ++i)
queue->Ring_Buff[queue->Ring_Buff_Tail++] = *p++;
}
else
{
uint16_t len = queue->Ring_Buff_size - queue->Ring_Buff_Tail;
for(i = 0; i < len; ++i)
queue->Ring_Buff[queue->Ring_Buff_Tail++] = *p++;
queue->Ring_Buff_Tail = 0;
for(i = len; i < size; ++i)
queue->Ring_Buff[queue->Ring_Buff_Tail++] = *p++;
}
queue->frame_recv_num++;
data_queue_unlock();
return size;
}
int data_queue_pop(struct data_queue *queue,uint8_t *data_ptr,uint16_t *size,uint16_t max_size)
{
if(queue->magic != INITOK) return 0;
if(queue->frame_recv_num == 0) return 0;
data_queue_lock();
uint8_t *p = data_ptr;
uint16_t i;
*size = queue->frame_size_fifo[queue->frame_r_index];
if(++queue->frame_r_index >= queue->frame_max_num) queue->frame_r_index = 0;
if(*size > max_size)
{
printf("Error: Not enough data memory to read fifo data!\r\n");
return 0;
}
if(queue->Ring_Buff_Head + *size < queue->Ring_Buff_size)
{
for(i = 0; i < *size; ++i)
*p++ = queue->Ring_Buff[queue->Ring_Buff_Head++];
}
else
{
uint16_t len = queue->Ring_Buff_size - queue->Ring_Buff_Head;
for(i = 0; i < len; ++i)
*p++ = queue->Ring_Buff[queue->Ring_Buff_Head++];
queue->Ring_Buff_Head = 0;
for(i = len; i < *size; ++i)
*p++ = queue->Ring_Buff[queue->Ring_Buff_Head++];
}
queue->frame_recv_num--;
data_queue_unlock();
return *size;
}
void data_queue_reset(struct data_queue *queue)
{
queue->frame_r_index = 0;
queue->frame_w_index = 0;
queue->frame_recv_num = 0;
queue->Ring_Buff_Head = 0;
queue->Ring_Buff_Tail = 0;
}
6.4 dataqueue.h
#ifndef DATAQUEUE_H__
#define DATAQUEUE_H__
#include <stdint.h>
struct data_queue
{
uint8_t *Ring_Buff;
uint8_t magic;
uint8_t frame_r_index;
uint8_t frame_w_index;
uint8_t frame_max_num;
uint8_t frame_recv_num;
uint16_t *frame_size_fifo;
uint16_t Ring_Buff_Head;
uint16_t Ring_Buff_Tail;
uint16_t Ring_Buff_size;
};
typedef struct data_queue data_queue_t;
int data_queue_init(struct data_queue *queue,uint8_t *ring_buff_ptr,uint16_t ring_buff_size,uint16_t *frame_size_ptr,uint16_t frame_max_num);
int data_queue_push(struct data_queue *queue,uint8_t *data_ptr,uint16_t size);
int data_queue_pop(struct data_queue *queue,uint8_t *data_ptr,uint16_t *size,uint16_t max_size);
void data_queue_reset(struct data_queue *queue);
#endif
七、参考资料
链接1: 一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制 链接2: STM32 DMA 循环模式DMA_Mode_Circular详解
|