9-《电子入门趣谈》第一章_一切从单片机开始-1.3.6串口通信

2019-04-15 15:38发布

1.3.6 串口通信   1.3.6.1 什么是通信 关于通信的概念我想大家肯定都心知肚明,就是“聊天”呗。我们把制作好的单片机系统叫做下位机,把电脑称为上位机,它们之间通常是需要相互通信的。比如: 单片机:主人,发现有人进入我们的领地。 电脑:来者即是客,好生招待。 单片机:好的,那该如何招待? 电脑:杀死。 单片机:。。。 单片机与单片机、单片机与电脑、电脑与电脑之间都需要通信,以了解彼此的工作状态或者进行相关控制,当然,他们之间的通信肯定不是吧啦吧啦地真在那聊天,要真那样就见鬼了,它们之间还是得通过一定的协议,一定的方法来传输“01010101”之类的二进制数而已,然后通过识别不同的数字来实现不同的功能。 1.3.6.2通信方式 机器之间的通信实际上就是指收发各种字节,通常有两种方式:并行通信,串行通信。 并行数据通信是指数据的各位同时进行传送的通信方式。其优点是传送速度快;缺点是数据有多少位,就需要多少根传送线。
串行数据通信是指数据是一位一位顺序传送的通信方式,它的突出优点是只需一对传送线,这样就大大降低了传送成本,特别适应于远距离通信;其缺点是传送速度较低。假设并行传送N位数据所需时间为T,那么串行传送的时间至少为N*T。 更多时候我们会使用串行通信。在串行通信中,按照收发双方是否具有同步时钟又可分为同步通信和异步通信 同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过外同步和自同步两种方法实现[7][8]。为自同步原理图如图所示。
异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。异步通信以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)也是任意的,但每个字符中的各位是以固定的时间传送的。原理图如图所示。
数据传输模式又分为单工、半双工、全双工和多工工作方式。单工方式时,数据仅按一个固定方向传送。因而这种传输方式的用途有限,常用于串行口的打印数据传输与简单系统间的数据采集。半双工方式时数据可实现双向传送,但不能同时进行,实际的应用采用某种协议实现收/发开关转换,生活中的对讲机就是半双工模式。全双工方式时允许双方同时进行数据双向传送,比如电话手机等。这三种传输方式都是用同一线路传输同一种频率信号,为了充分利用线路资源,可通过使用多路复用器或多路集线器,采用频分、时分或码分复用技术,即可实现在同一线路上共享功能,我们称之为多工传输方式。几种传输方式框图如图2-2所示。从前往后依次为单工、半双工和全双工。 按照上述的通信方式,在单片机的世界里集成了好多种通信模块,常见的有I2C、SPI、URAT(串口通信)等。它们之间的不同之处目前可以简单的理解为需要双方对的暗号不一样,打一个比较高尚的比方,比如: 甲:锄禾日当午 乙:黄河入海流 甲:举头望明月 乙:我爱喝酱油 甲:哦,原来是I2C兄啊,失敬失敬。 乙:嗯,上级让我给您传个信儿。。。 SPI可能又是另外一套暗号,关于I2C和SPI的靠谱点的详细介绍以及传输协议等等的,我们现在没有必要去深究,等用的时候再说吧。 这一节,我们主要学习51单片机上的“异步串行通信模块”即人们常说的“串口通信”,它是一种全双工工作模式。 1.3.6.3 串口通信 (1)结构 MCS-51单片机内部有一个全双工的串行通信口,即串行接收和发送缓冲器(SBUF),这两个在物理上独立的接发送器,但它们共占用一个地址99H,所以它们的名字都叫SBUF,而且既可以接收数据也可以发送数据。但接收缓冲器只能读出不能写入,而发送缓冲器刚只能写入不能读出。这个通信口既可以用于网络通信,亦可以实现串行异步通信,还可以构成同步移位寄存器使用。如果在传行口的输入输出引脚上加上电平转换器,就可以方便地构成标准的RS-232接口。80C51单片机的串行口的结构如图所示。
TXD对应单片机上的P3.1管脚,是数据发送管脚;RXD对应单片机上的P3.0管脚,是数据接收管脚;TH1、TL1您看着熟悉吗?没错,就是上文中T1定时器模块的计数器寄存器,在这用来设置串口通信的波特率(传播速度,下文中会详细介绍);TI和RI分别为发送数据成功标志位和接收数据成功标志位,当这两个标志位有一个置1的同时若串口通信中断允许位ES也为1的话,将进入串口通信中断函数。 (2)相关寄存器 1.特殊功能寄存器SCON     SCON 是一个特殊功能寄存器,用以设定串行口的工作方式、接收/发送控制以及设置状态标志,字节地址为98H。SCON寄存器的各位定义如表3-1所示。 7 6 5 4 3 2 1 0 SCON 字节地址:98H SM0 SM1 SM2 REN TB8 RB8 TI RI 表3-1 SCON寄存器 SM0、SM1:工作方式选择位,可选择四种工作方式,如表3-2所示。 SM0 SM1 方式 说明 波特率 0 0 0 移位寄存器 fosc/12 0 1 1 10位异步收发器(8位数据) 可变 1 0 2 11位异步收发器(9位数据) fosc/64或fosc/32 1 1 3 11位异步收发器(9位数据) 可变 表3-2 串口通信4种工作方式 SM2:多机通信控制位。主要用于方式2和方式3。当接收机的SM2=1时可以利用收到的RB8来控制是否激活RI(RB8=0时不激活RI,收到的信息丢弃;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走)。当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能)。通过控制SM2,可以实现多机通信。 在方式0时,SM2必须是0。在方式1时,若SM2=1,则只有接收到有效停止位时,RI才置1。 REN:允许串行接收位。由软件置REN=1,则启动串行口接收数据;若软件置REN=0,则禁止接收。 TB8:用在方式2或方式3中,是发送数据的第九位,可以用软件规定其作用。可以用作数据的奇偶校验位,或在多机通信中,作为地址帧/数据帧的标志位(在方式0和方式1中,该位未用) 。 RB8:用在方式2或方式3中,是接收到数据的第九位,作为奇偶校验位或地址帧/数据帧的标志位。在方式1时,若SM2=0,则RB8是接收到的停止位。 TI:发送中断标志位。在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。 RI:接收中断标志位。在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请。 2.特殊功能寄存器PCON PCON的字节地址为87H,它的第7位SMOD是与串口通信波特率的设置有关的选择位。SMOD(PCON.7)为波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍。复位时,SMOD=0。 (3)工作方式 根据SM0和SM1的值,串口通信有这么四种工作方式。 1.方式0     设置SCON寄存器的SM0、SM1=0 0时,串行口工作于方式0。此时,串行口为同步移位寄存器的输入输出方式。主要用于扩展并行输入或输出口。数据由RXD(P3.0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出。发送和接收均为8位数据,低位在先,高位在后。波特率固定为fosc/12。其中fosc为时钟频率。     2.方式1     设置SCON寄存器的SM0、SM1=0 1时,串行口工作于方式1。方式1是10位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如图所示。其中1位起始位,8位数据位,1位停止位。用软件置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断。方式一的输入输出图如图3-8、3-9所示。
图3-8 方式1输入
图3-9 方式1输出 3.方式2和方式3 设置SCON寄存器的SM0、SM1=1 0时,串行口工作于方式2,当SM0、SM1=1 1时,串行口工作于方式3。方式2或方式3为11位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚 。 方式2和方式3时起始位1位,数据9位(含1位附加的第9位,发送时为SCON中的TB8,接收时为RB8),停止位1位,一帧数据为11位。方式2的波特率固定为晶振频率的1/64或1/32,方式3的波特率由定时器T1的溢出率决定。 方式2和方式3输出:发送开始时,先把起始位0输出到TXD引脚,然后发送移位寄存器的输出位(D0)到TXD引脚。每一个移位脉冲都使输出移位寄存器的各位右移一位,并由TXD引脚输出。第一次移位时,停止位“1”移入输出移位寄存器的第9位上 ,以后每次移位,左边都移入0。当停止位移至输出位时,左边其余位全为0,检测电路检测到这一条件时,使控制电路进行最后一次移位,并置TI=1,向CPU请求中断。发送时序图如下图3-10所示。 图3-10 方式2或方式3的发送时序图 方式2和方式3输入:接收时,数据从右边移入输入移位寄存器,在起始位0移到最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的第9位数据为1)时,接收到的数据装入接收缓冲器SBUF和RB8(接收数据的第9位),置RI=1,向CPU请求中断。如果条件不满足,则数据丢失,且不置位RI,继续搜索RXD引脚的负跳变。接收时序图如图3-11所示。
图3--11 方式2或方式3的接收时序图 4.波特率的计算 在串行通信中,收发双方对发送或接收数据的速率要有约定。通过软件可对单片机串行口编程为四种工作方式,其中方式0和方式2的波特率是固定的,而方式1和方式3的波特率是可变的,由定时器T1的溢出率来决定。 串行口的四种工作方式对应三种波特率。由于输入的移位时钟的来源不同,所以,各种方式的波特率计算公式也不相同。 方式0的波特率=  fosc/12 方式2的波特率=(2SMOD/64)· fosc 方式1的波特率=(2SMOD/32)·(T1溢出率) 方式3的波特率=(2SMOD/32)·(T1溢出率) 当T1作为波特率发生器时,最典型的用法是使T1工作在自动再装入的8位定时器方式(即方式2,且TCON的TR1=1,以启动定时器)。这时溢出率取决于TH1中的计数值。计算公式如3-1所示:               T1 溢出率 = fosc /{12×[256 -(TH1)]}          (3-1) 在单片机的应用中,常用的晶振频率为:12MHz和11.0592MHz。所以,选用的波特率也相对固定。 (4)实例程序 上面说了不少,大家可能还在为串口通信不同工作方式下的时序图而纠结呢,我告诉大家一个秘密。那个时序图大家其实不用太在意,编程的时候是没有它们的事的,所有的时序都由硬件自动完成,除非该单片机没有集成串口通信模块,那就得自己编程来模拟整个时序过程了,万幸的是51单片机(甚至所有单片机)都集成了串口通信模块。 接下来利用串口通信能力实现一个单片机和电脑通信的功能。硬件电路图如下所示:

