串口相关寄存器

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

047.6.3 串口相关寄存器

一、串口相关寄存器

STC89C52的串口不是由程序逐位翻转引脚来完成发送和接收,而是由芯片内部UART硬件自动完成串并转换、起始位与停止位生成、接收采样和状态标记。程序主要负责配置寄存器、向SBUF写入待发送数据,以及在TIRI置位后处理结果。

本节涉及的寄存器及作用如下:

寄存器地址主要作用是否可位寻址
SCON0x98选择串口工作方式,控制接收,保存收发状态标志
PCON0x87通过SMOD控制方式1、2、3的波特率倍率
TMOD0x89设置定时器1的计数来源和工作方式
TCON0x88TR1启动或停止定时器1
TH1TL10x8D0x8B保存定时器1的重装值和当前计数值
SBUF0x99写入时进入发送通路,读取时取得接收数据
IE0xA8控制总中断和各中断源是否允许
XICON0xC0控制外部中断2、3,与串口中断无直接关系

这些寄存器在UART内部的关系可以概括为:

                           ┌──────── 波特率发生通路 ────────┐
系统时钟 ─→ 定时器1模式2 ─→ 溢出脉冲 ─→ SMOD倍率及16分频 ─┬→ TX时钟
                                                          └→ RX采样时钟

CPU写SBUF ─→ 发送缓冲器 ─→ 发送移位寄存器 ─→ P3.1/TXD
                                      │
                                      └→ 一帧发送到规定时刻,TI=1

P3.0/RXD ─→ 起始位检测 ─→ 16倍采样 ─→ 接收移位寄存器 ─→ 接收SBUF ─→ CPU读取
                                                       │
                                                       └→ 一帧有效接收后,RI=1

                         ┌─ TI ─┐
                         └─ RI ─┴→ 串口中断请求 ─→ ES门控 ─→ EA总门控 ─→ CPU

这是一张功能逻辑图,不是开发板外部接线图。图中的“缓冲器、移位寄存器、分频器、检测器”都在单片机内部;外部真正使用的串口信号引脚是P3.1/TXDP3.0/RXD

1.1 串行控制寄存器SCON

SCON是串行口控制寄存器,地址为0x98,支持位寻址。位从高到低排列如下:

符号含义
B7SM0/FESMOD0=0时作为SM0,与SM1共同选择串口方式;SMOD0=1时作为帧错误标志FE
B6SM1串口工作方式选择位
B5SM2多机通信控制位;方式1下还会影响接收停止位不为1时是否置RI
B4REN接收允许位,1允许接收,0禁止接收
B3TB8方式2、3中要发送的第9位数据
B2RB8方式2、3中接收到的第9位;方式1中保存接收到的停止位
B1TI发送中断请求标志,由硬件置1,必须由软件清0
B0RI接收中断请求标志,由硬件置1,必须由软件清0

SMOD0=0时,SM0SM1共同决定工作方式:

SM0SM1方式帧及波特率特点
00方式08位同步移位寄存器方式,波特率固定
01方式18位UART,1位起始位、8位数据位、1位停止位,波特率可变
10方式29位UART,波特率由系统时钟和SMOD决定
11方式39位UART,波特率可变

实验采用方式1并允许接收,常见配置为:

SCON = 0x50;  // 0101 0000B:SM0=0,SM1=1,SM2=0,REN=1,TI=0,RI=0

0x50的逐位拆解如下:

位号: B7   B6   B5   B4   B3   B2   B1   B0
符号: SM0  SM1  SM2  REN  TB8  RB8  TI   RI
数值:  0    1    0    1    0    0    0    0
作用: └方式1┘   普通接收  允许接收       清空收发标志

REN只控制接收通路

REN=1会使接收器监视RXD并允许接收一帧;REN=0会禁止接收。发送通路由“写SBUF”启动,与REN无关。因此,REN=1不能解释成“同时允许发送和接收”,单片机即使在REN=0时仍然可以发送数据。

SM2在方式1中的作用

