从单片机基础到程序框架(全集 2019pdf版).pdf - 第806页
第一百三十四节: “应用层半双工”双机串口通讯的程序框架。 【134.1 应用层的“半双工”和“全双工” 。 】 应用 层的“ 半双 工” 。 主机 与从机 在程 序应用 层采 用“ 一问 一答” 的查 询模式 ,主 机是 主动 方, 从机 是 被动 方,主 机问 一句从 机答 一句, “ 聊天 对话“ 的氛 围很无 趣很 呆板。 从机 没有 发言 权, 当从机 想主 动给 主 机发送一些 数据时就“憋得 慌” 。半 双工适用于大 多数…

//以下是定时器 0 的中断的配置
TMOD=0x01;
TH0=0xfc;
TL0=0x66;
EA=1;
ET0=1;
TR0=1;
//以下是串口接收中断的配置
//串口的波特率与内置的定时器 1 直接相关,因此配置此定时器 1 就等效于配置波特率。
u8_TMOD_Temp=0x20; //即将把定时器 1 设置为:工作方式 2,初值自动重装的 8 位定时器。
TMOD=TMOD&0x0f; //此寄存器低 4 位是跟定时器 0 相关,高 4 位是跟定时器 1 相关。先清零定时器 1。
TMOD=TMOD|u8_TMOD_Temp; //把高 4 位的定时器 1 填入 0x2,低 4 位的定时器 0 保持不变。
TH1=256-(11059200L/12/32/9600); //波特率为 9600。11059200 代表晶振 11.0592MHz,
TL1=256-(11059200L/12/32/9600); //L 代表 long 的长类型数据。根据芯片手册提供的计算公式。
TR1=1; //开启定时器 1
SM0=0;
SM1=1; //SM0 与 SM1 的设置:选择 10 位异步通信,波特率根据定时器 1 可变
REN=1; //允许串口接收数据
//为了保证串口中断接收的数据不丢失,必须设置 IP = 0x10,相当于把串口中断设置为最高优先级,
//这个时候,串口中断可以打断任何其他的中断服务函数实现嵌套,
IP =0x10; //把串口中断设置为最高优先级,必须的。
ES=1; //允许串口中断
EA=1; //允许总中断
}
void Delay(unsigned long u32DelayTime)
{
for(;u32DelayTime>0;u32DelayTime--);
}
void PeripheralInitial(void)
{
//发送任意数组
UsartSendBuffer((const unsigned char *)&Gu8SendBuffer[0],5);//从第 0 位置发送 5 个数据
UsartSendBuffer((const unsigned char *)&Gu8SendBuffer[6],5);//从第 6 位置发送 5 个数据
//发送带协议的数组
UsartSendMessage((const unsigned char *)&Gu8SendMessage[0],100); //必须从第 0 位置发送
}
第一百三十四节:“应用层半双工”双机串口通讯的程序框架。
【134.1 应用层的“半双工”和“全双工”。】
应用层的“半双工”。主机与从机在程序应用层采用“一问一答”的查询模式,主机是主动方,从机是
被动方,主机问一句从机答一句,“聊天对话“的氛围很无趣很呆板。从机没有发言权,当从机想主动给主
机发送一些数据时就“憋得慌”。半双工适用于大多数单向通讯的场合。
应用层的“全双工”。主机与从机在程序应用层可以实现任意双向的通讯,这时从机也可变为主机,主
机也可变为从机,就像两个人平时聊天,无所谓谁是从机谁是主机,也无所谓非要对方对我每句话都要应答
附和(只要对方能听得清我讲什么就可以),“聊天对话”的氛围很生动很活泼。全双工适用于通讯更复杂的
场合。
本节从“半双工“开始讲,让初学者先熟悉双机通讯的基本程序框架,下一节再讲“全双工“。
【134.2 双机通讯的三类核心函数。】
双机通讯在程序框架层面有三类核心的涵数,它们分别是:通讯过程的控制涵数,发送的队列驱动涵数,
接收数据后的处理涵数。
“通讯过程的控制涵数”的数量可以不止 1 个,每一个通讯事件都对应一个独立的“通讯过程的控制涵
数”,根据通讯事件的数量,一个系统往往有 N 个“通讯过程的控制涵数”。顾名思义,它负责过程的控制,
无论什么项目,凡是过程控制我都首选 switch 语句。此函数是属于上层应用的函数,它的基础底层是“发
送的队列驱动涵数”和“接收数据后的处理涵数”这两个函数。
“发送的队列驱动涵数”在系统中只有 1 个“发送的队列驱动涵数”,负责“通讯管道的占用”的分配,
负责数据的具体发送。当同时存在很多“待发送”的请求指令时,此函数会根据“if ,else if...”的优先
级,像队列一样安排各指令发送的先后顺序,确保各指令不会发生冲突。此函数属于底层的驱动函数。
“接收数据后的处理涵数”在系统中只有 1 个,负责处理当前接收到的数据,它既属于“底层函数”也
属于“应用层函数”,二者成分皆有。
我们一旦深刻地领悟了这三类函数各自的分工与关联方式,将来应付再复杂的通讯系统都会脉络清析,
游刃有余。
【134.3 例程的功能需求。】
上位机与下位机都有一个一模一样的 57 个字节的大数组。在上位机端按下独立按键 K1 后,上位机开始
与下位机建立通讯,上位机的目的是读取下位机的那 57 个字节的大数组,分批读取,每批读取 10 个字节,
最后一批读取的是余下的 7 个字节。读取完毕后,上位机把读取到的大数组与自己的大数组进行对比:如果
相等,表示通讯正确,蜂鸣器“长鸣”一声;如果不相等,表示通讯错误,蜂鸣器“短鸣”一声。在通讯过
程中,如果出现通信异常(比如因为接收超时或者接收某批次数据错误而导致重发的次数超过最大限制的次
数)也表示通讯错误,蜂鸣器也会发出“短鸣”一声的提示。
【134.4 例程的电路图。】
两个单片机进行 232 串口通讯,一共需要 3 根线:1 根作为共地线,其它 2 根是交叉的收发数据线(上
位机的“接收线”连接下位机的“发送线”,上位机的“发送线”连接下位机的“接收线”),如下图所示:

上图 134.4.1 双机通讯的 232 串口接线图
上图 134.4.2 上位机的独立按键
上图 134.4.3 上位机的有源蜂鸣器
【134.5 例程的通讯协议。】
(一)通讯参数。波特率 9600,校验位 NONE(无),数据位 8,停止位 1。
(二)上位机读取下位机的数组容量的大小的指令。
(1)上位机发送十六进制的数据:EB 01 00 00 00 07 ED。
EB 是数据头。
01 是指令类型,01 代表请求下位机返回大数组的容量大小。