目录
  • 一、串口相关信息
    • 1. 串口简介
    • 2. 串口线路的连接
    • 3. 串口电平标准
    • 4. 常见通信接口比较
  • 二、51单片机的UART
    • 1. STC89C52的UART资源
    • 2. 串口参数
    • 3. 串口模式图
    • 4. 中断通路的配置
    • 5. 串口相关寄存器
  • 三、相关寄存器的配置
    • SCON寄存器
      • 1. SM0,SM1
      • 2. REN
      • 3. TI和RI
      • 代码配置
    • PCON寄存器
    • 使用STC-ISP软件自动生成配置代码
  • 四、通过代码使用串口
    • 1. 通过串口发送数据
    • 2. 通过串口接收数据
  • 五、将串口相关功能封装成模块

参考资料:https://www.bilibili.com/video/BV1Mb411e7re?p=19

一、串口相关信息

1. 串口简介

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信

单片机的串口可以使单片机与单片机单片机与电脑单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。

51单片机内部自带UART (Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信

2. 串口线路的连接

  • 简单双向串口通信两根通信线(发送端TXD和接收端RXD)
  • TXDRXD要交叉连接
  • 当只需单向的数据传输时,可以直接一根通信线
  • 电平标准不一致时,需要加电平转换芯片

示意图:

image-20210820143544660

3. 串口电平标准

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

  • TTL电平: +5V表示10V表示0 (单片机使用的是这个)
  • RS232电平: -3--15V表示1,+3~+15V表示0
  • RS485电平:两线压差+2+6V表示1,-2-6V表示0 (差分信号)

4. 常见通信接口比较

image-20210820154209763

相关术语:

  • 全双工:通信双方可以在同一时刻互相传输数据
  • 半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
  • 单工:通信只能有一方发送到另一方,不能反向传输
  • 异步:通信双方各自约定通信速率
  • 同步:通信双方靠一根时钟线来约定通信速率
  • 总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)

二、51单片机的UART

1. STC89C52的UART资源

STC89C521个UART
STC89C52的UART有四种工作模式

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

2. 串口参数

波特率:串口通信的速率(发送和接收各数据位的间隔时间)
检验位:用于数据验证
停止位:用于数据帧间隔

示意图如下:

image-20210820155624570

3. 串口模式图

image-20210820160025475

4. 中断通路的配置

image-20210820160204045

从大的方向来说就是当单片机使用串口来发送数据接受数据,都会先把数据写入到缓存SBUF,然后会提出中断申请,然后再经过中断通路运行对应的中断程序

5. 串口相关寄存器

三、相关寄存器的配置

SCON寄存器

image-20210820162050496

1. SM0,SM1

image-20210820162343053

我们主要通过配置这两位来设置UART的工作模式,如上所说,串口有4种工作模式,而我们常用的为第二种,故需要设置SM0=0SM1=1

2. REN

image-20210820162942799

3. TI和RI

image-20210820162604210 image-20210820162645628

简单地说就是当单片机使用串口发送或接收数据后TIRI位就会置一,告诉我们数据已被发送或接收了,然后我们需要在代码层对其重新置零

代码配置

SM0 SM1 SM2 REN TB8 RB8 TI RI
0 1 0 1 0 0 0 0

所以配置的代码为:

SCON = 0x50; // 0101 0000

PCON寄存器

image-20210820163639194 image-20210820163823208

由于我们不需要检测帧错误,故我们这里只需要配置SMOD,我们不需要波特率加倍,故这里置零,所以总的配置为:

PCON = 0x7F;

至于为什么其他的位全部为1我也不太清楚,我是根据STC-ISP软件生成的代码配置的

使用STC-ISP软件自动生成配置代码

其实后面还有几项重要的配置,可是b站视频中的老师讲的很不清楚,直接就使用软件生成代码了,我也只能照做了。。。

打开STC-ISP软件并打开到【波特率计算器】的tab:

image-20210820170428993

然后按照上图这样配置需要的参数,然后我们就获得了串口配置的所有代码。

然后我们即可将这段代码复制到项目中,注意还需要将与AUXR相关的两个语句删除掉,上一部分已经说明过我们当前版本的单片机是不需要配置这个的。

总的代码如下:

void UartInit(void) // 4800bps@11.0592MHz
{
    PCON &= 0x7F; //波特率不倍速
    SCON = 0x50;  // 8位数据,可变波特率
    TMOD &= 0x0F; //清除定时器1模式位
    TMOD |= 0x20; //设定定时器1为8位自动重装方式
    TL1 = 0xFA;   //设定定时初值
    TH1 = 0xFA;   //设定定时器重装值
    ET1 = 0;      //禁止定时器1中断
    TR1 = 1;      //启动定时器1
}

四、通过代码使用串口

1. 通过串口发送数据

编写函数:

void UartSendByte(unsigned char Data) {
    SBUF = Data;
    while (TI == 0);
    TI = 0;
}

即我们将一个字节的数据写入到SBUF寄存器中,然后单片机即会自动将这个字节的数据发送出去,发送后TI位会被置一。此时代表信息发送完成,我们需要手动将TI位置零。

然后我们可以编写一个每隔一段时间发送一个递增的数字的程序:

unsigned char dataToSend = 0;

int main() {
    UartInit();
    while (1) {
        UartSendByte(dataToSend);
        dataToSend++;
        defaultDeley();
    }
}

tips:

接收数据可以使用STC-ISP软件自带的串口助手:

image-20210820171213135

此时我们需要注意将波特率设置为和我们前面配置的一致,这样我们才能确保接收到正确的数据。

运行结果:

image-20210820171404835

2. 通过串口接收数据

我们使用串口接收数据并进行处理需要借助中断系统,当单片机使用串口发送或接收数据时都会发出中断请求,此时我们需要先配置中断通路(二. 4),即:

ES = 1;
EA = 1;

Uart_Init()函数中添加这两句即可。

总的配置语句为:

void Uart_Init(void) // 4800bps@11.0592MHz
{
    PCON &= 0x7F; //波特率不倍速
    SCON = 0x50;  // 8位数据,可变波特率
    TMOD &= 0x0F; //清除定时器1模式位
    TMOD |= 0x20; //设定定时器1为8位自动重装方式
    TL1 = 0xFA;   //设定定时初值
    TH1 = 0xFA;   //设定定时器重装值
    ET1 = 0;      //禁止定时器1中断
    TR1 = 1;      //启动定时器1
	// 配置中断通路
    ES = 1;
    EA = 1;
}

然后我们在查看串口中断信号对应的中断号:

由图可知对应的中断号为4,因此我们可以像定时器那样编写中断程序(函数):

void UART_Routine() interrupt 4 {
    ...
}

因为接受数据和发送数据都会触发中断程序,我们这里只使用中断程序来接受数据,故我们可以先使用一个if语句进行过滤:

void UART_Routine() interrupt 4 {
    if(RI == 1) {
        ...
    }
}

RI == 1,即遇到接受信息而触发的中断时,我们将接受到的信息进行处理,我们可以用接收到的字节数据来控制LED灯的亮灭,则我们可以如下编写:

void UART_Routine() interrupt 4 {
    if(RI == 1) {
        // 将接收到的数据取反后赋值到P2,则bit为1时亮灯
        P2 = ~SBUF;
        // 然后将接收到的数据发送出去
        Uart_SendByte(SBUF);
        // RI需要手动置零
        RI = 0;
    }
}

不要忘记RI位需要手动置零

主函数:

int main() {
    Uart_Init();
    while (1) {}
}

然后我们即可使用【串口助手】发送信息到单片机上了。

fv4R0I.gif

五、将串口相关功能封装成模块

// Uart.c
#include "Uart.h"
#include<Atmel/REGX52.H>

void Uart_Init(void) // 4800bps@11.0592MHz
{
    PCON &= 0x7F; //波特率不倍速
    SCON = 0x50;  // 8位数据,可变波特率
    TMOD &= 0x0F; //清除定时器1模式位
    TMOD |= 0x20; //设定定时器1为8位自动重装方式
    TL1 = 0xFA;   //设定定时初值
    TH1 = 0xFA;   //设定定时器重装值
    ET1 = 0;      //禁止定时器1中断
    TR1 = 1;      //启动定时器1

    ES = 1;
    EA = 1;
}

void Uart_SendByte(unsigned char Data) {
    SBUF = Data;
    while (TI == 0);
    TI = 0;
}

/*
接收串口数据中断函数模板
void UART_Routine() interrupt 4 {
    if(RI == 1) {
        
        RI = 0;
    }
}
*/

头文件写上函数声明即可。