单片机模板七:串口通信

一、串口通信

1、通信

通信可以分为并行通信和串行通信。

并行通信:数据的各位同时进行发送或接收的通信方式。优点是速率高。缺点是需要的传输线多,成本高,只适合近距离的数据通信。

串行通信:一位一位的按顺序的进行发送或接收的通信方式。优点是需要的传输线少,成本低。缺点是传输的速率慢,适合远距离的数据通信。

2、RXD和TXD

       STC89C52
有两个引脚是专门用来做
UART
串行通信的,一个是
P3.0
一个是
P3.1
,它们还分别有另外的名字叫做 RXD

TXD
,由它们组成的通信接口就叫做串行接口,简称串口。

       图中,
GND
表示单片机系统电源的参考地,
TXD
是串行发送引脚,
RXD
是串行接收引脚。把两个单片机的 GND 相互连接起来,然后单片机 1 的 TXD 引脚接到单片机 2 的 RXD 引脚上,即此路为单片机 1 发送而单片机 2 接收的通道,单片机 1 的 RXD 引脚接到单片机 2 的 TXD 引脚上,即此路为单片机 2 发送而单片机 1 接收的通道。
当单片机
1
想给单片机
2 发送数据时,用二进制表示,采用低位先发,高位后发的原则。

   

3、波特率

       波特率就是发送二进制数据位的速率,习惯上用
baud
表示,即我们发送一位二进制数据的持续时间=1/baud
。在通信之前,单片机
1
和单片机
2
首先都要明确的约定好它们之间的通信波特率,必须保持一致,收发双方才能正常实现通信。

4、常见通讯接口

5、通信的基本类型

常用的通信从传输方向上可以分为单工通信、半双工通信、全双工通信三类。

单工通信就是指只允许一方向另外一方传送信息,而另一方不能回传信息。比如电视遥 控器、收音机广播等;

半双工通信是指数据可以在双方之间相互传播,但是同一时刻只能其中一方发给另外一方。比如我们的对讲机;

全双工通信就发送数据的同时也能够接收数据,两者同步进行。比如我们的电话。

6、UART

51
单片机的
UART
串口的结构由串行口控制寄存器
SCON
、发送和接收电路三部分构成。

