FRDM-MCXN236电子时钟
项目总结报告
目 录
1 项目介绍
本项目基于NXP FRDM-MCXN236开发板设计并实现了一款具备掉电保持能力的电子时钟系统。系统以芯片内置IRTC实时时钟为时基,采用0.96英寸SSD1306 OLED显示屏实时显示当前时间、日期与星期,通过两个按键完成年、月、日、时、分、秒的逐项校时,并通过串口以115200波特率输出时间信息。为保证掉电后时间不丢失,系统将时间备份到开发板板载W25Q64 SPI NOR Flash,上电后自动恢复。
系统功能包括:
1、RTC实时走时
2、OLED显示(时、分、秒、日期、中文星期)
3、按键时间调整
4、串口时间输出
5、掉电后时间保持。
工程基于MCUXpresso IDE v25.6开发,采用C语言编写,源码分为main.c、ssd1306.c/h与board_config.h四个文件,SDK外设驱动(LPSPI、LPFLEXCOMM等)随工程拷贝,便于在IDE中直接导入编译。
2 方案框图与设计思路
系统整体方案如图1所示:MCX N236作为控制核心,向上读取内置IRTC获取时间;向下驱动SSD1306 OLED完成显示;按键SW2、SW3提供人机交互输入;LPUART4串口负责调试信息与时间输出;W25Q64 SPI Flash承担掉电备份存储。

