UART(二)使用说明
STC89C52
系列单片机内部集成了一个功能强大的全双工串行通信口,以下是相关引脚。
该通信口具有四种工作模式,如下。
- 其中模式0为同步通讯,该模式下,
TxD
引脚会作为时钟信号线,RxD
作为数据信号线,该模式下只能实现半双工通讯。 模式1、2、3为经典的UART通讯,三者的区别在与是否有校验位,以及波特率是否可变。
模式1的一个数据帧包含1个起始位,8个数据位和1个停止位。
模式2和模式3的一个数据帧包含1个起始位,8个数据位,1个校验位和1个停止位。
另外模式1和模式3的波特率可由定时器1进行配置,因而可以自由设置,而方式2的波特率直接由系统时钟决定,因而不可自由配置。
1.1 设置工作模式
工作模式需要通过SCON(Serial Control,串行口控制)寄存器中的M0和SM1两个控制位进行设置,如下图所示。
如果使用方式1,则应将SM0
置为0,SM1
置为1。
1.2 设置波特率
1.2.1 设置原理
以方式1为例:
方式1的波特率会受到两个因素的影响,分别是SMOD
控制位和定时器1的溢出频率,具体作用如下图所示。
- 以中间水平线大致分割,上面为发送模块,下面为接收模块:
- 时钟脉冲决定了发送一帧数据的频率:
- 最初时钟来源为定时器1的溢出频率,每溢出一次向外输出一次时钟信号,经过
SMOD
根据配置是否分频,再往后分频16次即为波特率:
- 溢出频率由定时器初始值决定,
SMOD
由配置决定
根据结构图可以得出公式,模式一的波特率为:
1.2.2 设置步骤
UART常用的波特率有4800、9600、19200、38400、57600、115200等,此处以9600为例。
1.2.2.1 设置SMOD控制位
SMOD
控制位位于PCON(Power Control,电源控制)寄存器,如下图所示。
此处选择0,所以此时的波特率应为(定时器1的溢出率)/ 32。
1.2.2.2 设置定时器1
由于定时器1当前的作用仅仅是为串口提供时钟信号,因此可不开启定时器中断,只需完成以下配置即可。
(1)选择定时器工作模式
定时器1的工作模式需要通过TMOD
寄存器中的C/T
控制位,以及M1
和M0
两个控制位进行设置,如下图所示。
C/T
用于设置计数/定时的工作方式,此处应选择定时模式,因此需将C/T
设置为0。
M1
和M0
,用于配置具体的工作模式,相关配置如下。
由于没有启用定时器中断,因此无法在中断中重新设置定时器脉冲计数器的初始值,所以此处选择模式2(8位自动重装载)最为合理。
(2)设置脉冲计数器初始值
- 模式2的最大计数为
2⁸=256
,因此定时器1的溢出频率等于SYSclk / 12 /(256 - TH1)
或者是SYSclk / 6 /(256 - TH1
),假如当前MCU
工作在12T
模式,所以溢出频率就等于SYSclk / 12 /(256 - TH1)
。 SYSclk
为系统时钟频率,工作模式为12t
,则时钟频率分频12,即系统时钟频率除以12定时器计数+1
;- 寄存器为8位,可以存256个数,当TH1为255时,此时寄存器再来一次脉冲就溢出了;
- 每秒定时器溢出几次 = 定时器每秒加了多少次
(SYSclk / 12)
,再除以它每次溢出需要多少次(256 - TH1)
因此最终的波特率就应该等于SYSclk / 12 /(256-TH1)/32
,假如当前的系统时钟频率为11059200Hz
,目标的波特率为9600
,那么就能得到以下方程
所以可计算出TH1
的值应为253
。
(3)启动定时器
定时器1的启动也无需外部引脚控制,因此应将GATE
控制位设置为0,并将TR1
控制位设置为1。
1.3 发送数据
STC89C52
的串口的发送模块示意图如下。
数据发送的大致流程如下:
开发者将待发送的8位数据写入发送缓冲器(SBUF),此时发送控制器(Tx Control)就会开始工作,它会自动为数据添加起始位和结束位,从而构成一个完整的UART数据帧,然后逐位通过TxD引脚输出出去。当完成一个数据帧的输出之后,发送控制器会将发送中断控制位TI
置1,向CPU请求中断,CPU检测到中断请求后就执行相应的中断服务程序。
总结:发送数据只需要将待发送的数据写入SBUF即可。
1.4 接收数据
STC89C52
的串口的接收模块示意图如下。
默认情况下,串口并不会接收数据。如需接收数据,需要先将REN(Receive Enable)控制位置为1,REN
控制位位于SCON寄存器,如下图所示。
当REN置为1后,上图中的1到0跳变检测器(1-To-0 Transition Detector)就会开始工作,具体来讲就是不断检测RxD引脚的起始位。当检测到1到0的跳变后,就会启动接收控制器(Rx Control),接收控制器会将接收到数据逐位移入到输入移位寄存器(Input Shift REG),直到接收到停止位,就算完成了一帧数据的接收。
正常情况下,接下来,接收控制器会将输入移位寄存器(Input Shift REG)中的数据加载到读取缓冲器(SBUF)中,并将读取中断控制位RI置1,向CPU请求中断,CPU检测到中断请求后就执行相应的中断服务程序,开发者就能在中断服务程序中读取SBUF获取当前帧的数据了。
但是上述操作(加载数据到SBUF
和RI
置位)的执行是有条件的,满足条件才会执行,不满足,那么当前数据帧就会被丢弃,具体条件如下:
1)结束位正常
开发者可以配置是否检测停止位的有效性(高电平有效)。是否检测是由SCON寄存器中的SM2控制位来决定的。SM2=1
时,接收控制器就会检测控制停止位,当SM2=0
时,则不会检测停止位,建议将SM2设置为0。
1)读取中断标志位为复位状态
读取中断标志位RI
必须等于0,也就说要保证上一帧数据已经被读取或处理完毕,才能处理当前帧。
总结:接收数据需要先使能接收,也就是将REN控制位置1,然后开启串口中断,并在中断服务程序中读取SBUF。
1.5 串口中断注意事项
根据前文的描述,当使用串口发送完一帧数据后,会将发送中断标志位TI
置1;当串口接收到一帧数据后,会将接收中断标志位RI
置1。需要注意的是两个控制位请求的是同一个中断——串口中断(中断号为4)。
也就是说发送完一帧数据和接收完一帧数据之后,执行的都是串口中断的中断服务程序,因此,再编写该中段服务程序时,需要注意判断当前中断到底是由发送操作触发的,还是由接收操作触发的,代码示例如下。
/*
* 串口中断的中断号为4
*/
void Dri_UART_Handler() interrupt 4
{
/* 检查接收中断标志位RI,如果为1,表示有一帧数据接收完成 */
if (RI == 1) {
}
/* 检查发送中断标志位TI,如果为1,表示有一帧数据发送完成 */
if (TI == 1) {
}
}
另外RI和TI标志位,只能由软件复位,也就是需要在中断服务程序中将其设置为0,如下。
/*
* 串口中断的中断号为4
*/
void Dri_UART_Hander() interrupt 4
{
/* 检查接收中断标志位RI,如果为1,表示有一帧数据接收完成 */
if (RI == 1) {
RI = 0;
}
/* 检查发送中断标志位TI,如果为1,表示有一帧数据发送完成 */
if (TI == 1) {
TI = 0;
}
}
1
1
1
1
1
1
1
1
1
1