Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

串行口通信(一)

一个人的朝圣 2019-02-21 11:11:00 阅读数:192 评论数:0 点赞数:0 收藏数:0

前言

人类生存于世,少不了沟通。沟通使人类互相认知,传递信息,提升生活品质。但然单片机也需要,不然单单一个机器,无法构成一个系统,发挥更大的力量。只有单片机与外围设备传递信息,互相反馈才会有一个完美的系统。串行口通信就被发明出来,下面来了解串行口通信(uart)。

几个概念

为了能更好理解串行口通信,在介绍它之前,先来看看几个概念,补充知识,方便深入明白uart工作原理。

通信方式

通信方式通信方式
通信方式有两种,分别是并行通信串行通信
并行通信:并行是指多比特数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错。
串行通信:串行通信是指 使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信。
拿汽车通道来说,并行通信就是多车道,多辆车一起行驶,而串行通信就是单车道,只能一辆一辆车通过。

 

串行通信制式

串行通信制式串行通信制式
串行通信制式有单工通信,半双工通信,双工通信三种。
单工通信:单工通信信道是单向信道,发送端和接收端的身份是固定的,发送端只能发送信息,不能接收信息;接收端只能接收信息,不能发送信息,数据信号仅从一端传送到另一端,即信息流是单方向的。
半双工通信:半双工数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。例如,在一个局域网上使用具有半双工传输的技术,一个工作站可以在线上发送数据,然后立即在线上接收数据,这些数据来自数据刚刚传输的方向。像全双工传输一样,半双工包含一个双向线路(线路可以在两个方向上传递数据)。
双工通信:双工通信是指在同一时刻信息可以进行双向传输,和打电话一样,说的同时也能听,边说边听。这种发射机和接收机分别在两个不同的频率上(两个频率差有一定要求)能同时进行工作的双工机也称为异频双工机。
三种通信制式很容易理解,对照上图看,单工通信不就是单方面通信,只能发出指令或接收指令。半双工通信就是能接收又能发送,但是不能同时进行。双工就最厉害,可以同时发送和接收指令。

 

串行通信分类

串行通信有两种,一位异步串行通信,二为同步串行通信。
异步串行通信:异步串行通信是指通信双方以一个字符(包括特定附加位)作为数据传输单位且发送方传送字符的间隔时间不一定,具有不规则数据段传送特性的串行数据传输。
同步串行通信:所谓同步通信是指在约定的通信速率下,发送端和接收端的时钟信号频率和相位始终保持一致(同步),这就保证了通信双方在发送和接收数据时具有完全一致的定时关系。
两种串行通信不同就只有时间,在发送字符时,异步可以是不同时间间隔发送,但同步只能以固定的时间间隔发送。

波特率

波特率:波特率表示每秒钟传送的二进制位数,是衡量数据传送速率的指标,它用单位时间内载波调制状态改变的次数来表示。
相互通信的甲乙双方必须具有相同的波特率,不然无法成功完成串行通信。

概述

52单片机具有一个全双工串行通信口。一个全双工UART(通用异步接收发送器)的串行I/O口,用于实现单片机之间或单片机与微机之间的串行通信;片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。最佳振荡频率为6M—12M。管脚RXD( P3.0),TXD(P3.1)与串口通信有关。波特率可以设置。

内部结构

串行口内部结构逻辑图串行口内部结构逻辑图
串行口通信与三个寄存器有关,分别是:

 

 1. PCON寄存器
 2. SCON寄存器
 3. SBUF特殊功能寄存器

寄存器

PCON电源管理寄存器

电源管理寄存器电源管理寄存器
SMOD:该位与串口通信有关。
SMOD=0; 串口方式1,2,3时,波特率正常。
SMOD=1; 串口方式1,2,3时,波特率加倍
LVDF:低电压检测标志位,同时也是低电压检测中断请求标志位
GF1,GF0:两个通用工作标志位,用户可以自由使用。
PD:掉电模式设定位。
PD=0 单片机处于正常工作状态。
PD=1 单片机进入掉电(Power Down)模式 ,可由外部中断或硬件复位模式唤醒,进入掉电模式后,外部晶振停振,CPU、定时器、串行口全部停止工作,只有外部中断工作。在该模式下,只有硬件复位和上电能够唤醒单片机。
IDL:空闲模式设定位。
IDL=0 单片机处于正常工作状态。
IDL=1 单片机进入空闲(Idle)模式,除CPU不工作外,其余仍继续工作,在空闲模式下可由任一个中断或硬件复位唤醒。

 

这个寄存器只有SMOD位与串行口通信有关。系统复位默认为SMOD=0。当为用52单片机的定时器2产生波特率时,波特率不受SMOD影响。用定时器1产生波特率时,不去设置,就默认为0,波特率正常,设置为1时,波特率加倍。

SCON串行口控制寄存器

