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

//以下是定时 器 0 的中断的配 置 TMOD=0x01; TH0=0xfc; TL0=0x66; EA=1; ET0=1; TR0=1; //以下是串口 接收中断的配 置 //串口的波特 率与内置的定 时器 1 直接 相关,因此配 置此定时器 1 就等 效于配置波特 率。 u8_TMOD_Te mp=0x20; // 即将把定时 器 1 设置为:工 作方式 2,初值自动 重装的 8 位定 时器。 TMOD=TMOD& 0x0…

100%1 / 836
if(1==Gu8SendByteFinish)
{
break; //如果 Gu8SendByteFinish 1,则发送一个字节完成,退出当前循环等待
}
Su16TimeOutDelay--; //超时计时器不断递
}
//Delay();//在实际应用中,当连续发送一堆数据时如果发现丢失数据,可以尝试在此增加延
}
//发送任意起始位置任意长度的函数
void UsartSendBuffer(const unsigned char *pCu8SendBuffer,unsigned long u32SendSize)
{
static unsigned long i;
for(i=0;i<u32SendSize;i++) //u32SendSize 为发送的数据长度
{
UsartSendByteData(pCu8SendBuffer[i]); //基于“发送单字节的最小接口函数”来实现的
}
}
//发送带协议的函数
void UsartSendMessage(const unsigned char *pCu8SendMessage,unsigned long u32SendMaxSize)
{
static unsigned long i;
static unsigned long *pSu32;
static unsigned long u32SendSize;
pSu32=(const unsigned long *)&pCu8SendMessage[2];
u32SendSize=*pSu32; //从带协议的数组中提取整包数组的有效发送长度
if(u32SendSize>u32SendMaxSize) //如果“有效发送长度”大于“最大限制的长度”,数据异常
{
return; //数据异常,直接退出当前函数,预防数组越界
}
for(i=0;i<u32SendSize;i++) //u32SendSize 为发送的数据长度
{
UsartSendByteData(pCu8SendMessage[i]); //基于“发送单字节的最小接口函数”来实现的
}
}
void SystemInitial(void)
{
unsigned char u8_TMOD_Temp=0;
//以下是定时 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 根是交叉的收发数据线(上
位机的“接收线”连接下位机的“发送线”,上位机的“发送线”连接下位机的“接收线”,如下图所示: