stc12单片机的串口通讯发送详解(寄存器)
在stc12c5a60s2中关于串口的寄存器较为繁杂在这我将所有关于串口的寄存器做一个总结希望可以让读者对串口的理解更加深刻寄存器可位寻址和不可位寻址的区别在于可位寻址可以直接给比特位改值也可以通过寄存器控制但是不可位寻址的只能通过修改寄存器的方式来改变比特位。
- 1.概述
概述
在stc12c5a60s2中关于串口的寄存器较为繁杂在这我将所有关于串口的寄存器做一个总结
希望可以让读者对串口的理解更加深刻
寄存器可位寻址和不可位寻址的区别在于 可位寻址可以直接给比特位改值也可以通过寄存器控制
但是不可位寻址的只能通过修改寄存器的方式来改变比特位

AUXR辅助继电器(不可位寻址)
---------------------------------------------------------------------------------------------------------------------------------
在AUXR寄存器笼统的说它作用是设置串口和波特率的分频为1T和12T在单片机中我们的
程序运行速度,波特率,定时器的计算,息息相关
时钟的分频机制
12T模式中
- 将外部的晶振进行12分频作为运行程序的参考 举个例子:当我的时钟为12MHz时我的运行周期为 12MHz÷12=1MHZ 每个指令周期为12个时钟周期这种一般是传统的8051单片机
1T模式中
- 将外部晶振不分频作为运行程序的参考 还是在12MHZ中 它的运行周期为12MHZ÷1=12MHZ每个指令的周期为1个时钟周期比12T的要快12倍
定时器/计数器
12T模式中
- 定时器技术周期每加1秒要1us(12MHZ晶振)计算公式为
- 定时器处置计算为
其中T为时间(当t为1ms是T=0.001s)
1T模式中
- 定时器技术周期每加1秒要86ns(12MHZ晶振)计算公式为
- 定时器处置计算为
其中T为时间(当t为1ms是T=0.001s)
AUXR寄存器的每个位
在上文我已经介绍了时钟的分频现在让我们来看

一下WAKE_CLKO寄存器各比特位功能
T0x12定时器0速度设置(16位重载)
- 当T0x12=0时速度为传统8051单片机为12分频
- 当T0x12=1时定时器的速度为传统的12倍 为 不分频
T1x12定时器0速度设置
- 当T1x12=0时速度为传统8051单片机为 12分频 具体的计算公式后文会提到
- 当T1x12=1时速度为传统8051单片机12倍 为 不分频
- 如果要改变UART串口用定时器1做波特率发生器,只需要改变T1x12的数值就可以控制UART串口是12T还是1T了但要注意由于AUXR寄存器不可位寻址
AUXR |= 0x40; // T1x12=1,定时器1不分频(1T模式) AUXR &= ~0x40; // T1x12=0,定时器1使用12分频若是要给变UART的的分频方式必须给寄存器赋值而不是单独给比特位赋值
- 如果要改变UART串口用定时器1做波特率发生器,只需要改变T1x12的数值就可以控制UART串口是12T还是1T了但要注意由于AUXR寄存器不可位寻址
BRTR 和 S2SMOD 与BRTx12,S1BRS ,UART_M0x6
在51系列单片机中完整的波特率(异步通信模式)公式为
若是串口模式0 波特率(同步移位寄存器模式)公式则为
在串口二及独立波特率发生器中计算公式为
这是串口通讯的所有工作方式在使用串口1时可以选择使用独立波特率发生器和定时器来产生波特率但是在使用串口2时只能使用独立波特率发生器
现在我们来详细讲解一下单片机中的寄存器
1,BRTR 和S1BRS 独立波特率发生器运行控制
- BRTR 为1时开启独立波特率发生器 S1BRS为1时串口1开启独立波特率发生器
- 为0时关闭独立波特率发生器 S1BRS为0时串口1使用定时器1
2,S2SMOD BRTx12 这两位寄存器是用来控制我独立波特率的分频系数的
- S2SMOD 为1时 分频系数为 32(减半)
- S2SMOD 为0时 分频系数为 16
- BRTx12 为0时 分频系数为 12(默认模式,兼容传统 12T 单片机)。
- BRTx12 为1时 分频系数为 1(直接使用系统时钟,实现高速通信)
---------------------------------------------------------------------------------------------------------------------------------
控制寄存器SCON和PCON
---------------------------------------------------------------------------------------------------------------------------------
SCON:串行控制寄存器(可位寻址)

