找回密码
 立即注册
查看: 300|回复: 1

STC89C52RC, 12MHz 时钟,串口通信, 4800波特率,T1做波特率发生器

[复制链接]
已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25428
发表于 2026-7-16 19:56:30 | 显示全部楼层 |阅读模式
贴如下内容到   STC 【AI助手】,help.stcaimcu.com

STC89C52RC, 12MHz 时钟,串口通信,

4800波特率,T1做波特率发生器,误差是多少,

帮产生1个收到 1个数就对这个数+1后,
再发送出去的,通用串口程序


截图202607161957574144.jpg
回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25428
 楼主| 发表于 2026-7-16 19:58:30 | 显示全部楼层

好的,手册数据已获取。下面我为您完整解答。


一、4800bps @ 12MHz 的波特率误差计算

根据 STC89C52RC 手册"串行通信中波特率的设置"章节,STC89C52RC 默认工作在 12T模式(传统8051模式),定时器1方式2(8位自动重装)作为波特率发生器时:

公式:

方式1、3波特率 = 2SMOD / 32 × (定时器/计数器1的溢出率)

12T模式下,定时器1溢出率 = SYSclk / [12 × (256 − N)]

您的配置: SMOD=1(PCON|=0x80),N=0xF3=243,SYSclk=12MHz

计算过程:

溢出率 = 12,000,000 / [12 × (256 − 243)]
       = 12,000,000 / (12 × 13)
       = 12,000,000 / 156
       ≈ 76,923.08 Hz

实际波特率 = 2^1 / 32 × 76,923.08
           = 2 / 32 × 76,923.08
           = 1/16 × 76,923.08
           ≈ 4807.69 bps

理论目标波特率 = 4800 bps

误差 = (4807.69 − 4800) / 4800 × 100%
     ≈ +0.16%

结论:误差约为 +0.16%。

这个误差值在 理论上是可接受的(串口异步通信通常要求误差 < 2%),但实际使用中仍可能出问题,原因在于:

  1. 12MHz 无法整数分频出 4800,0.16% 是理论计算值,实际晶振本身还有制造误差
  2. PC 端 USB 转串口芯片(如 CH340、PL2303)的波特率也是近似值,双方误差叠加后可能超过容限
  3. 手册中 4800bps 的标准推荐配置是 11.059MHz + SMOD=0 + 重装值 0xFA,12MHz 不在推荐列表中

建议: 如果坚持用 12MHz,改用 2400bps(SMOD=1,重装值 0xE6)误差更小,或直接换 11.0592MHz 晶振。


二、串口回显程序(收到1个数+1后发回)

以下是完整的单文件 C 程序,实现 串口中断接收,收到一个字节后加1再发送回去:

/*****************************************************************************
 * 文件名: main.c
 * 描  述: STC89C52RC 串口通信——收到1个数+1后发回
 * 硬件连接: P3.0(RxD) — USB转串口模块的TxD
 *           P3.1(TxD) — USB转串口模块的RxD
 * 晶  振: 12.000MHz
 * 波特率: 4800bps (SMOD=1, 定时器1方式2, 重装值0xF3)
 * 编译环境: Keil C51
 * 
 * 操作说明:
 *   1. 用 STC-ISP 软件下载程序到单片机
 *   2. 打开串口助手,波特率设为4800
 *   3. 发送任意1个字节,单片机收到后+1再发回
 *   4. 例如:发送 0x01 → 收到 0x02;发送 0x66 → 收到 0x67
 *****************************************************************************/
#include <REGX52.h>

/* 接收缓冲区 */
volatile unsigned char rx_data;      // 存放接收到的数据
volatile unsigned char rx_flag;      // 接收完成标志,1=已收到新数据

/*****************************************************************************
 * 函数名: UART_Init
 * 功  能: 串口初始化,4800bps@12.000MHz
 * 参  数: 无
 * 返回值: 无
 *****************************************************************************/