普通8N1通信通常设置SM2=0。这样接收到一帧后,只要其他接收条件成立,就可以把RI置1。若方式1中设置SM2=1,只有接收到的停止位为1时才允许置RI,可用于附加的接收筛选。方式2、3下,SM2主要用于多机通信。

TI表示何时可以处理下一次发送

CPU写SBUF后,UART硬件自动发送起始位、8个数据位和停止位。在方式1中,发送停止位开始时,硬件把TI置1,表示本帧的主要移位过程已经完成,软件可以进行下一步发送管理。

写SBUF
   │
   ▼
START → D0 → D1 → D2 → D3 → D4 → D5 → D6 → D7 → STOP
                                                    ▲
                                            STOP开始时TI=1

硬件只负责把TI置1,不会自动把它清0。查询发送时应等待TI=1,随后由程序执行TI=0;使用中断发送时,则应在串口中断服务程序中判断并清除TI

RI表示一帧已经有效接收

接收器先检测RXD上的1→0跳变,再按16倍采样时钟在每一位中部附近判定电平。方式1下,一帧数据接收完毕且满足RI=0、停止位条件等要求后,数据进入接收SBUF,硬件把RI置1。RI同样必须由软件清0,否则下一帧可能无法正常形成新的接收事件。

RXD:空闲1 → START0 → D0…D7 → STOP1
          │                 │
          └检测起始跳变     └数据装入接收SBUF,RI=1

TIRI共用一个串口中断入口

51单片机没有独立的“发送中断函数”和“接收中断函数”。TIRI任意一个为1,都可能形成串口中断请求:

$ \text{串口中断进入条件}=(TI\lor RI)\land ES\land EA $

因此进入串口中断服务程序后,应分别判断RITI,再清除实际触发的标志。不能只看到进入了串口中断,就默认一定收到了数据。

1.2 电源控制寄存器PCON

PCON地址为0x87,不能位寻址。它不只包含串口波特率控制位,还包含电源管理和通用标志位:

符号含义
B7SMOD串口方式1、2、3的波特率倍率选择位
B6SMOD01时把SCON.7用作帧错误标志FE0时把它用作方式选择位SM0
B5-保留位,不应依赖其读出值
B4POF上电标志,可供程序辅助判断上电状态
B3GF1用户通用标志位1
B2GF0用户通用标志位0
B1PD掉电模式控制位
B0IDL空闲模式控制位

对方式1而言,SMOD的正确关系是:

SMOD波特率倍率含义
01倍不加倍
12倍在相同定时器1溢出率下,波特率加倍

也就是说,设置SMOD=1才会使方式1波特率变为原来的2倍;SMOD=0是不加倍。其关系式为:

$ \text{方式1波特率}=\frac{2^{SMOD}}{32}\times\text{定时器1溢出率} $

实验需要SMOD=0时,可使用掩码只清除B7:

PCON &= 0x7F;  // 0111 1111B:仅把SMOD清0,其余位保持原值

由于PCON不可位寻址,不能像EA=1那样直接写SMOD=0。使用按位与掩码还可以避免误改SMOD0POFGF1/GF0PDIDL。在不了解其他位当前用途时,不宜为清除SMOD而直接执行PCON=0x00

1.3 定时器计数器工作模式寄存器TMOD

方式1的波特率可变,本实验使用定时器1模式2产生稳定的溢出节拍。TMOD地址为0x89,不能位寻址;高4位控制定时器1,低4位控制定时器0:

定时器1符号定时器0符号
B7GATE1B3GATE0
B6C/T1B2C/T0
B5M1_1B1M1_0
B4M0_1B0M0_0

各类控制位的意义如下:

取值作用
GATE10只要TR1=1,定时器1就运行
GATE11还要求INT1引脚为高电平,定时器1才运行
C/T10定时模式,计数脉冲来自芯片内部系统时钟分频
C/T11计数模式,计数脉冲来自外部T1/P3.5引脚
M1_1 M0_100模式0,13位定时器/计数器
M1_1 M0_101模式1,16位定时器/计数器
M1_1 M0_110模式2,8位自动重装载
M1_1 M0_111定时器1停止计数

