串口实用的循环缓冲区

2023-05-16

小记。

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

单片机几多久没玩了,选用的是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,根据命令,执行相应的操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

串口实用的循环缓冲区 的相关文章

随机推荐

  • Qt5.7.0 mingw+qwtplot3D 编译

    一 基本编译问题 在安装的过程中却遇到了很多的问题 咨询了Google中大量的帖子 xff0c 发现按照他们说的方法根本没法解决我的实际问题 研究了整整宝几天 xff0c 总算是编译通过 xff0c 解决了所有的问题 下面将通过重现我的安装
  • 一篇实用的Latex的入门教程

    转载 LaTeX新人教程 xff0c 一小时从完全陌生到基本入门 作者 董楠 LaTeX新人教程 xff0c 一小时从完全陌生到基本入门 by Nan 对于真心渴望迅速上手LaTeX的人 xff0c 可以只看正文 曾经在缝缝补补中变得长长的
  • Qt5.8.0使用QChart的问题

    Qt Charts 的横空出世标识着 QWT QCustomPlot 时代的终结 xff0c 是时候向他们做一个简单的告别了 Qt Charts 强大并极具吸引力 xff0c 从现在开始 xff0c 让我们顺势拥抱灿烂的明天 引自 xff1
  • 成功实现NAS家庭服务器(流媒体播放、文件共享及下载机)

    一 家庭服务器实现的主要功能 1 流媒体播放服务 xff1a 利用DLNA实现电视 手机 电脑播放其上面的媒体文件 2 文件共享 xff1a 利用samba实现手机 电脑等终端与服务器的文件共享 3 自动下载 xff1a 利用aria2c实
  • 架构漫谈(八):从架构的角度看如何写好代码 + 我的思考

    我的总结 xff1a 当先前对于利益相关者的关系理清了 xff0c 系统基本架构和测试方法等也有了 xff0c 进入实际Coding阶段 在Coding前 xff0c 需要我们考虑好业务功能的分配 xff0c 关注于功能会频繁变更的部分 x
  • 插头类型:MX 1.25 PH间距2.0、XH间距2.5/2.54、VH间距3.96区别?

    PH xff0c XH xff0c ZH等这些都是JST xff08 Japan Standard Time 日本压着端子制造株式会社 xff09 的料号 xff0c 因为JST用的最多 xff0c 且很多工厂都在模仿制作 xff0c 故也
  • 树莓派NAS搭建——基于OMV

    之前树莓派的debian版系统里装了个以samba服务为基础的NAS xff0c 但在实际使用中发现写入速度只有2M s xff0c 特别慢 xff0c 用的是Class 10的高速TF卡也于事无补 实在无法直视 xff0c 于是一直在搜寻
  • 最短路径算法——Dijkstra介绍

    个人心得体会 xff1a 理解这种或这类算法 xff0c 可以先从小规模的问题入手 xff0c 并逐渐推广到问题变复杂的情况 xff0c 这样理解起来也可以更方便和透彻 和数学归纳法很相似 图简介 以使用地图APP为例 xff0c 假设你想
  • net framework4.0未能成功安装、另一个安装操作正在进行、请继续这个操作之前完成那个操作。

    转自百度 安装net framework 4 0出错的解决方法 步骤 xff1a 开始 运行 xff08 输入CMD xff09 确定或者回车 xff0c 打开管理员命令窗口 xff1b 在管理员命令窗口输入 xff1a net stop
  • java.lang.ClassNotFoundException: com.microsoft.jdbc.sqlserver.SQLServerDriver

    第一次写jsp xff0c 用jdbc连接数据库进行查询的时候 xff0c Class forName 34 com microsoft jdbc sqlserver SQLServerDriver 34 newInstance conn
  • numpy Array [: ,] 的取值方法

    numpy Array 的取值方法 NumPy数组是一个多维数组对象 xff0c 称为ndarray 创建一个numpy数组 如下所示 span class token keyword import span numpy span clas
  • 如何安装windows和linux双系统

    最近装了两天系统积攒了一下经验 原本一直使用win7 xff0c 现需要使用linux操作系统作为开发环境 xff0c 虚拟机的能力总是有限 xff0c 所以打算装双系统 xff0c 装系统以前一直对我来说一直很神秘 xff0c 为什么系统
  • sed -i命令详解

    sed i命令详解 root 64 www sed nefr 动作 选项与参数 xff1a n xff1a 使用安静 silent 模式 在一般 sed 的用法中 xff0c 所有来自 STDIN 的数据一般都会被列出到终端上 但如果加上
  • 模拟信号和数字信号的区别

    作者 xff1a 杨眀 链接 xff1a https www zhihu com question 38377832 answer 118464689 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业
  • 于渊《orange's 一个操作系统实现》的字符串输出函数disp_str有bug会导致异常

    于渊先生的 orange s 一个操作系统实现 的字符串输出函数disp str有bug会导致异常 发现在同一个函数中两次使用disp str就会导致异常 xff0c 这个问题困扰了我很久 xff0c 一般来说都是堆栈的没有恢复好的问题 x
  • 基于CMake导入第三方库,以OpenCV为例

    基于CMake导入第三方库 xff0c 以OpenCV为例 目录 基于CMake导入第三方库 xff0c 以OpenCV为例前言直接导入find package基于nuget进行包管理运行的问题 前言 除了自己写一个静态库或动态库 xff0
  • 进程是如何结束与回收的

    大致描述进程创建的过程 xff0c 系统中有一个叫做init的初始进程 xff0c 由它调用fork产生子进程 完全拷贝父进程的一切堆栈 xff0c 代码 xff0c 数据 xff0c 文件资源等 xff0c 子进程使用exec装载磁盘中的
  • 今天安装spring的时候遇到一些缺少repository的问题

    在安装spring的时候会对其依赖的一些库的进行一些链接检查 xff0c 导致会报一些缺少repository的问题 No repository found containing osgi bundle oracle eclipse too
  • 什么是maven

    maven是一种项目管理工具 xff0c maven项目与普通项目的不同是 xff0c 项目中多了一个pom xml的文件 xff0c 用于告诉当前项目所需要的jar的位置 xff0c 从而无须手动将需要用到的jar包引入
  • 串口实用的循环缓冲区

    小记 项目临时需要单片机进行节点控制 xff0c 主要用来控制模块的开关 xff0c 以串口进行通讯 单片机几多久没玩了 xff0c 选用的是C8051F920 xff0c 传说中增强型51 xff0c 不过看了Datesheet 还是51