从单片机基础到程序框架(全集 2019pdf版) - 第754页

【128.3 程序例程。 】 上图 12 8.3.1 有源蜂鸣器 电路 上图 12 8.3.2 232 串口 电路 程序功能如 下: (1)在上 位机的串口 助手里, 发送一串数据 ,控制蜂鸣器 发出不同长 度的声音 。 (2)波特 率 9600,校验 位 NONE(无 ) ,数 据位 8,停 止位 1。 (3)十六 进制的数据 格式如下 : EB 01 00 0 0 00 08 X X XX 其中 EB 是数 据头,01 是代表数 据…

100%1 / 836
第一百二十八节: 接收“固定协议”的串口程序框架。
【128.1 固定协议。
实际项目中串口一个回合的收发数据量远远不止 1 个字节,而是往往携带了某种“固定协议”的一串
数据(专业术语称“一帧数据”。一串数据的“固定协议”因为起到类似“校验”和“密码确认”的功能,
因此在安全可靠性方面大大增强。但是上一节也提到单片机利用最底层硬件的串口接口一次收发的最小
单位是1 个字节”那么,怎么样在此基础上搭建一个能快速收发并且能快速解析数据的程序框架就显得尤
为重要。本节我跟大家分享我常用的串口程序框架,此框架主要包含“数据头,数据类型,数据长度,其它
数据”这四部分。比如,为了通过串口去控制单片机的蜂鸣器发出不同长度的声音,我专门制定了一串十六
进制的数据:EB 01 00 00 00 08 03 E8 ,下面以此串数据来跟大家详细分析。
数据头(EB):占 1 个字节,作为“起始字节”,起到“接头暗号”的作用,平时用来过滤无关的数据。
只有“接头暗号”吻合,单片机才会进入到接收其它有效数据的步骤,否则一直被数据头”挡在门外视为
无效数据。注意,数据头不能用十六进制的 00 或者 FF,因 00 FF 的密码等级太弱,很多单片机一上电
的瞬间因为硬件的某种不确定的原因,会直接误发送 00 或者 FF 这类干扰数据。
数据类型(01):占用 1 个字节。数据类型是用来定义这串数据的用途,比如,01 代表用来控制蜂鸣器
的,02 代表控 LED 的,03 表机器启动,等等功能,都可以用这个字节的数据进行分类定义。本例子用
01 代表控制蜂鸣器发出不同时间长度的声音。
数据长度(00 00 00 08):占 4 个字节。用来告诉通信的对方,这串数据一共有多少个字节。本例子中
数据长度占用了 4 个字节,就意味着最大数据长度是一 unsigned long 类型的数据范围 0 4294967295。
比如,本例子中一串数据的长度 8 个字节(EB 01 00 00 00 08 03 E8 ),因此这“数据长度”四个字
分别 00 00 00 08,十六进制的 08 代表十进制 8 字节。注意,51 片机的内存是属于大端模式,因
十进制的 8 在四字节 unsigned long 的内存排列顺序 00 00 00 08,也就是低位放在数组的高下标。如果
stm32 的单片机,stm32 单片机的内存是属于小端模式,十进制的 8 在四字节 unsigned long 的内存排列
顺序是 08 00 00 00,低位放在数组的低下标。为什么强调这个?因为主要方便我们用指针的方法实现数
的拆分和整合,这个知识点的内容我在前面第 62 节详细讲解过。
其它数据(03 E8):此数据根据不同的“数据类型”可以用来做不同的用途,根据具体的项目而定。本
例子十六进制的 03 E8,代表一 unsigned int 十进制数 1000。此数据的大小用来控制蜂鸣器发声
长度,1000 代表长叫 1000ms。如果想让蜂鸣器短叫 100ms,只需把这两个字节改为:00 64。
【128.2 程序框架的四个要点分析。
第一点:先接收后处理,开辟一块专用的内存数组。要处理一串数据,必须先征用一块内存数组专门
来缓存接收到的数据,等接收完此串数据再处理。
第二点:接头暗号。本节例子的数据 EB 是接头暗号。一旦接头暗号吻合,才会进入到下一步接收
其它有效数据的步骤上。
第三点:如何识别接收一串数据的完毕。本节例子中,是靠“固定协议”提供的“数据长度”来判别是
否已经接收完一串数据。中断函数接收完一串数据后,应该用一个全局变量来给外部 main 函数一个通知
main 函数里面的相关函数来处理此串数据。
第四点:接收数据中相邻字节之间通信超时的异常处理。如果接头暗号吻合之后马上切换到“接受
它有效数据”的步骤,但是,如果在此步骤的通信过程中一旦发现通信不连贯就应该及时退出当下“接受
其它有效数据”的步骤,继续返回到刚开始“接头暗号”的步骤,为下一次接收新的一串数据做准备。那
么,如何识别通信不连贯?靠判断接收数据中相邻字节之间的时间是否超时来决定,详细内容请看下面的程
序例程。
【128.3 程序例程。
上图 128.3.1 有源蜂鸣器电路
上图 128.3.2 232 串口电路
程序功能如下:
(1)在上位机的串口助手里,发送一串数据,控制蜂鸣器发出不同长度的声音
(2)波特 9600,校验 NONE(无,数据位 8,停止位 1。
(3)十六进制的数据格式如下
EB 01 00 00 00 08 XX XX
其中 EB 是数据头,01 是代表数据类型,00 00 00 08 代表数据长度是 8 个(十进制)。XX XX 代表
一个 unsigned int 的数据,此数据的大小决定了蜂鸣器发出声音的长度。比如:
让蜂鸣器鸣 1000ms 的时间,发送十六进制的: EB 01 00 00 00 08 03 E8
让蜂鸣器鸣 100ms 的时间,发送十六进制的: EB 01 00 00 00 08 00 64
#include "REG52.H"
#define RECE_TIME_OUT 2000 //通信过程中字节之间的超时时间 2000ms
#define REC_BUFFER_SIZE 20 //接收数据的缓存数组的长度
void usart(void); //串口接收的中断函
void T0_time(); //定时器的中断函数
void UsartTask(void); //串口接收的任务函数,放在主函数内
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void BeepOpen(void);
void BeepClose(void);
void VoiceScan(void);
sbit P3_4=P3^4;
volatile unsigned char vGu8BeepTimerFlag=0;
volatile unsigned int vGu16BeepTimerCnt=0;
unsigned char Gu8ReceBuffer[REC_BUFFER_SIZE]; //开辟一片接收数据的缓存
unsigned long Gu32ReceCnt=0; //接收缓存数组的下
unsigned char Gu8ReceStep=0; //接收中断函数里的步骤变量
unsigned char Gu8ReceFeedDog=1; //“喂狗”的操作变量
unsigned char Gu8ReceType=0; //接收的数据类型
unsigned long Gu32ReceDataLength=0; //接收的数据长度
unsigned char Gu8FinishFlag=0; //是否已接收完成一串数据的标
unsigned long *pu32Data; //用于数据转换的指
volatile unsigned char vGu8ReceTimeOutFlag=0;//通信过程中字节之间的超时定时器的开关
volatile unsigned int vGu16ReceTimeOutCnt=0; //通信过程中字节之间的超时定时器,“喂狗”的对象
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
UsartTask(); //串口接收的任务函
}
}
void usart(void) interrupt 4 //串口接发的中断函数,中断号为 4
{