神农鼎 发表于 2026-7-11 21:48:12

给个实际能用的 STC8H8K64U-TSSOP20的RTC演示程序,RTC用外部晶振,USB-CDC通信和电脑对时


给个实际能用的 STC8H8K64U-TSSOP20的RTC演示程序,
RTC用外部晶振,USB-CDC通信和电脑对时



神农鼎 发表于 2026-7-11 21:48:36

<p>好的,信息已经足够。现在我来编写完整的演示程序。</p>
<hr />
<p>以下是 <strong>STC8H8K64U-TSSOP20</strong> 的完整 RTC 演示程序,使用 <strong>外部 32.768KHz 晶振</strong> 作为 RTC 时钟源,通过 <strong>USB-CDC 虚拟串口</strong> 与电脑通信,实现时间显示和电脑对时功能。</p>
<hr />
<h2>硬件接线说明(TSSOP20)</h2>
<table>
<thead>
<tr>
<th align="center">引脚</th>
<th align="center">功能</th>
<th align="left">连接</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">Pin 13 (P3.0)</td>
<td align="center">USB D-</td>
<td align="left">接 USB 的 D-</td>
</tr>
<tr>
<td align="center">Pin 14 (P3.1)</td>
<td align="center">USB D+</td>
<td align="left">接 USB 的 D+</td>
</tr>
<tr>
<td align="center">Pin 15 (P3.2)</td>
<td align="center">INT0/下载按键</td>
<td align="left">接按键到 GND(用于 USB 下载)</td>
</tr>
<tr>
<td align="center">Pin 17 (P1.6)</td>
<td align="center">X32I</td>
<td align="left">接 32.768KHz 晶振 + 20pF 电容到 GND</td>
</tr>
<tr>
<td align="center">Pin 18 (P1.7)</td>
<td align="center">X32O</td>
<td align="left">接 32.768KHz 晶振 + 20pF 电容到 GND</td>
</tr>
<tr>
<td align="center">Pin 20 (VCC)</td>
<td align="center">电源</td>
<td align="left">3.3V / 5V</td>
</tr>
<tr>
<td align="center">Pin 10 (GND)</td>
<td align="center">地</td>
<td align="left">GND</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>:TSSOP20 封装中,P1.6 和 P1.7 同时也是 X32I/X32O 功能脚,可直接外接 32.768KHz 晶振。</p>
</blockquote>
<hr />
<h2>程序代码</h2>
<h3>文件 1:main.c</h3>
<pre><code class="language-c">/*********************************************
* STC8H8K64U-TSSOP20 RTC + USB-CDC 演示程序
*
* 功能:
*   1. 使用外部 32.768KHz 晶振驱动 RTC 实时时钟
*   2. 通过 USB-CDC 虚拟串口每秒上报当前时间
*   3. 支持电脑通过串口发送时间字符串对时
*
* 对时命令格式(通过串口助手发送):
*   SET 2025-01-15 12:30:45 3&lt;CR&gt;&lt;LF&gt;
*   含义:SET YYYY-MM-DD HH:MM:SS W
*         W = 星期(0=周日,1=周一,...,6=周六)
*
* 硬件平台:STC8H8K64U-TSSOP20
* 编译环境:Keil C51
* 主频:使用内部高速IRC(默认 24MHz,可在ISP下载时设置)
*
* 使用步骤:
*   1. 在 Keil 中新建项目,芯片选择 STC8H8K64U
*   2. 将 stc_usb_cdc_8h_data.LIB 库文件添加到项目
*   3. 将本文件及下面的文件添加到项目
*   4. 编译下载到芯片
*   5. 用 USB 线连接电脑,识别出 USB-CDC 串口
*   6. 打开串口助手(波特率任意),即可看到每秒上报的时间
*********************************************/
#include &quot;stc8h.h&quot;
#include &quot;ai_usb.h&quot;
#include &quot;intrins.h&quot;
#include &quot;stdio.h&quot;

#define MAIN_Fosc 24000000L   // 主频 24MHz(根据实际ISP设置调整)

// 缓冲区定义
unsigned char rx_buffer;   // USB接收缓冲区
unsigned char rx_index;      // 接收索引
bit rx_complete;               // 一行接收完成标志