STC89C52的UART有四种工作模式:

  •   模式0:同步移位寄存器
  •   模式1:8位UART,波特率可变(常用)
  •   模式2:9位UART,波特率固定
  •   模式3:9位UART,波特率可变
  • 7、寄存器

            这是串口模式配置图,这里有一个很重要的寄存器,就是SBUF寄存器,也叫串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器。

           最左边的就是总线,两个不同意义上的SBUF寄存器使用一个总线连接,从输入端进入就会从输出端出。

    其中的这部分是使用定时器1实现的,主要作用就是控制波特率,也就是我们的数据收发速率。

    下图是串口相关寄存器:

    我们主要介绍SCON和PCON。

    SCON(串行控制寄存器)

    总的来说,如果我们只需要串口发送数据,我们只要把SCON中的SM1置为1,其他的置为0就可以了,这样我们就可以直接配置SCON = 0x40;或者是分开来。如果我们需要串口接收数据,就还要加一个REN = 1,即SCON = 0x50.

    PCON(电源控制寄存器)

    该寄存器较常用到就是SWOD位,知道SWOD就好了。

  • SWOD,波特率选择位,就是用于波特率是否加倍。
    ○ 当用软件置位SMOD,即SMOD=1,则使波特率加倍;
    ○ SMOD=0,则各工作方式的波特率不加倍不变。复位时默认SMOD=0。
  • 8、电平标准

    电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

  • TTL电平:+5V表示1,0V表示0
  • RS232电平:-3~-15V表示1,+3~+15V表示0
  • RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)
  • 二、串口配置

    1、配置SCON寄存器(用于串口初始化)

    以常用的串口模式1(8位UART,波特率可变)来举例。

    在模式1下,SM0和SM1置为0 1。

    REN,可置0 可置1,根据自己代码需求。

    代码:

    SCON = 0x40;     //模式1,REN置0,禁止接受信息

    SCON =0x50     //模式1,REN置1,启用TXD,允许接受信息

    2、配置PCON寄存器(用于串口初始化)

    这里我们只考虑SWOD位,需不需要波特率加倍,如果要加倍,则SWOD位置1。

    代码:

    PCON |= 0x80;      //波特率加倍

    3、串口初始化

    代码:

    //如果使用reg52.h头文件需要提前定义AUXR寄存器 (地址为0x8e)
    void UART_Init()
    {
        TH1=0xFD;//波特率定义
        TL1=0xFD;
        TMOD=0x20;//计时器模式设定
        AUXR=0x00;
        SCON=0x50;//串口初始化
        TR1=1;//打开计时器1
        ES=1;//打开串口中断
        EA=1;//打开总中断
    }

     

    4、配置发送数据函数

    串口发送一个字节数据。

    代码:

    void UART_SendByte(char Byte)
    {
        SBUF=Byte;     //向缓存器中写入数据
        while(TI==0); //等待是否完成
        TI=0;         //TI复位
    }

    5、配置中断函数

    代码:

     //串口中断函数格式
    void (函数名无所谓)() interrupt 4
    {
        中断程序;
    }
     
     
     //中断接收数据函数
    void UART_Routine() interrupt 4
    {
        if(RI==1)            //判断接收中断请求标志位是否置1
        {
            RI=0;            //清空标志位,复位
            UART_Send(SBUF); //查看接受的数据
        }
    }

    6、字符串的发送、接收与判断

    利用指针的移位,一个字节一个字节的发送,直到结束为'\0'。

    代码:

    //字符串发送

    void UART_Sendstring(unsigned char *str)
    {
        while(*str!='\0')
        {
            UART_SendByte(*str++);
        }
    }

    代码:

    //字符串接收

    void UART_Service() interrupt 4
    {
        unsigned char temp=0;
        if(RI==1)
        {
            temp=SBUF;
            RI=0;
            Receive[len++]=temp;
        }
    }

    代码:

    //字符串的判断,以接收Retur为例子n

    void UART_deal(){
        if(len==6)
        {
            if(Receive[0]=='R'&&Receive[1]=='e'&&Receive[2]=='t'&&Receive[3]=='u'&&Receive[4]=='r'&&Receive[5]=='n')
            {
                //执行一定操作
                len=0;
            }
            else{
                len=0;
            }
        }
    }

    7、串口中断

    在具体操作串行口之前,需要对单片机的一些与串口有关的特殊功能寄存器进行初始化设置,主要是设置产生波特率的定时器1,串行口控制,和中断控制,具体步骤如下:

    配置TMOD寄存器:确定定时器的工作模式(T2);
    配置TCON寄存器:定时器的控制开关;
    计数器初值配置:装载TH1,TL1;
    配置SCON寄存器:确定串行口工作模式;
    中断配置:串行口在中断方式时,要进行中断设置(配置IE,I);

    //*串口初始化
    void UART_Init()
    {
        //*TMOD配置:设置定时器模式,
        TMOD&=0x0F;
        TMOD|=0x20;
     
        //*TCON配置
        TR1=1;       //打开定时器
     
        //*计数器配置
        TH1=0xF3;    //设置定时器初始值 1111 0011
        TL1=0xF3;    //设置定时器重装值 1111 0011
        /********************************************
        波特率计算:
        1.二进制的OxF3=十进制的243;
        2.每隔256溢出一次,256-243=13(每计13个数就溢出1次)
        3.12MHz的晶振,每1us计一次数,以上计13个数就会溢出,也就需要13us
        4.溢出率=1/13us=0.07692MHz
        5.波特率=溢出率/16=0.00480769MHz=4807Hz
        6.波特率加倍=波特率*2=9614Hz
        **********************************************/
     
        //*SCON配置 波特率9600hz
        SCON=0x50;     //设置串口工作模式1,SCON=0101 0000
        PCON=0x80;   //设置波特率,SMOD=1,波特率加倍;PCON=1000 0000
     
        //*中断配置
        ES=1;        //打开串行口中断
        EA=1;        //打开总中断   
    }

    三、串口通信的应用

    1、实现发送与接收

           首先hex模式是十六进制模式,当我们用电脑以hex模式给单片机USART口发数据时,发的是十六进制,单片机接收的也是十六进制;当我们用电脑以文本模式给单片机发数据时,只能发字母(0-9,a-z,A-Z等其他符号),单片机收到的也是字母。

    代码:

    #include <reg52.h>

    sfr AUXR = 0x8e;  //定义辅助寄存器
    void Send_Byte(unsigned char dat);  
    //声明数据发送函数(因数据接收函数在发送函数之前要调用发送函数)

    void Select_HC573(unsigned char channel)
    {
        switch(channel)
        {
            case 4:
            P2 = (P2 & 0x1f) | 0x80;
            break;
            case 5:
            P2 = (P2 & 0x1f) | 0xa0;
            break;
            case 6:
            P2 = (P2 & 0x1f) | 0xc0;
            break;
            case 7:
            P2 = (P2 & 0x1f) | 0xe0;
            break;
        }
    }

    //============中断接收函数===============
    void Init_Uart()
    {
        TMOD = 0x20;
        TH1 = 0xfd;
        TL1 = 0xfd;
        TR1 = 1;
        
        SCON = 0x50;  
        AUXR = 0x00;
        
        ES = 1;
        EA = 1;
    }

    unsigned char urdat;
    void Service_Uart() interrupt 4 //数据接收(中断方式)
    {
        if(RI == 1)
        {
            RI = 0;  //软件复位
            urdat = SBUF;
            Send_Byte(urdat);
        }
    }
    //===================================

    void Send_Byte(unsigned char dat)  //数据发送(轮询方式)
    {
        SBUF = dat;  //SBUF 串口数据缓冲寄存器
        while(TI == 0);  //如果数据已发送则TI为1,跳出此循环
        TI = 0;
    }
    void Init_System()
    {
        Select_HC573(5);
        P0 = 0x00;
        Select_HC573(4);
        P0 = 0xff;
    }

    void main()
    {
        Init_System();
        Init_Uart();
        Send_Byte(0x5a);
        Send_Byte(0xa5);
        while(1);
    }


     

    2、控制LED灯

    实现下图功能:

    代码:

    #include <reg52.h>
    sfr AUXR = 0x8e;  //定义辅助寄存器

    void Select_HC573(unsigned char channel)  //锁存器选择函数
    {
        switch(channel)
        {
            case 4: P2 = (P2 &0x1f) | 0x80; break;  //控制LED
            case 5: P2 = (P2 &0x1f) | 0xa0; break;  //控制蜂鸣器、继电器
            case 6: P2 = (P2 &0x1f) | 0xc0; break;  //数码管com共阳公共端
            case 7: P2 = (P2 &0x1f) | 0xe0; break;  //数码管段码端
        }
    }

    //==============串口数据接收中断===================
    void Init_Uart()  //中断初始化函数
    {
        TMOD = 0x20;  //定时器1
        TH1 = 0xfd;  //设置波特率为9600
        TL1 = 0xfd;  //11.0592M或12M的12分频
        
        SCON = 0X50;  //串口参数为模式1且允许接收
        AUXR = 0x00;  //bit7=1:定时器1不分频,0则12分频
        
        TR1 = 1;  //启动定时器1
        ES = 1;  //使能串口中断
        EA = 1;  //打开总中断
    }

    unsigned char command = 0x00;  //注意赋初值为16进制
    void Service_Uart() interrupt 4  //中断服务函数
    {
        if(RI == 1)  //收到一个完整字节
        {
            command = SBUF;  //将SBUF缓冲器中数据赋值给command
            RI = 0;  //人工清零
        }    
    }
    //=================================================

    void SendByte(unsigned char dat)  //发送数据函数
    {
        SBUF = dat;  //将数据放入SBUF缓冲器
        while(TI == 0);  //如果成功发送数据,则TI为1跳出此循环
        TI = 0;  //人工清零
    }

    void  SendString(unsigned char *str)  //发送字符串函数
    {
        while(*str != '\0')  //判断指针是否指向字符串结束符
        {
            SendByte(*str++);  //先执行SendByte(*str),赋值完成后,指针++移向下一位
        }
    }
        
    void Working()  
    {
        Select_HC573(4);  //控制LED
        if(command != 0x00)
        {
            switch(command & 0xf0)  //高4位不变,低4位清零
            {
                case 0xa0:
                    P0 = (P0 | 0x0f) & (~command | 0xf0); 
                    command = 0x00;  //避免重复执行working函数
                break;
                
                case 0xb0:
                    P0 = (P0 | 0xf0) & ((~command << 4)| 0x0f); 
                    command = 0x00;
                break;
                
                case 0xc0:
                    SendString("The System is Working Normally…");
                    command = 0x00;
                break;
            }
        }
    }    

    void Init_System()  //程序初始化
    {
        Select_HC573(5);
        P0 = 0x00;
        Select_HC573(4);
        P0 = 0xff;
    }

    void main()
    {
        Init_Uart();
        Init_System();
        SendString("Welcome to the System…\r\n");
        while(1)
        {
            Working();
        }
    }

     

    作者:lmp_041018

    物联沃分享整理
    物联沃-IOTWORD物联网 » 单片机模板七:串口通信

    发表回复