应用——基于51单片机的串口通信与LED控制系统

基于51单片机的串口通信与LED控制系统

项目概述

本项目实现了一个基于51单片机的串口通信系统,支持通过串口命令控制LED显示,并具备完整的数据校验和应答机制。系统采用自定义通信协议,确保数据传输的可靠性。

目录结构

复制代码
project/
├── main.c          // 主程序文件
├── uart.c          // 串口驱动程序
├── uart.h          // 串口头文件
├── led.c           // LED控制程序
├── led.h           // LED控制头文件
├── delay.c         // 延时函数
└── delay.h         // 延时函数头文件

一、核心数据结构

1. 串口接收缓冲区

复制代码
// 定义在uart.c中
xdata unsigned char recv_buffer[32];  // 外部RAM中的接收缓冲区
unsigned int pos = 0;                  // 接收数据位置指针

说明:

  • 使用xdata关键字将缓冲区分配到外部RAM(64KB空间)

  • 缓冲区大小32字节,足够存储多组命令

  • pos变量记录当前接收位置,用于防止缓冲区溢出

二、串口驱动模块

1. 串口初始化函数

复制代码
// uart.c - 串口初始化
void uart_init(void)
{
    // 串口控制寄存器配置
    SCON &= ~(3 << 6);         // 清SM0、SM1位(bit6,7)
    SCON |= (1 << 6);          // SM1=1,选择工作模式1(8位UART)
    SCON |= (1 << 4);          // REN=1,允许串口接收数据
    
    // 电源控制寄存器配置
    PCON &= ~(1 << 6);         // 清SMOD0位
    PCON |= (1 << 7);          // SMOD=1,波特率加倍
    
    // 定时器模式寄存器配置
    TMOD &= ~(0x0F << 4);      // 清定时器1的高4位
    TMOD |= (1 << 5);          // 定时器1工作在模式2(8位自动重装)
    
    // 波特率设置(12MHz晶振,2400波特率,SMOD=1)
    // 计算公式:TH1 = 256 - (2^SMOD × fosc) / (384 × 波特率)
    // TH1 = 256 - (2 × 12000000) / (384 × 2400) ≈ 256 - 26.04 ≈ 230
    TL1 = 230;
    TH1 = 230;
    
    // 启动定时器1
    TCON |= (1 << 6);          // TR1=1,启动定时器1
    
    // 中断使能
    IE |= (1 << 7);            // EA=1,开启总中断
    IE |= (1 << 4);            // ES=1,开启串口中断
}

关键配置参数:

  • 工作模式:模式1(8位UART,波特率可变)

  • 波特率:2400 bps

  • 晶振频率:12MHz

  • SMOD:1(波特率加倍)

  • 定时器:定时器1,模式2(8位自动重装)

2. 串口中断服务程序

复制代码
// uart.c - 串口接收中断函数
void uart_RecvHandler(void) interrupt 4
{
    if ((SCON & (1 << 0)) == 1)     // 判断RI位是否为1(接收到数据)
    {
        if (pos < 32)                // 防止缓冲区溢出
        {
            recv_buffer[pos++] = SBUF;  // 读取接收到的数据
            recv_buffer[pos] = 0;      // 添加字符串结束符(可选)
        }
        SCON &= ~(1 << 0);           // RI=0,清除接收中断标志
    }
}

中断机制说明:

  • 中断号:4(8051串口中断固定编号)

  • 触发条件:接收到一字节数据(RI自动置1)

  • 数据处理:存入缓冲区,指针后移

  • 标志清除:必须软件清除RI标志

3. 数据发送函数

复制代码
// uart.c - 发送单个字符
void uart_sendchar(unsigned char ch)
{
    SBUF = ch;                        // 将数据写入发送缓冲区
    
    while ((SCON & (1 << 1)) == 0);   // 等待TI=1(发送完成)
    
    SCON &= ~(1 << 1);                // TI=0,清除发送完成标志
}

// uart.c - 发送字符串
void uart_sendstr(const char *p)
{
    while (*p)                        // 遍历字符串直到结束符
    {
        uart_sendchar(*p++);          // 发送当前字符并指针后移
    }
}

// uart.c - 发送缓冲区数据
void uart_sendbuffer(const char *p, int len)
{
    while (len--)                     // 发送指定长度的数据
    {
        uart_sendchar(*p++);          // 发送当前字节并指针后移
    }
}

发送流程:

  1. 数据写入SBUF寄存器,自动启动发送

  2. 查询TI标志位,等待发送完成

  3. 清除TI标志,准备下一次发送

三、通信协议设计

1. 命令帧格式

