基于51单片机的串口(RS232+485)通信程序设计

简介: 基于STC89C52RC单片机(8位,11.0592MHz晶振),实现RS232全双工通信与RS485半双工通信双模式。系统通过UART串口连接MAX232(RS232电平转换)和MAX485(RS485差分转换),支持双机通信(点对点)与总线通信(多点),具备数据收发、帧格式自定义、收发切换控制功能,适用于工业控制、仪器仪表通信等场景。

一、系统概述

基于STC89C52RC单片机(8位,11.0592MHz晶振),实现RS232全双工通信与RS485半双工通信双模式。系统通过UART串口连接MAX232(RS232电平转换)和MAX485(RS485差分转换),支持双机通信(点对点)与总线通信(多点),具备数据收发、帧格式自定义、收发切换控制功能,适用于工业控制、仪器仪表通信等场景。

二、硬件设计

2.1 核心组件选型

模块 型号/参数 功能说明
主控 STC89C52RC(8KB Flash,512B RAM) UART通信控制、RS232/RS485模式切换
RS232转换 MAX232(电荷泵电平转换) 将单片机TTL电平(0~5V)转换为RS232电平(±12V)
RS485转换 MAX485(差分收发器) 将TTL电平转换为RS485差分信号(A/B线)
通信接口 DB9(RS232)、凤凰端子(RS485) 连接PC或工业总线设备

2.2 硬件连接图

+-------------------+          +-------------------+          +-------------------+  
|   STC89C52RC       |          |      MAX232        |          |      PC/RS232设备  |  
|  P3.1(TXD) → MAX232|T1IN      | T1OUT → DB9-2(RXD) |          | DB9-3(TXD) → MAX232|R1IN |  
|  P3.0(RXD) ← MAX232|R1OUT      | R1IN ← DB9-3(TXD)  |          | DB9-2(RXD) ← MAX232|T1OUT |  
|  P3.2(CTRL) → MAX485|DE/RE      |                   |          +-------------------+  
|  P3.1(TXD) → MAX485|DI         |                   |          +-------------------+  
|  P3.0(RXD) ← MAX485|RO         |                   |          |    MAX485总线      |  
+-------------------+          +-------------------+          |  A/B线 → 其他节点   |  
                                                           +-------------------+

关键引脚说明

  • MAX232:T1IN(接单片机TXD)、R1OUT(接单片机RXD),实现RS232电平转换。

  • MAX485:DI(接单片机TXD,发送数据)、RO(接单片机RXD,接收数据)、DE/RE(方向控制,高电平发送,低电平接收),由单片机P3.2引脚控制。

三、软件设计(Keil C51)

3.1 开发环境

  • IDE:Keil μVision5(C51编译器)

  • 晶振:11.0592MHz(确保波特率误差<1%)

  • 通信协议:8位数据位、1位停止位、无校验位(8N1),波特率9600bps

3.2 核心原理

  1. UART初始化:配置定时器1为模式2(8位自动重装),作为波特率发生器,计算TH1初值(11.0592MHz下,9600bps对应TH1=0xFD)。

  2. RS232通信:全双工模式,直接通过TXD/RXD收发数据,无需方向控制。

  3. RS485通信:半双工模式,发送前置DE/RE=1(发送模式),发送完毕后置DE/RE=0(接收模式),避免总线冲突。

3.3 核心代码实现

3.3.1 头文件与全局变量

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

// ==================== 引脚定义 ====================
#define RS485_DE_RE P3_2  // RS485方向控制引脚(高=发送,低=接收)
#define UART_RX_BUF_SIZE 16  // 接收缓冲区大小
#define UART_TX_BUF_SIZE 16  // 发送缓冲区大小

// ==================== 全局变量 ====================
unsigned char uart_rx_buf[UART_RX_BUF_SIZE];  // 接收缓冲区
unsigned char uart_tx_buf[UART_TX_BUF_SIZE];  // 发送缓冲区
unsigned char rx_cnt = 0;  // 接收计数
unsigned char tx_cnt = 0;  // 发送计数
bit rx_flag = 0;  // 接收完成标志(1=接收一帧完成)

3.3.2 UART初始化(定时器1波特率发生器)

