从单片机基础到程序框架(全集 2019pdf版).pdf - 第772页
上图 13 0.3.2 232 串口 电路 程序功能如 下: (5)单 片机 模拟从 机, 上位 机的 串口助 手模 拟主 机。 在上位 机的 串口 助手里 ,发 送一 串数 据, 控制 蜂 鸣器发出不 同长度的声音 。 (6)本 节因 为还没 有讲 到数 据发 送的内 容, 因此 应答“ 动态 密匙 ”那 部分 的代码 暂时 不写 ,只 写验证 “异或” 那部分的代 码。 (7)波特 率 9600,校验 位 NONE(无 ) ,数 据…

数据类型(01):占用 1 个字节。数据类型是用来定义这串数据的用途。
数据长度(00 00 00 0B):占 4 个字节。用来告诉通信的对方,这串数据一共有多少个字节。
其它数据(03 E8):此数据根据不同的“数据类型”可以用来做不同的用途,根据具体的项目而定。
动态密匙(00 01):这两个字节代表一个 unsigned int 类型的数据,数据范围是从 0 到 65535,但是考
虑到数据更加安全可靠,一般丢弃了首尾的 0(十六进制的 00 00)与 65535(十六进制的 FF FF),只保留
从 1 到 65534 的变化。大部分的通信模型都是主机对从机的“一问一应答”模式,也就是,主机每发送一条
指令给从机,从机才返回一条消息作为应答。如果主机发送了信息后,在规定的时间内,没有收到从机的应
答指令,主机就继续发送信息给从机,但是此时,从机本来应该应答主机当前指令的,可能因为某种情况导
致反馈的信息发生了延时,导致此时应答的数据是主机的上一条指令,从而造成“一问一应答”的数据帧发
送了错位,这种情况加上“动态密匙”就能使问题得到有效的解决。主机每发送一条信息,信息里都携带了
2 个字节的“动态密匙”,从机每收到主机的一条信息,在应答此信息时都把收到的“动态密匙”原封不动的
反馈给主机,主机再查看发送的“动态密匙”与接收到的“动态密匙”是否一致,以此来判断应答数据是否
有效。“动态密匙”像流水号一样,每发送一次指令后都累加 1,不断发生变化,从 1 到 65534,依次循环。
这是数据校验的一种方式。
异或(0B)。“异或”放在数据串的最后一个字节,是前面所有字节的异或结果(不包括自己本身的字节)。
比如:本例子中,数据串是:EB 01 00 00 00 0B 03 E8 00 01 0B。其中最后一个字节 0B 就是“异或”字
节,前面所有字节相“异或”等于十六进制的 0B。验证“异或”的方法,可以借用电脑“附件”自带的“计
算器”软件来实现,打开“计算器”软件后,在“查看”的下拉菜单里,选择“程序员”,然后选择“十六
进制”,该计算器软件的异或运算按键是“Xor”。不管是主机还是从机,每接收到一串数据后,都要自己计
算一次“异或”,把自己计算得到的“异或”与接收到的最后一个字节的“异或”进行对比,来判断接收到
的数据是否发生了丢失或者错误。
【130.3 程序例程。】
上图 130.3.1 有源蜂鸣器电路

上图 130.3.2 232 串口电路
程序功能如下:
(5)单片机模拟从机,上位机的串口助手模拟主机。在上位机的串口助手里,发送一串数据,控制蜂
鸣器发出不同长度的声音。
(6)本节因为还没有讲到数据发送的内容,因此应答“动态密匙”那部分的代码暂时不写,只写验证
“异或”那部分的代码。
(7)波特率 9600,校验位 NONE(无),数据位 8,停止位 1。
(8)十六进制的数据格式:EB 01 00 00 00 0B XX XX YY YY ZZ 。其中:
EB 是数据头。
01 是代表数据类型。
00 00 00 0B 代表数据长度是 11 个(十进制)。
XX XX 代表一个 unsigned int 的数据,此数据的大小决定了蜂鸣器发出声音的长度。
YY YY 代表一个 unsigned int 的动态密匙,每收发一条指令,此数据累加一次 1,范围从 1 到 65534。
ZZ 代表前面所有字节的异或结果。
比如:
让蜂鸣器鸣叫 1000 毫秒,密匙为 00 01,发送十六进制的:EB 01 00 00 00 0B 03 E8 00 01 0B
让蜂鸣器鸣叫 100 毫秒, 密匙为 00 02,发送十六进制的:EB 01 00 00 00 0B 00 64 00 02 87
#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 int Gu16ReceYY=0; //接收的动态密匙
unsigned char Gu8ReceZZ=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
{
if(1==RI) //接收完一个字节后引起的中断
{
RI = 0; //及时清零,避免一直无缘无故的进入中断。
/* 注释一:
* 以下 Gu8FinishFlag 变量的用途。
* 此变量一箭双雕,0 代表正处于接收数据的状态,1 代表已经接收完毕并且及时通知主函数中的处理函数
* UsartTask()去处理新接收到的一串数据。除此之外,还起到一种“自锁自保护”的功能,在新数据还