声明:本文只是对个人知识盲点、知识弱点和关键部分的总结。我希望你不喜欢它。梳理顺序是根据书的实际顺序进行梳理。请注明转载的来源。
作者:sumjess
一、并行与串行的基本通信方式:
随着单片机系统的广泛应用和计算机网络技术的普及,单片机的通信功能越来越重要。单片机通信是指单片机与计算机或单片机之间的信息交换。通常,我们使用更多的单片机和计算机之间的通信。 通信有两种并行和串行方式。在单片机系统和现代单片机测控系统中,串行通信主要用于信息交换。
并行通信通常是用多条数据线同时传输数据字节,,如下图所示,8位数据总线的通信系统将需要8条数据线一次传输8位数据(1字节)。此外,还需要 需要一条信号线和几条控制信号线,只适用于短距离数据传输。例如,旧打印机通过并口连接到计算机,现在传输速度非常快USB 2.0接口通信。由于并口通信使用较少,我们在这里只做一个简单的介绍,我们只需要理解。
并行通信控制简单,相对传输速度快,但由于传输线路较多,长途传输成本高, 同时接收发方有困难。 串行通信是将数据字节一个接一个地以传输线的形式传输。此时,只需要一条数据线,加上一条公共信号线和几条控制信号线。因为一次只能传输一个,所以至少有8个字节数据,如下图所示。
串行通信的必要过程是将并行数据转换为串行数据并发送到线路上。接收时,将串行信号转换为并行数据,以便由计算机和其他设备处理。 串行通信传输线路少,长途传输成本低,可使用电话网等现成设备,但数据传输控制比并行通信复杂。 串行通信有两种方式:异步串行通信和同步串行通信。 异步串行通信是指通信的发送和接收设备使用各自的时钟控制数据的发送和接收过程。为了协调双方的收发,要求,如下图所示。 异步通信以字符(构成帧)为单位传输,,但的,即字符之间不一定有“位间隔"的 整数倍关系, 但 。 异步通信一帧字符信息 4 部分组成: ,如下图所示。有些字符信息。, 字符之间有空闲字符。 异步通信的特点:不要求收发时钟严格一致,容易实现,设备成本小,但每个字符应附加2~用于起止位、验证位和停止位,每帧之间有间隔,因此传输效率不高。 异步串行通信通常用于单片机和单片机之间的通信。 发送方时钟应直接控制接收方时钟,使双方完全同步。此时,传输数据的位置之间的距离是位间隔的整数倍。同时,传输的字符之间没有间隙,即保持位同步和字符同步。发送方对接收方的同步可以通过外同步和自同步来实现,如下图左右所示。 面向字符的同步格式如下图所示。 此时,传输的数据和控制信息必须由规定的字符集(如ASCII由码)中的字符组成。图6.1.7中帧头1或2个同步字符SYN(ASCII码为16H)。SOH为序始字符(ASCII码为OlH),标题包含源地址、目标地址、路由指示等信息。STX为文始字符(ASCII码为02H),数据块开始表示传输。数据块是传送的正文内容,由多个字符组成,数据块后面是组终字符ETB(ASCII码为17H)或文终字符ETX(ASCII码为03H),然后是验证码。面向字符的典型同步程序,如IBM二进制同步规程BSC。 面向位的同步格式如下图所示。 此时,数据块被视为数据流,序列0111110被用作开始和结束的标志。为了避免0111110序列在数据流中引起的混乱,发送人总是在其发送的数据流中每5个连续1插入一个额外的0;接收人每检测到5个连续1,然后删除0。 典型的面向位同步协议,如ISO高级数据链路控制程序HDLC和IBM同步数据链路控制程序SDLC。 同步通信的特点是以特定位置组合0111110作为帧的开始和结束标志,传输的帧数据可以是任意位置。传输效率高,但硬件设备比异步通信更复杂。 (1)单工。单工是指数据传输。,反向传输无法实现。 (2)半双工。半双工是指数据传输两个方向,但需要。 (3)全双工。全双工是指数据。进行双向传输。种类型如下图左、下图中和下图右所示。 (1)奇偶校验 发送数据时,数据位跟随的一个是奇偶校准位(1或0)。奇校验时,数据中1和校验位1之和应为奇数;偶校验时,数据中1和校验位1之和应为偶数。接收字符时,检查1的数量。如果发现不一致,则表明数据传输过程中存在错误。 (2)代码和验证 代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据时同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和“进行比较,相符则无差错,否则即认为传送过程中出现了差错。 (3)循环冗余验证 该验证是通过某种数学操作实现有效信息与验证位之间的循环验证,通常用于磁盘信息的传输和存储区域的完整性验证。该验证方法具有较强的纠错能力,广泛应用于同步通信中。
二、RS-232 电平与TTL 电平转换:
关于RS-232电平与TTL电平的特提到了电平的特性,本节主要讲解了使用较多的计算机RS-232电平与单片机TTL电平之间的转换模式。早期的MC1488, 75188等芯片可以实现TTL电平到RS-232电平转换;MC1489, 75189等芯片可以实现RS-232电平到TTL转换电平。但是现在用的比较多MAX232, MAX202, HIN同时集成了232等芯片RS-232电平和TTL平之间的互转。为丰富大家的知识,下面首先讲解在没有M心(232这种现成电平转换芯片时,如何用二极管、三极管、电阻、电容等分立元件搭建一个简单的RS-232电平与TTL电平之间的转换电路。 (下图) 集成芯片内部都是由最基本电子元件组成,如电阻、电容、二极管、三极管等元件,为了方便用户使用,制造商把这些具有一定功能的分立元件封装到一个芯片内,这样就制成了我们使用的各种芯片。学会本电路后,我们也就基本搞清了MAX232芯片内部的大致结构。 MAX232是把TTL电平从ov和5V转换到3V15V或-3V-15 V之间。分析图下图,首先TTL电平TXD发送数据时,若发送低电平0,这时Q3导通,PCRXD由空闲时的低电平变高电平(如PC用中断接收的话会产生中断),满足条件。发送高电平1时,TXD为高电平,Q3截止,由于PCRXD内部高阻,而PCTXD平时是-3~-15V,通过D1和R7将其拉低PCRXD至-3-15V,此时计算机接收到的就是1。下面再反过来,PC发送信号,由单片机来接收信号。当PCTXD为低电平-3-(-15V)时,Q4截止,单片机端的RXD被R9拉到5V高电平;当PCTXD变高时,Q4导通,RXD被Q4拉到低电平,这样便实现的双向转换,这是一个很好的电路,值得大家学习。
2. MAX232芯片实现RS-232电平与TTL电平转换 MAX232芯片是MAXIM公司生产的、包含两路接收器和驱动器的IC芯片,它的内部有一个电源电压变换器,可以把输入的+5V电源电压变换成为RS-232输出电平所需的+10V电压。所以,采用此芯片接口的串行通信系统只需单一的+5V电源就可以了。对千没有+12V电源的场合,其适应性更强,加之其价格适中,硬件接口简单,所以被广泛采用。 MAX232芯片实物如图6.2.2和6.2.3所示,其引脚结构和外围连接分别如图6.2.4和图6.2.5 所示。 图6.2.5中上半部分电容C1,C2,C3,C4及V+,V-是电源变换电路部分。在实际应用中,器件对电源噪声很敏感,因此Vee必须要对地加去耦电容Cs,其值为0.lµF。按芯片手册中介绍,电容C1,C2,C3,C4应取1.0µF/16V的电解电容,经大量实验及实际应用,这4个电容都可以选用O.lµF的非极性瓷片电容代替1.0µF/16V的电解电容,在具体设计电路时,这4个电容要尽量靠近MAX232芯片,以提高抗干扰能力。 图6.2.5下半部分为发送和接收部分。实际应用中,T1IN,T2IN可直接连接TTL/CMOS电平的51单片机串行发送端TXD;R10UT,R20UT可直接连接TTL/CMOS电平的51单片机的串行接收端RXD;T10UT,T20UT可直接连接PC机的RS-232串口的接收端RXD;R1IN,R2IN可直接连接PC机的RS-232串口的发送端TXD。 现从MAX232芯片中两路发送、接收中任选一路作为接口。要注意其发送、接收的引脚要对应。如使T1IN连接单片机的发送端TXD,则PC机的RS-232接收端RXD一定要对应接 T10UT引脚。同时,R10UT连接单片机的RXD引脚,PC机的RS-232发送端TXD对应接R1 IN引脚。 TX-1C实验板串口部分原理图如图6.2.6所示,实验板上实物如图6.2.7所示。 其数据传输过程如下:MAX232的11脚T1IN接单片机TXD端P3.1,TTL电平从单片机的TXD端发出,经过M心(232转换为RS-232电平后从MAX232的14脚T1OUT发出,再连接到实验板上串口座的第3脚,再经过随板配送的交叉串口线后,连接至PC机的串口座的第 2脚RXD端,至此计算机接收到数据。PC机发送数据时从PC机串口座第3脚TXD端发出数据,再逆向流向单片机的RXD端P3.0接收数据。 这里需要注意的是,MAX232与串口座连接时,无论是数据输出端,还是数据输入端,连接串口座的第2引脚或第3引脚都可以,选用不同的连接方法时,单片机与计算机之间的串口线都要谨慎选择,是选择平行串口线还是交叉串口线、是选择母头对母头串口线还是母头对公头串口线这些都要非常注意,每种选择都有对应的电路,但无论哪种搭配方式,大家必须要明白,在单片机与计算机之间必须要有一条数据能互相传输的回路,只要把握好每个交接点就一定能通信成功。
三、波特率与定时器初值的关系:
单片机或计算机在串口通信时的速率用波特率表示,它定义为每秒传输二进制代码的位数,即1波特=1位/秒,单位是bps(位/秒)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的波特率为10位X240个/秒=2400bps。 串行接口或终端直接传送串行信息位流的最大距离与传输速率及传输线的电气特性也有关。当传输线使用每0.3m(约1英尺)有50pF电容的非平衡屏蔽双绞线时,传输距离随传输速率的增加而减小。当比特率超过1000bps时,最大传输距离迅速下降,如9600bps时最大距离下降到只有76m(约250英尺)。因此我们在做串口通信实验选择较高速率传输数据时,尽量缩短数据线的长度,为了能使数据安全传输,即使是在较低传输速率下也不要使用太长的数据线。 在串行通信中,收、发双方对发送或接收数据的速率要有约定。通过编程可对单片机串行口设定为4种工作方式,其中方式0和方式2的波特率是固定的,而方式1和方式3的波特率是可变的,由定时器T1的溢出率来决定。 串行口的。由于输入的移位时钟的来源不同,所以各种方式的波特率计算公式也不相同,以下是4种方式波特率的计算公式。 的波特率=fos/12。 的波特率=(2^SMOD/32) x(T1溢出率)。 的波特率=(2^SMOD/64)Xfoc。 的波特率=(2SMOD/32)x(Tl溢出率)。 式中,fosc为系统晶振频率,通常为12MHz或11.0592MHz; SMOD是PCON寄存器的最高位(关于PCON寄存器请看下一个知识点);T1溢出率即定时器Tl溢出的频率。
知识点:电源管理寄存器PCON
电源管理寄存器在特殊功能寄存器中,字节地址为87H,不能位寻址,PCON用来管理单片机的电源部分,包括等。单片机复位时PCON全部被清0。其各位的定义如下表所示。 。 SMOD=0: 串口方式1, 2,3时,波特率正常。 SMOD=1: 串口方式1, 2,3时,波特率加倍。 。 GF1,GF0—。 PD—。 PD=0:单片机处于正常工作状态。 PD=1:单片机进入掉电(PowerDown)模式,可由外部中断低电平触发或由下降沿触发或者硬件复位模式唤醒,进入掉电模式后,外部晶振停振,CPU、定时器、串行口全部停止工作,只有外部中断继续工作。 。 IDL=0:单片机处于正常工作状态。 IDL=1:单片机进入空闲(Idle)模式,除CPU不工作外,其余仍继续工作,在空闲模式下可由任一个中断或硬件复位唤醒。 T1溢出率就是T1定时器溢出的频率,只要算出T1定时器每溢出一次所需的时间T,那么T的倒数1/T就是它的溢出率。这个问题还是比较容易理解的,在第3章讲解过定时器T0和T1方式1的操作方法,若我们设定定时器T1每50ms溢出一次,那么其溢出率就为20Hz,再将20代入串口波特率计算公式中即可求出相应的波特率,当然也可根据波特率反推出定时器的溢出率,进而计算出定时器的初值。通常单片机在通信时,波特率都较高,因此T1溢出率也必定很高,如果我们使用定时器1的工作方式1在中断中装初值的方法来求T1溢出率的话,在进入中断、装值、出中断这个过程中很容易产生时间上微小的误差,当多次操作时微小的误差不断累积,终会产生错误。有效的解决办法是,使用T1定时器的工作方式2,8位初值自动重装的8位定时器/计数器,定时器方式2逻辑结构图如图6.3.1所示。 在学习定时器方式2时可参考3.5节讲解的定时器方式1,在方式1中,当定时器计满溢出时,自动进入中断服务程序,然后我们需要手动再次给定时器装初值,而在后,,并且无须进入中断服务程序进行任何处理,这样定时器溢出的速率就会绝对稳定。:先设定M0M1选择定时器方式2,在TLX和THX中装入计算好的初值,启动定时器,然后TLX寄存器便在时钟的作用下开始加1计数,当TLX计满溢出后,CPU会自动将THX中的数装入TLX中,继续计数。因此我们在启动定时器之前必须先将TLX和THX中装好合适的数值,以让定时器输出产生的溢出率,这里TLX和THX中装的数值必须是一样的,因为每次计数溢出后TLX中装入的新值是从THX中取出的。 下面我们举一个例子来讲解根据已知波特率,如何计算定时器1 方式2下计数寄存器中的初值。 【例6.3.1】 解:设所求的数为X,则定时器每计256-X个数溢出一次,每计一个数的时间为一个机器周期,一个机器周期等于12个时钟周期,所以计一个数的时间为12/11.0592MHz(s), 那么定时器溢出一次的时间为[256-X]x12/11.0592MHz(s),T1的溢出率就是它的倒数,方式1的波特率=[(2SMOD)/32]x(T1溢出率),这里我们取SMOD=0,则2SMOD=1,将已知的数代入公式后得9600=(1/32)x11059200/[256-X]x12,求得X=253,转换成十六进制为0xFD。上面若将SMOD置1的话,那么X的值就变成250了。可见,在不变化X值的状态下,SMOD由0变1后,波特率便增加一倍。 大家一定要搞明白上面这段由波特率计算定时器初值的方法,通常波特率都是固定的一些数据,如1200,2400,4800,9600等,所以都是根据所要使用的波特率来求定时器初值,而没有说根据定时器初值来求波特率的,以后大家若要使用不同的波特率来做单片机和单片机之间或单片机与计算机之间的通信实验时,可参考上面的计算方法来求定时器初值。 大家可能会有疑惑,为什么单片机系统的晶振要选11.0592MHz 呢?通过上面的计算可能有些人已经明白了,我们用一个小知识点来为大家解答这个疑惑。
知识点:为什么51系列单片机常用11.0592MHz的晶振设计?
常用波特率通常按规范取为1200, 2400,4800,9600,…,若采用晶振12MHz或6MHz,计算得出的T1定时初值将不是一个整数,这样通信时便会产生积累误差,进而产生波特率误差,影响串行通信的同步性能。解决的方法只有调整单片机的时钟频率fosc,通常采用11.0592MHz晶振。因为用它能够非常准确地计算出T1定时初值,即使对于较高的波特率(19600,19200),。 表6.3.2列出了串口方式1定时器1方式2产生常用波特率时,TL0和TH0中所装入的值。
四、51 单片机串行口结构描述:
51 单片机的串行口是一个可编程全双工的通信接口, 具有 也可作为同步移位寄存器使用。 51 单片机的串行口主要由
知识点:串行口控制寄存器SCON
串行口控制寄存器SCON在特殊功能寄存器中,字节地址为98H,可位寻址,
五、串行口方式1编程与实现:
串行口方式1是最常用的通信方式,其传送一帧数据的格式如图6.5.1所示。 串行口方式1传送
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar flag,a,i;
uchar code table[]="I get";
void init()
{
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
REN=1;
SM0=0;
SM1=1;
EA=1;
ES=1;
}
void main()
{
init();
while(1)
{
if(flag==1)
{
ES=0;
for(i=0;i<6;i++)
{
SBUF=table[i];
while(!TI);
TI=0;
}
SBUF=a;
while(!TI);
TI=0;
ES=1;
flag=0;
}
}
}
void ser() interrupt 4
{
RI=0;
a=SBUF;
flag=1;
}
分析如下: ① "
六、串行口打印在调试程序中的应用:
编译程序下载到实验板,打开串口调试助手,分别发送01,02,03,当开启A/D转换后,适当调节实验板上A/D电压调节电位器,可看到返回的电压实际值在变化,最终界面显示如图6.6.1所示。 分析如下:在分析本节例程之前,先来看上一节的例程。其实在6.5节串口测试程序中有个小问题:如果我们先打开串口调试助手软件,再打开实验板上电源的话,会看到上位机软件会在实验板刚一上电的时候收到一串字符’'I get",可是这时我们并没有向单片机发送任何命令,而单片机为何为主动发数据呢?也许大多数人并没有注意到这个现象,如果作为一个产品的话,这样的系统肯定不能算稳定工作的,6.5节的问题若没有解决,本节的例程将不可能调试成功,因此我们必须解决掉任何一个不正常的问题。 (1)先来解决6.5节的问题。我们在串口初始化函数中有这样几条语句: REN=1; SM0=0; SM1=1; 这三位都是串行口控制寄存器SCON里面的,单片机刚上电时SCON被清0,因为串口方式为方式0,串行口为同步移位寄存器的输入/输出方式,当执行完REN置1这条语句后,它便直接开始从RXD引脚接收数据,并不管与它连接的系统有无发送数据,这时SM0和SM1还未被操作,可单片机串口寄存器已经收到数据,并且已经产生了串口中断,因此串口中断中的标志位flag将被置1,当运行完下面两条指令后,串口方式才被设置为方式1,这时才终止串口接收数据。当程序运行到while(1)大循环中时,因为串口中断服务程序中的标志位flag已经被置1,所以接下来将发送里面的"I get",至于后面的a被发送到上位机之后为什么变成了一个空格,这个由大家自己来研究。 本问题解决办法如下: ① 将上面三条语句顺序改为 SM0=0; SM1=1; REN=1; 先设置串口模式,再允许串口接收,这样就会避开串口方式0接收数据。 ② 不要对SCON寄存器进行位操作,而是直接对整个寄存器进行设置,如SCON=0x50。大家可亲自做实验体检各种现象,若将本节实验中这三句改回原样后实验,产生的错误便不会如6.5节一样简单了,将会影响整个系统的正常运行,大家务必尝试一下。像这种看似很小的问题,在没有解决之前可能需要花费很长的时间才能找到问题的根源,我也不隐讳地告诉大家,我在调试本例程时,花了近3个小时才将它调试成功,无论如何也没有想到错误的原因竟然是因为把"REN=1;“这条语句提前写了两行,当然每解决一个问题,我们学到的知识都将更进一步,甚至更多,单片机是硬件,必须要经过大量的实验方可掌握,像这种现象光靠学书本上的理论,是永远都学不到的。 (2)#include<stdio.h>头文件中包含有我们要使用的函数printf()和puts()。我们到Keil\C51\INC文件夹下打开STDIO.H, 可看到里面申明了一些外部函数,内容如下: extern char _getkey(void); extern char getchar(void); extern char ungetchar(char); extern char putchar(char); extern int printf(const char*,…); extern int sprintf(char*,const char*,…); extern int vprintf(const char*,char*); extern int vsprinf(char*,const char*,char*); extern chargets(char,int n); extern int scanf(const char*,…); extern int sscanf(char*,const char*,…); extern int puts(const char*); extern表示在这里申明的是一个外部函数,外部函数的函数体不在本文件中,而是在其他某个文件中写有这个函数的实现部分。 在本例中用到的printf()和puts()函数,在它内部都是由putchar()这个函数实现的,关于这两个函数的用法大家请看C51的帮助文件。在Keil\C5l\LIB文件夹下打开PUTCHAR.C文件可看到内容如下: 这个函数的主要作用是通过串口发送一个字符,在代码的最后我们看到有等待TI为1才将字符发送出去,否则一直等待下去,这也就是我们在本节例程中看到的每次在调用printf()和puts()函数之前先要手动将TI置1的原因,这一点至关重要,大家可改变例程亲自做实验,体验去掉TI=1后的现象。 (3)printf()和puts()的区别:从串口调试助手收到的数据我们可以看出,两个函数在参数中都加有”\n",即回车的意思。而puts()函数输出到上位机后还多了一个换行,这是区别之一;区别之二是,printf()可以在后面追加要输出的变量,而puts()只能输出字符串。 (4)在使用stdio.h这个头文件中的函数之前,必须将串口部分初始化完毕,最好将串口设置为方式1,波特率与上位机一致。 (5)在每次调用完printf()和puts()函数后,必须检测是否发送完毕,即检测TI是否为1,当发送完毕后要把TI清0,否则程序会出错,大家可自行验证。 (6)每次调用printf()和puts()函数之前,必须将串口中断先关闭。若不关闭串口中断,每发送一个字节,程序就会申请进入串口中断,从而导致程序出错。 (7)“ad_val=get_ad();“是将A/D采集回来的8位二进制数赋给ad_val,”(float)ad_val"的意思是将字符型变量ad_val的值强制转换成浮点型,然后经过”(float)ad_val*5.0/256.0"运算得出以浮点数表示的A/D实际采集到的电压标准值。注意:在浮点数运算中,原来是整数的常量后面需要加".0变成浮点数,原来是整数的变量需要强制转换成浮点数再进行运算。