串口实验演示

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

045.6.1 串口实验演示

#include "reg52.h"
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit LED1 = P1^0;
sbit LED2 = P1^1;
sbit LED3 = P1^2;
sbit LED4 = P1^3;
sbit BEEP = P1^6;
sbit JDQ1 = P2^0;

uchar recv;

void Delay_xms(uint xms)	//@11.0592MHz
{
	uchar data i, j;
	while(xms)
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
    xms--;		
	}
} 
//串口发送一个字节
void UART_Send_Byte(uchar send_byte)
{
	SBUF = send_byte;
	while(!TI);
	TI=0;
}

//串口发送字符串
void UART_Send_Str(uchar *send_str)
{	
	while(*send_str != '\0') //判断字符串结束符,如果识别到‘\0',则跳出while,结束发送
	{
		UART_Send_Byte(*send_str++);	//本质是一个字节一个字节的发送,
		//发送完一个字节,地址+1,等待发送下一个字节,直到检测到字符串结束符为止
	}	
} 
void main()
{
	SCON = 0x50; 	//串口配成工作方式1
	PCON &= 0x7F; //波特率不加倍
	TMOD &= 0x0f;	
	TMOD |= 0x20;	//定时器1,模式二,自动重装初值,串口按照一定速率发数据,需要用的定时器
	TH1 = 0xFD;	
	TL1 = 0xFD; //@11.0592MH时钟,9600比特率定时器初值
	TR1 = 1; //定时器开始运行   
	ES = 1;  //串口中断打开
	EA=1;					//总中断打开

	while(1)
  {
		UART_Send_Str("I am wfeng!\r\n");
		Delay_xms(1000);
  }
}

/*****************
串口中断处理函数,接收数据
*****************/
void UART_Routine(void) interrupt 4
{
	if(1 == RI) //判断接收标志位是否为1
	{
		RI = 0;  //RI接收标志位清0
		recv = SBUF;   //接收数据
		switch (recv)
		{
			case 0x1:
				LED1 = 0;
				break;
			case 0x2:
				LED2 = 0;				
				break;
			case 0x3:
				LED3 = 0;
				break;
			case 0x4:
				LED4 = 0;				
				break;	
			case 0x5:
				P1 |= 0x0F;
				break;
			case 0x6:
				BEEP = 0;				
				break;
			case 0x7:
				BEEP = 1;
				break;
			case 0x8:
				JDQ1 = 0;				
				break;
			case 0x9:
				JDQ1 = 1;				
				break;			
			default:
				break;
		}
	}

}

一、串口实验演示

本实验先建立一个直观认识:电脑与51单片机之间可以进行双向串行通信。单片机每隔约1秒向电脑发送一行字符串;电脑也可以发送命令字节,让单片机控制LED、蜂鸣器和继电器。后续分析会把电脑端的USB通信、CH340转换电路和单片机内部UART联系起来。

串口通信的完整数据通路如下:

单片机发送:程序写SBUF → P3.1/TXD → CH340的RXD → USB D+/D- → 电脑串口助手
单片机接收:电脑串口助手 → USB D+/D- → CH340的TXD → P3.0/RXD → SBUF → 串口中断

这里的CH340不是简单的“换一个插头”,而是协议与电平转换芯片:电脑一侧使用USB差分信号D+D-,单片机一侧使用TTL电平UART信号TXDRXD。TTL串口也不同于传统DB9接口的RS-232电平,二者不能直接相连。

1.1 实验一接线方式

实验一需要一根USB数据线和一个继电器模块。开发板上虽然有两个Type-C接口,但功能并不相同:

接口原理图中的名称连接的主要电路能否进行串口通信
带CH340串口芯片的一侧供电/下载USBUSB供电、USB数据线、CH340N自动下载电路可以,同时可以供电和下载程序
另一侧供电USBVBUS、滤波电容和电源网络不可以,只能供电

实验时应把数据线插入靠近CH340芯片、能够下载程序的接口。若插入仅供电接口,开发板虽然会亮灯并运行程序,但电脑不会枚举出COM端口,串口助手也无法通信。

继电器模块的控制侧按下面的方法连接:

继电器模块引脚开发板连接点作用
VCC5V给继电器模块供电
GNDGND建立共同电位基准
INP2.0接收单片机输出的控制电平
开发板5V  ───────── VCC   继电器模块
开发板GND ───────── GND
P2.0      ───────── IN