// RTC 秒中断标志
bit B1S_Flag;

/*********************************************
* 函数声明
*********************************************/
void RTC_Config(void);
void RTC_SetTime(unsigned char year, unsigned char month, unsigned char day,
               unsigned char hour, unsigned char min, unsigned char sec,
               unsigned char week);
void Print_RTC_Time(void);
void Parse_Time_Command(void);

/*********************************************
* USB-CDC 初始化(由 usb_init() 完成)
* 无需额外配置
*********************************************/

/*********************************************
* RTC 配置:使用外部 32.768KHz 晶振
*********************************************/
void RTC_Config(void)
{
    // 启动外部 32.768KHz 晶振
    // X32KCR: ENX32K=1, GAIN32K=1(高增益,更易起振)
    X32KCR = 0xC0;

    // 等待外部晶振稳定
    while (!(X32KCR &amp; 0x01));

    // 选择外部 32K 作为 RTC 时钟源
    // RTCCFG.1 (RTCCKS) = 0:选择外部32K
    RTCCFG &amp;= ~0x02;

    // 设置 RTC 初始时间(默认值:2025-01-01 00:00:00 周三)
    INIYEAR   = 25;    // 2025年
    INIMONTH= 1;   // 1月
    INIDAY    = 1;   // 1日
    INIHOUR   = 0;   // 0时
    INIMIN    = 0;   // 0分
    INISEC    = 0;   // 0秒
    INISSEC   = 0;   // 0/128秒
    // INIWEEK 在 FE6FH,与 RTCWEEK 共用地址
    // 通过 XFR 方式写入
    *(unsigned char volatile xdata *)0xFE6F = 3;// 星期三

    // 触发 RTC 初始化(将初始值装载到计数寄存器)
    RTCCFG |= 0x01;
    while (RTCCFG &amp; 0x01);   // 等待初始化完成

    // 清中断标志
    RTCIF = 0;

    // 使能 RTC 秒中断
    RTCIEN = 0x08;   // ESECI = 1

    // 启动 RTC
    RTCCR = 0x01;    // RUNRTC = 1
}

/*********************************************
* 设置 RTC 时间
*********************************************/
void RTC_SetTime(unsigned char year, unsigned char month, unsigned char day,
               unsigned char hour, unsigned char min, unsigned char sec,
               unsigned char week)
{
    // 停止 RTC
    RTCCR = 0x00;

    // 设置初始值寄存器
    INIYEAR   = year;
    INIMONTH= month;
    INIDAY    = day;
    INIHOUR   = hour;
    INIMIN    = min;
    INISEC    = sec;
    INISSEC   = 0;
    *(unsigned char volatile xdata *)0xFE6F = week;// 设置星期

    // 触发初始化
    RTCCFG |= 0x01;
    while (RTCCFG &amp; 0x01);

    // 清中断标志
    RTCIF = 0;

    // 重新启动 RTC
    RTCCR = 0x01;
}

/*********************************************
* 通过 USB-CDC 打印当前 RTC 时间
*********************************************/
void Print_RTC_Time(void)
{
    unsigned char year, month, day, hour, min, sec, week;
    unsigned char buf;

    // 读取 RTC 计数值
    year= RTCYEAR;
    month = RTCMONTH;
    day   = RTCDAY;
    hour= RTCHOUR;
    min   = RTCMIN;
    sec   = RTCSEC;
    week= *(unsigned char volatile xdata *)0xFE6F;// 读取星期
    week &amp;= 0x07;

    // 格式化输出
    sprintf((char *)buf, &quot;20%02bd-%02bd-%02bd %02bd:%02bd:%02bd Week=%bd\r\n&quot;,
            year, month, day, hour, min, sec, week);

    // 通过 USB-CDC 发送
    USB_SendData(buf, strlen((char *)buf));
}

