注:本文为 “51 单片机串口通信” 相关合辑。
图片清晰度受引文原图所限。
略作重排,如有内容异常,请看原文。
单片机与 PC 机串口通信实验
Lzjusc2017 于 2019-01-22 17:57:24 发布
实验原理
51 单片机的串行口是一个可编程的全双工通信接口,具备 UART(通用异步收发器)的全部功能,能够同时进行数据的发送和接收,也可作为同步移位寄存器使用。51 单片机的串行口主要由两个独立的串行数据缓冲寄存器 SBUF(一个发送缓冲寄存器,一个接收缓冲寄存器)、发送控制器、接收控制器、输入移位寄存器及若干控制电路组成。串行口方式 1 是最常用的通信方式,传输的数据共 10 位,包括 1 位起始位、8 位数据位(最低位在前,最高位在后)和 1 位停止位,帧与帧之间可以有空闲,也可以无空闲。
发送方式
当数据被写入 SBUF 寄存器后,单片机自动从起始位开始发送数据。发送到停止位的开始时,内部硬件将 TI 置 1,向 CPU 申请中断。此时,可以在中断程序中进行相应处理,也可以选择不进入中断。
接收方式
通过软件将 REN 置 1 时,接收器以选择波特率的 16 倍速率采样 RXD 引脚电平。检测到 RXD 引脚输入电平发生负跳变时,说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器的右侧移入。当起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位。若 RI = 0 且 SM2 = 0(或接收到的停止位为 1),则将接收到的 9 位数据的前 8 位装入接收 SBUF,第 9 位(停止位)进入 RB9,并置 RI = 1,向 CPU 请求中断。
在具体操作串行口之前,需要对单片机的一些与串口有关的特殊功能寄存器进行初始化设置,主要设置产生波特率的定时器 1、串行口控制和中断控制,具体步骤如下:
- 确定 T1 的工作方式(编程 TMOD 寄存器);
- 计算 TI 的初值,装载 TH1 和 TL1;
- 确定串行口工作方式(编程 SCON 寄存器);
- 若串行口工作在中断方式,需进行中断设置(编程 IE 和 IP 寄存器)。
相关寄存器的选择说明



实验代码
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
uchar num, a; // 定义变量
void main()
{
TMOD = 0x20; // 定时器设置
TH1 = 0xFD; // 设置初值 9600 = (2^SMOD / 32) * (TI 溢出率) = (1/32) * 11059200 / (256 - X) * 12
TL1 = 0xFD;
TR1 = 1; // 开启定时器 1
SM0 = 0; // 设置串行口工作方式
SM1 = 1;
REN = 1; // 允许串行接收位
EA = 1; // 开启总中断
ES = 1; // 开启串口中断
while (1)
{
if (num == 1) // 判断是否有串口数据的传送
{
ES = 0; // 关闭串口中断
num = 0;
SBUF = a; // 发送数据 a 到 SBUF,即将单片机的数据发送到计算机
while (!TI); // 发送数据后,TI 自动置 1
TI = 0; // TI 置 0,才能进行下次发送
ES = 1; // 打开中断
}
}
}
void ser() interrupt 4
{
RI = 0; // 接收到数据后,将 RI 置 0
a = SBUF; // 接收数据
num = 1; // 标志位
}
串口通信的特征
串口通信具有以下六个特征:
- 物理连接:至少需要 3 根线,分别是 Tx(数据发送线)、Rx(数据接收线)和 GND(共用地线)。
- 电平约定:
- RS232 电平:约定 -5V 至 -25V 之间的电压信号为 1,+5V 至 +25V 之间的电压信号为 0。
- TTL 电平:约定 5V 的电压信号为 1,0V 电压信号为 0。
- CMOS 电平:约定 3.3V 的电压信号为 1,0V 电压信号为 0(常用于 ARM 芯片)。
- 发送顺序:低位先发。
- 波特率:收发双方共同约定的一个数据位(0 或 1)在数据传输线上维持的时间,也可理解为每秒可以传输的位数。常用的波特率有 300 bit/s、600 bit/s、2400 bit/s、4800 bit/s、9600 bit/s。
- 起始信号:发送方在没有发送数据时,应将 Tx 置 1。当需发送时,先将 Tx 置 0,并保持 1 位的时间。接收方不断侦测 Rx,若发现 Rx 从高电平突然变为低电平,则视为发送方将要发送数据,迅速启动自己的定时器,从而保证收发双方定时器同步。
- 停止信号:发送方发送完最后一个有效位后,必须再将 Tx 保持 1 位的时间,即为停止位。
注意事项
读取 SBUF 数据或接收 SBUF 数据均采用 ASCII 码,数据类型为 unsigned char,以二进制形式处理。
实验:从串口中输入 0-999,数码管显示 0-999
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit wela1 = P1^0; // 第一位
sbit wela2 = P1^1; // 第二位
sbit wela3 = P1^2; // 第三位
uint a;
uchar num, ge, shi, bai;
uchar code table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; // 不带小数点的共阴数码管段值 0-9
uchar code tablewe[] = {0x04, 0x02, 0x01};
uchar table1[2];
uchar k = 0;
void delay(uchar x) // 延时
{
uchar i, j;
for (j = x; j > 0; j--)
{
for (i = 113; i > 0; i--){}
}
}
void main()
{
TMOD = 0x20; // 用定时器设置串口波特率 9600
TH1 = 0xFD; // 赋初值 9600 = (2^SMOD / 32) * (TI 的溢出率) = (1/32) * 11059200 / (256 - X) * 12, X = 253
TL1 = 0xFD; // 赋初值
TR1 = 1; // 开启定时器 1
SM0 = 0; // 模式选择
SM1 = 1;
REN = 1; // 串口初始化
EA = 1; // 开启总中断
ES = 1; // 开启串口中断
P1 = 0x00;
while (1)
{
if (num == 1) // 判断是否有串口数据的传送
{
num = 0;
if (table1[1] == 0x00)
{
bai = table1[1];
shi = (table1[0] >> 4) & 0xf;
ge = table1[0] & 0xf;
}
else
{
bai = (table1[0] >> 4) & 0xf;
shi = table1[0] & 0xf;
ge = table1[1];
}
}
if (bai != 0x00)
{
P1 = 0x04;
P0 = table[bai];
delay(5);
}
if (shi != 0x00)
{
P1 = 0x02;
P0 = table[shi];
delay(5);
}
P1 = 0x01;
P0 = table[ge];
delay(5);
}
}
void ser() interrupt 4 // 串口中断号 4
{
table1[0] = 0;
table1[1] = 0; // 清零
if (RI)
{
for (k = 0; k < 2; k++)
{
while (!RI); // 查询接收标志
RI = 0;
table1[k] = SBUF; // 一次读 8 位
delay(10);
if (RI == 0) break; // 表示只发一位
}
}
num = 1; // 表示有接收
}
51 单片机之——串口通信(含实现部分)
Patarw_Li 已于 2024-01-30 17:10:26 修改
一 串口通信简介