// UART初始化(9600bps,8N1)
void UART_Init() {
   
    SCON = 0x50;        // 模式1(8位UART),允许接收(REN=1)
    TMOD |= 0x20;       // 定时器1模式2(8位自动重装)
    TH1 = 0xFD;         // 波特率9600bps(11.0592MHz:(2^SMOD/32)*(fosc/(12*(256-TH1))) → SMOD=0时,TH1=253=0xFD)
    TL1 = 0xFD;
    TR1 = 1;            // 启动定时器1
    ES = 1;             // 允许串口中断
    EA = 1;             // 开总中断
}

3.3.3 RS232发送函数(查询模式)

// RS232发送一个字节
void RS232_SendByte(unsigned char dat) {
   
    SBUF = dat;          // 数据写入发送缓冲区
    while (!TI);         // 等待发送完成(TI=1)
    TI = 0;              // 清除发送中断标志
}

// RS232发送字符串
void RS232_SendString(unsigned char *str) {
   
    while (*str != '\0') {
   
        RS232_SendByte(*str++);
    }
}

3.3.4 RS485发送函数(含方向控制)

// RS485发送一个字节
void RS485_SendByte(unsigned char dat) {
   
    RS485_DE_RE = 1;     // 置高电平,切换到发送模式
    DelayUs(10);         // 延时10μs,确保方向切换完成(关键!)
    SBUF = dat;          // 发送数据
    while (!TI);         // 等待发送完成
    TI = 0;
    DelayUs(10);         // 延时10μs,确保最后一个字节发送完成
    RS485_DE_RE = 0;     // 置低电平,切换到接收模式
}

// RS485发送字符串
void RS485_SendString(unsigned char *str) {
   
    RS485_DE_RE = 1;     // 发送模式
    DelayUs(10);
    while (*str != '\0') {
   
        SBUF = *str++;
        while (!TI);
        TI = 0;
    }
    DelayUs(10);
    RS485_DE_RE = 0;     // 接收模式
}

3.3.5 串口中断服务函数(接收数据)

// 串口中断(接收数据)
void UART_ISR() interrupt 4 {
   
    if (RI) {
               // 接收中断标志
        RI = 0;          // 清除接收标志
        uart_rx_buf[rx_cnt++] = SBUF;  // 存入接收缓冲区
        if (rx_cnt >= UART_RX_BUF_SIZE) rx_cnt = 0;  // 缓冲区溢出保护
        if (SBUF == '\n') rx_flag = 1;  // 检测到换行符,标记接收完成(可自定义帧尾)
    }
}

3.3.6 延时函数(微秒级)

// 微秒延时(11.0592MHz晶振,1μs≈12个机器周期)
void DelayUs(unsigned int us) {
   
    while (us--) {
   
        _nop_(); _nop_(); _nop_(); _nop_();  // 4个_nop_≈1μs(误差±1μs)
        _nop_(); _nop_(); _nop_(); _nop_();
    }
}

3.3.7 主函数(测试逻辑)

void main() {
   
    UART_Init();       // 初始化UART
    RS485_DE_RE = 0;    // 初始为接收模式(RS485)

    while (1) {
   
        // 示例1:RS232接收PC数据并回传(Echo)
        if (rx_flag) {
   
            rx_flag = 0;
            RS232_SendString("RS232 Recv: ");
            RS232_SendString(uart_rx_buf);  // 回传接收数据
            rx_cnt = 0;  // 清空缓冲区
        }

        // 示例2:RS485定时发送数据(每2秒发送一次)
        static unsigned int timer = 0;
        if (++timer >= 2000) {
     // 2秒(假设主循环1ms一次)
            timer = 0;
            RS485_SendString("RS485 Test: Hello!\r\n");  // 发送测试字符串
        }

        DelayMs(1);  // 主循环延时1ms(需实现DelayMs函数)
    }
}

// 毫秒延时(粗略)
void DelayMs(unsigned int ms) {
   
    unsigned int i, j;
    for (i=0; i<ms; i++)
        for (j=0; j<110; j++);  // 110次循环≈1ms(11.0592MHz)
}

参考代码 基于51单片机的串口(RS232+485)通信程序 www.youwenfan.com/contentalh/182671.html