作为串口波特率发生器时,需要定时器1、内部定时、模式2,因此高4位应为:

GATE1  C/T1  M1_1  M0_1
  0      0     1     0     = 0010B = 0x2

为了只改定时器1而保留定时器0的配置,可写成:

TMOD = (TMOD & 0x0F) | 0x20;

先用TMOD & 0x0F清空高4位并保留低4位,再用| 0x20把定时器1设置为模式2。直接执行TMOD=0x20虽然也能得到所需模式,但会同时把定时器0的配置清零。

模式2为什么适合产生波特率

模式2把TL1作为8位计数器,把TH1作为自动重装值。TL1从初值向上计数,达到0xFF后再加1发生溢出,硬件立即把TH1中的值重新装入TL1,然后开始下一轮。因此每轮计数长度始终相同,不需要程序在每次溢出后手工重装。

                  ┌──────────────────────────┐
                  │                          │ 自动重装
系统时钟 ─→ 分频 ─→ TL1递增 ─→ 0xFF后溢出 ──┴← TH1保存重装值
                                  │
                                  └→ UART波特率分频电路

TH1=0xFD时,每次溢出需要3个计数脉冲:

TL1:0xFD → 0xFE → 0xFF → 溢出并重新装入0xFD
                         共经过 256 - 0xFD = 3 个计数

在11.0592MHz晶振、定时器1采用12T计数、串口方式1、SMOD=0时:

$ \text{定时器1溢出率}=\frac{f_{osc}}{12(256-TH1)} $

$ \text{波特率}=\frac{2^{SMOD}}{32}\times\frac{f_{osc}}{12(256-TH1)} $

代入fosc=11 059 200HzSMOD=0TH1=0xFD=253

$ \text{波特率}=\frac{1}{32}\times\frac{11,059,200}{12\times(256-253)} =\frac{1}{32}\times307,200 =9,600\text{ bit/s} $

初始化时通常同时写入TH1TL1,再由TR1启动定时器1:

TH1 = 0xFD;
TL1 = 0xFD;
TR1 = 1;

如果STC芯片把定时器1改成6T模式,计数频率会变为12T模式的2倍,以上重装值和计算结果也会变化。因此计算前必须确认定时器的1T/6T/12T设置以及实际晶振频率。

定时器1不会要求程序每次溢出发送一位

真正的数据移位由UART硬件完成。定时器1溢出只向串口内部提供时基,经过SMOD选择和16分频后成为发送时钟、接收采样时钟:

定时器1溢出脉冲
       │
       ├─ SMOD=0:先÷2 ─┐
       └─ SMOD=1:不÷2 ─┴→ ÷16 ─┬→ TX移位时钟
                                └→ RX的16倍采样节拍

程序只需启动定时器1,无须开启定时器1中断,也无须在定时器中断中逐位发送。换句话说,TR1=1是启动波特率时基;ET1=1只是允许CPU响应定时器1溢出中断,通常不应为串口波特率发生器而打开ET1

1.4 串行口数据缓冲寄存器SBUF

SBUF的特殊功能寄存器地址为0x99。从程序角度看只有一个名字,但芯片内部实际有两个不同的缓冲器:

                         同一个SFR地址0x99

CPU执行 SBUF = data ─→ 发送SBUF ─→ 发送移位寄存器 ─→ P3.1/TXD

CPU执行 data = SBUF ←─ 接收SBUF ←─ 接收移位寄存器 ←─ P3.0/RXD

地址译码电路会同时判断读写方向:写0x99时选择发送缓冲器,读0x99时选择接收缓冲器。因此“写入后又读取同一个SBUF”并不表示能原样读回刚写入的发送字节;读操作得到的是接收通路保存的数据。

SBUF启动发送

SBUF = 1;

这条语句写入的是数值0x01,UART随后自动发送一帧。它不是ASCII字符'1';若希望串口助手按文本显示字符1,应写:

SBUF = '1';  // ASCII码0x31

两者在线路上发送的8个数据位不同:

表达式字节值含义
SBUF = 1;0x01数值1,串口助手用HEX显示时通常看到01
SBUF = '1';0x31ASCII字符1,文本显示时看到1