这里单片机和电脑相连,单片机的电平为TTL电平,它默认VCC(5V)为逻辑正,0为逻辑负,而电脑使用的是RS232电平,它默认+12V为逻辑负,-12V为逻辑正,为了让这俩正常通讯,比如单片机发高电平5V,电脑也能感受到高电平,就需要让单片机的电平发生一个转换,5V转换为-12V,0转换为+12V,MAX232芯片就有这样的转换能力。J1是一个串口接头,一般的笔记本上没有串口,我们可以用一根USB转串口线来代替,不过提前得安装一个USB转串口的驱动程序。什么?你没有USB转串口线?少年,太落伍了,赶紧去买一根,也不贵,十几块钱左右吧。什么?你没钱?也好办,把手里的这本书卖了换钱去。什么?卖了就没书看了?简单啊,再把USB转串口线卖了买书。 编写一个单片机向电脑发送字符串的程序,如下所示 #include #define uintunsigned int /******************************************************** **函数名: delay(uint x) **返回:无 **函数功能描述:延时函数,延时大概x毫秒 **********************************************************/ void delay(uint x)            //延时函数。 {       uinti,j;     for(i=0;i<=x;i++)        for(j=0;j<=100;j++); } char  code MESSAGE[]= "123456"; unsigned chara;   /******************************************************** **函数名:main(void) **返回:无 **函数功能描述:单片机通过串口向电脑发送数据 **********************************************************/   void main(void) {         SCON = 0x50;   //REN=1允许串行接受状态,串口工作模式2                TMOD|= 0x20;   //定时器工作方式2                               PCON|= 0x80;   //波特率提高一倍                                                           TH1 = 0xF3; //  波特率4800、数据位8、停止位1。效验位无 (12M)         TL1= 0xF3;         TR1 = 1;        //开启定时器1                                                              ES  = 0;        //开串口中断                          EA  = 0;        // 开总中断       while(1)       { a=0;          while(MESSAGE[a] != '