前言
前面我们进行了串口的收发测试,以及串口最大速度测试。现在我们就来基于fifo实现一个更方便使用的串口驱动。
实现
添加如下文件
FIFO实现参考公众号文章
https://mp.weixin.qq.com/s/MvL9eDesyuxD60fnbl1nag
串口驱动实现参考上述公众号文章其他文章。
Fifo.c如下
#include <string.h>
#include "fifo.h"
#define FIFO_PARAM_CHECK 0
/**
* in为写入索引 0~(buffer_len-1)。
* out为读出索引 0~(buffer_len-1)。
* in == out时可能是满,也可能是空,可以通过len有效数据长度来确认。
* 写数据in增加,直到追赶到out则满。
* 读数据则out增加,直到追赶到in则空。
* in大于out时则[out,in)区间是有效数据。
* in小于out时则[out,buffer_len)和[0,in)区间是有效数据。
***********************************************************
* 0 buffer_len-1 buffer_len
* (1)开始 in和out都是0
* | |
* in(0)
* out(0)
* len = 0
* (2)写入n字节数据 in变为n和out还是0 对应in大于out的情况
* | |
* out(0)————————————>in(n) |
* len = n
* (3)读出m字节数据(m<n) in还是n和out变为m 对应in大于out的情况
* | |
* out(m)————>in(n)
* len = n-m
* (4)继续写入数据,绕回到开头,对应in小于out的情况
* | |
* out(m)————————————————————————————————>
* ——>in(k)
* len = k + buffer_len-m
*/
uint32_t fifo_in(fifo_st* dev, uint8_t* buffer, uint32_t len)
{
uint32_t space = 0; /* 用于记录空闲空间大小 */
/* 参数检查 */
#if FIFO_PARAM_CHECK
if((dev == 0) || (buffer == 0) || (len == 0))
{
return 0;
}
if(dev->buffer == 0)
{
return 0;
}
#endif
/* 限制len的最大长度为buffer大小 */
if(len > dev->buffer_len)
{
len = dev->buffer_len;
}
/* 计算空闲空间大小
* 正常dev->len不应该大于dev->buffer_len
*/
if(dev->buffer_len >= dev->len)
{
space = dev->buffer_len - dev->len;
}
else
{
/* 这里不应该出现, 出现则是异常 */
dev->len = 0;
space = dev->buffer_len;
}
/* 计算待写入大小, 如果len大于剩余空间则只写入剩余空间大小 */
len = (len >= space) ? space : len;
if(len == 0)
{
return 0; /* 这里有可能无剩余空间,直接返回 */
}
/* 计算len的长度是否需要有绕回,需要分次写入 */
space = dev->buffer_len - dev->in; /* 当前写入位置in到缓存末尾剩余可写入空间 */
if(space >= len)
{
/* 当前写入位置in到缓存末尾足够一次写入 */
memcpy(dev->buffer+dev->in,buffer,len);
}
else
{
/* 当前写入位置in到缓存末尾不够,还需要绕回到前面写 */
memcpy(dev->buffer+dev->in,buffer,space); /* 先写入tail部分 */
memcpy(dev->buffer,buffer+space,len-space); /* 再写入绕回头部分 */
}
/* 更新写入索引和有效数据长度 */
dev->in += len;
if(dev->in >= dev->buffer_len)
{
dev->in -= dev->buffer_len; /* 判断加减法 替代 dev->in %= dev->buffer->len */
}
dev->len += len; /* dev->len最大dev->buffer->len,无需%= dev->buffer->len */
return len;
}
uint32_t fifo_out(fifo_st* dev, uint8_t* buffer, uint32_t len)
{
uint32_t space = 0;
/* 参数检查 */
#if FIFO_PARAM_CHECK
if((dev == 0) || (buffer == 0) || (len == 0))
{
return 0;
}
if(dev->buffer == 0)
{
return 0;
}
#endif
/* 判断是否有数据 */
if(dev->len == 0)
{
return 0;
}
/* 可读出数据量取需要的和有的之间的小值 */
len = (dev->len) > len ? len : dev->len;
/* 计算len的长度是否需要有绕回,需要分次读出 */
space = dev->buffer_len - dev->out; /* 当前读出位置out到缓存末尾剩余可读出空间 */
if(space >= len)
{
/* 当前读出位置out到缓存末尾足够一次读出 */
memcpy(buffer,dev->buffer+dev->out,len);
}
else
{
/* 当前读出位置out到缓存末尾不够,还需要绕回到前面读 */
memcpy(buffer,dev->buffer+dev->out,space); /* 先读出tail部分 */
memcpy(buffer+space,dev->buffer,len-space); /* 再读出绕回头部分 */
}
/* 更新读出索引和有效数据长度 */
dev->out += len;
if(dev->out >= dev->buffer_len)
{
dev->out -= dev->buffer_len; /* 判断加减法 替代 dev->out %= dev->buffer->len */
}
dev->len -= len; /* 这里dev->len 不可能小于len,不会溢出 */
return len;
}
uint32_t fifo_getlen(fifo_st* dev)
{
#if FIFO_PARAM_CHECK
if(dev == 0)
{
return 0;
}
#endif
return dev->len;
}
void fifo_clean(fifo_st* dev)
{
#if FIFO_PARAM_CHECK
if(dev == 0)
{
return 0;
}
#endif
dev->len = 0;
dev->in = 0;
dev->out = 0;
}
uint32_t fifo_getfree(fifo_st* dev)
{
if(dev == 0)
{
return 0;
}
return dev->buffer_len - dev->len;
}
Fifo.h如下
#ifndef FIFO_H
#define FIFO_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/**
* \struct fifo_st
* FIFO缓冲区结构.
*/
typedef struct
{
uint32_t in; /**< 写入索引 */
uint32_t out; /**< 读出索引 */
uint32_t len; /**< 有效数据长度 */
uint32_t buffer_len; /**< 有效长度 */
uint8_t* buffer; /**< 缓存,用户分配 */
} fifo_st;
/**
* \fn fifo_in
* 往fifo里写数据
* \param[in] dev \ref fifo_st
* \param[in] buffer 待写入的数据
* \param[in] len 待写入的长度
* \retval 返回实际写入的数据量
*/
uint32_t fifo_in(fifo_st* dev, uint8_t* buffer, uint32_t len);
/**
* \fn fifo_out
* 从fifo读出数据
* \param[in] dev \ref fifo_st
* \param[in] buffer 存读出的数据
* \param[in] len 需要读出的数据长度
* \retval 返回实际读出的数据量
*/
uint32_t fifo_out(fifo_st* dev, uint8_t* buffer, uint32_t len);
uint32_t fifo_getlen(fifo_st* dev);
void fifo_clean(fifo_st* dev);
uint32_t fifo_getfree(fifo_st* dev);
#ifdef __cplusplus
}
#endif
#endif
串口驱动实现如下
Uart.c
#include "me32g030.h"
#include "me32g030_uart.h"
#include "me32g030_sys.h"
#include "me32g030_ioconfig.h"
#include "uart.h"
#include "fifo.h"
#define CriticalAlloc()
#define EnterCritical() __disable_irq()
#define ExitCritical() __enable_irq()
uint8_t s_uart_rx_buffer[16];
static fifo_st s_uart_fifo_dev[1]=
{
{
.in = 0,
.len = 0,
.out = 0,
.buffer = s_uart_rx_buffer,
.buffer_len = sizeof(s_uart_rx_buffer),
},
};
void uart_rx_cb(int id, uint8_t* buff, uint32_t len)
{
fifo_in(&(s_uart_fifo_dev[id]), buff, len);
}
void uart_init(int id, uint32_t baud)
{
//init UART pin
PA2_INIT(PA2_UART1_TX);
PA3_INIT(PA3_UART1_RX);
//initial UART1
UART_Open(UART1,baud,UART_NO_PARITY,UART_TRIGGER_LEVEL_4_BYTES);
UART_EnableInt(UART1, UART_RX_INT);
NVIC_EnableIRQ(UART1_IRQn);
}
void UART1_IRQHandler(void)
{
uint8_t byte_data;
if (UART1->LSR_b.RDR)
{
byte_data = UART1->RBR;
uart_rx_cb(0, &byte_data, 1);
}
}
uint32_t uart_send(int id, uint8_t* buffer, uint32_t len)
{
for(uint32_t i=0;i<len;i++)
{
//send a test data
while(UART1->LSR_b.THRE == 0);
UART1->THR=buffer;
}
return len;
}
uint32_t uart_read(int id, uint8_t* buffer, uint32_t len)
{
uint32_t rlen;
CriticalAlloc();
EnterCritical();
rlen = fifo_out(&(s_uart_fifo_dev[id]), buffer, len);
ExitCritical();
return rlen;
}
Uart.h
#ifndef UART_H
#define UART_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
void uart_init(int id, uint32_t baud);
uint32_t uart_send(int id, uint8_t* buffer, uint32_t len);
uint32_t uart_read(int id, uint8_t* buffer, uint32_t len);
#ifdef __cplusplus
}
#endif
#endif
测试
Main.c中
先打印HelloWorld然后收到数据原样返回。
#include <string.h>
#include "me32g030.h"
#include "me32g030_sys.h"
#include "uart.h"
uint8_t str[] = "Hello World";
int main(void)
{
SYS_SetAHBClkDivider(1);
uart_init(0, 3000000);
uint8_t rx_buf[64];
uart_send(0, str, strlen((const char*)str));
while(1)
{
uint32_t len = uart_read(0, rx_buf, sizeof(rx_buf));
if(len > 0){
uart_send(0, rx_buf, len);
}
}
}
使用串口调试助手,这里使用3M波特率,进行发送,可以看到开发板原样返回,无数据丢失,说明我们的驱动可以可靠稳定的实现3M的速度传输。
总结
以上基于FIFO实现了串口驱动,提供了简单的接口给应用层使用,测试可以看出很稳定,3M速率也完全没有问题,后面就可以方便的使用串口了。