基于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协议(工业总线常用),适用于多节点数据采集与控制系统。

相关文章
|
28天前
|
人工智能 自然语言处理 API
Graphify:为代码库构建知识图谱,以图遍历替代向量检索
Graphify 是一款Python代码知识图谱工具,支持Claude Code。它通过AST解析、本地语音转录和语义提取三阶段构建带置信度标签(EXTRACTED/INFERRED/AMBIGUOUS)的结构化图谱,将混合语料查询Token消耗降低71.5倍,大幅提升大型代码库分析效率与可解释性。
457 1
Graphify:为代码库构建知识图谱,以图遍历替代向量检索
|
28天前
|
SQL 关系型数据库 MySQL
【全网最详细】Navicat下载免费版 | Navicat数据库管理工具安装图解(2026最新)
Navicat是一款专业、直观的数据库管理工具,支持MySQL、PostgreSQL、Oracle等十余种主流数据库。图形化界面替代命令行,轻松实现建表、SQL编写、数据导入导出与同步,大幅提升开发与DBA工作效率。(239字)
|
28天前
|
人工智能 安全 量子技术
《三步构建QClaw防幻觉体系,告别虚假信息》
本文直击QClaw使用中最致命的虚假信息痛点,从实践视角剖析大模型幻觉是概率生成的本质属性,而非单纯的模型能力缺陷。文章批判了“加一句不要撒谎”等无效防幻觉方法,系统拆解了从输入到输出再到流程的完整防幻觉体系:输入侧通过限定问题范围、区分事实观点、要求信息来源压缩模型发挥空间;输出侧建立逻辑、交叉、外部三层验证机制;流程侧通过多轮把关与错误库沉淀加固防线。同时澄清了搜索功能无法完全消除幻觉的误区,强调防幻觉的核心是构建可靠工作流程,人类判断力始终是最后一道防线。
|
20天前
|
传感器 数据处理
基于51单片机的土壤湿度检测仪与自动浇水系统设计
以STC89C52RC单片机为核心,结合土壤湿度传感器、水泵驱动电路、LCD显示模块和按键设置,实现土壤湿度的实时检测与自动浇水功能。系统可根据预设湿度阈值自动控制水泵启停,适用于菜园、花园等小型种植场景,具备低功耗、低成本、易部署的特点。
|
28天前
|
人工智能 Java 数据挖掘
Python 为什么是零基础学编程的最佳选择?
Python 的“全能性”让它覆盖了从日常工具到工业级项目的全场景,新手学习后能快速获得“成就感”:
160 3
|
28天前
|
SQL 安全 API
【SpringSecurity新手村系列】(7)基于资源权限码(Authority)的接口权限控制实战
本章完成从“用户-角色-权限资源”数据模型到 `@PreAuthorize` 方法级拦截的完整闭环。和上一章“角色控制(Role)”不同,本章重点是 **资源权限码(Authority)**,即 `clue:list`、`clue:edit` 这类细粒度权限。你将得到一套能直接用于企业项目的权限控制方案,同时规避 `Controller 未注册`、`Mapper SQL 字段写错`、`权限码字段映射错位` 等高频坑位。
174 4
|
28天前
|
数据采集 并行计算
基于MATLAB的时变Copula实现方案
基于MATLAB的时变Copula实现方案
102 2
|
28天前
《深耕QClaw协作逻辑,构建无误解的智能体沟通体系》
本文聚焦QClaw多Agent协作中普遍存在的信息误解难题,跳出常规角色配置的浅层思路,从实践沉淀视角剖析智能体间语义歧义、上下文断层、反馈缺失等核心协作痛点。文章深度阐释专属沟通协议的四层构建逻辑,从语义统一、上下文完整传递,到反馈确认、分歧调和,给出可落地的搭建思路与落地准则。结合真实实践对比,印证规范沟通协议能大幅消解认知偏差、减少协作内耗,打破多数人只优化单体能力、忽视交互规则的误区。
125 1
|
28天前
|
JSON 安全 Java
【SpringSecurity新手村系列】(6)基于角色的权限控制、权限拦截注解与自定义无权限页面
本章在 RBAC 角色体系上,开启 @EnableMethodSecurity,使用 @PreAuthorize 配合 hasRole / hasAnyRole 实现 Controller 方法级拦截,并配置自定义 403 无权限页面,重点拆解 ROLE_ 前缀重复拼接的常见坑位。
137 1
|
28天前
|
编解码 算法 数据可视化
雷达脉冲多普勒体制,HPRF波形的重频选择策略
雷达脉冲多普勒体制,HPRF波形的重频选择策略

热门文章

最新文章