文章目录
- 一、串口环形缓冲区概念
- 二、STC12例程
- (1)环形串口缓冲区结构体
- (2)串口环形缓冲区存和取数据
- (3)完整工程demo
一、串口环形缓冲区概念
串口环形缓冲区应用于嵌入式、物联网开发中处理接收串口数据量过大的问题。串口环形缓冲简单理解就是建立一个数组,将串口的数据存到数组里缓存,待空闲时处理。而缓存区越大,那么可以缓存的数据就越多。
环形串口缓冲区建立的数组还要再定义一个头和一个尾。这时有三种情况:
1、当头等于尾时,我们便知道此时环形串口缓冲区无数据,此时不进行读操作。
2、有数据来时,数组存储数据,并且头按数据的长度向前移动,空闲时尾开始取数据,直到尾等于头为止。
3、当头存到数组长度的最后一位时,返回数组第一位开始存数据。
因为头开始存数据到最后一位时,便有返回数组第一位开始存数据,此时我们就可以想象这个数据是一个类似甜甜圈的环形,这便是串口环形缓冲区。
二、STC12例程
要想写串口环形缓冲区,首先需要初始化串口和接收中断处理,保证串口收发正常,初始化串口部分默认大家都懂了,直接略过,我自己编写的例程使用STC12,因为STC12单片机是51单片机的升级版本,几乎学过51单片机的都能看得懂,比较简单,现在企业的开发标配是STM32了,这时只需移植过去即可。我提供两种MCU的串口初始化供大家参考:
STM32F1x固件库函数学习笔记(一)
STC12C5A60S2串口通信(使用独立波特率发生器)
(1)环形串口缓冲区结构体
串口初始化好后,建立一个环形缓冲区结构体
//环形缓冲区结构体
typedef struct
{
uint8_t head_count; //头计数
uint8_t tail_count; //尾计数
uint8_t buf[UART_RX_MAX]; //缓冲区数组
}UART_S;
下一步,我们便可以定义结构体变量初始化结构体
UART_S uart;//定义串口缓冲区结构体变量
//串口环形缓冲区初始化
void uart_Buffer_Init(void)
{
int i;
uart.head_count = 0;
uart.tail_count = 0;
for(i=0; i<UART_RX_MAX; i++)
{
uart.buf[i] = 0x00;
}
}
(2)串口环形缓冲区存和取数据
下面就是串口环形缓冲代码里比较关键的部分,分别写一个放数据和取数据的函数。
放数据的函数逻辑是有数据来时,将数据存入串口环形缓冲区,当存到数组的最后一位时,返回从第一位存起。
取数据的逻辑是先进行一个判断,当判断缓冲区的头和尾不一样时,这时认为缓冲区有数据,我们便开始从缓冲环形缓冲区里取数据,当取到数组最后一位时,返回数组第一位,从第一位开始取数据。
//往缓冲器里放数据
void uart_buf_put(uchar ch)
{
uart.buf[uart.head_count++] = ch;
if(uart.head_count == UART_RX_MAX)
{
uart.head_count = 0;
}
}
//判断缓冲区是否有数据
int fifo_is_empty(void)
{
if(uart.head_count != uart.tail_count)//判断如果头不等于尾
{
return 0;//有数据
}
else return 1;//无数据
}
//从缓冲区取数据
int uart_buf_get(uchar *ch)
{
if(fifo_is_empty() == 0)//如果有数据
{
*ch = uart.buf[uart.tail_count++];//取数据
if(uart.tail_count == UART_RX_MAX)
{
uart.tail_count = 0;
}
return 1;//返回成功
}
else return 0;//返回失败
}
下一步我们再写一个读数据的函数,一个简单的串口环形缓冲区就写完了,我们使用的时候只需把存数据放到中断里,然后在主函数里取数据就行了。
//读数据
void debug_read(void)
{
uchar ch;
/*如果有数据就一直取,直到取完为至*/
while(!fifo_is_empty())
{
if(uart_buf_get(&ch))
{
putchar(ch);
}
}
}
void main()
{
UartInit();//初始化串口
uart_Buffer_Init();//初始化串口环形缓冲区
while(1)
{
debug_read();
}
}
//串口中断处理函数
void uart_Interrupt() interrupt 4
{
unsigned char UartData;//单字节串口数据
if(RI)
{
RI = 0;
UartData = SBUF;
uart_buf_put(UartData);//往串口缓冲区存数据
}
}
(3)完整工程demo
#include <STC12C5A60S2.h>
//宏定义
#define uchar unsigned char
#define uint unsigned int
#define UART_RX_MAX 255 //数组最大值
//环形缓冲区结构体
typedef struct
{
uchar head_count; //头计数
uchar tail_count; //尾计数
uchar buf[UART_RX_MAX]; //缓冲区数组
}UART_S;
UART_S uart;//定义串口缓冲区结构体变量
//串口初始化,晶振11.0592,波特率9600
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x04; //独立波特率发生器时钟为Fosc,即1T
BRT = 0xDC; //设定独立波特率发生器重装值
AUXR |= 0x01; //串口1选择独立波特率发生器为波特率发生器
AUXR |= 0x10; //启动独立波特率发生器
EA = 1;//开总中断
ES = 1;//开串口中断
}
//向串口发送一个字符
void putchar(char ch)
{
SBUF = ch;
while(!TI);TI = 0;
}
//向串口发送一段字符串
//void prints(char *s)
//{
// while(*s != '\0')//发送字符串,直到遇到0才结束
// {
// SBUF = *s++;
// while(!TI);
// TI = 0;
// }
//}
//串口环形缓冲区初始化
void uart_Buffer_Init(void)
{
int i;
uart.head_count = 0;
uart.tail_count = 0;
for(i=0; i<UART_RX_MAX; i++)
{
uart.buf[i] = 0x00;
}
}
//往缓冲器里放数据
void uart_buf_put(uchar ch)
{
uart.buf[uart.head_count++] = ch;
if(uart.head_count == UART_RX_MAX)
{
uart.head_count = 0;
}
}
//判断缓冲区是否有数据
int fifo_is_empty(void)
{
if(uart.head_count != uart.tail_count)//判断如果头不等于尾
{
return 0;//有数据
}
else return 1;//无数据
}
//从缓冲区取数据
int uart_buf_get(uchar *ch)
{
if(fifo_is_empty() == 0)//如果有数据
{
*ch = uart.buf[uart.tail_count++];//取数据
if(uart.tail_count == UART_RX_MAX)
{
uart.tail_count = 0;
}
return 1;//返回成功
}
else return 0;//返回失败
}
//读数据
void debug_read(void)
{
uchar ch;
/*如果有数据就一直取,直到取完为至*/
while(!fifo_is_empty())
{
if(uart_buf_get(&ch))
{
putchar(ch);
}
}
}
void main()
{
UartInit();//初始化串口
uart_Buffer_Init();//初始化串口环形缓冲区
while(1)
{
debug_read();
}
}
//串口中断处理函数
void uart_Interrupt() interrupt 4
{
unsigned char UartData;//单字节串口数据
if(RI)
{
RI = 0;
UartData = SBUF;
uart_buf_put(UartData);//往串口缓冲区存数据
}
}