1.1 程序功能
基于 STC89C52/AT89C52(reg52 内核)实现串口通信控制单只共阳极数码管:
- 串口波特率固定 9600,上电打印就绪提示字符串;
- PC 串口助手发送字符
0~9、A~F、a~f,数码管实时显示对应十六进制数字; - 收到非法字符时数码管熄灭;
- 串口接收采用中断方式,不阻塞主循环,实时响应上位机指令。
1.2 硬件资源分配
表格
| 外设 | 引脚 | 说明 |
|---|---|---|
| 数码管段选 | P0 口 | 共阳极数码管段码输出,无位选(单数码管) |
| 串口通信 | P3.0(RXD)、P3.1(TXD) | 标准 51 硬件 UART |
| 定时器 1 | Timer1 | 串口波特率发生器,模式 2 自动重装 |
1.3 开发环境
- 内核:8051 标准内核
- 头文件:
reg52.h - 编译软件:Keil C51
- 下载工具:STC-ISP / 普中 / 郭天祥下载器
- 上位机:串口调试助手(波特率 9600,8N1)
二、宏定义与数据类型说明
2.1 类型重定义
c
运行
typedef unsigned char uchar; // 8位无符号字符
typedef unsigned int uint; // 16位无符号整型
#define uint8 uchar // 标准8位类型别名
#define uint16 uint // 标准16位类型别名
统一变量宽度命名,兼容嵌入式标准 uint8/uint16 写法,提升代码可移植性。
2.2 系统参数宏
c
运行
#define BAUD_9600_TH 0xFA // 9600波特率TH1初值(11.0592MHz晶振)
#define UART_INT_NUM 4 // 串口中断号,51单片机串口中断固定为4
#define SMG_A_DP_PORT P0 // 数码管段码输出端口P0
BAUD_9600_TH:11.0592MHz 晶振、定时器 1 模式 2、波特率加倍 (SMOD=1) 下 9600 波特重装值;UART_INT_NUM:中断服务函数 interrupt 关键字配套中断编号;SMG_A_DP_PORT:数码管段口统一宏,修改硬件仅需改宏。
2.3 共阳极数码管段码表
c
运行
uint8 gsmg_code[16]={
0xC0,0xF9,0xA4,0xB0, // 0 1 2 3
0x99,0x92,0x82,0xF8, // 4 5 6 7
0x80,0x90,0x88,0x83, // 8 9 A B
0xC6,0xA1,0x86,0x8E // C D E F
};
共阳极逻辑:段脚输出高电平熄灭、低电平点亮,数组下标对应 0~F 十六进制数值。
三、全局变量说明
c
运行
uchar uart_rec_buf; // 串口单字节接收缓存,存储上位机下发指令
bit rec_flag = 0; // 接收完成标志位,bit型占用单bit存储空间
uart_rec_buf:中断中缓存串口接收字节,避免中断内处理耗时操作;rec_flag:中断置 1,主循环查询标志处理显示逻辑,分离中断与业务逻辑。
四、函数接口详细说明
4.1 延时函数 void delay_ms(uint16 x)
函数功能
软件毫秒级延时,仅用于预留拓展(本程序未调用,可用于多数码管动态扫描)。
参数
uint16 x:延时毫秒数
实现说明
双层循环软件延时,适配 11.0592MHz 标准晶振。
4.2 串口单字节发送 void UART_SendByte(uchar dat)
函数功能
通过硬件 UART 发送 1 字节数据,阻塞等待发送完成。
参数
uchar dat:待发送 8 位数据
执行流程
- 将数据写入发送寄存器 SBUF 启动发送;
- 循环等待发送完成标志 TI 置 1;
- 软件清零 TI 标志,退出函数。
4.3 串口字符串发送 void UART_SendString(uchar *str)
函数功能
循环调用单字节发送函数,输出以\0结尾的字符串。
参数
uchar *str:字符串首地址
执行流程
遍历字符串,逐字节发送,检测到结束符\0停止。
4.4 串口初始化 void UART_Init(uchar th_val)
函数功能
配置定时器 1、串口寄存器、中断总开关,初始化串口波特率。
参数
uchar th_val:定时器 1 高 8 位重装初值(波特率对应值)
寄存器配置拆解
TMOD |= 0x20:Timer1 设为模式 2(8 位自动重装);SCON = 0x50:串口模式 1,8 位 UART,允许接收 REN=1;PCON |= 0x80:SMOD=1,波特率加倍;- TH1、TL1 装载波特率初值;
ES=1开启串口中断,EA=1开启总中断;TR1=1启动定时器 1 产生波特率时钟。
4.5 数码管全熄灭 void SMG_AllOff(void)
函数功能
共阳极数码管全部熄灭。
实现逻辑
P0 端口输出0xFF,所有段脚高电平,数码管无点亮段。
4.6 串口中断服务函数 void UART_ISR() interrupt UART_INT_NUM
函数功能
串口接收中断处理,仅缓存数据、置接收标志,无耗时操作。
中断触发条件
RXD 收到完整 1 字节数据,RI 硬件置 1 触发中断。
执行流程
- 判断接收标志 RI;
- 软件清零 RI;
- 将 SBUF 接收数据存入全局缓存
uart_rec_buf; - 置位
rec_flag=1通知主循环处理。
设计规范
中断函数禁止延时、数码管 IO 操作,保证中断快速退出,不干扰其他中断。
4.7 主函数 void main(void)
函数功能
系统入口,初始化外设、上电提示、循环检测串口指令并驱动数码管。
执行流程
- 调用
UART_Init初始化串口 9600 波特率; - 串口发送就绪提示字符串;
- 数码管上电熄灭;
- 死循环查询接收标志
rec_flag:- 收到新指令后清零标志;
- 读取缓存字符,先熄灭数码管;
- switch 匹配字符
0~9,a/A~f/F,输出对应段码; - 非法字符保持数码管熄灭。
五、程序工作流程
- 上电初始化:串口、定时器、中断开启,打印就绪提示,数码管熄灭;
- 上位机下发字符:串口硬件接收触发中断;
- 中断缓存数据:中断保存字节,置接收标志后立即退出;
- 主循环响应指令:检测到标志后解析字符,输出对应段码到 P0 驱动数码管;
- 非法字符处理:收到非 0~F 字符,数码管保持熄灭。
六、通信协议规范
6.1 串口参数
- 波特率:9600
- 数据位:8
- 校验位:无
- 停止位:1
6.2 指令集
表格
| 上位机发送字符 | 数码管显示内容 |
|---|---|
| '0' ~ '9' | 数字 0~9 |
| 'A' / 'a' | A |
| 'B' / 'b' | B |
| 'C' / 'c' | C |
| 'D' / 'd' | D |
| 'E' / 'e' | E |
| 'F' / 'f' | F |
| 其他任意字符 | 数码管熄灭 |
6.3 上电打印信息
UART Ready! Send 0-9 A-F to display\r\n 串口助手上电自动接收,提示设备就绪。
七、硬件注意事项
- P0 口为开漏输出,驱动数码管建议外接上拉电阻(10K 排阻),否则亮度不足;
- 晶振必须使用 11.0592MHz,否则 9600 波特率存在较大误差,串口乱码;
- 数码管为共阳极 ,若使用共阴极需重新修改
gsmg_code段码表; - 单数码管无位选引脚,如需多位数码管拓展,需增加位选 IO 并添加动态扫描逻辑。
八、程序优点
- 中断接收,不阻塞主循环,实时响应上位机;
- 中断轻量化,无延时与 IO 操作,系统实时性高;
- 硬件端口、波特率使用宏定义,便于硬件修改移植;
- 大小写字母兼容,上位机操作更友好;
- 分层封装:发送、初始化、显示功能独立函数,可读性强;
- 非法字符容错,避免乱码导致数码管误显示。
cpp
#include "reg52.h"
// 类型重定义统一
typedef unsigned char uchar;
typedef unsigned int uint;
#define uint8 uchar
#define uint16 uint
// 系统参数宏定义
#define BAUD_9600_TH 0xFA
#define UART_INT_NUM 4
// 数码管端口定义 P0为段码输出
#define SMG_A_DP_PORT P0
// 共阳极数码管 0~F 段码表
uint8 gsmg_code[16]={
0xC0,0xF9,0xA4,0xB0,
0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,
0xC6,0xA1,0x86,0x8E
};
// 全局变量
uchar uart_rec_buf; // 串口接收缓存
bit rec_flag = 0; // 接收完成标志
/****************************
* @brief ms延时函数
****************************/
void delay_ms(uint16 x)
{
uint16 j,i;
for(j=0;j<x;j++)
{
for(i=0;i<1100;i++);
}
}
/****************************
* @brief 串口发送单个字节
****************************/
void UART_SendByte(uchar dat)
{
SBUF = dat;
while(TI == 0);
TI = 0;
}
/****************************
* @brief 串口发送字符串
****************************/
void UART_SendString(uchar *str)
{
while(*str != '\0')
{
UART_SendByte(*str++);
}
}
/****************************
* @brief 串口初始化 9600波特率
****************************/
void UART_Init(uchar th_val)
{
TMOD |= 0x20;
SCON = 0x50;
PCON |= 0x80;
TH1 = th_val;
TL1 = th_val;
ES = 1;
EA = 1;
TR1 = 1;
}
/****************************
* @brief 数码管全灭(输出0xFF,共阳极全熄灭)
****************************/
void SMG_AllOff(void)
{
SMG_A_DP_PORT = 0xFF;
}
/****************************
* @brief 串口中断服务函数
* 仅缓存数据+置标志,无延时、无IO操作
****************************/
void UART_ISR() interrupt UART_INT_NUM
{
if(RI == 1)
{
RI = 0;
uart_rec_buf = SBUF;
rec_flag = 1; // 标记收到新指令
}
}
/****************************
* @brief 主函数
****************************/
void main(void)
{
uchar cmd;
UART_Init(BAUD_9600_TH);
UART_SendString("UART Ready! Send 0-9 A-F to display\r\n");
SMG_AllOff(); // 上电数码管熄灭
while(1)
{
if(rec_flag == 1)
{
rec_flag = 0;
cmd = uart_rec_buf;
SMG_AllOff(); // 收到指令先熄灭数码管
switch(cmd)
{
case '0': SMG_A_DP_PORT = gsmg_code[0]; break;
case '1': SMG_A_DP_PORT = gsmg_code[1]; break;
case '2': SMG_A_DP_PORT = gsmg_code[2]; break;
case '3': SMG_A_DP_PORT = gsmg_code[3]; break;
case '4': SMG_A_DP_PORT = gsmg_code[4]; break;
case '5': SMG_A_DP_PORT = gsmg_code[5]; break;
case '6': SMG_A_DP_PORT = gsmg_code[6]; break;
case '7': SMG_A_DP_PORT = gsmg_code[7]; break;
case '8': SMG_A_DP_PORT = gsmg_code[8]; break;
case '9': SMG_A_DP_PORT = gsmg_code[9]; break;
case 'A':
case 'a': SMG_A_DP_PORT = gsmg_code[10]; break;
case 'B':
case 'b': SMG_A_DP_PORT = gsmg_code[11]; break;
case 'C':
case 'c': SMG_A_DP_PORT = gsmg_code[12]; break;
case 'D':
case 'd': SMG_A_DP_PORT = gsmg_code[13]; break;
case 'E':
case 'e': SMG_A_DP_PORT = gsmg_code[14]; break;
case 'F':
case 'f': SMG_A_DP_PORT = gsmg_code[15]; break;
default: SMG_AllOff(); break; // 非法字符熄灭数码管
}
}
}
}