本篇文章将实现两个部分:第一部分为 单片机通过串口向电脑发送数据;第二部分为 电脑通过串口控制单片机 LED 灯。
二 前置知识
简单的串口通信需要两根通信线:发送端 TXD(transmit exchange data) 和接收端 RXD(receive exchange data),并且它们是交叉连接的。

如下图所示,RXD 和 TXD 即为单片机芯片上串口通信的两个引脚(注意它们与 P3.0 和 P3.1 引脚是复用的)。

上图中的两个引脚连接至单片机 USB 转 TTL 下载模块的 RXD-U 和 TXD-U 引脚(如下图所示,TXD 为发送端,RXD 为接收端,并且可以看到它们与 RXD-U 和 TXD-U 交叉连接)。

以下是一些术语的简单介绍:

接下来介绍需要用到的寄存器:

1. SCON:串行控制寄存器(可位寻址),用于选择串行通信的工作方式和一些控制功能

- SM0 和 SM1:用来确定串行口的工作方式。常用的模式为模式 1,因此初始化为 SM0 = 0 和 SM1 = 1。
- SM2:控制模式 2 和模式 3 的多机通信。由于使用模式 1,因此初始化为 0。
- REN:控制是否允许串行接收。REN = 1 表示允许串行接收,可以启动串行接收器 RXD;REN = 0 表示禁止接收。若仅使用发送功能,则初始化为 0;若需要接收功能,则初始化为 1。
- TB8 和 RB8:用于模式 2 和模式 3,此处初始化为 0。
- TI:发送中断请求标志位。在模式 1 中,停止位开始发送时由内部硬件置 1,表示向主机请求中断;中断响应后需要软件复位,即 TI = 0。初始化为 0。
- RI:接收中断请求标志位。在模式 1 中,接收到停止位时由内部硬件置 1;中断响应后由软件复位 0。初始化为 0。
因此,在代码中,可以将 SCON 寄存器初始化为 0x40(单片机通过串口向电脑发送数据)或 0x50(单片机通过串口接收电脑发送的数据)。
2. PCON:电源控制寄存器(不可位寻址)

- SMOD:波特率选择位,控制波特率是否加倍。
- SMOD0:帧错误检测有效位,控制是否启用帧错误检测。
3. SBUF:串口数据缓存寄存器,用于存放待发送数据
此外,还需要配置定时器相关寄存器,具体配置方法将在后续文章中详细介绍。此处只需按照示例进行配置,其他未提及的寄存器均无需配置。
三 前置准备
硬件准备
- STC89C52RC 单片机 一块(要操作的 LED 灯为划红线部分)
- 电脑 一台

软件准备
- Keil5:用于编写程序代码,生成
.hex文件。 - STC-ISP:用于将生成的
.hex文件烧录到单片机中,同时使用其附带的串口助手工具。