/*********************************************
* 解析电脑发来的对时命令
* 格式:SET 2025-01-15 12:30:45 3
*********************************************/
void Parse_Time_Command(void)
{
    unsigned char year, month, day, hour, min, sec, week;
    unsigned char buf;

    // 复制接收缓冲区到本地(避免操作中被覆盖)
    memcpy(buf, rx_buffer, rx_index);
    buf = '\0';

    // 解析命令:SET YYYY-MM-DD HH:MM:SS W
    if (sscanf((char *)buf, &quot;SET %bd-%bd-%bd %bd:%bd:%bd %bd&quot;,
               &amp;year, &amp;month, &amp;day, &amp;hour, &amp;min, &amp;sec, &amp;week) == 7)
    {
      // 参数有效性检查
      if ((year &gt;= 0) &amp;&amp; (year &lt;= 99) &amp;&amp;
            (month &gt;= 1) &amp;&amp; (month &lt;= 12) &amp;&amp;
            (day &gt;= 1) &amp;&amp; (day &lt;= 31) &amp;&amp;
            (hour &lt;= 23) &amp;&amp; (min &lt;= 59) &amp;&amp; (sec &lt;= 59) &amp;&amp;
            (week &lt;= 6))
      {
            // 设置 RTC 时间
            RTC_SetTime(year, month, day, hour, min, sec, week);
         
            USB_SendData(&quot;Time set OK!\r\n&quot;, 15);
      }
      else
      {
            USB_SendData(&quot;Parameter error!\r\n&quot;, 18);
      }
    }
    else
    {
      USB_SendData(&quot;Format: SET YY-MM-DD HH:MM:SS W\r\n&quot;, 34);
    }
}

/*********************************************
* RTC 秒中断服务程序
*********************************************/
void RTC_Isr() interrupt 13
{
    if (RTCIF &amp; 0x08)   // 判断是否秒中断
    {
      RTCIF &amp;= ~0x08;// 清中断标志
      B1S_Flag = 1;    // 置秒标志
    }
}

/*********************************************
* 主函数
*********************************************/
void main(void)
{
    unsigned char i;

    P_SW2 |= 0x80;   // 使能 XFR 访问

    // 设置所有 I/O 口为准双向口
    P0M1 = 0x00; P0M0 = 0x00;
    P1M1 = 0x00; P1M0 = 0x00;
    P2M1 = 0x00; P2M0 = 0x00;
    P3M1 = 0x00; P3M0 = 0x00;
    P4M1 = 0x00; P4M0 = 0x00;
    P5M1 = 0x00; P5M0 = 0x00;
    P6M1 = 0x00; P6M0 = 0x00;
    P7M1 = 0x00; P7M0 = 0x00;

    // 初始化 USB-CDC
    usb_init();

    // 配置 RTC(使用外部 32.768KHz 晶振)
    RTC_Config();

    // 初始化接收缓冲区
    rx_index = 0;
    rx_complete = 0;

    // 打开总中断
    EA = 1;

    // 等待 USB 配置完成
    while (DeviceState != DEVSTATE_CONFIGURED);

    // 发送启动信息
    USB_SendData(&quot;STC8H8K64U RTC + USB-CDC Demo\r\n&quot;, 33);
    USB_SendData(&quot;Send: SET YY-MM-DD HH:MM:SS W to set time\r\n&quot;, 44);

    while (1)
    {
      // ---- USB 数据接收处理(查询方式) ----
      if (bUsbOutReady)
      {
            // 将接收到的数据逐个存入缓冲区
            for (i = 0; i &lt; OutNumber; i++)
            {
                if (UsbOutBuffer == '\n')   // 遇到换行符,表示一行结束
                {
                  rx_complete = 1;
                }
                else if (UsbOutBuffer != '\r')// 忽略回车符
                {
                  if (rx_index &lt; sizeof(rx_buffer) - 1)
                  {
                        rx_buffer = UsbOutBuffer;
                  }
                }
            }
         
            // 处理完整的一行命令
            if (rx_complete)
            {
                rx_buffer = '\0';
                Parse_Time_Command();
                rx_index = 0;
                rx_complete = 0;
            }
         
            usb_OUT_done();   // 通知 USB 库接收下一包数据
      }
      
      // ---- RTC 秒中断处理 ----
      if (B1S_Flag)
      {
            B1S_Flag = 0;
            Print_RTC_Time();   // 每秒打印一次时间
      }
    }
}
</code></pre>
<h3>文件 2:硬件初始化与延时辅助(可选,如需要可添加)</h3>
<p>本程序未使用额外的延时函数,所有时序依赖 RTC 秒中断和 USB 库。如果需要在 main.c 中使用 <code>strlen</code>、<code>memcpy</code>、<code>sprintf</code> 等标准库函数,Keil C51 会自动链接对应的库,无需额外文件。</p>
<hr />
<h2>使用说明</h2>
<h3>1. 项目配置</h3>
<table>
<thead>
<tr>
<th align="left">项目</th>
<th align="left">设置</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">芯片型号</td>
<td align="left">STC8H8K64U</td>
</tr>
<tr>
<td align="left">编译工具</td>
<td align="left">Keil C51</td>
</tr>
<tr>
<td align="left">添加库文件</td>
<td align="left"><code>stc_usb_cdc_8h_data.LIB</code>(从 STC 官网 USB 库压缩包中获取)</td>
</tr>
<tr>
<td align="left">包含头文件</td>
<td align="left"><code>stc8h.h</code>、<code>ai_usb.h</code>(通过 ISP 软件的&quot;添加型号和头文件到 Keil&quot;自动安装)</td>
</tr>
<tr>
<td align="left">主频设置</td>
<td align="left">在 ISP 下载时选择内部 IRC 频率(建议 24MHz)</td>
</tr>
</tbody>
</table>
<h3>2. 硬件连接</h3>
<pre><code>USB 口 (电脑) ──── USB 线 ──── STC8H8K64U-TSSOP20
                                  Pin13(P3.0/D-) ──── USB D-
                                  Pin14(P3.1/D+) ──── USB D+
                                  Pin17(P1.6/X32I) ── 32.768KHz ──┬── 20pF ── GND
                                  Pin18(P1.7/X32O) ── 晶振 ────────┬── 20pF ── GND
                                  Pin15(P3.2) ────── 按键 ──── GND (下载用)
                                  Pin20(VCC) ─────── 3.3V/5V
                                  Pin10(GND) ─────── GND
