好的,信息已经足够。现在我来编写完整的演示程序。
以下是 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 中使用 strlen、memcpy、sprintf 等标准库函数,Keil C51 会自动链接对应的库,无需额外文件。
使用说明
1. 项目配置
| 项目 |
设置 |
| 芯片型号 |
STC8H8K64U |
| 编译工具 |
Keil C51 |
| 添加库文件 |
stc_usb_cdc_8h_data.LIB(从 STC 官网 USB 库压缩包中获取) |
| 包含头文件 |
stc8h.h、ai_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. 下载程序
- 按住 P3.2 按键(接 GND)
- 给芯片上电
- 在 ISP 软件中点击"下载/编程"
- 下载完成后,电脑会识别出一个 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通信和电脑对时"
📌 相关论坛讨论
- stc8H8K64U主晶振和rtc晶振不能同时存在?
- RTC对时
- AI8H1K08外部32768晶振起振不了
⏱️ 查找用时 25.7s | 总回答用时 59.0s