当SOMD为1时SM0和SM1共同决定串口的工作方式

当SOMD为0时SM0变为帧错误检测标志位(FE)用于检测串口通讯的异常情况
- 起始位未检测到低电平;
- 停止位未检测到高电平;
- 数据位校验失败(若启用校验功能)
SM2 有二种使用方式
- 在sm0和sm1将串口方式变成2和3时串口处于1主多从状态 当主机发送时将第9位变为1时(地址帧)这时所有从机都会开始接受数据但是只有地址对应的从机才会将SM2变为0方便后续的数据接受地址帧和数据帧在后续会说到
// 从机配置(模式3,SM2=1) SCON = 0xF0; // SM0=1, SM1=1(模式3), SM2=1, REN=1(允许接收)在噪声环境中可以停止校验以保证通讯的可靠性(模式1)
// 模式1,启用停止位校验(SM2=1) SCON = 0x50; // SM0=0, SM1=1(模式1), SM2=1, REN=1REN 启动和停止串行接受口 为1开启串口的接受功能 为0时关闭串口的接收
TB8和TR8
- TR8的作用则是发送数据位和地址位的标志位切换需要用软件置0
- RB8则是负责接收TB8的标志位由软件置0
- 具体的使用我放在了SADEN和SADDR中
TI发送中断标志位在停止位开始发送的时候由内部硬件自动置为TI=1 后续必须由软件复位
RI接受中断标志位在接受到停止位中间时刻由内部数据置位(RI=1)由软件复位
那么为什么在置位时可以由硬件自动而复位时却不行呢因为RI和TI是用或逻辑来向单片机请求中断的所以单片机无法判断是TI还是由RI请求的中断 所以必须在中断程序中查询现在是RI还是TI然后分别处理所以无法由硬件自动置0 要不然会有出现一次请求多次响应的的错误
---------------------------------------------------------------------------------------------------------------------------------
串行口数据缓存器SBUF
---------------------------------------------------------------------------------------------------------------------------------
在STC89系列单片机中其拥有两个SBUF寄存器1个是只读寄存器1个是只写寄存器他们之间互不干扰
在串行通道中都会设有数据寄存器在写入SBUF信号的控制下会自动将数据存进寄存器中其中寄存器的前8位为数据字节最低位是输出位TB8的数值单独存进第9位
在接受数据时会将数据暂时存进一个移位寄存器中等到数据传输完成后再将数据装入SBUF寄存器中其中数据一共9位(在模式0时为8位)但要注意移位寄存器是一帧一帧传输给SBUF中的所以当SBUF接受到数据后因将数据取走不然下次传输时会将SBUF中数据刷新(在地址帧对不上的情况下SBUF和RB8中的数值不会改变)
---------------------------------------------------------------------------------------------------------------------------------
IE中断允许寄存器
---------------------------------------------------------------------------------------------------------------------------------

在IE寄存器中我们只需要知道EA和ES这两个比特位就行了
EA是单片机的总中断的控制位 如同是企业中的董事长别的中断如同企业的主管想要干什么都要得到董事长的同意所以当EA=1时开放中断 EA=0关闭所有的中断
ES 就是企业中的主管了管理下属所以当ES=1时串口中断开启当ES=0时关闭串口中断
---------------------------------------------------------------------------------------------------------------------------------
IPH/IP中断优先级控制寄存器高/低(不可/可位寻址)
串口的中断优先级控制位PS/PSH位于中断的IPH/IP寄存器中格式如下

从机地址控制寄存器SADEN和SADDR