串行口控制寄存器串行口控制寄存器
SM0,SM1:共同决定串行口工作模式。
SM2:多机通信控制位。在方式0中,SM2一定要等于0。在方式1中,当SM2为1时则只有接收到有效停止位时,RI才置1。在方式2或3中,当SM2为1且接收到的第9位数据RB8为0时,RI才置1。
REN:接收允许控制位。由 软件置位以允许接收,又由 软件清零来禁止接收。
TB8:要发送数据的第9位。在方式2或3中,要发送的第9位数据,根据需要由软件置1或清零软件置1或清零。例如,可约定作为奇偶校验位,或在多机通信中作为区别地址帧或数据帧的标志位。(很少用)
RB8:接收到的数据的第9位。在方式0中不使用RB8。在方式1中,若SM2为0,RB8为接收到的停止位。在方式2或3中,RB8为到的第9位数据。(很少用)
TI:发送中断标志。在方式0中,第8位发送结束时,由 硬件置位。在其它方式的发送停止位前,由 硬件置位。TI置位既表示一帧信息发送结束,同时也向CPU申请中断。可根据需要,用软件查询的方法获得数据已发送完毕的信息,或用中断的方式来发送下一个数据。TI必须用 软件清零
RI:接收中断标志位。在方式0中,当接收完第8位数据后,由 硬件置位。在其它方式中,在接收到停止位的中间时刻由硬件置位(例外情况见对SM2的说明)。RI表示一帧数据接收完毕,可用查询的方法获知或者用中断的方法获知。RI也必须用 软件清零
串行口工作模式串行口工作模式
由于52单片机内部有一个硬件模块,让它自动接收数据,接收完了,通知我们一下就可以了,就不需要再手动配置TB8,RB8。只要配置好SCON,内部就自动帮弄好了。

 

SBUF特殊功能寄存器

特殊功能寄存器特殊功能寄存器
SBUF是指串行口中的两个缓冲寄存器,一个是发送寄存器,一个是接收寄存器,在物理结构上是完全独立的,但地址是重叠的。它们都是字节寻址的寄存器,字节地址均为99H。只要在编写程序时,用不同指令即可操作两个寄存器。
比如,SBUF=A;这个是指将A的数据移入发送寄存器,然后发送寄存器再把数据发送出去。A=SBUF;则是指将接受寄存器中的数据赋值给A。

 

串行口方式1

方式1逻辑图方式1逻辑图
串行口为10位通用异步接口。发送或接收一帧数据信息为10位,包括1位起始位“0”、8位数据位、1位停止位“1”。发送数据:数据从TXD端口输出,当数据写入发送缓冲器SBUF时,就启动发送器发送。发送完一帧数据后,置中断标志TI=1,申请中断,通知CPU可以发送下一个数据了。接收数据:首先使REN=1(允许接收数据),串行口从RXD接收数据,当采样到1至0跳变时,确认是起始位“0”,就开始接收一帧数据,当接收完一帧数据时,置中断标志RI=1,申请中断,通知CPU从SBUF取走接收到的数据 。

 

计算波特率

方式1波特率计算方式1波特率计算
溢出速率即溢出频率,只要算出定时器每溢出一次所需要的时间T,那溢出率就是1/T。计算在没有波特率加倍(SMOD=0)的情况下,波特率为9600bps时怎样赋值计数器。这里说明一下,由于波特率是需要很精确的,不然通信会出错。如果采用定时器工作模式1,采用人工重载,会有较大误差,因为进入中断函数也需要时间,累积时间就会出错。所以这里运用工作模式2,8位自动重装。计数器自动重装不需人工干预,减少误差。
对照上面公式,波特率不加倍,SMOD=0,工作模式2,那n=8,波特率=9600,就可以算出x=253,十六进制为fd。

 

编写步骤

  1. 设置串行口方式
  2. 设置定时器工作模式
  3. 计数器寄存器赋值
  4. 中断寄存器控制
  5. 启动定时器中断
  6. 发送函数
  7. 接收函数

范例1

#include<reg52.h>
unsigned char date;    //定义变量数据中断
bit flag;  //定义变量标志
void send (); //声明发送函数
void receive (); //声明接收函数
void initialize ();    //声明初始化函数
main()
{        
    initialize();  //调用初始化函数
    while(1)
    {
        send();    //调用发送函数
        receive(); //调用接收函数
    }
}
void initialize()  //初始化函数
{
    SCON=0X50;     //0011 0000 串行口工作模式1
    TMOD = 0X20;    //定时器1工作模式2,8为自动重装
    TH1 = 0xFD;     //设定定时初值        波特率为9600
    TL1 = 0xFD;     //设定定时器重装值
    TR1 = 1;        //启动定时器1
    IE=0;
}
void send()         //发送函数
{
    if(flag==1)    //证明已经接收数据
    {   
        SBUF=date;    //将接收的数据发送出去
        while(!TI);   //等待发送中断
        TI=0;         //软件置0
        flag=0;       //清零
    }

}
void receive()       //接收函数
{
    while(!RI);   //等待接收中断
    date=SBUF;    //将收到的数据存进接收缓冲寄存器
    RI=0;        //软件置0
    flag=1;      //将标志位置1
}

范例1是将接受的数据原封不动发送出去。采用查询法,故不需要打开串口中断,不用中断函数。只需不断查询TI,RI的值,就知道数据接收发送情况。

范例2

#include<reg52.h>
unsigned char date;
bit flag;
main ()
{
    SCON=0X50;      //初始化
    TMOD=0X20;
    TH1=0XFD;                                   
    TL1=0XFD;
    IE=0X90;
    TR1=1;
    while(1);
}
void interrupt_uart() interrupt 4     //中断函数
{
    if(RI==1)        //判断有无数据接收
    {
        date=SBUF;     //将寄存器的值赋给变量
        RI=0;          //置0
        flag=1;        //标志位置1
    }
    if(flag==1)             //已接收数据
    {
        SBUF=date;        //将数据发出
        while(!TI);
        TI=0;
        flag=0;
    }
}

与范例1如出一撤,就不详细注释。

总结

在串口通信中,方式1是最常用的,要认真理解方式1。串行数据一位一位的已经不用处理了,52单片机已经有一个模块处理完了。只要知道接收一个数据,产生一次接收中断,要软件置0.,发送一个数据会产生一次发送中断,也要软件置0。自行置0,处理好数据就OK。下一篇将介绍其他三种方式,欢迎关注。

版权声明
本文为[一个人的朝圣]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/juzige/p/10411183.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领