CPU写入发送SBUF后,发送数据进入移位寄存器,UART依次生成START、D0~D7、STOP。如果连续发送多个字节,应使用TI确认前一帧已经推进到允许后续处理的时刻,不能无间隔地反复覆盖发送缓冲。

SBUF取得接收数据

unsigned char a;

if (RI == 1)
{
    RI = 0;
    a = SBUF;
}

只有RI=1时,才表示接收SBUF中已经装入一帧有效数据。尚未收到新数据时读取SBUF,可能得到上一次保留的内容,不能把它当成新字节。

经典8051串口通常没有多字节接收FIFO。如果上一字节进入接收SBUF后,程序长时间没有清除RI,随后到达的帧就无法装入SBUF而被丢弃。实际程序应及时响应RI,把SBUF内容转存到变量或环形缓冲区,再处理耗时任务。

1.5 中断允许寄存器IE和XICON

IE是中断允许寄存器,地址为0xA8,支持位寻址:

符号控制的中断源
B7EACPU总中断允许位
B6-保留位
B5ET2定时器2中断允许位
B4ES串行口中断允许位
B3ET1定时器1中断允许位
B2EX1外部中断1允许位
B1ET0定时器0中断允许位
B0EX0外部中断0允许位

EA是所有可屏蔽中断前面的总开关,但EA=1不等于“所有中断都会自动打开”。每个中断源还要经过自己的分开关。串口中断需要:

TI或RI产生请求 ─→ ES=1允许串口中断 ─→ EA=1允许CPU总中断 ─→ 进入中断号4

所以,使用串口中断通常需要:

ES = 1;  // 打开串行口中断分开关
EA = 1;  // 打开CPU总中断开关
EAESTIRI能否进入串口中断
0任意值1不能,总开关关闭
101不能,串口分开关关闭
110不能,没有串口请求
111可以进入

串口中断号为4,中断向量地址为0x0023。Keil C51中通常使用interrupt 4声明串口中断服务程序。由于发送和接收共用入口,处理逻辑应类似:

void UART_Routine(void) interrupt 4
{
    if (RI == 1)
    {
        RI = 0;
        /* 读取SBUF并保存接收数据 */
    }

    if (TI == 1)
    {
        TI = 0;
        /* 处理后续发送 */
    }
}

如果发送采用查询TI、接收采用中断RI,仍要记住TI也连接到同一个串口中断请求逻辑。开启ESEA后,不能长期让TI保持为1,否则CPU可能重复进入串口中断。

XICON地址为0xC0,也支持位寻址。它的各位如下:

符号含义
B7PX3外部中断3优先级低位
B6EX3外部中断3允许位
B5IE3外部中断3请求标志
B4IT3外部中断3触发方式选择
B3PX2外部中断2优先级低位
B2EX2外部中断2允许位
B1IE2外部中断2请求标志
B0IT2外部中断2触发方式选择

XICON管理的是INT2/P4.3INT3/P4.2两路扩展外部中断,不控制UART。串口中断使用IE.ES,而不是XICON.EX2XICON.EX3。把XICONIE并列学习,是因为两者都属于中断控制寄存器;配置普通串口收发时通常无须改动XICON

最后把串口方式1、9600波特、中断接收所需条件串联起来:

SCON=0x50       → 方式1,允许接收,TI/RI初始为0
PCON.SMOD=0     → 波特率不加倍
TMOD高4位=0010 → 定时器1采用模式2自动重装
TH1=TL1=0xFD   → 11.0592MHz、12T条件下得到9600波特
TR1=1           → 启动定时器1,为UART提供时基
ES=1 且 EA=1   → 允许TI/RI触发串口中断
写SBUF          → 启动一帧发送
RI=1后读SBUF    → 取得一帧接收数据,并由软件清RI

排查串口故障时,可以按“时钟与波特率→工作方式→收发标志→中断门控→SBUF读写方向”的顺序检查。这样能把“完全不收发、乱码、只发送不接收、反复进中断”等现象分别定位到对应寄存器,而不是只反复修改一个数值。