必须连接GND。控制信号电压是相对于地定义的;只有IN而没有公共地,继电器模块无法可靠判断高、低电平。本实验程序中的JDQ1=P2.0,发送0x08时执行JDQ1=0,发送0x09时执行JDQ1=1。视频所用模块为低电平触发,因此IN=0时继电器吸合,IN=1时释放;其他继电器模块可能带高/低电平触发选择,使用时应以模块丝印和说明书为准。

继电器模块可以把控制侧和负载侧分开理解:

低压控制侧                              触点侧

P2.0 → IN → 驱动管/光耦 → 继电器线圈  ║  COM ─┬─ NO(常开)
5V  ────────────────┘                  ║       └─ NC(常闭)
GND ─────────────────                  ║
  • 线圈未吸合时,COMNC导通,COMNO断开。
  • 线圈吸合后,COMNO导通,COMNC断开。
  • COM/NO/NC属于负载触点,不应和VCC/GND/IN混为一组。若以后控制市电负载,还必须考虑触点额定电压、电流、绝缘距离和人身安全;本实验只观察指示灯和吸合声即可。

板载CH340串口电路可以拆成五部分理解:

电脑USB
  │ D+、D-
  ▼
Type-C数据接口
  │
  ▼
CH340N USB转TTL
  ├─ TXD ───────────────→ P3.0/RXD(单片机接收)
  ├─ RXD ←── R1 300Ω ─── P3.1/TXD(单片机发送)
  ├─ RTS#、C7、R2、Q2 ── 自动下载辅助控制网络
  └─ V3、C5 100nF ────── 内部电源去耦
  • CH340N负责USB协议与TTL UART之间的转换。
  • R1=300Ω串联在串口信号线上,可限制瞬态电流并减小两个器件端口直接冲突时的冲击。
  • D2=1N4148参与接收/自动下载信号的单向隔离,避免有关控制信号互相倒灌。
  • RTS#、C7、R2、Q2组成自动下载辅助网络,将电脑端控制线的变化整形成板级控制动作,使下载软件可以配合芯片进入下载流程。它们不承担正常字符串数据的编码工作。
  • C5=100nF是CH340内部电源节点的高频去耦电容,用来吸收快速开关产生的小幅电压波动。

开发板上的串口指示灯采用低电平灌电流方式:

+5V ── 限流电阻 ── LED ── TX或RX信号线
                         │
信号线为低电平时:+5V → 电阻 → LED → 芯片端口 → GND,LED点亮
信号线为高电平时:两端电位接近,LED熄灭

串口空闲状态通常为高电平,传输时会出现低电平脉冲,所以TX/RX灯会随数据收发闪烁。程序每隔约1秒发送一次"I am wfeng!\r\n",因此能看到发送指示灯约每秒闪一下。指示灯只能说明线路上出现过活动,不能代替对具体数据内容和波特率的检查。

1.1.1 写代码

实验程序位于教学资料的第二部分51单片机教学篇/代码/6.1串口通信_实验演示目录。烧写步骤如下:

  1. 打开STC烧写工具,确认芯片型号与开发板一致。
  2. 在串口列表中选择开发板对应的端口,例如COM4。COM编号由电脑实际分配,不应照搬示例。
  3. 打开工程Objects目录中生成的HEX程序文件。
  4. 点击“下载/编程”,再按工具提示让单片机重新上电或复位。
  5. 下载时观察板载串口收发灯;数据传输期间对应指示灯会闪烁。

程序运行后,while(1)反复执行:

UART_Send_Str("I am wfeng!\r\n") → 延时约1000ms → 再次发送

字符串由13个字节组成,其中\r是回车,\n是换行。UART_Send_Str()逐字节调用UART_Send_Byte();后者把数据写入SBUF,等待发送完成标志TI=1,随后用软件将TI清零。

本实验采用UART模式1和常见的8N1格式,一帧包含:

空闲高电平 ─ 起始位0 ─ D0 ─ D1 ─ D2 ─ D3 ─ D4 ─ D5 ─ D6 ─ D7 ─ 停止位1
                            8位数据,低位先发送

8N1表示8个数据位、无校验位、1个停止位。每个数据字节实际占用10个串行位,所以在没有帧间空隙的理想情况下,9600 bit/s最多传输:

