参考资料:https://www.bilibili.com/video/BV1Mb411e7re?p=19
一、串口相关信息
1. 串口简介
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51单片机内部自带UART (Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
2. 串口线路的连接
- 简单双向串口通信有两根通信线(发送端
TXD
和接收端RXD
) TXD
与RXD
要交叉连接- 当只需单向的数据传输时,可以直接一根通信线
- 当电平标准不一致时,需要加电平转换芯片
示意图:
3. 串口电平标准
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
- TTL电平:
+5V
表示1
,0V
表示0
(单片机使用的是这个) - RS232电平: -3--15V表示1,+3~+15V表示0
- RS485电平:两线压差+2+6V表示1,-2-6V表示0 (差分信号)
4. 常见通信接口比较
相关术语:
- 全双工:通信双方可以在同一时刻互相传输数据
- 半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
- 单工:通信只能有一方发送到另一方,不能反向传输
- 异步:通信双方各自约定通信速率
- 同步:通信双方靠一根时钟线来约定通信速率
- 总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)
二、51单片机的UART
1. STC89C52的UART资源
STC89C52
有1个UART
STC89C52
的UART有四种工作模式:
- 模式0 :同步移位寄存器
- 模式1 : 8位UART,波特率可变(常用)
- 模式2 : 9位UART,波特率固定
- 模式3 : 9位UART,波特率可变
2. 串口参数
波特率:串口通信的速率(发送和接收各数据位的间隔时间)
检验位:用于数据验证
停止位:用于数据帧间隔
示意图如下:
3. 串口模式图
4. 中断通路的配置
从大的方向来说就是当单片机使用串口来发送数据或接受数据,都会先把数据写入到缓存SBUF
中,然后会提出中断申请,然后再经过中断通路运行对应的中断程序。
5. 串口相关寄存器
三、相关寄存器的配置
SCON寄存器
1. SM0,SM1
我们主要通过配置这两位来设置UART
的工作模式,如上所说,串口有4种工作模式,而我们常用的为第二种,故需要设置SM0=0
,SM1=1
。
2. REN
3. TI和RI
简单地说就是当单片机使用串口发送或接收数据后,TI
或RI
位就会置一,告诉我们数据已被发送或接收了,然后我们需要在代码层对其重新置零。
代码配置
SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
---|---|---|---|---|---|---|---|
0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
所以配置的代码为:
SCON = 0x50; // 0101 0000
PCON寄存器
由于我们不需要检测帧错误,故我们这里只需要配置SMOD
,我们不需要波特率加倍,故这里置零,所以总的配置为:
PCON = 0x7F;
至于为什么其他的位全部为1我也不太清楚,我是根据STC-ISP软件生成的代码配置的
使用STC-ISP软件自动生成配置代码
其实后面还有几项重要的配置,可是b站视频中的老师讲的很不清楚,直接就使用软件生成代码了,我也只能照做了。。。
打开STC-ISP软件并打开到【波特率计算器】的tab:
然后按照上图这样配置需要的参数,然后我们就获得了串口配置的所有代码。
然后我们即可将这段代码复制到项目中,注意还需要将与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软件自带的串口助手:
此时我们需要注意将波特率设置为和我们前面配置的一致,这样我们才能确保接收到正确的数据。
运行结果:
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) {}
}
然后我们即可使用【串口助手】发送信息到单片机上了。
五、将串口相关功能封装成模块
// 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;
}
}
*/
头文件写上函数声明即可。