</code></pre>
<h3>3. 下载程序</h3>
<ol>
<li>按住 P3.2 按键(接 GND)</li>
<li>给芯片上电</li>
<li>在 ISP 软件中点击&quot;下载/编程&quot;</li>
<li>下载完成后,电脑会识别出一个 USB-CDC 虚拟串口</li>
</ol>
<h3>4. 串口通信</h3>
<p>打开串口助手,选择识别到的 USB-CDC 串口(波特率任意,USB-CDC 忽略波特率),即可看到:</p>
<pre><code>STC8H8K64U RTC + USB-CDC Demo
Send: SET YY-MM-DD HH:MM:SS W to set time
2025-01-01 00:00:01 Week=3
2025-01-01 00:00:02 Week=3
2025-01-01 00:00:03 Week=3
...
</code></pre>
<h3>5. 电脑对时</h3>
<p>发送命令(注意发送新行 <code>\r\n</code>):</p>
<pre><code>SET 25-06-15 14:30:00 0
</code></pre>
<p>返回:</p>
<pre><code>Time set OK!
</code></pre>
<p>之后 RTC 将从设置的时间开始运行,每秒上报。</p>
<hr />
<h2>关键寄存器说明</h2>
<table>
<thead>
<tr>
<th align="left">寄存器</th>
<th align="center">地址</th>
<th align="left">功能</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><strong>X32KCR</strong></td>
<td align="center">FE08H</td>
<td align="left">外部 32K 晶振控制:<code>ENX32K=1, GAIN32K=1</code> 启动高增益振荡</td>
</tr>
<tr>
<td align="left"><strong>RTCCFG</strong></td>
<td align="center">FE61H</td>
<td align="left">RTC 配置:<code>RTCCKS=0</code> 选外部晶振,<code>SETRTC=1</code> 触发初始化</td>
</tr>
<tr>
<td align="left"><strong>RTCCR</strong></td>
<td align="center">FE60H</td>
<td align="left">RTC 控制:<code>RUNRTC=1</code> 启动 RTC 计数</td>
</tr>
<tr>
<td align="left"><strong>RTCIEN</strong></td>
<td align="center">FE62H</td>
<td align="left">RTC 中断使能:<code>ESECI=1</code> 使能秒中断</td>
</tr>
<tr>
<td align="left"><strong>RTCIF</strong></td>
<td align="center">FE63H</td>
<td align="left">RTC 中断标志:<code>SECIF</code> 秒中断标志,需软件清 0</td>
</tr>
<tr>
<td align="left"><strong>INIYEAR~INISSEC</strong></td>
<td align="center">FE68H~FE6EH</td>
<td align="left">RTC 初始值设置寄存器(写)</td>
</tr>
<tr>
<td align="left"><strong>RTCYEAR~RTCSSEC</strong></td>
<td align="center">FE70H~FE76H</td>
<td align="left">RTC 当前计数值寄存器(只读)</td>
</tr>
<tr>
<td align="left"><strong>INIWEEK/RTCWEEK</strong></td>
<td align="center">FE6FH</td>
<td align="left">星期设置/读取</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>:所有 RTC 相关寄存器均为 XFR(扩展特殊功能寄存器),访问前必须设置 <code>P_SW2 |= 0x80</code>(EAXFR=1)。</p>
</blockquote>
<hr />
<p>如果您在实际调试中遇到问题,欢迎提供更多信息(如 ISP 下载时设置的主频、串口助手截图等),我可以进一步协助排查。</p>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&amp;mod=forum&amp;q=%E7%BB%99%E4%B8%AA%E5%AE%9E%E9%99%85%E8%83%BD%E7%94%A8%E7%9A%84%20STC8H8K64U-TSSOP20%E7%9A%84RTC%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F%20RTC%E7%94%A8%E5%A4%96%E9%83%A8%E6%99%B6%E6%8C%AF%20USB-CDC%E9%80%9A%E4%BF%A1%E5%92%8C%E7%94%B5%E8%84%91%E5%AF%B9%E6%97%B6&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;给个实际能用的 STC8H8K64U-TSSOP20的RTC演示程序 RTC用外部晶振 USB-CDC通信和电脑对时&quot;</a></p>
<p><strong>📌 相关论坛讨论</strong></p>
<ol>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=24765&amp;highlight=%E7%BB%99%E4%B8%AA%E5%AE%9E%E9%99%85%E8%83%BD%E7%94%A8%E7%9A%84%20STC8H8K64U-TSSOP20%E7%9A%84RTC%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F%EF%BC%8CRTC%E7%94%A8%E5%A4%96%E9%83%A8%E6%99%B6%E6%8C%AF%EF%BC%8CUSB-CDC%E9%80%9A%E4%BF%A1%E5%92%8C%E7%94%B5%E8%84%91%E5%AF%B9%E6%97%B6&amp;page=1&amp;extra=#pid222867">stc8H8K64U主晶振和rtc晶振不能同时存在?</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=5461&amp;highlight=%E7%BB%99%E4%B8%AA%E5%AE%9E%E9%99%85%E8%83%BD%E7%94%A8%E7%9A%84%20STC8H8K64U-TSSOP20%E7%9A%84RTC%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F%EF%BC%8CRTC%E7%94%A8%E5%A4%96%E9%83%A8%E6%99%B6%E6%8C%AF%EF%BC%8CUSB-CDC%E9%80%9A%E4%BF%A1%E5%92%8C%E7%94%B5%E8%84%91%E5%AF%B9%E6%97%B6&amp;page=1&amp;extra=#pid39999">RTC对时</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=11507&amp;highlight=%E7%BB%99%E4%B8%AA%E5%AE%9E%E9%99%85%E8%83%BD%E7%94%A8%E7%9A%84%20STC8H8K64U-TSSOP20%E7%9A%84RTC%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F%EF%BC%8CRTC%E7%94%A8%E5%A4%96%E9%83%A8%E6%99%B6%E6%8C%AF%EF%BC%8CUSB-CDC%E9%80%9A%E4%BF%A1%E5%92%8C%E7%94%B5%E8%84%91%E5%AF%B9%E6%97%B6&amp;page=1&amp;extra=#pid116204">AI8H1K08外部32768晶振起振不了</a></li>
</ol>
<p>⏱️ 查找用时 25.7s | 总回答用时 59.0s</p>
页: [1]
查看完整版本: 给个实际能用的 STC8H8K64U-TSSOP20的RTC演示程序,RTC用外部晶振,USB-CDC通信和电脑对时