图1 系统方案框图
设计思路遵循模块化与分层原则:应用层只负责时间逻辑、按键状态机与显示策略,驱动层封装IRTC、I2C、SPI、串口等外设操作,二者通过board_config.h中的引脚与参数宏解耦,便于移植。
掉电保持采用“主动备份+上电恢复”策略:设置时间成功后立即保存一次,此后每1分钟周期保存一次,将时间写入W25Q的固定扇区;上电时先读IRTC,若读出的时间无效(超出合法范围,如复位初值2111-15-31 31:63:63),则从Flash恢复最近一次保存的时间。为提高系统健壮性,运行中还加入了无效时间过滤(连续5秒无效才重新恢复)与OLED I2C通信失败自动重初始化兜底,保证显示异常时系统仍能自动恢复。
3 硬件介绍
系统所用硬件以FRDM-MCXN236开发板为核心,外接OLED显示模块与按键,复用板载SPI Flash与串口,硬件接线示意图如图2所示。OLED模块支持5 V供电,所以OLED电源与地线取自mikroBUS J5的Pin7(P5V0,5 V!)与Pin8(GND),其I2C信号SCL为J5引脚5(P4_1)、SDA为引脚6(P4_0),通过LPI2C2(FC2)工作于400 kHz,地址0x3C。W25Q64挂在LPSPI6(FC6)上,P3_20(SDO)、P3_21(SCK)、P3_22(SDI)、P3_23(PCS0)分别连接Flash的DI、CLK、DO、CS引脚,SPI通信速率为4 MHz。按键、串口与Flash等外设按3.3 V电平接入。
图2 系统硬件接线示意图
硬件清单如表1所示。
表1 硬件清单
器件 | 型号/参数 | 用途 |
主控开发板 | FRDM-MCXN236(MCX N236,Cortex-M33,150 MHz) | 系统控制与逻辑处理 |
实时时钟 | IRTC(芯片内置,独立低频时钟) | 秒级走时与时基 |
OLED显示屏 | SSD1306,0.96英寸,128×64,I2C地址0x3C | 时间、日期、星期显示 |
按键 | SW2(P0_20)、SW3(P0_6),按下为低 | 校时模式切换与数值调整 |
串口 | LPUART4,115200-8-N-1 | 调试日志与时间输出 |
SPI Flash | W25Q64JVSSIQ(U12,8 MB,SPI NOR) | 掉电时间备份与恢复 |
4 调试软件介绍、软件流程图及关键代码
4.1 调试软件
开发与调试在MCUXpresso IDE v25.6中进行。该IDE集成了代码编辑、编译、烧录与调试功能,本项目通过LinkServer将生成的hex固件下载到开发板;编译采用IDE headless命令行模式验证,构建结果为0 errors、0 warnings。串口调试使用通用串口助手,以115200-8-N-1参数监视启动日志与时间输出,用于定位运行期问题。
4.2 软件流程图
软件主流程如图3所示。上电后依次完成时钟与引脚、IRTC、SPI Flash、时间恢复、OLED的初始化,随后进入主循环:喂看门狗、扫描按键并处理校时状态机、每秒更新时钟(读IRTC、过滤无效时间、串口输出、周期保存),检测到OLED通信异常时自动重初始化。
图3 软件主流程示意图
4.3 关键代码介绍
(1)RTC实时走时。系统以MCXN236内置IRTC作为时基,初始化时将内部16 kHz FRO时钟接入VBAT域,即使主电源掉电、VBAT供电时RTC仍可独立计时;同时使能IRTC秒级报警中断作为看门狗,主循环每秒喂狗,一旦卡死超过1秒立即自动复位自愈。主循环每秒通过IRTC_GetDatetime读取时间,读回值经IsTimeValid合法性校验,无效值(复位初值、读毛刺)直接丢弃并沿用上次有效时间,代码如下。
static void Board_InitRtc(void)
{
irtc_config_t cfg;
/*内部16k FRO接入VBAT域,掉电后RTC继续走时*/
CLOCK_SetupClk16KClocking(kCLOCK_Clk16KToVbat | kCLOCK_Clk16KToMain);
IRTC_GetDefaultConfig(&cfg);
(void)IRTC_Init(RTC, &cfg);
}
void RTC_IRQHandler(void) /*秒中断:主循环超过1秒未喂狗即复位自愈*/
{
if ((IRTC_GetStatusFlags(RTC) & kIRTC_AlarmFlag) != 0U)
{
IRTC_ClearStatusFlags(RTC, kIRTC_AlarmFlag);
if (!s_wdFeed) NVIC_SystemReset();
s_wdFeed = false;
ArmWatchdogAlarm(); /*重新arm 1秒后的alarm */
}
}
(2)OLED显示(时、分、秒、日期、中文星期)。显示刷新由UpdateDisplay完成,显示屏为SSD1306(I2C 0x3C,128×64)。如图4所示,第0行以2倍字模居中显示日期YYYY-MM-DD;第2行x=5、y=22以2倍字模显示HH:MM:SS,其右侧x=110用8×8中文字模2倍显示中文星期(星期由CalcWeekDay按年月日自动算出,不提供手动修改);状态行(RUN、SET YEAR等)2倍字模居中显示,校时过程中正在调整的字段每500 ms闪烁提示;屏幕左下角显示I加I2C失败次数、右下角显示F加1 Hz心跳计数,便于在线排查,代码如下。
/*第0行:日期YYYY-MM-DD,2倍字模居中(x=4, y=0)*/
SSD1306_DrawString(4U, 0U, dateStr, 2U);
/*第2行:HH:MM:SS(x=5, y=22)+中文星期(x=110)*/
SSD1306_DrawString(5U, 22U, t, 2U);
SSD1306_DrawCN(110U, 22U, (uint8_t)(CalcWeekDay(s_now.year, s_now.month, s_now.day) - 1U), 2U);
/*状态行:2倍字模水平居中*/
SSD1306_DrawString((uint8_t)((128U - (uint8_t)(len * 12U)) / 2U), 42U, pStatus, 2U);
图4 OLED实际显示效果
(3)按键时间调整。SW2(MODE,P0_20)与SW3(ADJ,P0_6)经软件消抖扫描后进入校时状态机:RUN → SET YEAR → SET MONTH → SET DAY → SET HOUR → SET MINUTE → SET SECOND,每按一次SW2切换一个字段;SW3将当前字段加一(年2026~2100、月1~12、日按当月天数并处理闰年、星期根据年月日自动换算,时0~23、分/秒0~59)。设置完秒后再次按SW2确认退出校时,立即调用RtcCommit将时间写回IRTC并保存Flash备份,串口输出time set OK,代码如下。
case MODE_SET_SEC:
if (BtnPressed(SW3)) s_now.second = (s_now.second + 1U) % 60U;
if (BtnPressed(SW2)) { /*确认退出校时*/
RtcCommit(); /*写回IRTC + Flash备份*/
s_mode = MODE_RUN;
UartPuts("[CLOCK] time set OK\r\n");
}
break;
校时状态机如图5和图6所示运行态下按SW2依次进入年、月、日、时、分、秒的设置,SW3对当前字段加一,设置完成后再次按SW2确认并立即保存时间。
图5 校时按键状态机示意图
图6按键设定
(4)串口时间输出。通过LPUART4(FC4)以115200 bit/s输出调试信息。UartPuts采用轮询TX寄存器方式逐字符发送,内置超时保护,硬件异常时自动重新初始化外设自愈,绝不永久卡死主循环;每秒由SerialLogTime格式化输出一行时间心跳YYYY-MM-DD W HH:MM:SS I<n> F<n>,其中W为星期、I为I2C失败次数、F为1 Hz心跳计数,如图7所示,代码如下。
static void SerialLogTime(void)
{
char buf[34]; uint8_t i = 0U;
/*年月日、星期、时分秒逐位手动补零,避免库函数开销*/
buf[i++] = (char)('0' + (s_now.year / 1000U) % 10U);
buf[i++] = (char)('0' + wd);
buf[i++] = (char)('0' + s_now.hour / 10U);
buf[i++] = (char)('0' + s_now.second % 10U);
buf[i++] = ' '; buf[i++] = 'I';
buf[i++] = (char)('0' + SSD1306_GetI2cFailCount());
buf[i++] = ' '; buf[i++] = 'F';
buf[i++] = (char)('0' + s_diagFrame);
buf[i++] = '\r'; buf[i++] = '\n'; buf[i] = '\0';
UartPuts(buf);
}