. SADDR 与 SADEN 的功能
SADDR
存储从机设备的唯一地址(8位),用于在多机通信中标识自身。当主机发送地址帧时,从机通过比对 SADDR 的值判断是否响应后续数据
-
SADEN
定义地址掩码(8位),用于决定 SADDR 的哪些位需要严格匹配。通过逻辑运算(如按位与),筛选出有效的地址帧。例如:- 若
SADEN = 0xFF,则所有位均需匹配,实现精确地址过滤。 - 若
SADEN = 0x0F,则仅高4位需要匹配,允许同一组设备共享部分地址位
- 若
为多机通讯时的接受流程为 主机发送地址帧RB为1 从机将接收到的地址SADDR&SADENN做比较如果相同则触发中断开始接受数据RI=1;下面是SADDR和SADEN的配置
// 从机地址设置为 0xA0,地址掩码为 0xFE(仅最高位需匹配)
SADDR = 0xA0; // 从机地址
SADEN = 0xFE; // 掩码:二进制 11111110
// 配置串口为模式3,启用多机通信
SCON = 0xF0; // SM0=1, SM1=1(模式3), SM2=1, REN=1
从机代码
#include <STC12C5A60S2.H>
#include <intrins.h>
#define SLAVE_ADDR 0xA0 // 从机地址
#define SLAVE_MASK 0xFE // 地址掩码(仅最高位需匹配)
// 串口初始化(模式3,波特率9600,允许多机通信)
void UART_Init() {
// 设置波特率(使用定时器1,12MHz晶振)
TMOD |= 0x20; // 定时器1,模式2(自动重装)
TH1 = 0xFD; // 9600波特率
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
// 配置串口模式3,SM2=1(启用地址过滤)
SCON = 0xF0; // SM0=1, SM1=1(模式3), SM2=1, REN=1
// 设置从机地址和掩码
SADDR = SLAVE_ADDR; // 从机地址寄存器
SADEN = SLAVE_MASK; // 地址掩码寄存器
EA = 1; // 开总中断
ES = 1; // 开串口中断
}
// 串口中断服务函数
void UART_ISR() interrupt 4 {
if (RI == 1) { // 接收中断
RI = 0; // 清除接收标志
// 检查接收的是否为地址帧(RB8=1)
if (RB8 == 1) {
// 地址匹配判断:接收的地址与 (SADDR & SADEN) 是否一致
if ((SBUF & SADEN) == (SADDR & SADEN)) {
SM2 = 0; // 匹配成功,准备接收数据帧
}
} else {
// 数据帧处理
if (SM2 == 0) {
// 处理接收的数据(例如发送到P1口)
P1 = SBUF;
SM2 = 1; // 重新启用地址过滤
}
}
}
}
void main() {
UART_Init();
while(1) {
// 主循环可添加其他任务
}
}
主机代码
#include <STC12C5A60S2.H>
#include <intrins.h>
#define SLAVE_ADDR 0xA0 // 从机地址
#define SLAVE_MASK 0xFE // 地址掩码(仅最高位需匹配)
// 串口初始化(模式3,波特率9600,允许多机通信)
void UART_Init() {
// 设置波特率(使用定时器1,12MHz晶振)
TMOD |= 0x20; // 定时器1,模式2(自动重装)
TH1 = 0xFD; // 9600波特率
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
// 配置串口模式3,SM2=1(启用地址过滤)
SCON = 0xF0; // SM0=1, SM1=1(模式3), SM2=1, REN=1
// 设置从机地址和掩码
SADDR = SLAVE_ADDR; // 从机地址寄存器
SADEN = SLAVE_MASK; // 地址掩码寄存器
EA = 1; // 开总中断
ES = 1; // 开串口中断
}
// 串口中断服务函数
void UART_ISR() interrupt 4 {
if (RI == 1) { // 接收中断
RI = 0; // 清除接收标志
// 检查接收的是否为地址帧(RB8=1)
if (RB8 == 1) {
// 地址匹配判断:接收的地址与 (SADDR & SADEN) 是否一致
if ((SBUF & SADEN) == (SADDR & SADEN)) {
SM2 = 0; // 匹配成功,准备接收数据帧
}
} else {
// 数据帧处理
if (SM2 == 0) {
// 处理接收的数据(例如发送到P1口)
P1 = SBUF;
SM2 = 1; // 重新启用地址过滤
}
}
}
}
void main() {
UART_Init();
while(1) {
// 主循环可添加其他任务
}
}
更多推荐



所有评论(0)