单片机串口通信理解(一)
SBUF(serial data buffer)用于串口数据传输,在51单片机内部分为两个物理上独立的8位缓冲器:发送缓冲器和接收缓冲器。而这两个缓冲器共用一个名称SBUF,共用一个地址99H,这意味着在程序中对SBUF进行修改时,发送缓冲器和接收缓冲器的值都会改变,且发送缓冲器与接受缓冲器的值始终相等。
那么物理上独立是什么意思呢?物理上独立指的是在51单片机的硬件中存在发送缓冲器和接收缓冲器。其中,
在执行写指令 MOV SBUF , A 时发送缓冲器起作用
在执行读指令 MOV A , SBUF 时接受缓冲器起作用
这意味着通过物理上独立的SBUF缓冲器可以实现全双工通信。
用于多机通信中的从机,仅用于从机被寻址的阶段。在一次通信过程正式开始前,从机接收并侦测主机发出的每一个信息帧,试图寻找呼叫自身的地址码。换句话说,SM2位用于从机筛选自身,一旦筛选完毕,该位的使命就完成了。
对于单片机来说,数据的发送过程是主动的,只要将发送数据送入SBUF,发送过程就开始了。但数据的接收是被动的,一旦数据到来,串口便会自动接收而不管CPU是否需要接收。因此需要有控制的接收行为。REN=1,允许串口接收;REN=0,不允许串口接收。
在方式2和方式3中发送的第9位数据,TB8=1,说明主机发送的是地址;TB8=0,说明主机发送的是数据。
在方式2和方式3中接收的第9位数据。RB8=1,说明从机接收到的是地址帧;RB8=0,说明从机接收的是数据帧。
在第8位发送结束时自动置位,需软件清零。
在第8位接收结束时自动置位,需软件清零。
仅D7位与穿孔通信有关,为波特率倍增位SMOD,在方式1,方式2,方式3中波特率倍增。
在工作方式1和3中会会涉及TMOD,TCON寄存器,这里不提。想看自己去别的地方看
在图上发现一个很有趣的地方,在接收时需要一个移位寄存器,而发送时没有。因为发送时CPU是主动的,不会发生帧重叠错误,而接收时CPU是被动的,通过移位寄存器与接收缓冲器的双缓冲结构可以避免在数据接收过程中产生帧重叠。
接下来介绍串口通信的数据格式,方式0为8位同步首发,直接传送8位数据。
方式1为10位异步收发,第一位为起始位,第二至第九位为传送的8位数据,第十位为停止位。
方式2和方式3为11位异步收发,第一位为起始位,第二至第九位为传送的8位数据,第十位为传输数据的第九位,当发送数据时第九位为TB8,接收数据时第九位为RB8,TB8和RB8用来判断数据位的前8位为数据还是地址,第十一位为停止位。
串口通信的数据格式可以如下表示
方式0:数据位(8位)
方式1:起始位+数据位(8位)+停止位
方式2:起始位+数据位(8位)+第九位数据位+停止位
方式3:起始位+数据位(8位)+第九位数据位+停止位
比特率的单位是bps(bit per second),为每秒传输的位的数量,例如4000bps意味着一秒钟传送4000个位。
波特率的单位是bd(boud),意为每秒传输码元的数量,码元指的是携带信息的信号,对于方式0,8bit(位)=1码元,对于方式1,10bit=1码元,对于方式2和方式3,11bit=1码元。所以对于方式1,4000bps=400bd。
不过多数搞单片机的人认为波特率=比特率,我翻的书上也确实都是这么写的,他们说波特率4800的意思是4800bps,不过上应该是比特率4800,可能这也是约定俗成,将错就错?
这篇文章仅涉及单机通信,我认为双机通信原理更加简单,本文不涉及
单片机的串口通信是一位一位输入,在单片机时钟周期的下降沿读出或写入数据,其中,TI置位条件是数据位全部发送完成,停止位发送之前。RI置位条件是停止位发送结束后,换句话说,除方式0,其他3种方式TI置位提前于RI,提前量为1个时钟周期。
上图为单片机中断原理图,在第1个机器周期的第5个时钟周期的第1个振荡周期结束后进行采样,在第2个机器周期的第6个时钟周期的第1个振荡周期结束后进入中断。(为了方便,我将用X表示机器周期,S表示时钟周期,P表示振荡周期,X1S5P2即为上图中的采样置标志)
1.中断响应条件是在采样置标志采集成功,在下一个机器周期查询标志转入处理(该处理是为了确定先响应哪个中断),之后根据中断优先级进入中断,即使只有一个中断信号,也必须在下一个机器周期结束后进入中断。
2.采样置标志在每个机器周期的S5P2都会查询。
3.中断的响应必须在该条语句结束后。
4.若当前执行的指令是RETI或对IE/IP寄存器进行修改时,不响应中断。
5.在中断中只响应中断优先级高于正在执行的中断的中断。
6.存在这样一种情况,中断标志位被置位但由于某种条件没有被响应,在下一个机器周期中断置位标志消失(对,说的就是你,低电平触发的外部中断),此时该中断被忽略(!)换句话说,如果未及时响应,虽然曾经产生过中断信号,我们不予处理。
我们给出这样的一个程序段:
MOV SBUF,A
JNB TI,$
CLR TI
SJMP $
ZHONGDUAN: JB TI,EXIT//中断子程序入口
CLR RI
RETI
EXIT: CLR TI
RETI
接下来开始分析,对于方式1,共有10位数据需要发送,MOV SBUF , A 这条语句将会占用2个机器周期,在S3结束时TI置位,S4结束时RI置位,由于查询标志在S5P2,此时TI,RI均置位,在MOV SBUF , A 语句结束后,因为单片机还没有运行到查询标志转入处理(X2S6P2),下一机器周期执行JNB TI , $ 指令,由于此时TI为1,所以顺序执行的下一条语句为CLR TI。但此时必须进行中断响应,此时TI仍然为1,程序跳转到EXIT清空TI。
此时出现了一个很不可思议的情况:虽然在中断返回时,他应该执行CLR TI指令,但在中断中TI被清零了,所以JNB TI,$指令被重新判断,此时TI为零,所以单片机将停在JNB TI,$这条语句,而非SJMP $。
实际上,在单片机执行过程中,由于RI=1,所以单片机还能继续进入中断,在JNB TI,$和中断程序中不断反复。这很奇怪,我在中断中清空RI,TI也为0,为什么还会进入中断呢?因为虽然我们通过软件清空了RI,但由于SBUF寄存器未清零,单片机将一直认为有数据发送至单片机,将有硬件自动将RI置位。
最后捎带扯一下多机通信,多机通信极极极极极极极极极极极极少出现TI,RI同时置1的情况(我认为可能性为0),在实际实现过程中若RI或TI置1,那么单片机将不再写或读,上述例子只是为了理解。
执行 MOV A ,SBUF指令可以自动清零SBUF。