zoukankan      html  css  js  c++  java
  • MCU 51-6 Serial Communication

    串行通信基础知识

    计算机与外界的信息交换称为通信。通信的基本方式可分为并行通信和串行通信两种。

    并行通信是指数据的各位同时在多根数据线上发送或接收。

    并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。

    串行通信是指 使用一条数据线,将数据一位一位地依次传输,每 一位数据占据一个固定的时间长度。其只需要少数几条线就可以 在系统间交换信息,特别适用于计算机与计算机、计算机与外设 之间的远距离通信。

    串行通信的特点:传输线少,长距离传送时成本低,但数据的传送控制比并行通信复杂。

    异步通信与同步通信

    异步通信 异步通信是指通信的发送与接收设备使用各自的时钟控制数据 的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。

    异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的时间间隔是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。

    同步通信 同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。

    串行通信的制式

    在串行通信中,数据是在两个站之间传送的。按照数据传送方向,串行通信可分为三种制式。

    传输速率

    比特率是每秒钟传输二进制代码的位数, 单位是:位/秒(bps)。如每秒钟传送 240个字符,而每个字符格式包含10位(1 个起始位、1个停止位、8个数据位),这 时的比特率为: 10位×240个/秒 = 2400 bps

    串行接口的结构

    在逻辑上,SBUF只有一个,它既表示发送寄存器,又表示接收寄存器,具有同一个单元地址99H。

    但在物理结构上,则有两个完全独立的SBUF,一个是发送缓冲寄存器SBUF,另一个是接收缓冲寄存器SBUF。

    如果CPU写SBUF,数据就会被送入发送寄存器准备发送;如果CPU读SBUF,则读入的数据一定来自接收缓冲器。即CPU对SBUF的读写,实际上是分别访问上述两个不同的寄存器。 a = SBUF; SBUF = a;

    串行控制寄存器SCON 地址98H

    用于设置串行口的工作方式、监视串行口的工作状态、控制发送与接收的状态等。它是一个既可以字节寻址又可以位寻址的8位特殊功能寄存器。其格式如下图。

    REN:串行接受允许控制位。该位由软件置位或复位。当REN=1,允许接收;当REN=0,禁止接收。

    RI:接收中断标志位。RI=1,表示一帧数据接收结束。可由软件查询RI位标志,也可以向CPU申请中断。 注意:RI在任何工作方式下也都必须由软件清0。

    TI:发送中断标志位。TI=1,表示已结束一帧数据发送,可由软件查询TI位标志,也可以向CPU申请中断。 注意:TI在任何工作方式下都必须由软件清0。

    串行发送中断TI和接收中断RI的中断入口地址是同是0023H,因此在中断程序中必须由软件查询TI和RI的状态才能确定究竟是接收还是发送中断,进而作出相应的处理。单片机复位时,SCON所有位均清0。

    SM0和SM1为工作方式选择位,可选择四种工作方式:

    电源控制寄存器PCON

    SMOD:在串行口工作方式 1、2、3 中, 是波特率加倍位 (产生高波特率时启用平时不用,比如用11.0592晶振产生57600波特率时就要设置成SMOD=1

    =1 时,波特率加倍(PCON=0x80;)

    =0 时,波特率不加倍。(PCON=0x00;) (在PCON中只有这一个位与串口有关)

    中断允许控制寄存器IE(A8H)

    ES,串行口中断允许位;=0 时禁止中断; =1 时允许中断。

    波特率的计算

    在串行通信中,收发双方对发送或接收数据的速率要有约定。通过软件可对单片机串行口编程为四种工作方式,其中方式0和方式2的波特率是固定的,而方式1和方式3的波特率是可变的,由定时器T1的溢出率来决定。

    方式0的波特率 = fosc/12

    方式2的波特率 =(2^SMOD/64)· fosc

    方式1的波特率 =(2^SMOD/32)·(T1溢出率)

    方式3的波特率 =(2^SMOD/32)·(T1溢出率)

    当T1作为波特率发生器时,最典型的用法是使T1工作在自动再装入的8位定时器方式(即方式2,且TCON的TR1=1,以启动定时器)。这时溢出率取决于TH1中的计数值。

    T1 溢出率 = fosc /{12×[256 -(TH1)]}

    常用串口波特率: 300、600、1200、2400、4800、9600、19200 ……115200;

    串行口工作之前需对相关寄存器进行配置,设定其工作模式。

    (1)设置T1的工作方式(编程TMOD寄存器);

    (2)计算T1的初值,装载TH1、TL1;

    (3)启动T1(编程TCON中的TR1位);

    (4)确定串行口控制(编程SCON寄存器);

    如需串行口在中断方式工作时,要进行中断设置编程IE寄存器。

    串行通信接口标准

    一、RS-232C接口 RS-232C是EIA(美国电子工业协会)1969年修订RS-232C标准。RS-232C定义了数据终端设备(DTE)与数据通信设备(DCE)之间的物理接口标准。

    1、机械特性 RS-232C接口规定使用25针连接器,连接器的尺寸及每个插针的排列位置都有明确的定义。(阳头)

    2、功能特性

     

    典型的RS-232C通信电路   解决电平不兼容的问题

    将计算机的232电平(逻辑‘1’ 是+3V to +15V,逻辑‘0’是 -3V to -15V)转换为单片机TTL电平(0V to 0.8V,逻辑‘1’ 是+3V to +5V,逻辑‘0’是 0V to 0.8V )

    串口的发送(给计算机发送数据):

    #include <reg52.h>
    
    #define uchar unsigned char
    #define uint  unsigned int
    
    uchar num;
    
    void delay(uint z)
    {
        uint x,y;
        for(x = z; x > 0; x--)
            for(y = 114; y > 0 ; y--);
    }    
    void UART_init()
    {
        TMOD = 0x20;      //T1工作模式2  8位自动重装
    //波特率:9600 TH1=256- 11.0592*10^6/(9600*32*12)=253 = 0xFD TH1 = 0xfd; TL1 = 0xfd; //比特率9600 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 REN = 1; //串口允许接收 } void main() { UART_init(); //串口初始化 while(1)
    {
    SBUF = num; //发送数据给计算机
    while(!TI);//发送成功
    TI= 0; //软件清中断
    num++;
    delay(500);
    } }
    单片机不停地给计算机发送数据:

    串口的接收(计算机发送数据给单片机 并点亮第一个灯):

    #include <reg52.h>
    
    #define uchar unsigned char
    #define uint  unsigned int
    
    uchar num;
    
    void delay(uint z)
    {
        uint x,y;
        for(x = z; x > 0; x--)
            for(y = 114; y > 0 ; y--);
    }    
    void UART_init()
    {
        TMOD = 0x20;      //T1工作模式2  8位自动重装
        TH1 = 0xfd;
        TL1 = 0xfd;     //比特率9600
        TR1 = 1;        //启动T1定时器
        SM0 = 0;
        SM1 = 1;         //串口工作方式1 10位异步
        REN = 1;        //串口允许接收
    }
    void main()
    {
        UART_init(); //串口初始化
        while(1)
        {
          while(!RI);//接收成功
          P1 = SBUF; //接收计算机发的数据,并给P1 IO
          RI= 0; //软件清中断
        }   
    }

    利用串口中断进行计算机与串口通信:

    #include <reg52.h>
    
    #define uchar unsigned char
    #define uint  unsigned int
    
    uchar num;
    
    void UART_init()
    {
        TMOD = 0x20;      //T1工作模式2  8位自动重装
        TH1 = 0xfd;
        TL1 = 0xfd;     //比特率9600
        TR1 = 1;        //启动T1定时器
        SM0 = 0;
        SM1 = 1;         //串口工作方式1 10位异步
        REN = 1;        //串口允许接收
        EA = 1; //开总中断
        ES =1; //开串口中断
    }
    
    void main()
    {
        UART_init(); //串口初始化
        while(1);
    }
    
    void UART() interrupt 4
    {
      if(RI)//如果计算机发送给电片机数据成功
      {
        num = SBUF;//将数据取出来
        P1 = num;
        num ++;
        RI =0;
        SBUF = num;//将数据在单片机端送到Buff,给计算机
        while(!TI);  //发送完成
        TI = 0//清中断
      }
    }

    串口接收一字节数码管以十进制显示

    /*
    以4800bps从计算机发任意一字节数据,通过数码管以十进制显示
    的形式显示出来。
    */
    #include <reg52.h>
    
    #define uchar unsigned char
    #define uint  unsigned int
    
    sbit we = P2^7;    //数码管位选
    sbit du = P2^6;    //数码管段选
    
    /*数码管段码*/
    uchar code leddata[]={ 
     
                    0x3F,  //"0"
                    0x06,  //"1"
                    0x5B,  //"2"
                    0x4F,  //"3"
                    0x66,  //"4"
                    0x6D,  //"5"
                    0x7D,  //"6"
                    0x07,  //"7"
                    0x7F,  //"8"
                    0x6F,  //"9"
                    0x77,  //"A"
                    0x7C,  //"B"
                    0x39,  //"C"
                    0x5E,  //"D"
                    0x79,  //"E"
                    0x71,  //"F"
                    0x76,  //"H"
                    0x38,  //"L"
                    0x37,  //"n"
                    0x3E,  //"u"
                    0x73,  //"P"
                    0x5C,  //"o"
                    0x40,  //"-"
                    0x00,  //熄灭
                    0x00  //自定义
     
                             };
    
    /*1毫秒延时函数*/
    void delay(uint z)    
    {
        uint x,y;
        for(x = z; x > 0; x--)
            for(y = 114; y > 0 ; y--);
    }
    
    /*
    串口初始化函数
    工作模式1 10位异步收发 发送速率4800bps
    */
    void UART_init()  
    {
        TMOD = 0x20;      //T1工作模式2  8位自动重装
        TH1 = 0xfa;
        TL1 = 0xfa;     //比特率4800
        TR1 = 1;        //启动T1定时器
        SM0 = 0;
        SM1 = 1;         //串口工作方式1 10位异步
        REN = 1;        //串口允许接收
    }
    
    /*3位数码管显示函数*/
    void display(uchar num)
    {
        uchar bai,shi,ge;
        bai = num / 100; //求模
        shi = num % 100 / 10; //求余100后求出有多少个10 
        ge  = num % 10; //求余
        
        P0 = 0xff;    //清除断码
        we = 1;
        P0 = 0xfe;     //点亮第一位数码管
        we = 0;
    
        du = 1;
        P0 = leddata[bai];    //显示百位
        du = 0;
        delay(1);
    
        P0 = 0xff;    //清除断码
        we = 1;
        P0 = 0xfd;//点亮第二位数码管
        we = 0;
    
        du = 1;
        P0 = leddata[shi];    //显示十位
        du = 0;
        delay(1);
    
        P0 = 0xff;    //清除断码
        we = 1;
        P0 = 0xfb;//点亮第三位数码管
        we = 0;
    
        du = 1;
        P0 = leddata[ge];  //显示各位
        du = 0;
        delay(1);    
    }
    
    void main()
    {
        UART_init();//串口配置初始化
        while(1)
        {
            if (RI)    //检测是否接收完成
            {
                RI = 0;    //清除接收标志位,以便于下次接收
            }
            display(SBUF); //取出接收SBUF的值赋给数码管显示
        }
    }

    发送矩阵键盘的按键值到计算机

    /*
    把矩阵键盘的键值以2400bps上传到计算机串口助手
    */
    #include <reg52.h>
    
    #define uchar unsigned char
    #define uint  unsigned int
    
    /*1毫秒延时函数*/
    void delay(uint z)    
    {
        uint x,y;
        for(x = z; x > 0; x--)
            for(y = 114; y > 0 ; y--);
    }
    
    /*
    串口初始化函数
    工作模式1 10位异步收发 发送速率2400bps
    */
    void UART_init()  
    {
        TMOD = 0x20;      //T1工作模式2  8位自动重装
        TH1 = 0xf4;
        TL1 = 0xf4;     //比特率2400,计算公式256-11059200/2400/32/12
        TR1 = 1;        //启动T1定时器
        SM0 = 0;
        SM1 = 1;         //串口工作方式1 10位异步
    //    REN = 1;        //串口允许接收
    }
    
    /*
        4*4矩阵键盘扫描函数
        带返回值,返回键值码
    */
    uchar KeyScan()
    {
        uchar cord_l,cord_h;//声明列线和行线的值的储存变量
        P3 = 0xf0;//1111 0000
        if( (P3 & 0xf0) != 0xf0)//判断是否有按键按下
        {
            delay(5);//软件消抖
            if( (P3 & 0xf0) != 0xf0)//判断是否有按键按下
            {
                  cord_l = P3 & 0xf0;// 储存列线值
                  P3 = cord_l | 0x0f;
                  cord_h = P3 & 0x0f;// 储存行线值
                  while( (P3 & 0x0f) != 0x0f );//松手检测
                  return (cord_l + cord_h);//返回键值码
            }    
        }
            
    }
    
    /*
        4*4矩阵键盘键值码处理函数
        返回转换后的键值码
    */
    uchar KeyPro()
    {
        uchar key_value; //存放转换后的按键值
        switch( KeyScan() )
        {
             //第一行键值码
            case 0xee: key_value = 0x01;        break;
            case 0xde: key_value = 0x02;        break;
            case 0xbe: key_value = 0x03;        break;
            case 0x7e: key_value = 0x04;        break;
            
            //第二行键值码
            case 0xed: key_value = 0x05;        break;
            case 0xdd: key_value = 0x06;        break;
            case 0xbd: key_value = 0x07;        break;
            case 0x7d: key_value = 0x08;        break;
    
            //第三行键值码
            case 0xeb: key_value = 0x09;        break;
            case 0xdb: key_value = 0x0a;        break;
            case 0xbb: key_value = 0x0b;        break;
            case 0x7b: key_value = 0x0c;        break;
    
            //第四行键值码
            case 0xe7: key_value = 0x0d;        break;
            case 0xd7: key_value = 0x0e;        break;
            case 0xb7: key_value = 0x0f;        break;
        case 0x77: key_value = 0x10;        break;
        }
        return (key_value);//返回转换后的键值码    
    }
    
    
    void main()
    {
        UART_init();//串口初始化
        while(1)
        {
            SBUF = KeyPro();//调用带返回值的键值码转换函数,把转换后的键值码送入发送SBUF
            while(!TI);        //检测是否发送完毕
            TI = 0;            //清楚发送完毕标志位,已便于下次发送
        }
    }
    
    
    #include <reg52.h>
    #include <stdio.h>
    
    typedef unsigned int  INT16U;
    typedef unsigned char INT8U;
    
    void Delay_Ms(INT16U z)
    {
        INT16U x,y;
        for(x = z; x > 0; x--)
            for(y = 114; y > 0 ; y--);
    }    
    
    void UART_Init()
    {
        TMOD |= 0x20;      //T1工作模式2  8位自动重装
        TH1 = 0xfd;
        TL1 = 0xfd;     //比特率9600
        TR1 = 1;        //启动T1定时器
        SM0 = 0;
        SM1 = 1;         //串口工作方式1 10位异步
    }
    
    void main()
    {
        UART_Init(); //串口初始化
        while(1)
        {
            TI = 1;
            puts("大家好!欢迎学习单片机");
            while(!TI);
            TI = 0;
            Delay_Ms(1000);
        }    
    }
    /*************************************
    备注:
    使用printf()puts()前都应软件置位TI = 1;
    printf()puts()  使用putchar函数发送字节
    
    void putchar(uchar sbyte )
    {
        while(!TI);   //等待发送完
    
        SBUF=sbyte;
    
    }
    
    所第调用putchar前没TI=1 永远等待
    *************************************/


    51单片机的晶振选取为什么是11.0592MHz,而不直接选取12MHz?
    由波特率计算公式计算时,12MHz计算时除不尽,有较大误差,所以选用11.0592MHz的晶振对于串口通信的通信速率很重要。
    如:
    11.0592MHz晶振:256-11059200/2400/32/12 = 256-12=244
    12MHz晶振:256-12000000/2400/32/12 =256-13.020833333333...

    通信双方波特率如果不同,通信传输数据就会错乱



  • 相关阅读:
    洛谷P2330: [SCOI2005]繁忙的都市(最小生成树)
    洛谷P1019: 单词接龙(dfs)
    洛谷P1019: 单词接龙(dfs)
    hdu1598:find the most comfortable road(并查集)
    hdu1598:find the most comfortable road(并查集)
    洛谷P1616 :疯狂的采药(完全背包)
    洛谷P1616 :疯狂的采药(完全背包)
    洛谷P1119: 灾后重建(最短路)
    洛谷P1119: 灾后重建(最短路)
    1008 N的阶乘 mod P(51NOD基础题)
  • 原文地址:https://www.cnblogs.com/darren-pty/p/13285994.html
Copyright © 2011-2022 走看看