51单片机学习----串口通讯(UART)

前言

STC89C52RC系列单片机内部集成有一个功能很强大的全双工串口通信口。

与传统8051单片机的串口完全兼容;

有两个互相独立的的发送和接收缓冲器,可以同时收数据和发数据;

那么问题来了,什么是串行通信?什么是全双工?什么是发送缓冲器?什么是接收缓冲器?

还有一个比较重要的事,要想搞明白串口通讯的原理,必须先把单片机的定时器学明白,尤其定时器的8位自动重装工作模式。

什么是串行通讯?

就是把数据串成一串,然后一个一个BIT的发出去,

比如要发送数据0xAA,转换成二进制就是1010 1010,按照BIT7、BIT6、BIT5、BIT4、BIT3、BIT2、BIT1、BIT0的顺序发出去。

什么是全双工?

与全双工相对的是半双工,全双工指的是发送数据的同时也可以接收数据,接收数据的时候也可以发送数据。半双工指的是发送数据的时候不准接收数据,接收数据的时候不能发送数据。

打个比方,边吃饭边拉屎那就是全双工;吃饭的时候不能拉屎,拉屎的时候不能吃饭那就是半双工。

什么是发送缓冲器?

首先这个缓冲器是一个寄存器,它是起缓冲作用的。单片机把要发送出去的数据,先准备好,放在这个缓冲器里面,这个数据是一个字节大小的,也就是8个BIT。当数据准备就绪之后,就可以发送出去。这个缓冲器跟枪的枪膛有点像,子弹上膛的过程就跟数据放入缓冲器的过程相似,只要扣动扳机子弹就发射出去了,同理数据发送出去也有个类似的“扳机”。

什么是接收缓冲器?

接收外面发过来的数据,也是先存在接收缓冲器里面,然后CPU取走,这个接收缓冲器有点类似外卖柜,外卖员把外面送过来的时候,不是直接送你手上,而是放在外卖柜中,然后你去外面柜中取走外卖。

发送缓冲器和节数缓冲器在单片机里面的名字是同一个SBUF,地址也是同一个。

什么是波特率

串行通信有4种工作模式:又两种是固定的波特率,有两种是可变的波特率。那么问题由来了,先不说通讯模式的事,先告诉我什么是波特率?为什么又要引入波特率这个东西?

波特率 Baud Rate,一般简称Baud,意思就是每秒几个码元,码元这个字太拗口,位了解释一个名词又引入一个新的名词,这是很不对的,其实一般情况下可以把码元理解为BIT,波特率就可以理解为每秒发送多少个BIT。

我们要明白一个事情,数据发送,不是发送给空气的,肯定有一个接收数据的对象;同理数据接收,肯定有一个数据发送方。你说发过来的是0xAA,那数据发送的时候就是一串波形,我怎么去识别,那就要告诉对方每个BIT数据的宽度是多少?那个BIT数据的宽度就是波特率。比如波特率9600,就表示每秒能发9600给BIT。这样发送和接收的双方就知道每个BIT的宽度是多少了。

发送与接收引脚

STC89C51RC系列单片机的接收引脚P3.0/RxD

STC89C51RC系列单片机的发送引脚P3.1/TxD

这个P3.0/RxD就表示P3.0引脚有两个功能,普通GPIO功能和串口接收功能;

这个P3.1/TxD就表示P3.1引脚有两个功能,普通GPIO功能和串口发送功能;

怎么理解?拿起枪就是兵,放下枪就是民,人还是那个人;引脚还是那个引脚。

串口相关寄存器

串口控制寄存器器(SCON)

SCON == Serial Control 用于选择串行通讯的工作方式和某些控制功能,这个某些有点那个啥了

SM0/FE:这一看就有两个功能,SM0功能和FE功能,FE就是帧错误检测功能,平时用的不多,就不要花精力来解释了,越解释越糊涂;主要还是用它的SM0功能吧,既用于串行口工作模式的设定,要想用这个功能,PCON的Bit6,既SMOD0的值必须为0。

SM1:与SM0组合使用用来确定串口的工作方式

STC89C5RC系列单片机工作在12T模式,既1个机器周期等于12个时钟周期,定时器1工作在8位自动重装模式,所以定时器1的溢出率 = SYSCLK/12/(256-TH1)。

SM2:允许方式2或方式3多机通讯控制,多机通讯用得少,有机会再展开。

REN:串行接收控制位,由软件来控制,当REN=1时,允许串口接收数据,这是启用串行接收器RxD,就可以接收数据了,当REN=0,禁止串口接收数据。

TB8:在方式2和方式3,它就是要发送的第9位数据,由软件来写1或者写0。

RB8:在方式2或方式3,它就是要接受的第9位数据,如果工作在方式1,若SM2=0,RB8就是接收到的停止位。

TI:串口发送中断请求标志位,在方式0的时候,第8位数据发送接收之后,单片机自己就把TI位置1,即TI= 1,并向CPU请求中断,中断响应之后必须用软件来复位,就是在代码中让TI=0。在方式1/2/3,停止位开始发送时,单片机自己就把TI位置1,中断响应之后必须用软件来复位。

RI:串口接收中断请求标志位,在方式0,当串行接收到第8位结束时,由内部硬件自动置位RI=1,向主机请求中断,响应中断后必须软件复位。在方式1/2/3串行接收到停止位的中间时刻由内部硬件置位,即RI=1,响应中断后必须软甲复位。

SCON的所有位都可以通过复位变成0,每位都可以位寻址。

电源控制寄存器(PCON)