复制代码
| 字节 | 名称     | 值      | 说明                   |
|------|----------|---------|------------------------|
| 0    | 帧头     | 0xAA    | 数据帧开始标志         |
| 1    | 地址码   | 0x01    | 设备地址(本机地址)   |
| 2    | 功能码   | 0x01-0x04 | 功能编号             |
| 3    | 保留     | 0x00    | 预留字节               |
| 4    | 数据     | 0x00-0xFF | 控制数据              |
| 5    | 校验和   | SUM     | 前5字节累加和校验      |
| 6    | 帧尾     | 0xBB    | 数据帧结束标志         |

示例命令:

复制代码
控制LED显示:AA 01 01 00 42 EE BB
└── LED显示数据0x42(二进制01000010)

2. 应答帧格式

复制代码
| 字节 | 名称     | 值      | 说明                   |
|------|----------|---------|------------------------|
| 0    | 帧头     | 0xAA    | 应答帧开始标志         |
| 1    | 地址码   | 0x01    | 设备地址               |
| 2    | 功能码   | 0x81    | 原功能码最高位置1      |
| 3    | 保留     | 0x00    | 预留字节               |
| 4    | 数据     | 0x42    | 原数据                 |
| 5    | 校验和   | 0x6E    | 重新计算的校验和       |
| 6    | 帧尾     | 0xBB    | 应答帧结束标志         |

四、命令解析与处理模块

1. 协议解析函数

复制代码
// main.c - 解析接收到的数据
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)  // DEV_ADDRESS定义为0x01
        {
            // 计算校验和(前5字节累加)
            for (i = 0; i < 5; i++)
            {
                sum += recv_buffer[i];
            }
            
            // 校验和验证
            if (sum == recv_buffer[5])
            {
                ret = recv_buffer[2];  // 返回功能码
            }
        }
    }
    
    return ret;  // 返回0表示无效命令,非0为有效功能码
}

校验流程:

  1. 帧头帧尾检查:确保数据帧完整

  2. 地址匹配:验证是否为发送给本机的命令

  3. 校验和验证:防止数据传输错误

2. 命令处理函数

复制代码
// main.c - 根据功能码执行相应操作
void do_handler(unsigned int n)
{
    switch (n)
    {
        case 1:  // LED控制
            led_show(recv_buffer[4]);  // 显示指定模式
            break;
        case 2:  // 数码管显示
            // 数码管控制代码
            break;
        case 3:  // 蜂鸣器控制
            // 蜂鸣器控制代码
            break;
        case 4:  // 温度采集
            // 温度传感器读取代码
            break;
        default:
            break;
    }
}

3. 应答函数

复制代码
// main.c - 向主机发送应答
void callback(void)
{
    unsigned char send_buffer[10];
    unsigned char sum = 0;
    int i = 0;
    
    // 复制接收缓冲区数据到发送缓冲区
    memcpy(send_buffer, recv_buffer, 7);
    
    // 设置应答标志(功能码最高位置1)
    send_buffer[2] |= (1 << 7);
    
    // 重新计算校验和
    for (i = 0; i < 5; i++)
    {
        sum += send_buffer[i];
    }
    send_buffer[5] = sum;  // 更新校验和
    
    // 发送应答数据
    uart_sendbuffer(send_buffer, 7);
}

应答机制:

  • 复制原命令帧

  • 功能码最高位置1(0x01 → 0x81)

  • 重新计算校验和

  • 发送7字节应答帧

五、LED控制模块

1. LED初始化与基本控制

复制代码
// led.c - LED控制函数
#include <reg51.h>
#include "led.h"

// LED初始化(P2口控制LED,高电平熄灭)
void led_init(void)
{
    P2 = 0xFF;  // 全部熄灭
}

// LED全亮
void led_all_on(void)
{
    P2 = 0;     // 全部点亮
}

// LED全灭
void led_all_off(void)
{
    P2 = 0xFF;  // 全部熄灭
}

// LED状态翻转
void led_nor(void)
{
    P2 = P2 ^ 0xFF;  // 异或操作翻转所有位
}

// 指定LED显示模式
void led_show(unsigned int n)
{
    P2 = ~n;  // 取反操作(0亮1灭)
}

LED连接方式:

  • P2口连接8个LED

  • 输出0:LED点亮

  • 输出1:LED熄灭

  • led_show(0x42):显示二进制01000010(第2、7位点亮)

六、主程序流程

复制代码
// main.c - 主函数
int main(void)
{
    int ret = 0;
    
    // 外设初始化
    uart_init();  // 初始化串口(开启中断)
    led_init();   // 初始化LED
    
    // 主循环
    while (1)
    {
        // 检查是否有数据接收
        if (pos != 0)  // 接收缓冲区不为空
        {
            delay(0xAFFF);  // 延时等待数据接收完整
            
            // 解析命令
            ret = Parse();
            
            // 执行命令
            if (ret != 0)
            {
                do_handler(ret);  // 执行功能操作
            }
            
            // 发送应答
            if (ret != 0)
            {
                callback();  // 回复主机
            }
            
            // 重置接收指针
            pos = 0;
        }
    }
    
    return 0;
}

主程序流程:

  1. 初始化串口和LED

  2. 循环检查接收缓冲区

  3. 收到数据后延时确保完整接收

  4. 解析并验证命令

  5. 执行相应功能

  6. 发送应答帧

  7. 重置接收状态

七、延时函数模块

复制代码
// delay.c - 简单延时函数
void delay(unsigned int n)
{
    while (n--);  // n次空循环
}

延时说明:

  • 参数n决定延时时间

  • 12MHz时钟下大致延时:n × 2μs

  • 用于数据接收稳定和操作间隔

八、系统配置与优化建议

1. 波特率精确计算

对于12MHz晶振,2400波特率:

公式:

TH1=256−2SMOD×𝑓osc384×波特率TH1=256−384×波特率2SMOD×fosc​​

计算:

  • SMOD=1:TH1 = 256 - (2 × 12,000,000) / (384 × 2400) ≈ 230

  • 实际值可能需要微调:230或232

2. 缓冲区管理优化

复制代码
// 建议优化:环形缓冲区
#define BUFFER_SIZE 32
xdata unsigned char recv_buffer[BUFFER_SIZE];
unsigned int read_pos = 0;
unsigned int write_pos = 0;

// 中断服务程序修改
void uart_RecvHandler(void) interrupt 4
{
    if (RI)
    {
        recv_buffer[write_pos] = SBUF;
        write_pos = (write_pos + 1) % BUFFER_SIZE;
        RI = 0;
    }
}

3. 错误处理增强

复制代码
// 添加错误码定义
#define ERROR_NONE        0
#define ERROR_FRAME       1
#define ERROR_ADDRESS     2
#define ERROR_CHECKSUM    3

// 增强的解析函数
int ParseWithError(unsigned char *error)
{
    *error = ERROR_NONE;
    
    // 帧头帧尾检查
    if (recv_buffer[0] != 0xAA || recv_buffer[6] != 0xBB)
    {
        *error = ERROR_FRAME;
        return 0;
    }
    
    // ... 其他检查
}

九、常见问题与调试

1. 串口无响应

  • 检查晶振频率:确认是否为12MHz

  • 检查波特率设置:确保TH1值正确

  • 验证硬件连接:TX/RX线序正确

  • 查看中断配置:EA和ES必须置1

2. 数据接收不全

  • 增加延时:确保一帧数据完全接收

  • 检查缓冲区大小:确保足够存储完整帧

  • 验证协议格式:帧头帧尾匹配

3. LED显示异常

  • 检查硬件连接:LED共阳/共阴配置

  • 验证控制逻辑:0亮1灭或相反

  • 测试IO口:直接操作P2口验证

十、项目扩展方向

  1. 添加更多外设控制:数码管、蜂鸣器、继电器等

  2. 实现双向通信:主动上报传感器数据

  3. 增加协议安全性:加密校验、命令授权

  4. 优化性能:中断优先级、DMA传输

  5. 添加配置文件:支持参数动态调整

总结

本项目展示了完整的8051串口通信系统设计,具备以下特点:

  1. 模块化设计:各功能模块分离,便于维护和扩展

  2. 可靠通信协议:帧头帧尾、地址校验、累加和校验三重保障

  3. 中断驱动:高效的数据接收处理机制

  4. 完整应答机制:确保命令执行状态反馈

  5. 易扩展性:支持添加多种外设控制功能

相关推荐
ting_zh10 小时前
STM32F7系列MCU上电启动流程
stm32·单片机·嵌入式硬件
Tel1992530800410 小时前
全新C-Components高压继电器P/N 500-214
单片机·物联网·自动化·工业自动化
五羟基己醛13 小时前
【嵌入式入门】STM32之封装自己的静态链接库(.lib文件)
stm32·单片机·嵌入式硬件
摇滚侠13 小时前
三天学通 Groovy—Groovy 程序设计,Groovy 中的数据类型,笔记 1-13
笔记·groovy
wdfk_prog13 小时前
[Linux]学习笔记系列 -- [driver]base
linux·笔记·学习
am心14 小时前
学习笔记-套餐接口
笔记·学习
思为无线NiceRF14 小时前
UWB 智能门锁系统在现有手机生态下的可行性分析
嵌入式硬件·物联网·智能家居
钿驰科技15 小时前
TC-BL2430无刷电机驱动板在多领域的应用
单片机·嵌入式硬件
boneStudent15 小时前
BLDC电机无感FOC控制代码实例分享
stm32·单片机·嵌入式硬件