图7串口显示数据
(5)掉电后时间保持。时间通过板载W25Q64 SPI Flash持久保存:FlashSaveTime将时间打包写入最后一个4 KB扇区的16个256 B槽位(带magic校验),写满16槽才擦除一次,兼顾Flash擦写寿命;上电时RtcLoad检测到IRTC时间无效(如无VBAT电池导致RTC域未初始化)即从Flash反向扫描恢复最新有效备份并写回IRTC,如图8;正常运行每60秒自动备份一次,如图9;校时确认时立即备份,如图10,因此掉电最多丢失1分钟,代码如下。
/*周期备份:RUN模式每60秒写一次Flash,掉电最多丢1分钟*/
if ((uint32_t)(s_msTicks - s_lastSaveTick) >= 60000U)
{
s_lastSaveTick = s_msTicks;
FlashSaveTime();
}
图8上电读取W25Q64 数据
图9 每60秒自动备份时间到W25Q64
图10 校时确认时立即备份
5 功能展示图及说明
图11为OLED显示效果示意图(按128×64点阵等比例还原,黑底蓝字像素风):第一行(x=4, y=0)显示日期;第二行以2倍字号显示“时:分:秒”(x=5, y=22),右侧x=110显示2倍中文星期;y=42处以2倍字号居中显示状态行RUN(运行)或SET(校时,如SET HOUR);左下角I0为I2C通信计数、右下角F7为Flash保存计数,便于现场观察通信状态。 图4为OLED实际显示效果实拍图,蓝色像素点亮显示日期、时间、星期、状态行与I/F诊断计数。
图11 OLED显示效果示意图
系统通过LPUART4(FC4)以115200 bit/s向串口输出运行信息:开机自检提示、每秒时间心跳(含I2C失败计数与Flash保存计数)、按键校时状态提示、时间保存与设置成功日志等,便于实时观察程序运行与调试。图12为XCOM V2.2串口助手实测输出界面,图中以不同颜色区分开机自检、时间心跳、校时提示与保存日志。
图12 串口显示信息示意(终端日志)
主要功能展示说明如下:
(1)正常走时:上电初始化后每秒更新一次显示,秒位跳动,串口同步输出“2026-10-15 19:06:15 I0 F2”格式日志;
(2)按键校时:短按SW2依次进入SET YEAR、SET MONTH、SET DAY、SET HOUR、SET MINUTE、SET SECOND,SW3对当前字段加一,当前字段以0.5 s周期闪烁提示;设置完成回到RUN并立即保存;
(3)掉电保持:拔电再上电后,IRTC读出无效时间时自动从W25Q恢复最近保存值,时间连续不丢;
(4)自动恢复:OLED通信因线缆接触不良中断时,程序检测到连续I2C失败后自动重初始化,恢复后约2 s内重新点亮。
6 项目中遇到的难题及解决方法
开发过程中遇到的典型问题及解决方法如表2所示。
表2 典型难题与解决方法
难题 | 现象 | 原因 | 解决方法 |
OLED显示乱码 | 字符镜像、缺笔画 | 5×7字模列/行序被改写 | 重排字模,恢复标准列行顺序 |
校时到秒后卡死 | SET SECOND后界面停顿约3 s | 内部Flash编程命令受保护,等待超时(FSTAT=0x900) | 改用板载W25Q保存时间,弃用内部Flash |
W25Q读ID全FF | 读不到芯片ID,无法保存 | LPSPI每8位帧间PCS拉高,命令被丢弃 | 使能kLPSPI_MasterPcsContinuous连续模式 |
串口出现脏时间 | 复位后偶现2111-15-31 31:63:63 | IRTC计数寄存器复位初值 | 增加IsTimeValid过滤,无效值沿用上次有效值 |
OLED运行中黑屏 | 程序正常但显示中断 | 杜邦线接触不良导致I2C失败 | I2C失败计数达9次自动重初始化并重画 |
复位进入HardFault | 访问PORT3时报总线错误 | 时钟门控顺序与安全/非安全地址混淆 | 调整门控顺序并统一使用NS别名地址 |
以“校时卡死”为例,该问题曾持续困扰多轮调试:现象为设置到最后一位秒后界面不再刷新。通过串口日志与心跳计数确认程序并未死机,而是执行了耗时约3 s的Flash编程等待;进一步检查FSTAT寄存器值为0x900,其CMDPRT位表明内部Flash命令保护出厂配置为“安全特权访问”,非安全应用程序的擦写命令永远不会完成。查明原因后,将时间备份改走板载W25Q SPI Flash,问题彻底解决,并顺带获得了更大的存储空间与更灵活的保存策略。
7 心得体会
通过本项目的完整开发,我对嵌入式系统设计有了更深入的体会。一是分层调试是排查问题的利器:例如W25Q读不到ID时,先确认芯片接线与供电,再用GPIO位带模拟(bit-bang)软SPI读取,得到正确的芯片ID(EF 40 17),从而把问题精确锁定在LPSPI的片选时序配置而非硬件,最终一眼定位到PCS连续模式缺失。二是健壮性设计要落到代码:无效时间过滤、通信失败计数与自动重初始化、看门狗等“软件兜底”,看似增加代码量,却让系统在真实环境(线缆松动、上电毛刺)下依然可靠,这正是嵌入式产品与实验板程序的重要区别。三是在方案层面要学会权衡:内部Flash虽方便,但受保护限制与擦写寿命约束,改用板载外部Flash并设计“设置确认+ 1分钟周期”的保存策略,在数据安全与Flash寿命之间取得了平衡。四是善用工具链:IDE headless命令行编译、串口日志打点、心跳计数观察,让每一轮修改都能快速验证,显著提升了调试效率。