神农鼎 发表于 6 天前

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

贴如下内容到   STC 【AI助手】,help.stcaimcu.com

STC89C52RC, 12MHz 时钟,串口通信,
4800波特率,T1做波特率发生器,误差是多少,

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



神农鼎 发表于 6 天前

<p>好的,手册数据已获取。下面我为您完整解答。</p>
<hr />
<h2>一、4800bps @ 12MHz 的波特率误差计算</h2>
<p>根据 STC89C52RC 手册&quot;串行通信中波特率的设置&quot;章节,STC89C52RC 默认工作在 <strong>12T模式</strong>(传统8051模式),定时器1方式2(8位自动重装)作为波特率发生器时:</p>
<p><strong>公式:</strong></p>
<blockquote>
<p>方式1、3波特率 = 2<sup>SMOD</sup> / 32 × (定时器/计数器1的溢出率)</p>
<p>12T模式下,定时器1溢出率 = SYSclk / </p>
</blockquote>
<p><strong>您的配置:</strong> SMOD=1(PCON|=0x80),N=0xF3=243,SYSclk=12MHz</p>
<p><strong>计算过程:</strong></p>
<pre><code>溢出率 = 12,000,000 /
       = 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%
</code></pre>
<p><strong>结论:误差约为 +0.16%。</strong></p>
<p>这个误差值在 <strong>理论上是可接受的</strong>(串口异步通信通常要求误差 &lt; 2%),但实际使用中仍可能出问题,原因在于:</p>
<ol>
<li><strong>12MHz 无法整数分频出 4800</strong>,0.16% 是理论计算值,实际晶振本身还有制造误差</li>
<li><strong>PC 端 USB 转串口芯片</strong>(如 CH340、PL2303)的波特率也是近似值,双方误差叠加后可能超过容限</li>
<li>手册中 <strong>4800bps 的标准推荐配置是 11.059MHz + SMOD=0 + 重装值 0xFA</strong>,12MHz 不在推荐列表中</li>
</ol>
<p><strong>建议:</strong> 如果坚持用 12MHz,改用 <strong>2400bps</strong>(SMOD=1,重装值 0xE6)误差更小,或直接换 <strong>11.0592MHz 晶振</strong>。</p>
<hr />
<h2>二、串口回显程序(收到1个数+1后发回)</h2>
<p>以下是完整的单文件 C 程序,实现 <strong>串口中断接收,收到一个字节后加1再发送回去</strong>:</p>
<pre><code class="language-c">/*****************************************************************************
* 文件名: 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 &lt;REGX52.h&gt;

/* 接收缓冲区 */
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 &amp;= 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);// 发送回去
      }
    }
}
</code></pre>
<h3>程序说明</h3>
<table>
<thead>
<tr>
<th align="left">项目</th>
<th align="left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><strong>波特率</strong></td>
<td align="left">4800bps @ 12.000MHz,SMOD=1,重装值 0xF3</td>
</tr>
<tr>
<td align="left"><strong>接收方式</strong></td>
<td align="left"><strong>中断接收</strong>,不占用CPU轮询</td>
</tr>
<tr>
<td align="left"><strong>发送方式</strong></td>
<td align="left"><strong>查询发送</strong>,简单可靠</td>
</tr>
<tr>
<td align="left"><strong>回显逻辑</strong></td>
<td align="left">收到1字节 → 该字节+1 → 发回</td>
</tr>
<tr>
<td align="left"><strong>中断服务</strong></td>
<td align="left">仅做最简操作(读SBUF、清标志),数据处理在主循环中完成</td>
</tr>
</tbody>
</table>
<h3>测试方法</h3>
<ol>
<li>用 <strong>STC-ISP 下载软件</strong> 将程序烧录到 STC89C52RC</li>
<li>打开串口助手,设置:<strong>波特率 4800</strong>,数据位 8,停止位 1,无校验</li>
<li>发送 <strong>0x01</strong> → 应收到 <strong>0x02</strong></li>
<li>发送 <strong>0x66</strong> → 应收到 <strong>0x67</strong></li>
<li>发送 <strong>0xFF</strong> → 应收到 <strong>0x00</strong>(0xFF+1=0x100,溢出后为0x00)</li>
</ol>
<h3>如果仍然只能收到 0x00</h3>
<p>请检查以下两点:</p>
<ol>
<li><strong>晶振频率</strong>:确认板子上是 12.000MHz 还是 11.0592MHz?如果是 11.0592MHz,请将重装值改为 <strong>0xFA</strong>,并将 <code>PCON |= 0x80;</code> 改为 <code>PCON &amp;= 0x7F;</code>(SMOD=0)</li>
<li><strong>串口助手波特率</strong>:确认串口助手的波特率设置与单片机一致(4800)</li>
<li><strong>接线</strong>:确认 P3.0(RxD) 接 USB转串口的 TxD,P3.1(TxD) 接 RxD,<strong>交叉连接</strong></li>
</ol>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&amp;mod=forum&amp;q=STC89C52RC%2012MHz%20%E6%97%B6%E9%92%9F%20%E4%B8%B2%E5%8F%A3%E9%80%9A%E4%BF%A1&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;STC89C52RC 12MHz 时钟 串口通信&quot;</a></p>
<p>⏱️ 查找用时 3.1s | 总回答用时 21.3s</p>
页: [1]
查看完整版本: STC89C52RC, 12MHz 时钟,串口通信, 4800波特率,T1做波特率发生器