串口实用的循环缓冲区

本文介绍了一种基于C8051F920单片机的串口通讯设计,重点讨论了循环缓冲区的实现方法及其在通讯协议中的应用。通过具体的代码示例展示了如何进行串口数据的收发处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小记。

项目临时需要单片机进行节点控制,主要用来控制模块的开关,以串口进行通讯。

单片机几多久没玩了,选用的是C8051F920,传说中增强型51,不过看了Datesheet.

还是51而已。。无难度,项目要求主要是功耗的问题,5年内只能更换一次电池。

蛀牙用到模块是定时器,几个GPIO,smaRTClock,串口。

主要在通讯协议这部分花的时间较多,串口接收采用循环缓冲区的方式,以FIFO方式进行读写

串口的缓冲区需要两个一个接收,一个发送,通讯协议中规定数据长度在16字节,就把缓冲区

大小定为64字节

#defineUART0_BUF_SIZE 256

static uchar data UART0_Rx_head; //OUT

static uchar data UART0_Rx_tail; //IN

static uchar data UART0_Tx_head; //IN

static uchar data UART0_Tx_tail; //OUT

串口采用中断方式:

if(RI0 == 1)

{

RI0= 0; // Clear interrupt flag

tmp_data = SBUF0;

if(((UART0_Rx_In - UART0_Rx_Out) & ~(UART0_RX_BUFF_SIZE - 1)) == 0){

UART0_RxBuffer[UART0_Rx_In & (UART0_RX_BUFF_SIZE - 1)] =tmp_data;

UART0_Rx_In++;

}

}

if(TI0 == 1) // Check if transmit flag is set

{

TI0= 0; // Clear interrupt flag

if (UART0_Tx_In != UART0_Tx_Out) {

SBUF0 = UART0_TxBuffer[UART0_Tx_Out & (UART0_TX_BUFF_SIZE -1)];

UART0_Tx_Out++;

tx_restart_uart0= 0;

}

else tx_restart_uart0 = 1;

}

这里:

UART0_Rx_In&(UART0_RX_BUFF_SIZE - 1),相当于UART0_Rx_In% UART0_RX_BUFF_SIZE,对IN取模

结果相当于映射到对应的缓冲区位置,比如这里IN的值是0~255而buffer大小只有64字节,即

IN的值对应四段缓冲区,064 128 192都对应缓冲区的起始地址。

((UART0_Rx_In- UART0_Rx_Out) & ~(UART0_RX_BUFF_SIZE - 1)) == 0 //这是判断缓冲区是否满

因为IN始终在OUT的前面(当IN=OUT时缓冲区为空,所以OUT不可能在IN前面),即便是IN=5

OUT=240时,IN<OUT,但IN也在前面,IN- OUT = -235 =21,这里IN和OUT是无符号字符型,相减也

为无符号型。故此时缓冲区有21字节数据。UART0_Rx_In++;IN指向的节点内容是空的,当前值应

该在Buffer[IN- 1]

同理:

发送中断产生时,首先判断缓冲区是否为空,为空可以直接发送,否则,继续发送缓冲区数据

怎样才能产生发送中断呢,

1.可以先将数据写入发送缓冲区,然后手动置TI0= 1.产生中断,使中断函数自动发送缓冲区数据。

2.发送数据前判断串口有无数据待发送(包含正在发送和缓冲区数据),没有则直接发送,有则将

数据继续加入缓冲区

uint8put_char_uart0(uint8 Data)

{

if(UART0_TX_BUFF_LEN >= UART0_TX_BUFF_SIZE)

return(-1);

if(tx_restart_uart0)

{

tx_restart_uart0= 0;// ==0 ,串口正在发送

SBUF0= Data;

}

else

{

UART0_TxBuffer[UART0_Tx_In& (UART0_TX_BUFF_SIZE - 1)] = Data;//数据放入发送BUF

UART0_Tx_In++;

}

return(0);

}

如果要连续发送一个协议包,只需调用put_char_uart0(),一次写入一个字节

从缓冲区读写数据,这里有个策略问题,读数据是有数据就读,还是只有当有一个完成的数据包才读。

写数据有足够空间才写,还是有空间才写。

基于串口的特性,采用读数据时从OUT端点开始向后scan,浏览到数据头才开始组包。

写数据,只有当缓冲区大小能够装下数据时,才一次写入。

缓冲区的情况分为这几种:

0 size

OUT IN

BUFFER size

空闲大小为:SIZE – IN + OUT (这里为0)

数据大小: IN– OUT

0 OUT IN size

B

A B C

先来看看linux内核怎么写循环缓冲区的:

len= min(len, fifo->size - fifo->in + fifo->out);

//len是要发送的数据长度,size为缓冲区大小,这里IN/OUT始终在size范围内,这里的结果是空闲的数据区大小,结果len为一次可以写的大小

/*first put the data starting from fifo->in to buffer end */

l= min(len, fifo->size - (fifo->in & (fifo->size - 1)));

//这里的结果为本次循环能写的大小,如图C区

memcpy(fifo->buffer+ (fifo->in & (fifo->size - 1)), buffer, l);

//先写l大小,写到缓冲区的最后一个字节,

/*then put the rest (if any) at the beginning of the buffer */

memcpy(fifo->buffer,buffer + l, len - l);

//缓冲区满在写到缓冲区头部A区,如果已写满,这里写的0字节

fifo->in+= len;

//in加上已写长度,可能溢出计数

//若要使in/out的值始终小于size:

//fifo->in= (fifo->in +len) % fifo->size;这个上面指向的内容是一样的

returnlen;//这里返回上层,通知应用程序写了多少字节。

linux内核读缓冲区示例:

unsignedint l;

len= min(len, fifo->in - fifo->out);

//可读数据长度

/*first get the data from fifo->out until the end of the buffer */

l= min(len, fifo->size - (fifo->out & (fifo->size - 1)));

//结果是从OUT到BUF尾部的数据长度,教IN/OUT交换后如C区

memcpy(buffer,fifo->buffer + (fifo->out & (fifo->size - 1)), l);

//首先读取从OUT到SIZE这部分

/*then get the rest (if any) from the beginning of the buffer */

memcpy(buffer+ l, fifo->buffer, len - l);

//若未读取完,在读取前面的一部分A区

fifo->out+= len;

//OUT端点加上读取长度

returnlen;

数据的检索:

包头的搜索:包头通常位于一个有效数据包的前端,当缓冲区数据大小大于最小包长时,就开始读取包头,每读取一次包头,缓冲区指针+1,直到不可读(小于最小包长)。

由于这样,不是包头的内容就会丢弃,故包头的标识应该为唯一的!这适用与简单的数据协议,不牵涉文件的传输。

我们来看看协议的一般处理流程:

数据处理的起始条件是:缓冲区中的有效数据长度大于或者等于最小包长。

1,搜索包:如果成功,转到2。失败则丢弃一个字节,继续搜索。

2,包长度的检查:scan出长度域,通过协议中可能出现的最大和最小包长检查,如果正常,则转到3,否则丢弃一个字节,转到1。

3,命令的检查:scan出帧号,检查帧号是否为有效的帧号,有效,则转到4,否则,丢弃一个字节,转到1。

4,校验和的检查:scan出长度后的数据域和校验域,检查校验和是否正确,错误则丢弃一个字节,转到1。如果正确,则读取这一完整的命令,取出命令和数据。转到5。

5,根据命令,执行相应的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值