第38节:判断数据尾来接收一串数据的串口通用程序框架
在实际项目中,串口通讯不可能一次通讯只发送或接收一个字节,大部分的项目都是一次发送或者接受一串的数据。我们还要在这一串数据里解析数据协议,提取有用的数据。
这一节要教会大家三个知识点:
第一个:如何识别一串数据已经发送接收完毕。
第二个:如何在已经接收到的一串数据中解析数据尾协议并且提取有效数据。
第三个:接收一串数据的通用程序框架涉及到main循环里的串口服务程序,定时器的计时程序,串口接收中断程序的密切配合。大家要理解它们三者之间是如何关联起来的。
具体内容,请看源代码讲解。
(1)硬件平台:
基于朱兆祺51单片机学习板。
(2)实现功能:
波特率是:9600 。
通讯协议:XX YY EB 00 55
其中后三位 EB 00 55就是我所说的数据尾,它的有效数据XX YY在数据尾的前面。
任意时刻,单片机从电脑“串口调试助手”上位机收到的一串数据中,只要此数据中包含关键字EB00 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=uiRcregTotal; //由于是判断数据尾,所以下标移动变量从数组的最尾端开始向0移动
while(uiRcMoveIndex>=5) //如果处理的数据量大于等于5(2个有效数据,3个数据头)说明还没有把缓冲区的数据处理完
{
if(ucRcregBuf[uiRcMoveIndex-3]==0xeb&&ucRcregBuf[uiRcMoveIndex-2]==0x00&&ucRcregBuf[uiRcMoveIndex-1]==0x55) //数据尾eb 00 55的判断
{
if(ucRcregBuf[uiRcMoveIndex-5]==0x01&&ucRcregBuf[uiRcMoveIndex-4]==0x02) //有效数据01 02的判断
{
uiVoiceCnt=const_voice_short; //蜂鸣器发出声音,说明数据尾和有效数据都接收正确
}
break; //退出循环
}
uiRcMoveIndex--; //因为是判断数据尾,下标向着0的方向移动
}
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-50
数据尾程序框 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)