四、关键问题与解决方案

4.1 波特率误差

  • 问题:51单片机定时器1重载值计算错误导致波特率偏差(如12MHz晶振下9600bps误差大)。

  • 解决:使用11.0592MHz晶振(可被1.8432MHz整除,波特率误差为0),计算公式:

    $TH1=256−\frac{fosc}{12×32×波特率}$

    (9600bps时,TH1=256 - 11059200/(12×32×9600)=256-3=253=0xFD)。

4.2 RS485收发冲突

  • 问题:发送未完成时切换为接收模式,导致数据丢失。

  • 解决

  • 发送前后添加10μs延时(确保MAX485方向切换完成);

  • 总线末端并联120Ω终端电阻,减少信号反射。

4.3 接收数据溢出

  • 问题:缓冲区过小或中断未及时响应导致数据覆盖。

  • 解决

  • 增大缓冲区(如32字节);

  • 使用环形缓冲区(头指针+尾指针),避免溢出覆盖。

五、测试与验证

5.1 RS232测试

  1. 硬件连接:单片机TXD/RXD→MAX232→DB9串口线→PC串口。

  2. 工具:PC端使用串口助手(如SSCOM),设置波特率9600、8N1。

  3. 预期结果:PC发送“Hello”,单片机回传“RS232 Recv: Hello”。

5.2 RS485测试

  1. 硬件连接:两台单片机通过MAX485模块连接(A-A相连,B-B相连),终端电阻120Ω。

  2. 测试步骤:主机定时发送“RS485 Test: Hello!”,从机接收后通过RS232转发至PC串口助手。

  3. 预期结果:从机串口助手显示主机发送的测试字符串,无丢包。

六、总结

基于51单片机实现了RS232/RS485双串口通信,核心是UART初始化、波特率精确控制与RS485方向切换时序。通过模块化设计(发送/接收分离、中断处理),可扩展为Modbus RTU协议(工业总线常用),适用于多节点数据采集与控制系统。

相关文章
|
6天前
|
缓存 人工智能 自然语言处理
我对比了8个Claude API中转站,踩了不少坑,总结给你
本文是个人开发者耗时1周实测的8大Claude中转平台横向评测,聚焦Claude Code真实体验:以加权均价(¥/M token)、内部汇率、缓存支持、模型真实性及稳定性为核心指标。
2638 18
|
18天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
16135 48
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
14天前
|
人工智能 JavaScript Ubuntu
低成本搭建AIP自动化写作系统:Hermes保姆级使用教程,长文和逐步实操贴图
我带着怀疑的态度,深度使用了几天,聚焦微信公众号AIP自动化写作场景,写出来的几篇文章,几乎没有什么修改,至少合乎我本人的意愿,而且排版风格,也越来越完善,同样是起码过得了我自己这一关。 这个其实OpenClaw早可以实现了,但是目前我觉得最大的区别是,Hermes会自主总结提炼,并更新你的写作技能。 相信就冲这一点,就值得一试。 这篇帖子主要就Hermes部署使用,作一个非常详细的介绍,几乎一步一贴图。 关于Hermes,无论你赞成哪种声音,我希望都是你自己动手行动过,发自内心的选择!
3079 29
|
3天前
|
云安全 人工智能 安全
|
3天前
|
人工智能 测试技术 API
阿里Qwen3.6-27B正式开源:网友直呼“太牛了”!
阿里云千问3.6系列重磅开源Qwen3.6-27B稠密大模型!官网:https://t.aliyun.com/U/JbblVp 仅270亿参数,编程能力媲美千亿模型,在SWE-bench等权威基准中表现卓越。支持多模态理解、本地部署及OpenClaw等智能体集成,已开放Hugging Face与ModelScope下载。
|
2天前
|
机器学习/深度学习 缓存 测试技术
DeepSeek-V4开源:百万上下文,Agent能力比肩顶级闭源模型
DeepSeek-V4正式开源!含V4-Pro(1.6T参数)与V4-Flash(284B参数)双版本,均支持百万token上下文。首创混合注意力架构,Agent能力、世界知识与推理性能全面领先开源模型,数学/代码评测比肩顶级闭源模型。
1404 6