zoukankan      html  css  js  c++  java
  • 串行口通信(一)

    前言

    人类生存于世,少不了沟通。沟通使人类互相认知,传递信息,提升生活品质。但然单片机也需要,不然单单一个机器,无法构成一个系统,发挥更大的力量。只有单片机与外围设备传递信息,互相反馈才会有一个完美的系统。串行口通信就被发明出来,下面来了解串行口通信(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。下一篇将介绍其他三种方式,欢迎关注。

  • 相关阅读:
    练习题
    java关键字
    循环结构
    第一天的学习
    爬虫的简单介绍
    Windows10 专业版秘钥激活
    flask补充
    Flask框架
    小程序登录、授权、支付
    赃读、不可重复读 和 幻读
  • 原文地址:https://www.cnblogs.com/juzige/p/10411183.html
Copyright © 2011-2022 走看看