51单片机之UART
有关串口的相关概念看我的这篇博客
串口相关寄存器配置
与51单片机串口相关的寄存器有SBUF,SCON,PCON,以及与定时器1(T1)相关的寄存器。
SBUF:串口缓冲寄存器,物理上实际有两个寄存器,地址重叠而已,一个用来发送,一个用来接受。通过读或者写不同操作,才操作不同寄存器,设计在一起是为了简化操作。写入SBUF寄存器的数据会通过TXD引脚发送出去,从RXD引脚的数据可以通过读SBUF获取。
SCON:串口配置寄存器。
位序号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位符号 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
SM0和SM1决定了串口的工作方式
SM0 SM1 | 工作方式 | 功能 | 波特率 |
---|---|---|---|
00 | 方式0 | 8位同步移位寄存器 | 晶振频率/12 |
01 | 方式1 | 10位UART | 可变 |
10 | 方式2 | 11位UART | 晶振频率/64或者晶振频率/32 |
11 | 方式3 | 11位UART | 可变 |
10位UART是我们常用的模式,包括起始位,8个数据位(SBUF),一个停止位。11位UART多了一个数据位(TB8,发送数据的第八位;RB8,接受数据的第八位)。波特率可变是由于此时的定时器1 作为UART的时序信号,定时器1的溢出率决定了波特率,同时也受到PCON种的SMOD位的加倍影响。
SM2是多机通信控制位。只有用到了11位UART才会将SM2置1。
REN是接受使能,REN=1时,允许接收,REN=0时,禁止接收。
TI和RI分别是发送和接受中断标志位,当完成一次发送或者接收会将相应的标志位硬件置1,必须要软件清“0”。当设置了串口中断,还会进入中断函数。注意这两者会进入同一个中断函数,注意判断标志位来区别到底是放松中断还是软件中断。
详细信息移步百度
PCON:电源控制寄存器,其实只有最高位SMOD对串口有影响,SMOD=1,波特率加倍,SMOD=0,波特率不加倍。
T1相关寄存器的配置:使用可变波特率,T1一般用8位自动重装载模式,最关键的是确定T1的自动重装数RELOAD,步骤如下:
- 计算RELOAD(SMOD=0时)
a) 12T模式的计算公式:RELOAD=256-(int) (Fosc/Baud0/32/12+0.5)
b) 1T模式的计算公式:RELOAD=256-(int) (Fosc/Baud0/32+0.5)
式中: (int) (X+0.5)表示对X进行四舍五入
Fosc晶振频率
Baud0标准波特率 - 计算用RELOAD产生的波特率
a) 12T Baud = Fosc/(256-RELOAD)/32/12
b) 1T Baud = Fosc/(256-RELOAD)/32 - 计算误差
error=(Baud-Baud0)/Baud0*100% - 如果绝对误差>4.5% 要更换波特率频率或更换晶振频率,再重复1-4
上面的步骤来自于STC的官方手册。上面提到的12T模式是大多数51单片机的工作模式,STC的后续单片机支持1T模式,速度会快12倍。
串口的使用
下图展示了一些常用重装值(RELOAD)配置
对于常用的11.0592Mhz晶体下,9600Baud配置的代码封装成C文件和H文件,方便调用
uart.h文件
#ifndef __UART_H
#define __UART_H
#include <reg52.h>
void Uart_Init(void);
void Uart_SendData(unsigned char dat);
void Uart_SendString(unsigned char *s);
#endif
uart.c文件
#include "uart.h"
void Uart_Init(void)
{
SCON = 0x50; // SCON: 模式 1, 8位UART, 接收使能
TMOD |= 0x20; // TMOD: Timer1,模式2, 8位自动重装
TH1 = 0xFD; // TH1: 重装值 波特率:9600 晶振:11.0592MHz
TR1 = 1; // TR1: Timer1打开
EA = 1; //打开总中断
//ES = 1; //打开串口中断
}
void Uart_Isr() interrupt 4 using 1
{
if (RI)
{
RI = 0; //清除接收标志位
}
if (TI)
{
TI = 0; //清除发送标志位
}
}
void Uart_SendData(unsigned char dat)
{
SBUF = dat;
while (!TI);
TI=0;
}
void Uart_SendString(unsigned char *s)
{
while (*s)
Uart_SendData(*s++);
}