PCON == Power Control

SMOD:设置串口的波特率是否加倍,SMOD=1,串行通讯1/2/3的波特率加倍;SMOD=0,波特率不加倍。

SMOD0:设置为SMOD0=0,位了让SCON的SM0起作用。

其他的位跟串口没什么关系,就不说了。

中断使能寄存器(IE)

IE == Interrupt Enable

EA:中断总开关

ES:串口中断开关

中断优先级寄存器高(IPH)

中断优先寄存器低(IP)

从地址屏蔽(SADEN):多机通讯使用

从地址(SADDR):多机通讯使用

串口缓存寄存器(SBUF)

SBUF == Serial Buffer

STC89C51RC系列单片机的串行口缓冲器SBUF地址是99H,实际上由2个缓冲器,一个是发送缓冲器,也叫发送寄存器,只能写数据进去;一个是接收缓冲器,也叫接收寄存器,只能读里面的数据。

串行通道内有一个数据寄存器

串口方式1的功能和结构示意图

发送过程:数据由串行发送端TxD输出。

1、当CPU执行一条写“SBUF”的指令就启动串口的发送;

2、写“SBUF”信号的同时还把1装入发送移位寄存器的第9位,因为第9位就是停止位;

并通知TX控制单元开始发送。发送每个BIT的定时由16分频计数器同步;

3、移位寄存器将数据不断的右移,送到TxD端口发送,右移的过程中,左边的空位就来填入0作补充;

4、当数据的最高位移到输出位置的时候,紧跟其后的是第9位,也就是停止位,这个时候TX控制单元作最后一次移位输出,然后让发送允许信号失效,完成一帧数据的发送;

5、这个时候将中断请求标志位TI置1,TI=1,向主机请求中断处理

接收过程:

1、软件将接收允许位REN置1,REN=1,接收器便以设定的波特率去采样接收端口RxD;

2、当检测到RxD端口从1变为0的跳变时,就启动接收器准备接收数据;并立即复位16分频计数器,将1FFH的值写入移位寄存器。(复位16位分频计数器是为了与输入位的时间同步)

3、16分频计数器的16个状态将每个BIT的接收时间分成16份,在每个BIT时间的7、8、9份由检测器对RxD端口进行采样,所采样到的3个值,要么为0,要么为1,只要三个值有两个以上为1,那这个BIT就是1,如果三个值有两个以上是0,那这个BIT就是0;

4、接收数据从接收移位寄存器的右边移入,已装入1FFH向左边移出,当起始位0移到移位寄存器的最左边时,使Rx控制器作最后一次移位,完成一帧数据的接收,

5、如果RI=0,且SM2=0(或接收到的停止位位1),则接收到的数据是有效的,实现装载入SBUF,停止位进入RB8,置位RI,既RI= 1,向主机请求中断,如果RI=0,且SM2=0(或接收到的停止位位1)这两个条件有一个不满足,那就收到的数据就作废,重新开始接收

串口模式3是9位UART,比模式1多一位,多的这一位就是校验位,由TB8提供。

C语言实现


#include <STC89C5xRC.H>
#include "intrins.h"

#define FOSC    11059200L  //单片机的时钟频率
#define BAUD    9600  //串口通讯的波特率

bit     busy;

unsigned char Rx_Buffer[5] = {0};
//unsigned char Tx_Buffer[5] = {0};
unsigned char i;
unsigned char j;

void SendData(unsigned char dat);
void SendString(char *s);
unsigned char SendNByte(unsigned char *p, unsigned char n);
void Delay1000ms();

void main()
{
    
    SCON = 0x50;  //SCON==0101 0000,  模式1,8 bit UART,接收使能
    TMOD = 0x20; //定时器1设置为8位自动重装模式
    
    /*
    TH1 = TL1 = -(FOSC/12/32/BAUD)
    等同于TH1 = TL1 = 256 -(FOSC/12/32/BAUD);
    */
    TH1 = TL1 = -(FOSC/12/32/BAUD); //设定自动重装值
    
    TR1 = 1;//定时器1开始运行
    ES  = 1;//串口中断使能
    ET1 = 0;//定时器中断关闭
    EA  = 1;//中断总开关使能
    
    
    while(1)
    {
        if(Rx_Buffer[0] == 1) //检查接收到的第一个数据是不是1,
        {
            SendNByte(Rx_Buffer, 5); //把接收到的5个数据发出去
            for(j=0;j<5;j++)  //把Rx_Buffer清零
            {
                Rx_Buffer[j] = 0;
            }
        }
        
        Delay1000ms();
    }
}

void Uart_Isr()    interrupt 4
{
    if(RI)
    {
        RI = 0; //接收中断标志位清零

        Rx_Buffer[i++] = SBUF;
    
        if(i == 5)
        {
            i = 0;
        }
    }
    
    if(TI)
    {
        TI = 0;
        busy = 0;
    }
}

void SendData(unsigned char dat)
{
    while(busy);//等待前面的数据发送完成
    busy = 1; //发送数据时,busy标志为1
    SBUF = dat; //把数据发给SBUF
}

void SendString(char *s)
{
    while(*s)
    {
        SendData(*s++);
    }
}

unsigned char SendNByte(unsigned char *p, unsigned char n)
{
    unsigned char i;
    for(i=0;i<n;i++)
    {
        SendData(p[i]);
    }
    
    return 1;
}

void Delay1000ms()        //@11.0592MHz
{
    unsigned char i, j, k;

    _nop_();
    i = 8;
    j = 1;
    k = 243;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值