void UART_Init(void)
{
    SCON = 0x50;            // 方式1(8位UART),REN=1允许接收
    PCON |= 0x80;           // SMOD=1,波特率加倍

    TMOD &= 0x0F;           // 清除定时器1的模式位
    TMOD |= 0x20;           // 定时器1,方式2(8位自动重装)

    TH1 = 0xF3;             // 4800bps 重装值 (12MHz, SMOD=1)
    TL1 = 0xF3;

    ET1 = 0;                // 禁止定时器1中断(波特率发生器不需要)
    ES  = 1;                // 使能串口中断
    EA  = 1;                // 开启总中断

    TR1 = 1;                // 启动定时器1
}

/*****************************************************************************
 * 函数名: UART_SendByte
 * 功  能: 串口发送1个字节(查询方式)
 * 参  数: dat — 要发送的数据
 * 返回值: 无
 *****************************************************************************/
void UART_SendByte(unsigned char dat)
{
    SBUF = dat;             // 将数据写入发送缓冲器,启动发送
    while (TI == 0);        // 等待发送完成(TI被硬件置1)
    TI = 0;                 // 软件清除TI标志
}

/*****************************************************************************
 * 函数名: UART_ISR
 * 功  能: 串口中断服务函数
 * 说  明: 收到数据后存入rx_data,置位rx_flag
 *         中断服务程序尽量简洁,数据处理放在主循环中
 *****************************************************************************/
void UART_ISR(void) interrupt 4
{
    if (RI == 1)            // 接收中断
    {
        RI = 0;             // 清除接收中断标志
        rx_data = SBUF;     // 读取接收到的数据
        rx_flag = 1;        // 置位接收标志
    }
    /* TI发送中断未使用,因为发送采用查询方式 */
}

/*****************************************************************************
 * 函数名: main
 * 功  能: 主函数,初始化串口后循环处理接收数据
 * 参  数: 无
 * 返回值: 无
 *****************************************************************************/
void main(void)
{
    unsigned char temp;

    rx_flag = 0;            // 初始化接收标志

    UART_Init();            // 初始化串口

    while (1)
    {
        if (rx_flag == 1)   // 检查是否收到新数据
        {
            rx_flag = 0;    // 清除标志
            temp = rx_data; // 读取收到的数据
            temp = temp + 1;// 数据+1
            UART_SendByte(temp);  // 发送回去
        }
    }
}

程序说明

项目 说明
波特率 4800bps @ 12.000MHz,SMOD=1,重装值 0xF3
接收方式 中断接收,不占用CPU轮询
发送方式 查询发送,简单可靠
回显逻辑 收到1字节 → 该字节+1 → 发回
中断服务 仅做最简操作(读SBUF、清标志),数据处理在主循环中完成

测试方法

  1. 用 STC-ISP 下载软件 将程序烧录到 STC89C52RC
  2. 打开串口助手,设置:波特率 4800,数据位 8,停止位 1,无校验
  3. 发送 0x01 → 应收到 0x02
  4. 发送 0x66 → 应收到 0x67
  5. 发送 0xFF → 应收到 0x00(0xFF+1=0x100,溢出后为0x00)

如果仍然只能收到 0x00

请检查以下两点:

  1. 晶振频率:确认板子上是 12.000MHz 还是 11.0592MHz?如果是 11.0592MHz,请将重装值改为 0xFA,并将 PCON |= 0x80; 改为 PCON &= 0x7F;(SMOD=0)
  2. 串口助手波特率:确认串口助手的波特率设置与单片机一致(4800)
  3. 接线:确认 P3.0(RxD) 接 USB转串口的 TxD,P3.1(TxD) 接 RxD,交叉连接

🔍 搜索 "STC89C52RC 12MHz 时钟 串口通信"

⏱️ 查找用时 3.1s | 总回答用时 21.3s

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-7 14:42 , Processed in 0.069961 second(s), 46 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表