$ \frac{9600\ \text{bit/s}}{10\ \text{bit/byte}}=960\ \text{byte/s} $

“9600波特”在这里等于每秒传输9600个二进制码元,不等于每秒传输9600个字符。

1.1.2 串口助手需要准备的软件

串口助手可从教学资料的“51单片机工具”目录复制到桌面并直接运行。电脑端参数必须与单片机一致:

设置项本实验选择含义
串口号实际枚举出的COM端口可通过设备管理器或拔插设备确认
波特率9600每秒9600个码元
校验位NONE无校验位
数据位8每帧8位有效数据
停止位1每帧末尾1位停止位
流控制NONE不使用RTS/CTS硬件流控
接收显示ASCII将收到的字节按字符显示
发送格式HEX输入1时发送一个字节0x01

设置完成后点击“打开”。程序会每隔约1秒发送I am wfeng!并换行,串口助手的接收区可以清空,但清空只影响屏幕显示,不会停止单片机继续发送。

发送格式选择HEX是本实验能正确控制外设的关键。HEX发送框中的1通常会被补成字节0x01,恰好匹配程序中的case 0x1。如果选择ASCII后发送字符1,线路上传输的是字符编码0x31,不会命中case 0x01。若希望使用ASCII命令,代码应写成case '1':case 0x31:

命令与动作的对应关系如下:

HEX命令字节程序动作实验现象
01LED1=0LED1点亮
02LED2=0LED2点亮
03LED3=0LED3点亮
04LED4=0LED4点亮
05P1|=0x0FP1.0~P1.3置1,四个LED熄灭
06BEEP=0蜂鸣器响
07BEEP=1蜂鸣器停止
08JDQ1=0低电平触发继电器吸合
09JDQ1=1继电器释放

LED“写0点亮”由硬件连接决定,而不是C语言中0代表“关闭”:

+5V ── LED ── 10kΩ ── P1.x

P1.x=0:端口向地灌电流,LED点亮
P1.x=1:LED两端压差很小,LED熄灭

蜂鸣器电路同样是低电平有效。P1.6R3=1kΩ连接PNP三极管S9012的基极;P1.6=0时,基极电位被拉低,三极管导通,+5V经过三极管和蜂鸣器流向GND,于是蜂鸣器发声。单片机端口只负责提供小基极电流,蜂鸣器的主要工作电流由三极管承担,避免端口直接驱动较大负载。

1.2 实验二接线方式

实验二使用独立的CH340 USB转TTL模块,目的是说明外接模块能够完成与板载CH340相同的USB-UART转换。接线必须交叉并共地:

USB转TTL模块51单片机原因
TXDP3.0/RXD模块发送端接单片机接收端
RXDP3.1/TXD模块接收端接单片机发送端
GNDGND两端共用电平参考
电脑                    USB转TTL模块                     51单片机
USB ────────────────→ CH340 TXD ─────────────────────→ P3.0/RXD
USB ←──────────────── CH340 RXD ←───────────────────── P3.1/TXD
电脑地 ─────────────── 模块GND ──────────────────────── 单片机GND

TXDRXD按信号方向命名:每一端的发送端必须接另一端的接收端,因此是TX→RXRX←TX。如果误接成TX→TX,两个输出端可能同时驱动一条线,不但无法通信,还可能产生冲突电流。

将模块USB端插入电脑后,系统会出现另一个COM端口,例如COM5。可以新建一个串口助手窗口,仍然设置为9600、8、NONE、1、NONE,接收选ASCII、发送选HEX。这样既能收到单片机周期发送的字符串,也能发送0109控制开发板外设。

板载CH340和外接CH340的功能对应关系为:

板载方案:板载数据USB口 + 板载CH340N + P3.0/P3.1
外接方案:模块USB插头   + 模块CH340  + 杜邦线接P3.0/P3.1/GND

如果开发板已由自身USB接口供电,外接模块通常只连接TXD、RXD、GND,不要再连接模块的5V/VCC,以免两个USB电源互相反灌。视频所用模块上的黄色跳帽按接线说明取下;不同型号模块的跳帽可能用于选择3.3V/5V、供电或收发方式,实际操作必须先看模块丝印,不能只凭颜色判断。

