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

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

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-11 21:48:12 | 显示全部楼层 |阅读模式

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

截图202607112147002015.jpg

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-11 21:48:36 | 显示全部楼层

好的,信息已经足够。现在我来编写完整的演示程序。


以下是 STC8H8K64U-TSSOP20 的完整 RTC 演示程序,使用 外部 32.768KHz 晶振 作为 RTC 时钟源,通过 USB-CDC 虚拟串口 与电脑通信,实现时间显示和电脑对时功能。


硬件接线说明(TSSOP20)

引脚 功能 连接
Pin 13 (P3.0) USB D- 接 USB 的 D-
Pin 14 (P3.1) USB D+ 接 USB 的 D+
Pin 15 (P3.2) INT0/下载按键 接按键到 GND(用于 USB 下载)
Pin 17 (P1.6) X32I 接 32.768KHz 晶振 + 20pF 电容到 GND
Pin 18 (P1.7) X32O 接 32.768KHz 晶振 + 20pF 电容到 GND
Pin 20 (VCC) 电源 3.3V / 5V
Pin 10 (GND) GND

注意:TSSOP20 封装中,P1.6 和 P1.7 同时也是 X32I/X32O 功能脚,可直接外接 32.768KHz 晶振。


程序代码

文件 1:main.c

/*********************************************
 * STC8H8K64U-TSSOP20 RTC + USB-CDC 演示程序
 * 
 * 功能:
 *   1. 使用外部 32.768KHz 晶振驱动 RTC 实时时钟
 *   2. 通过 USB-CDC 虚拟串口每秒上报当前时间
 *   3. 支持电脑通过串口发送时间字符串对时
 * 
 * 对时命令格式(通过串口助手发送):
 *   SET 2025-01-15 12:30:45 3<CR><LF>
 *   含义: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 "stc8h.h"
#include "ai_usb.h"
#include "intrins.h"
#include "stdio.h"

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

// 缓冲区定义
unsigned char rx_buffer[64];   // 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 & 0x01));
  
    // 选择外部 32K 作为 RTC 时钟源
    // RTCCFG.1 (RTCCKS) = 0:选择外部32K
    RTCCFG &= ~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 & 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 & 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[64];
  
    // 读取 RTC 计数值
    year  = RTCYEAR;
    month = RTCMONTH;
    day   = RTCDAY;
    hour  = RTCHOUR;
    min   = RTCMIN;
    sec   = RTCSEC;
    week  = *(unsigned char volatile xdata *)0xFE6F;  // 读取星期
    week &= 0x07;
  
    // 格式化输出
    sprintf((char *)buf, "20%02bd-%02bd-%02bd %02bd:%02bd:%02bd Week=%bd\r\n",
            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[64];
  
    // 复制接收缓冲区到本地(避免操作中被覆盖)
    memcpy(buf, rx_buffer, rx_index);
    buf[rx_index] = '\0';
  
    // 解析命令:SET YYYY-MM-DD HH:MM:SS W
    if (sscanf((char *)buf, "SET %bd-%bd-%bd %bd:%bd:%bd %bd",
               &year, &month, &day, &hour, &min, &sec, &week) == 7)
    {
        // 参数有效性检查
        if ((year >= 0) && (year <= 99) &&
            (month >= 1) && (month <= 12) &&
            (day >= 1) && (day <= 31) &&
            (hour <= 23) && (min <= 59) && (sec <= 59) &&
            (week <= 6))
        {
            // 设置 RTC 时间
            RTC_SetTime(year, month, day, hour, min, sec, week);
          
            USB_SendData("Time set OK!\r\n", 15);
        }
        else
        {
            USB_SendData("Parameter error!\r\n", 18);
        }
    }
    else
    {
        USB_SendData("Format: SET YY-MM-DD HH:MM:SS W\r\n", 34);
    }
}

/*********************************************
 * RTC 秒中断服务程序
 *********************************************/
void RTC_Isr() interrupt 13
{
    if (RTCIF & 0x08)   // 判断是否秒中断
    {
        RTCIF &= ~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("STC8H8K64U RTC + USB-CDC Demo\r\n", 33);
    USB_SendData("Send: SET YY-MM-DD HH:MM:SS W to set time\r\n", 44);
  
    while (1)
    {
        // ---- USB 数据接收处理(查询方式) ----
        if (bUsbOutReady)
        {
            // 将接收到的数据逐个存入缓冲区
            for (i = 0; i < OutNumber; i++)
            {
                if (UsbOutBuffer[i] == '\n')   // 遇到换行符,表示一行结束
                {
                    rx_complete = 1;
                }
                else if (UsbOutBuffer[i] != '\r')  // 忽略回车符
                {
                    if (rx_index < sizeof(rx_buffer) - 1)
                    {
                        rx_buffer[rx_index++] = UsbOutBuffer[i];
                    }
                }
            }
          
            // 处理完整的一行命令
            if (rx_complete)
            {
                rx_buffer[rx_index] = '\0';
                Parse_Time_Command();
                rx_index = 0;
                rx_complete = 0;
            }
          
            usb_OUT_done();   // 通知 USB 库接收下一包数据
        }
      
        // ---- RTC 秒中断处理 ----
        if (B1S_Flag)
        {
            B1S_Flag = 0;
            Print_RTC_Time();   // 每秒打印一次时间
        }
    }
}

文件 2:硬件初始化与延时辅助(可选,如需要可添加)

本程序未使用额外的延时函数,所有时序依赖 RTC 秒中断和 USB 库。如果需要在 main.c 中使用 strlenmemcpysprintf 等标准库函数,Keil C51 会自动链接对应的库,无需额外文件。


使用说明

1. 项目配置

项目 设置
芯片型号 STC8H8K64U
编译工具 Keil C51
添加库文件 stc_usb_cdc_8h_data.LIB(从 STC 官网 USB 库压缩包中获取)
包含头文件 stc8h.hai_usb.h(通过 ISP 软件的"添加型号和头文件到 Keil"自动安装)
主频设置 在 ISP 下载时选择内部 IRC 频率(建议 24MHz)

2. 硬件连接

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

3. 下载程序

  1. 按住 P3.2 按键(接 GND)
  2. 给芯片上电
  3. 在 ISP 软件中点击"下载/编程"
  4. 下载完成后,电脑会识别出一个 USB-CDC 虚拟串口

4. 串口通信

打开串口助手,选择识别到的 USB-CDC 串口(波特率任意,USB-CDC 忽略波特率),即可看到:

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
...

5. 电脑对时

发送命令(注意发送新行 \r\n):

SET 25-06-15 14:30:00 0

返回:

Time set OK!

之后 RTC 将从设置的时间开始运行,每秒上报。


关键寄存器说明

寄存器 地址 功能
X32KCR FE08H 外部 32K 晶振控制:ENX32K=1, GAIN32K=1 启动高增益振荡
RTCCFG FE61H RTC 配置:RTCCKS=0 选外部晶振,SETRTC=1 触发初始化
RTCCR FE60H RTC 控制:RUNRTC=1 启动 RTC 计数
RTCIEN FE62H RTC 中断使能:ESECI=1 使能秒中断
RTCIF FE63H RTC 中断标志:SECIF 秒中断标志,需软件清 0
INIYEAR~INISSEC FE68H~FE6EH RTC 初始值设置寄存器(写)
RTCYEAR~RTCSSEC FE70H~FE76H RTC 当前计数值寄存器(只读)
INIWEEK/RTCWEEK FE6FH 星期设置/读取

注意:所有 RTC 相关寄存器均为 XFR(扩展特殊功能寄存器),访问前必须设置 P_SW2 |= 0x80(EAXFR=1)。


如果您在实际调试中遇到问题,欢迎提供更多信息(如 ISP 下载时设置的主频、串口助手截图等),我可以进一步协助排查。


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

📌 相关论坛讨论

  1. stc8H8K64U主晶振和rtc晶振不能同时存在?
  2. RTC对时
  3. AI8H1K08外部32768晶振起振不了

⏱️ 查找用时 25.7s | 总回答用时 59.0s

回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-7-22 10:31 , Processed in 0.070729 second(s), 47 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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