第39节:判断数据头来接收一串数据的串口通用程序框架

发布者:WhisperingSoul最新更新时间:2016-03-15 来源: eefocus关键字:数据头  串口  程序框架 手机看文章 扫描二维码
随时随地手机看文章
开场白:

上一节讲了判断数据尾的程序框架,但是在大部分的项目中,都是通过判断数据头来接收数据的,这一节要教会大家两个知识点:

第一个:如何在已经接收到的一串数据中解析数据头协议并且提取有效数据。

第二个:无论是判断数据头还是判断数据尾,无论是单片机还是上位机,最好在固定协议前多发送一个填充的无效字节0x00,因为硬件原因,第一个字节往往容易丢失。

具体内容,请看源代码讲解。

(1)硬件平台:

基于朱兆祺51单片机学习板。

(2)实现功能:

波特率是:9600 。

通讯协议:EB 00 55 XX YY

加无效填充字节后,上位机实际上应该发送:00 EB 00 55 XX YY

其中第1位00是无效填充字节,防止由于硬件原因丢失第一个字节。

其中第2,3,4位EB 00 55就是数据头

后2位XX YY就是有效数据

任意时刻,单片机从电脑“串口调试助手”上位机收到的一串数据中,只要此数据中包含关键字EB 00 55 ,并且此关键字后面两个字节的数据XX YY 分别为01 02,那么蜂鸣器鸣叫一声表示接收的数据头和有效数据都是正确的。

(3)源代码讲解如下:

#include "REG52.H"

#define const_voice_short 40 //蜂鸣器短叫的持续时间

#define const_rc_size 10 //接收串口中断数据的缓冲区数组大小

#define const_receive_time 5 //如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完,这个时间根据实际情况来调整大小

void initial_myself(void);

void initial_peripheral(void);

void delay_long(unsigned int uiDelaylong);

void T0_time(void); //定时中断函数

void usart_receive(void); //串口接收中断函数

void usart_service(void); //串口服务程序,在main函数里

sbit beep_dr=P2^7; //蜂鸣器的驱动IO口

unsigned int uiSendCnt=0; //用来识别串口是否接收完一串数据的计时器

unsigned char ucSendLock=1; //串口服务程序的自锁变量,每次接收完一串数据只处理一次

unsigned int uiRcregTotal=0; //代表当前缓冲区已经接收了多少个数据

unsigned char ucRcregBuf[const_rc_size]; //接收串口中断数据的缓冲区数组

unsigned int uiRcMoveIndex=0; //用来解析数据协议的中间变量

unsigned int uiVoiceCnt=0; //蜂鸣器鸣叫的持续时间计数器

void main()

{

initial_myself();

delay_long(100);

initial_peripheral();

while(1)

{

usart_service(); //串口服务程序

}

}

void usart_service(void) //串口服务程序,在main函数里

{

/* 注释一:

* 识别一串数据是否已经全部接收完了的原理:

* 在规定的时间里,如果没有接收到任何一个字节数据,那么就认为一串数据被接收完了,然后就进入数据协议

* 解析和处理的阶段。这个功能的实现要配合定时中断,串口中断的程序一起阅读,要理解他们之间的关系。

*/

if(uiSendCnt>=const_receive_time&&ucSendLock==1) //说明超过了一定的时间内,再也没有新数据从串口来

{

ucSendLock=0; //处理一次就锁起来,不用每次都进来,除非有新接收的数据

//下面的代码进入数据协议解析和数据处理的阶段

uiRcMoveIndex=0; //由于是判断数据头,所以下标移动变量从数组的0开始向最尾端移动

/* 注释二:

* 判断数据头,进入循环解析数据协议必须满足两个条件:

* 第一:最大接收缓冲数据必须大于一串数据的长度(这里是5。包括2个有效数据,3个数据头)

* 第二:游标uiRcMoveIndex必须小于等于最大接收缓冲数据减去一串数据的长度(这里是5。包括2个有效数据,3个数据头)

*/

while(uiRcregTotal>=5&&uiRcMoveIndex<=(uiRcregTotal-5))

{

if(ucRcregBuf[uiRcMoveIndex+0]==0xeb&&ucRcregBuf[uiRcMoveIndex+1]==0x00&&ucRcregBuf[uiRcMoveIndex+2]==0x55) //数据头eb 00 55的判断

{

if(ucRcregBuf[uiRcMoveIndex+3]==0x01&&ucRcregBuf[uiRcMoveIndex+4]==0x02) //有效数据01 02的判断

{

uiVoiceCnt=const_voice_short; //蜂鸣器发出声音,说明数据头和有效数据都接收正确

}

break; //退出循环

}

uiRcMoveIndex++; //因为是判断数据头,游标向着数组最尾端的方向移动

}

uiRcregTotal=0; //清空缓冲的下标,方便下次重新从0下标开始接受新数据

}

}

void T0_time(void) interrupt 1 //定时中断

{

TF0=0; //清除中断标志

TR0=0; //关中断

if(uiSendCnt

{

uiSendCnt++; //表面上这个数据不断累加,但是在串口中断里,每接收一个字节它都会被清零,除非这个中间没有串口数据过来

ucSendLock=1; //开自锁标志

}

if(uiVoiceCnt!=0)

{

uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫

beep_dr=0; //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。

}

else

{

; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。

beep_dr=1; //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。

}

TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b

TL0=0x0b;

TR0=1; //开中断

}

void usart_receive(void) interrupt 4 //串口接收数据中断

{

if(RI==1)

{

RI = 0;

++uiRcregTotal;

if(uiRcregTotal>const_rc_size) //超过缓冲区

{

uiRcregTotal=const_rc_size;

}

ucRcregBuf[uiRcregTotal-1]=SBUF; //将串口接收到的数据缓存到接收缓冲区里

uiSendCnt=0; //及时喂狗,虽然main函数那边不断在累加,但是只要串口的数据还没发送完毕,那么它永远也长不大,因为每个中断都被清零。

}

else //我在其它单片机上都不用else这段代码的,可能在51单片机上多增加" TI = 0;"稳定性会更好吧。

{

TI = 0;

}

}

void delay_long(unsigned int uiDelayLong)

{

unsigned int i;

unsigned int j;

for(i=0;i

{

for(j=0;j<500;j++) //内嵌循环的空指令数量

{

; //一个分号相当于执行一条空语句

}

}

}

void initial_myself(void) //第一区 初始化单片机

{

beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。

//配置定时器

TMOD=0x01; //设置定时器0为工作方式1

TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b

TL0=0x0b;

//配置串口

SCON=0x50;

TMOD=0X21;

TH1=TL1=-(11059200L/12/32/9600); //这段配置代码具体是什么意思,我也不太清楚,反正是跟串口波特率有关。

TR1=1;

}

void initial_peripheral(void) //第二区 初始化外围

{

EA=1; //开总中断

ES=1; //允许串口中断

ET0=1; //允许定时中断

TR0=1; //启动定时中断

}

总结陈词:

这一节讲了常用的判断数据头来接收一串数据的程序框架,但是在很多项目中,仅仅靠判断数据头还是不够的,必须要有更加详细的通讯协议,比如可以包含数据类型,有效数据长度,有效数据,数据校验的通讯协议。这样的程序该怎么写?欲知详情,请听下回分解-----常用的自定义串口通讯协议。

关键字:数据头  串口  程序框架 引用地址:第39节:判断数据头来接收一串数据的串口通用程序框架

上一篇:第38节:判断数据尾来接收一串数据的串口通用程序框架
下一篇:第40节:常用的自定义串口通讯协议

推荐阅读最新更新时间:2024-03-16 14:47

STM32之串口通信
实验目的: 实现利用串口1 不停的打印一个信息到电脑上,同时接收从串口发过来的数据,把发送过来的数据直接送回给电脑。 实验平台: 基于STM32F103C8T6的彩屏开发板 硬件接口: 注意:因为我的开发板上的串口和LED共用了PA9和PA10,所以在使用USART1时务必屏蔽LED,不然两者会互相影响而导致实现现象无法呈现。 相关寄存器: 1,串口时钟使能。串口作为STM32 的一个外设,其时钟由外设时钟使能寄存器控制,这 里我们使用的串口1 是在APB2ENR 寄存器的第14 位。 2,串口复位。串口1 的复位是通过配置APB2RSTR 寄存器的第14 位来实现的。。通
[单片机]
单片机不支持printf函数,实现几个常用串口打印函数
在keil C51或者iar for c8051编译器下: 一、int 占两个字节 范围:-32768~+32767 二、long占四个字节 范围:-2147483648~+2147483647 三、float占四个字节 范围:3.40E+38 ~ +3.40E+38 四、double占8个字节 范围:-1.79E+308 ~ +1.79E+308 51单片机是对所有兼容Intel 8031指令系统的单片机的统称。该系列单片机的始祖是Intel的8004单片机,后来随着Flash rom技术的发展,8004单片机取得了长足的进展,成为应用最广泛的8位单片机之一。 在单机开发中经常使用串口调试或者收集实时各种数据类型不同的数据少
[单片机]
一个完整的人机界面程序框架
程序功能如下: (1)数码管前四位可切换3个窗口,1-XX,2-YY,3-ZZ,XX,YY,ZZ分别代表3个可编辑的数据Gu8SetDate_1,Gu8SetDate_2,Gu8SetDate_3,数据范围0-99; (2)按键1有短按和长按功能。长按第三或第四数码管闪烁,再短按在三四数码管之间切换;再长按没有闪烁,显示3个窗口其中一个窗口,短按在三个窗口间切换; (3)按键2,在闪烁条件下累加 (4)按键3,在闪烁条件下依次减小 在窗口切换中用到整屏更新和局部更新。(此框架值得好好研究) #include REG52.H #define KEY_FILTER_TIME 25 //按键的“短按”兼“滤波”的“稳定时
[单片机]
STM32F10x 学习笔记之USART实现串口通讯 DMA 方式
STM32F10x 的USART 支持DMA 方式,并且在DMA完成后可以产生中断。这对于需要接收或发送大量数据的应用情景是很有帮助的。 在普通的8位或16位单片机中很少有包含DMA控制器的,所以可能许多嵌入式程序员对DMA方式并不熟悉。简单的说,直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。由于无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 STM32F10x 上具有两个DMA控制器,共有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。
[单片机]
STM32F10x 学习笔记之USART实现<font color='red'>串口</font>通讯 DMA 方式
STM32串口通信和I/O口简单代码
#include stm32f10x_lib.h #include sys.h #include delay.h #include usart.h #include led.h int main(void) { u8 k; //点亮的灯位数 u8 i; Stm32_Clock_Init(9); //系统时钟初始化 delay_init(72); //延时初始化 uart_init(72,9600); //串口初始化 led_init(); //灯I/O配置初始化 while(1) { if(USART_RX_STA&0x80) //接受到数据
[单片机]
STM32<font color='red'>串口</font>通信和I/O口简单代码
ARM串口(UART)的使用 (S3C2440)
UART概述 通用异步收发器简称UART(Universal Asynchronous Receiver and Transmitter) 工作方式为:串行、全双工。 只需3根线:TXD(发送数据)、RXD(接收数据)、GND。 数据传输流程: 原来是高电平(空闲状态) 发送方拉低电平,保持1bit时间,为起始位。 接收方检测到起始位,然后通信双方按照波特率发送和接收5-8位数据。 若使用校验功能,则发送完数据位后还要接着发送奇偶校验位。 最后,发送停止位。 S3C2440的UART的特性 3个独立异步串行I/O端口,每个都可以是基于中断或基 于 DMA 模式的操作。 UART 通过使用系统时钟可以支持最高 115.2
[单片机]
ARM<font color='red'>串口</font>(UART)的使用 (S3C2440)
8051单片机串口接收字符并将其ASCII码(小于100)显示在数码管上
#include STC89C5xRC.H unsigned char code DIG_CODE = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; char buf; void delay()//提供时延 { int i, j; for(i = 0; i 15; i ++) for(j = 0; j 15; j ++) ; } void disp_digit(char d) { //显示个位数字 P0 = DIG_CODE ; P2 = 0;// 0000 0000 - (P24, P23,
[单片机]
8051单片机<font color='red'>串口</font>接收字符并将其ASCII码(小于100)显示在数码管上
TMS320C32扩展异步串口的方法
摘要:介绍了TMS320C32 DSP实现异步串行通信接口的三种方法:软件模拟实现、硬件实现、专用协议芯片实现。给出了具体实现的硬件接口和软件编程。在接口的第二种和第三种实现方法中,都使用了FPGA实现逻辑接口。 关键词:数字信号处理 异步串行接口 现场可编程逻辑阵列 电气传动控制 高速数字信号处理器(DSP)在现代工业控制中,特别是电气传动控制中的应用非常广泛。大量文献介绍的应用于电气传动控制的DSP使用的是TI公司的,TMS320系列DSP芯片,这其中又以TMX210C3X和TMS320F24X为主流应用产品。TMS320C32(以下简称为C32)是TMS320C3X系列产品中应用比较多的一种。主工业控制中,常常需要使用
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved