C51串口通信(张毅刚)例8-1程序解释


前言

学习微机原理这门课,在张毅刚的书中有一段关于串口通信的程序,逻辑上觉得SBUF=nSendByte这句不用写两次,而且对中断进入不太理解。


后来在平台上提问,经大佬提点明白了自己的误解之处。
这篇文章是根据个人理解对这道例题的分析和解释。

一、题目

[例8-1]如图8-6所示,编写程序控制8个发光极管流水点亮。 图中 74LS164的8脚(CLK端)为同步脉冲输入端,9脚为控制端,9脚的电平由单片机的P1.0控制,当9脚为0时,允许串行数据由RXD端(P3.0) 向74LS164 的串行数据输入端A和B(1脚和2脚)输入,但是74LS164的8位并行输出端关闭:当9脚为1时,A和B输入端关闭,但是允许74LS164中的8位数据并行输出。当串行口将8位串行数据发送完毕后,申请中断,在中断服务程序中,单片机向串行口输出下一个8位数据。

#include <reg51.h>
#include <stdio.h>
sbit P1_0=0x90;
unsigned char nSendByte;
void delay(unsigned int i)
{
unsigned char j;
for(;i>0;i--)
for(j=0;j<125;j++)
;
}
void main()
{
SCON=0x00;
EA=1;
ES=1;
nSendByte=1;
SBUF=nSendByte;
P1_0=0;
while(1)
{;}
}
void Serial_Port() interrupt 4 using 0
{
//if(TI)
//{
P1_0=1;
SBUF=nSendByte;//不明白这句为什么要写,逻辑上感觉可以没有这句,但进到Proteus仿真后这句必须要有才能顺利实现流水点灯
delay(500);
P1_0=0;
nSendByte=nSendByte<<1;
if(nSendByte==0)
nSendByte=1;
SBUF=nSendByte;//感觉有这句足矣
//}
TI=0;
RI=0;
}

任课教师课上解释说可能是74LS164芯片的问题。

二、网友给出的中断程序分析

先看,网友给出的代码如下:

void Serial_Port() interrupt 4 using 0
{
TI=0;
RI=0;
//if(TI)
//{
P1_0=1;
SBUF=nSendByte;//不明白这句为什么要写,逻辑上感觉可以没有这句,但进到Proteus仿真后这句必须要有才能顺利实现流水点灯
delay(500);
P1_0=0;
nSendByte=nSendByte<<1;
if(nSendByte==0)
nSendByte=1;
//SBUF=nSendByte;//将这行去掉
//}
//TI=0;
//RI=0;
}
 

网友给出的中断程序感觉更好理解些。

1.74LS164

网友解释说当P1_0=0的时候就是相当于给74LS164复位了,并不代表有数据输到74LS164里,它储存起来不发出去怎么样的。也就是说P1_0=0只起到一个作用,就是等SBUF那边发数据好进中断。
只有P1_0=1后,SBUF发的数据才算是有效发出的。

2.从主函数到中断函数

可以看到,他把TI=0,RI=0,放在整个中断程序的最前面,中断到底怎么进入?首先,他最前面main函数有一个SBUF语句发出了nSendByte的信息,当时他给nSendByte置的是1,但从逻辑上,这个初值不管是不是一都无所谓,因为他之后p1_0=0,也就是这个时候,74 LS164他是处于一个复位状态,也就不接收数据,所以这个时候SBUF发啥数都没关系,但是关键它要发。
这个时候不能叫没发数据,叫空发,它发的时候没人开门,只是它发完刚好令TI=1得以进入中断。因此中断程序里才有给164开门的语句,这个时候刚刚发的就没有用了,就发丢了,因为刚刚没有谁给它存着,它必须得赶紧再发一个1,数据才能进164.
这个时候还值得注意的是P1_0=0这个话它写在SBUF后面了,事实上,这个语句写在sbuf之前也是可以的,甚至更好,当成一个初始化语句来理解.
那按我们的理解为什么SBUF发了数据没有立刻进中断,而是运行了P1_0=0,甚至还运行了while语句
因为串口通信和程序语句的读取速度是不一样的,完成串口通信的时间要长于程序读取,读一条程序(1行)大概是单机器或者双机器周期的样子,但发完数据用了这么多个。
在这里插入图片描述
也就是运行到while里面开始死循环了,数据还没有发完。
所以在while里走了一会儿才发完,然后TI=1了,可以进入中断了,类似于定时器中断,这位网友一进串口中断的程序,他就立刻把TI这个标志位置成零,有点把定时器清零重新计时的意思。

