基于 51 单片机的多功能嵌入式系统代码分析笔记
一、项目概述
本项目是一个基于 8051 单片机的嵌入式系统,实现了 UART 通信、LED 控制、数码管显示、蜂鸣器频率控制、DS18B20 温度传感器读取等多种功能。系统采用模块化设计,通过自定义的通信协议接收命令并执行相应操作。
二、核心模块分析
1. 主程序模块 (main.c)
通信协议格式
协议帧结构:AA ADDR FUNC DATA1 DATA2 SUM BB
字节位置: 0 1 2 3 4 5 6
长度:固定 7 字节
字段说明:
AA:帧头 (0xAA)
ADDR:设备地址 (0x01)
FUNC:功能码 (1-4)
DATA1:数据1(数码管位选/功能参数)
DATA2:数据2(LED状态/数码管段选/蜂鸣器频率)
SUM:校验和(前5字节累加和)
BB:帧尾 (0xBB)
主要函数分析
1.1 数据解析函数 parse()
cpp
int parse(void)
{
int ret = 0;
int i = 0;
unsigned char sum = 0;
// 检查帧头和帧尾
if (recv_buffer[0] == 0xAA && recv_buffer[6] == 0xBB)
{
// 检查设备地址
if(recv_buffer[1] == DEV_ADDRESS)
{
// 计算校验和
for(i = 0; i < 5; i++)
{
sum += recv_buffer[i];
}
// 验证校验和
if(sum == recv_buffer[5])
{
ret = recv_buffer[2]; // 返回功能码
}
}
}
return ret;
}
功能:验证接收数据的合法性,提取功能码
1.2 功能执行函数 do_handler()
cpp
void do_handler(unsigned int n)
{
switch (n)
{
case 1:
led_show(recv_buffer[4]); // LED控制
break;
case 2:
digiter_show(recv_buffer[4]); // 数码管显示
break;
case 3:
beep_choose(recv_buffer[4]); // 蜂鸣器频率选择
break;
case 4:
ds18b20_printf(recv_buffer[4]); // 温度读取
break;
default:
break;
}
}
1.3 响应回调函数 callback()
cpp
void callback()
{
unsigned char send_buffer[10] = {0};
unsigned char sum = 0;
int i = 0;
memcpy(send_buffer, recv_buffer, 7);
send_buffer[2] |= (1 << 7); // 设置响应标志位
// 重新计算校验和
if (send_buffer[0] == 0xAA && send_buffer[6] == 0xBB)
{
if(send_buffer[1] == DEV_ADDRESS)
{
for(i = 0; i < 5; i++)
{
sum += send_buffer[i];
}
send_buffer[5] = sum;
}
}
uart_sendbuffer(send_buffer, 7); // 发送响应
}
2. 串口通信模块 (uart.c)
2.1 串口初始化
cpp
void uart_init(void)
{
// SCON: 串口控制寄存器
SCON &= ~(3 << 6); // 清除SM0, SM1位
SCON |= (1 << 6); // SM0=0, SM1=1 (模式1: 8位UART,波特率可变)
SCON |= (1 << 4); // REN=1 (允许接收)
// PCON: 电源控制寄存器
PCON &= ~(1 << 6); // SMOD0=0
PCON |= (1 << 7); // SMOD1=1 (波特率加倍)
// TMOD: 定时器模式寄存器
TMOD &= ~(0x0F << 4); // 清除T1相关位
TMOD |= (1 << 5); // T1模式1 (16位定时器)
// 波特率设置: 9600bps (SMOD=1)
// 计算公式: 波特率 = (2^SMOD / 32) * (fosc / (256 - TH1))
TL1 = 232; // 重载值
TH1 = 232; // 定时器初值
TCON |= (1 << 6); // TR1=1 (启动定时器1)
// IE: 中断使能寄存器
IE |= (1 << 7); // EA=1 (全局中断使能)
IE |= (1 << 4); // ES=1 (串口中断使能)
}
2.2 串口接收中断
cpp
void uart_recvhandler(void) interrupt 4
{
if ((SCON & (1 << 0)) == 1) // 检查RI标志位
{
if(pos < 32) // 缓冲区未满
{
recv_buffer[pos++] = SBUF; // 读取数据
recv_buffer[pos] = 0; // 字符串结束符
}
SCON &= ~(1 << 0); // 清除RI标志
}
}
3. 定时器模块 (timer.c)
3.1 定时器0初始化
cpp
void timer0_init(void)
{
// TMOD配置
TMOD &= ~(0x0F << 0); // 清除T0相关位
TMOD |= (1 << 0); // T0模式1 (16位定时器)
// 定时器初值
TH0 = g_i >> 8; // 高8位
TL0 = g_i; // 低8位
TCON |= (1 << 4); // TR0=1 (启动定时器0)
// 中断使能
IE |= (1 << 7); // EA=1
IE |= (1 << 1); // ET0=1 (定时器0中断使能)
}
3.2 定时器0中断服务函数
cpp
void timer0_handler(void) interrupt 1
{
// 重新加载定时器初值
TH0 = g_i >> 8;
TL0 = g_i;
// P2.1引脚电平翻转(用于蜂鸣器控制)
P2 ^= (1 << 1);
}
4. DS18B20温度传感器模块
4.1 复位时序
cpp
int ds18b20_reset(void)
{
int t = 0;
// 主机拉低总线480μs-960μs
DQ_DOWN;
delay10us(70); // 700μs
// 主机释放总线
DQ_HIGH;
delay10us(6); // 60μs
// 等待DS18B20响应信号
while(DQ_CHECK && t < 30) // 检测低电平
{
delay10us(1);
t++;
}
if(t >= 30) return 0; // 超时失败
t = 0;
// 等待DS18B20释放总线
while(!DQ_CHECK && t < 30) // 检测高电平
{
delay10us(1);
t++;
}
if(t >= 30) return 0; // 超时失败
return 1; // 复位成功
}
4.2 温度读取流程
cpp
float get_temp(void)
{
unsigned char tl = 0;
unsigned char th = 0;
short t = 0;
// 1. 发送温度转换命令
ds18b20_reset();
ds18b20_write(0xCC); // 跳过ROM
ds18b20_write(0x44); // 温度转换
delaylms(1000); // 等待转换完成
// 2. 读取温度数据
ds18b20_reset();
ds18b20_write(0xCC); // 跳过ROM
ds18b20_write(0xBE); // 读暂存器
tl = ds18b20_read(); // 低字节
th = ds18b20_read(); // 高字节
// 3. 计算温度值
t = th << 8;
t |= tl;
return t * 0.0625; // 12位精度,LSB = 0.0625°C
}
5. LED控制模块
cpp
// LED初始化:所有LED灭
void led_init(void)
{
P2 = 0xFF; // P2口高电平,LED灭(共阳接法)
}
// 显示指定LED(位操作)
void led_show(unsigned int n)
{
P2 = ~n; // 取反,相应位为0的LED亮
}
三、通信协议详细说明
协议帧示例:
控制LED:
AA 01 01 00 0F SUM BB
解释:
AA: 帧头
01: 设备地址
01: 功能码(LED控制)
00: 数据1(未使用)
0F: 数据2(二进制00001111,低4位LED亮)
SUM: 校验和(0xAA+0x01+0x01+0x00+0x0F=0xBB)
BB: 帧尾
功能码定义:
-
1: LED控制 (DATA2: LED状态,低4位有效)
-
2: 数码管显示 (DATA2: 显示内容)
-
3: 蜂鸣器控制 (DATA2: 频率选择1-5)
-
4: 温度读取 (进入连续读取模式)
四、系统工作流程
-
初始化阶段
-
串口初始化(波特率9600)
-
LED初始化
-
定时器0初始化
-
-
主循环
cppwhile(1) { if(pos != 0) // 接收到数据 { delay(0x0FFF); // 短延时确保数据完整 ret = parse(); // 解析数据 if(ret != 0) // 解析成功 { do_handler(ret); // 执行功能 if(ret != 4) // 非温度读取功能需回复 { callback(); // 发送响应 } } pos = 0; // 清空接收位置 } } -
中断处理
-
串口接收中断:将数据存入缓冲区
-
定时器0中断:产生蜂鸣器方波信号
-
五、关键配置参数
蜂鸣器频率对应表
#define HZ_200 63231 // 200Hz对应的定时器初值
#define HZ_400 64383 // 400Hz
#define HZ_600 64767 // 600Hz
#define HZ_800 64959 // 800Hz
#define HZ_1000 65074 // 1000Hz
计算公式 :TH0TL0 = 65536 - fosc/(12*freq),其中fosc=11.0592MHz
串口波特率计算
SMOD=1, TH1=232时:
波特率 = (2^1/32) × 11059200/(256-232) = 9600bps
六、注意事项
-
硬件连接
-
DS18B20数据线连接P3.7
-
LED连接P2口(低电平有效)
-
蜂鸣器连接P2.1
-
数码管连接其他IO口(代码中已注释)
-
-
特殊处理
-
温度读取功能(功能码4)不发送响应,而是进入连续读取模式
-
蜂鸣器控制通过修改全局变量g_i改变频率
-
接收缓冲区大小为32字节,需防止溢出
-
-
调试建议
-
使用串口调试助手发送协议帧
-
注意校验和计算
-
温度读取需要1秒转换时间
-
七、总结
本系统展示了8051单片机在嵌入式控制中的典型应用,涵盖了:
-
自定义通信协议设计
-
多中断协同工作
-
外设驱动开发(LED、蜂鸣器、数码管、温度传感器)
-
模块化编程思想
通过解析通信协议,系统实现了灵活的远程控制功能,具有良好的扩展性,可以方便地添加新的功能模块。