四 实现单片机通过串口向电脑发送数据
首先新建工程,选择芯片型号为 AT89C52。

创建项目后,确保勾选生成 .hex 文件的选项,以便后续将该文件烧录到单片机中。

首先编写一个函数用于初始化各个寄存器的参数。可以使用 STC-ISP 软件的波特率计算器生成初始化函数,但需根据单片机的具体参数进行配置,否则可能会出现接收错误字节。此处波特率设置为 4800(可根据实际需求调整)。

以下是初始化函数:
void UART_Init()
{
/**串口寄存器配置**/
SCON = 0x40; // 初始化串行口控制寄存器
PCON |= 0x80; // 使能波特率倍速位 SMOD
/**定时器寄存器配置**/
TMOD &= 0x0f; // TMOD 高四位清 0,低四位保持不变
TMOD |= 0x20; // 设定定时器 1 为 8 位自动重装方式
TL1 = 0xf4; // 设定定时初值
TH1 = 0xf4; // 设定定时器重装值
ET1 = 0; // 禁止定时器 1 中断
TR1 = 1; // 启动定时器 1
}
接下来创建一个函数,用于将一个字节的数据写入 SBUF(串口数据缓冲寄存器) 中。写入后,硬件将自动完成发送,发送完成后硬件会将 TI(发送中断请求标志位)置 1。
void UART_SendByte(unsigned char Byte)
{
SBUF = Byte; // 将数据写入串口数据缓存寄存器
while (TI == 0); // 等待 TI 置 1,即发送完成前一直等待
TI = 0; // 发送完成后,需要软件复位
}
在 main 函数中调用这两个函数:
void main()
{
UART_Init(); // 初始化寄存器
while (1)
{
UART_SendByte(0x66); // 发送 0x66 字节
}
}
编译该代码后,使用 STC-ISP 将编译好的 .hex 文件烧录到单片机中。
打开 STC-ISP 内置的串口助手,设置好波特率(此处波特率设置为 4800,若未正确配置可能会出现接收错误字节),然后打开串口助手。此时,可在接收缓冲区中看到单片机发送的字节,表示单片机通过串口向电脑发送数据的实验成功。

五 实现单片机通过串口接收电脑发送的数据
接收串口信息需要将串口控制寄存器中的 REN 位置为 1,即允许单片机通过串口接收信息。因此,SCON 寄存器应从原来的 0x40 变为 0x50。由于接收信息是被动的,串口通过中断提醒单片机有信息要接收,因此需要打开串口中断(EA = 1, ES = 1)。

以下是初始化函数:
void UART_Init()
{
/**串口寄存器配置**/
SCON = 0x50; // 初始化串行口控制寄存器,REN 位为 1
PCON |= 0x80; // 使能波特率倍速位 SMOD
/**定时器寄存器配置**/
TMOD &= 0x0f; // TMOD 高四位清 0,低四位保持不变
TMOD |= 0x20; // 设定定时器 1 为 8 位自动重装方式
TL1 = 0xf4; // 设定定时初值
TH1 = 0xf4; // 设定定时器重装值
ET1 = 0; // 禁止定时器 1 中断
TR1 = 1; // 启动定时器 1
EA = 1; // 启动所有中断
ES = 1; // 启动串口中断
}
查询单片机手册可知,串口的中断号为 4。因此,可以编写串口中断子函数(在函数圆括号后加上 interrupt 4),无需调用,当串口中断产生时,将自动执行函数中的内容。
void UART_Routine() interrupt 4
{
if (RI == 1)
{
// 当接收到数据时,让控制 LED 的 P2 端口等于串口数据缓冲寄存器中的值,从而实现串口控制 LED 灯
P2 = ~SBUF; // 因为 0 为灯亮,所以要取反
RI = 0; // 软件复位
}
}
注意,由于串口发送和接收都会引起这个中断,因此需要通过 RI 位(接收中断请求标志位)来判断是否接收到数据。接收到数据后,对单片机 LED 进行操作,并将 RI 位清零。
此实验中,main 函数只需调用初始化函数即可,无需调用中断函数。
void main()
{
UART_Init();
while (1)
{
// 主循环为空,所有操作在中断中完成
}
}
编译该代码后,使用 STC-ISP 将编译好的 .hex 文件烧录到单片机中。
打开串口助手,若希望 LED 的高四位亮起,可发送十六进制数 0xF0(二进制为 1111 0000),点击发送数据即可。

此时,单片机的高四位 LED 灯将亮起。

至此,关于单片机串口通信的两个简单实验已经完成。可以根据这些基本的实验拓展出更高级的功能。
via:
-
单片机与 PC 机串口通信实验_单片机和 pc 机的痛训实验-优快云博客
https://blog.youkuaiyun.com/qq_40318498/article/details/86598906 -
51 单片机之——串口通信(含实现部分)_rxd 和 txd-优快云博客
https://blog.youkuaiyun.com/qq_51103378/article/details/127031162
5137

被折叠的 条评论
为什么被折叠?