3.中断函数怎么解释

这个时候p1_0=1,74LS164可以正常工作了,也就是这个时候sbuf发啥那那边灯就亮啥。
延时500,之后P1_0=0,其实这个时候是那些灯的引角上全是零,但因为我们刚才延时了好久,所以我们能看到灯点亮了一个过程,这之后,短短的熄灭就就是它跑剩下的程序到点亮下一个灯时间也就不足为虑
这之后他对nSendByte移位,这个好理解些,后面还有一句判断nSendByte是否为0这个也可以理解,然后就是为什么可以再进中断了。

4.如何再进中断

刚才我们说中断程序里P1_0=1了,sbuf发啥灯亮啥,这个时候就敏锐的发现,TI从中断开始置成0了,马上又会变成1了(至于什么时候变成的1,应该就是在delay(500)这个语句当中,这个语句持续的时间比较长,足够SBUF发完数据)一发完,500剩下的比如499.99的时间都用来点亮灯了。
那这个时候,会让人觉得很疑惑,他既然TI=1了,他为什么不留在这个中段里,而是出了中断,再进中断。
因为判断是否进入中断的条件,只能在主函数里进行。
所以他出了中断函数,必须要主函数来判断这个时候的TI是否是1,然后才决定下一次是否再进中断,所以也需要他在while函数里做一个死循环,就可能非常短的一个时间内他判断,啊TI=1,可以进入中断就再进入中断函数。

5.辅助理解

delay这个语句是个例外,它持续的时间比一般语句久,TI是一个标志位,SBUF=nSendByte这个语句出现之后,串口通信开始,但这个时候程序是往下运行的也就到了delay(500)语句,在里面过了9个周期后TI标志位变成1了,与此同时灯响应到了号召亮起来了,这个时候程序当然还在delay(500)里跑。
需要把程序的时间和串口的时间分开。

三、原程序解惑

void Serial_Port() interrupt 4 using 0
{
//if(TI)
//{
P1_0=1;
SBUF=nSendByte;//不明白这句为什么要写,逻辑上感觉可以没有这句,但进到Proteus仿真后这句必须要有才能顺利实现流水点灯
delay(500);
P1_0=0;
nSendByte=nSendByte<<1;
if(nSendByte==0)
nSendByte=1;
SBUF=nSendByte;//感觉有这句足矣
//}
TI=0;
RI=0;
}
 

这个程序和它改的,那个程序有两点不一样,一个是中断函数里sbuf出现了两次,一个是它的TI=0(清零)放在后面。

1.从主函数到中断

我们还是从主函数进入,现在就好理解第一次怎么进中断了。
进中断后第一句就是164要开门,它立刻把SBUF往里送(按照我们刚说的主函数里虽然SBUF发的也是1但没人收,白发,所以现在得再发一次)
然后delay(500)亮灯啥的,咱发现这个时候TI=1,突然觉得这不就直接满足中断条件了,那走完中断函数程序不就成了。
就以为走完中断函数程序再回主程序判断中断条件就可以了
But
TI标志位有它的强制要求, 要求它在中断程序里置0一次。

2.第2次SBUF

因为有了强制性要求,也就有了这3句

	SBUF=nSendByte;
	TI=0;
	RI=0;

我们知道按照程序运行的顺序,它先运行SBUF这句,串口通信那边的时间也开始了,这个时候我们的程序就执行到TI=0,RI=0了(用了2-4个周期),但是此时串口通信那边的时间还没有结束(需要9个周期),它发呀发呀,我中断都跑完了,它应该还没发完。
这个时候回到主程序,在while里过了一小会9个周期跑完了TI终于等于1了,可以再进中断了。

可能有些地方解释的也有错误,欢迎各位批评指正。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

澄渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值