STC89C51——串行通信、串口介绍及配置

 前言

        本文介绍基于常见的51单片机,即如下图的芯片:

  •  AT89C51具备一个全双工串行通信接口。设有2个相互独立的接收、发送缓冲器,可以同时发送和接收数据。
  • 两个缓冲器(SBUF)共用一个物理地址即99H。如果CPU写SBUF,数据就会被送入发送寄存器准备发送;如果CPU读SBUF,则读入的数据来自接收缓冲器。发送缓冲器只能写入而不能读出,接收缓冲器只能读出而不能写入。
  •  串行通信设有4种工作方式,工作方式0和2波特率固定,方式1和3波特率可变。因为平时使用串口一般用于单片机和电脑调试,即使用串口打印功能,因此本文介绍工作方式1。其它工作方式的话很少用到就不做过多介绍了。
  • 一、串口相关寄存器介绍

    1.SCON : 串行控制寄存器

    位地址 9FH 9EH 9DH 9CH 9BH 9AH 99H 98H
    SCON SM0 SM1 SM2 REN TB8 RB8 T1 R1

            下面对寄存器控制位进行介绍:

    ①SM0和SM1

            两者不同的组合即可配置串行通信的工作方式,直接给出手册中的截图供各位参考

             波特率计算中的SMOD会在后文介绍,其作用是控制波特率倍增。

    ②SM2:多机控制位。在工作方式1时,该位置1的话,则在接收到有效的停止位时才至中断请求  . .             标志位RI为1,一般使用工作方式1的情况下,该位配置为0。

    ③REN:允许/禁止串行接收控制位。该位置1时,允许串行接收;为0时禁止接收。

    ④TB8:(与工作方式1无关,在此不介绍)

    ⑤RB8:在方式1中,若SM2=0,则RB8是接收到的停止位。

    ⑥TI:发送中断请求标志位。工作方式1下,停止位开始发送时由内部硬件置位为1,需要软件清0

    ⑦RI:接收中断请求标志位。工作方式1下,停止位接收的中间时刻由内部硬件置位为1,需要软件清0

               在实际应用工作方式1时,单片机如果只负责发送,则寄存器配置语句为:

    SCON=0x40 ;

    单片机如果负责发送和接收,则寄存器配置语句为:

    SCON=0x50 ;

    2.PCON:电源控制寄存器

    D7 D6 D5 D4 D3 D2 D1 D0
    PCON SMOD X X X X X X X

            对于PCON寄存器,只需要掌握该寄存器的最高位SMOD位,该位在工作方式1-3中,如果SMOD=1,串行口波特率加倍;SMOD=0,波特率不变。

            在实际应用工作方式1时,如果单片机不需要波特率加倍,则无需配置该寄存器。

            若波特率需要加倍,则寄存器配置语句为:

    PCON |= 0x80;

            

    3.波特率的计算

             波特率由内部定时器产生,配置波特率时需要配置定时器。

             以下波特率计算式基于工作方式1,定时器工作方式2。定时器方式2为8位自动重装定时器,其对于其它方式可以减小软件重装载产生的误差,一般应用中,也是这样的组合。

                    BPS=\frac{2^{SMOD}\cdot f}{32\cdot 12\cdot \left ( 256-T \right )}

            其中 BPS 为波特率数值,f 为单片机晶振值,T 为定时器装载初值。实际配置中,需要知道单片机的晶振、预设的波特率,然后通过计算式求出定时器初值即可。

            下面举例说明更浅显易懂:

            假设在工作方式1下,定时器工作方式2,波特率为9600且无需增倍,单片机晶振为11.0592Mhz,单片机定时器初值为:

            9600=\frac{2^{0}\cdot 11.0592\cdot 10^{6}}{32\cdot 12\cdot \left ( 256-T \right )}

    则 T = 253,转换为十六进制为0xFD

            在程序中,定时器初值配置语句为:

    TH1 = TL1 =0xFD;

    二、程序设计

            目前单片机一般用的是12M晶振和11.0592M晶振,晶振不同,配置波特率定时器的初始值就不同,下面介绍以上两种晶振下的代码示例。

            需要注意的是12M晶振不适合4800以上的波特率,可用2400或4800,因为超过4800会出现较大误差。但是11.0592M的晶振就不受那么多限制,因为其本身的值代入波特率计算公式中能得到一个整数。

            示例代码的功能是串口调试助手向单片机发送什么内容,单片机就返回什么内容,需要注意串口助手要勾选发送新行。

    1. 12M晶振下 2400 / 4800 波特率(已验证)

    #include <REGX51.H>
    #include "string.h"
    #include "intrins.h"
    unsigned char Data[32];
    
    void USART_SendByte(unsigned char byte)
    {
    	   SBUF = byte;
    	   while(!TI);
    	   TI = 0;
    }
    void USART_SendStr(unsigned char *p)
    {   
            while( *p != '\0')
            {			
                USART_SendByte(*p);            
                p++;            
            }            
    }
            
    void USART( ) interrupt  4
    {    
        static unsigned char i=0;
    	if(RI==1)
    	{	RI=0;
    		if(i>=32)   //收到字符串超过数组长度
    		{ 
    			i=0;
    			USART_SendStr("Data overflow !\r\n");
    			memset(Data,0x00,sizeof(Data)); //数组清零
    		}
            else
            {
    			Data[i++]=SBUF;
    			if( (Data[i-1]=='\n')  && (Data[i-2]=='\r') )  //判断结尾字符为回车换行符
    			{
    				USART_SendStr(Data) ;   //将收到的内容发送出去
                    memset(Data,0x00,sizeof(Data));//数组清零
                    i=0;                 
    			}
            }
    	}   
    }
    
    void main( )
    {
    	TMOD=0x20;	//定时器1工作方式2
    	TH1=0XE6;	//当前是2400波特率 , F3是4800波特率
    	TL1=0XE6;	//当前是2400波特率 , F3是4800波特率 
    	SCON=0X50;	//串口工作方式1,接收REN位置1
    	PCON=0x80;	//波特率加倍!
    	TR1=1;		//开启定时器1
    	ES=1;		//开启串口中断
    	EA=1;		//允许总中断
    	while(1);
    }

    2. 11.0592M晶振下 9600 波特率(因无设备原因未验证)

    #include <REGX51.H>
    #include "string.h"
    #include "intrins.h"
    unsigned char Data[32];
    
    
    void USART_SendByte(unsigned char byte)
    {
    	   SBUF = byte;
    	   while(!TI);
    	   TI = 0;
    }
    void USART_SendStr(unsigned char *p)
    {   
            while( *p != '\0')
            {			
                USART_SendByte(*p);            
                p++;            
            }            
    }
            
    void USART( ) interrupt  4
    {    
        static unsigned char i=0;
    	if(RI==1)
    	{	RI=0;
    		if(i>=32)   //收到字符串超过数组长度
    		{ 
    			i=0;
    			USART_SendStr("Data overflow !\r\n");
    			memset(Data,0x00,sizeof(Data)); //数组清零
    		}
            else
            {
    			Data[i++]=SBUF;
    			if( (Data[i-1]=='\n')  && (Data[i-2]=='\r') )  //判断结尾字符为回车换行符
    			{
    				USART_SendStr(Data) ;   //将收到的内容发送出去
                    memset(Data,0x00,sizeof(Data));//数组清零
                    i=0;                 
    			}
            }
    	}   
    }
    
    void main( )
    {
    	TMOD=0x20;	//定时器1工作方式2
    	TH1=0XFD;	//@11.0592M,9600波特率
    	TL1=0XFD;	//@11.0592M,9600波特率
    	SCON=0X50;	//串口工作方式1,接收REN位置1
    	TR1=1;		//开启定时器1
    	ES=1;		//开启串口中断
    	EA=1;		//允许总中断
    	while(1);
    }

    物联沃分享整理
    物联沃-IOTWORD物联网 » STC89C51——串行通信、串口介绍及配置

    发表评论