当板载CH340与外接模块同时接在P3.0/P3.1上时,单片机的TXD输出可以被两个接收器同时观察,所以两个串口助手都能收到同一字符串。但两个CH340的TXD都属于输出端,不应同时向P3.0驱动相反电平。实验时只从一个串口助手发送,正式应用则优先只保留一个发送源,避免总线争用。

1.2.1 代码分析

程序从main()开始,先配置串口、定时器1和中断,再进入主循环发送字符串。各条初始化语句的含义如下:

语句作用
SCON=0x50串口工作于模式1,允许接收
PCON&=0x7F清除SMOD,波特率不加倍
TMOD&=0x0F清除定时器1的高四位配置,保留定时器0配置
TMOD|=0x20定时器1选择模式2、内部时钟、无门控
TH1=TL1=0xFD设置定时器1自动重装值,为9600波特提供时钟
TR1=1启动定时器1
ES=1允许串口中断
EA=1打开CPU总中断开关

SCON=0x50=0101 0000B,各位可以逐位展开:

名称本实验值含义
B7SM0/FE0在通常配置下与SM1共同选择串口模式;STC扩展功能启用时也可作帧错误标志FE
B6SM11与B7组合为01,选择模式1:8位UART、波特率可变
B5SM20关闭多机通信筛选,正常接收模式1的数据帧
B4REN1允许串口接收
B3TB80模式2/3的第9个发送位;模式1不用
B2RB80模式2/3的第9个接收位;模式1中可反映停止位
B1TI0发送完成标志,由硬件置1、软件清0
B0RI0接收完成标志,由硬件置1、软件清0

定时器1的TMOD高四位依次为GATE1、C/T1、M1、M0。写入0x20后的高四位为0010BGATE1=0表示只由TR1控制运行,C/T1=0表示使用内部时钟,M1M0=10表示8位自动重装模式。TH1保存重装值,TL1从该值开始向上计数,溢出后硬件自动把TH1重新装入TL1

在11.0592MHz、12T、SMOD=0条件下:

$ f_{T1_overflow}=\frac{11,059,200}{12\times(256-0xFD)} =\frac{11,059,200}{12\times3} =307,200\ \text{Hz} $

模式1的波特率为定时器1溢出率的$1/32$:

$ baud=\frac{307,200}{32}=9600\ \text{bit/s} $

这说明0xFD不是经验数字,而是由晶振、12T时钟、定时器1模式2和SMOD=0共同计算出的结果。如果这块STC89C52被切换成6T模式,仍机械使用0xFD就无法得到9600波特;换用1T的8051兼容芯片时也必须按对应数据手册重新计算,否则收发两端会因波特率不匹配而出现乱码或完全无法通信。

SBUF的读写地址相同,但芯片内部包含彼此独立的发送缓冲和接收缓冲:

写SBUF → 发送缓冲/移位寄存器 → P3.1/TXD
P3.0/RXD → 接收移位寄存器/接收缓冲 → 读SBUF

发送流程是SBUF=send_byte → 等待TI=1 → TI=0。字符串函数持续移动指针,遇到C字符串结束符\0后停止,因此字符串中的\r\n会被发送,而编译器自动附加的\0不会被发送。

接收采用串口中断。8051串口的发送完成TI和接收完成RI共用中断号4,入口地址为0023H;只要二者之一置1且EA=1、ES=1,CPU就会进入UART_Routine()。本程序在中断函数中判断RI,清零后读取SBUF,再用switch执行外设命令。发送完成的TI则由UART_Send_Byte()轮询并清零。两个标志都必须由软件清除,否则会反复提出中断请求。

从接收到执行命令的过程可概括为:

串口助手发送HEX字节
        │
        ▼
P3.0/RXD接收完整8N1帧
        │
        ▼
硬件把数据装入SBUF,并置RI=1
        │
        ▼
CPU进入串口中断:RI=0,recv=SBUF
        │
        ▼
switch(recv)匹配0x01~0x09
        │
        ├─ 控制P1.0~P1.3:LED
        ├─ 控制P1.6:蜂鸣器
        └─ 控制P2.0:继电器

排查通信故障时,应按信号链从外到内检查:确认使用数据USB口和正确COM号,再检查两端参数是否同为9600、8N1、无流控,然后确认TX/RX交叉、GND共地,最后核对发送格式究竟是HEX还是ASCII。这样比反复修改程序更容易定位问题。