Funpack5-4 - 基于FRDM-MCXN236实现电子时钟设计
该项目使用了恩智浦(NXP)FRDM-MCXN236,实现了电子时钟的设计,它的主要功能为:使用RTC获取系统时间,使用按键实现时间调整,通过串口输出当前时间,掉电后时间能够保持。
标签
Funpack活动
NXP
RTC
恩智浦
电子时钟
MCXN236
子午鼠
更新2026-10-19
3

基于FRDM-MCXN236的电子时钟设计

一、项目介绍

1.1 项目背景

image.png

本项目是参加硬禾科技联合DigiKey发起的Funpack活动第五季第4期(Funpack S5 #4)。本期板卡是来自NXP的FRDM-MCXN236开发板,核心是一颗MCX N236微控制器,搭载Arm Cortex-M33内核,主频最高150MHz,配备1MB双组闪存和352KB ECC RAM,集成SmartDMA加速器。

1.2 项目概述

image.png

本项目基于FRDM-MCXN236开发板,实现了一个完整的电子时钟系统。系统利用芯片内置的独立RTC(IRTC)模块进行精准计时,通过板载按键实现年、月、日、时、分、秒的全字段时间调整,并通过串口实时输出当前时间。按下复位按键后,IRTC时间能够继续保持,不会丢失。

本项目对应Funpack S5 #4活动任务1——电子时钟设计。

1.3 功能需求

根据活动任务1要求,本项目实现以下功能:

  • 使用RTC获取系统时间
  • 显示当前时、分、秒信息(通过串口输出)
  • 使用按键实现时间调整
  • 通过串口输出当前时间
  • 掉电后时间能够保持(通过复位按键验证)

在基础要求之上,还实现了以下增强功能:

  • 全字段时间调整:支持年、月、日、时、分、秒六个字段的独立调整
  • 短按/长按双模式按键:SW2短按循环切换调整字段,长按保存并退出;SW3短按+1,长按-1
  • 串口高频输出:每0.5秒输出一次当前时间
  • 软件去抖与状态机:基于SysTick的按键扫描机制,实现4ms去抖和1秒长按判定

1.4 项目成果

  • ✅ 深入理解FRDM-MCXN236的IRTC模块及其复位保持机制
  • ✅ 掌握MCUXpresso SDK在VS Code环境下的开发流程
  • ✅ 实现基础任务:基于IRTC的电子时钟,支持串口时间输出和复位后时间保持
  • ✅ 实现全字段时间调整,支持年月日时分秒六个字段的独立修改
  • ✅ 实现短按/长按双模式按键交互

二、硬件介绍

2.1 微控制器:MCX N236

MCX N236是NXP MCX N系列的高性能微控制器,基于Arm Cortex-M33内核,主频最高150MHz。芯片配备1MB双组闪存(支持完整ECC)和352KB ECC RAM,集成SmartDMA加速器。在安全方面,内置EdgeLock安全区域,提供硬件级保护。

image.png

2.2 FRDM-MCXN236开发板

FRDM-MCXN236开发板的主要板载资源包括:

  • MCU-Link调试器:通过USB-C一线完成供电、调试和串口通信,支持VCOM虚拟串口
  • 用户按键:SW1为复位按键,SW2(P0_20)为唤醒按键,SW3(P0_6)为ISP模式开关(可作普通GPIO输入使用)
  • RGB LED:用于状态指示
  • 扩展接口:支持Arduino、mikroBUS、FRDM接头等多种扩展接口

2.3 引脚分配

本项目使用的引脚分配如下:

功能

引脚

说明

SW2按键

P0_20

模式切换/保存退出

SW3按键

P0_6

数值增减

串口TX

P1_8

LPUART4,通过MCU-Link输出

串口RX

P1_9

LPUART4

复位按键

SW1

复位MCU,验证时间保持

三、方案框图和项目设计思路

3.1 系统方案框图

image.png

系统方案框图展示了电子时钟的整体架构与数据流向。核心模块包括IRTC、主循环、LPUART4、SysTick和按键。IRTC作为时间基准,由OSC32K提供精准时钟源,负责日历计时;主循环负责读取IRTC当前时间、处理按键事件,并通过LPUART4将时间信息输出至PC串口终端。SysTick定时器产生1ms中断,一方面累计计数实现0.5秒的时间更新标志置位,另一方面执行按键扫描,完成去抖和短按/长按判定。按键SW2(P0_20)和SW3(P0_6)通过GPIO连接,经SysTick扫描后产生短按或长按事件,主循环据此进行时间调整状态机的切换与数值增减。调整完成后,新时间写回IRTC,实现掉电保持。整体构成一个以IRTC为时间基准、SysTick为调度时基、按键为人机交互、串口为输出通道的电子时钟系统。

3.2 项目设计思路

IRTC初始化

MCXN236的IRTC初始化有一个关键时序要求:RTC的CTRL[CLK_SEL]位默认选择FRO16K作为时钟源,FRO16K必须在切换到OSC32K之前保持使能,否则访问任何RTC寄存器都会导致系统挂起。因此初始化顺序为:

  1. 同时使能FRO16K和OSC32K到VBAT域和Main域
  2. 使能RTC时钟
  3. 配置irtcConfig.clockSelect = kIRTC_Clk32K
  4. 调用IRTC_Init()完成初始化

每秒时间更新

由于IRTC驱动不支持独立的秒中断枚举,本项目采用SysTick定时器产生1ms中断,在中断中累计计数,每500次置位时间更新标志,实现0.5秒的串口输出周期。

按键处理机制

按键采用SysTick轮询方式,在1ms中断中执行扫描:

  • 去抖:使用4位移位寄存器,连续4次采样一致才认为状态稳定
  • 短按判定:按下后1000ms内释放,触发短按事件
  • 长按判定:持续按下超过1000ms,触发长按事件

时间调整状态机

系统采用7状态状态机:

NORMAL → ADJUST_YEAR → ADJUST_MONTH → ADJUST_DAY 
→ ADJUST_HOUR → ADJUST_MINUTE → ADJUST_SECOND → NORMAL(保存)

SW2短按循环切换调整字段,SW2长按保存并退出;SW3短按当前字段+1,SW3长按当前字段-1。

复位后时间保持

IRTC由独立时钟源供电,复位MCU时IRTC继续运行。程序初始化时检查年份范围(2020-2100),若超出范围则设置默认时间,否则保留原有时间。按下复位按键SW1后,程序重新运行,串口输出显示时间从复位前继续计时。

四、开发环境、软件流程图及关键代码

4.1 开发环境

  • 硬件:FRDM-MCXN236开发板、Type-C USB线
  • IDE:Visual Studio Code + MCUXpresso for VS Code扩展
  • SDK:MCUXpresso SDK 26.09(FRDM-MCXN236)
  • 工具链:Arm GNU Toolchain 14.2.rel1
  • 构建系统:CMake + Ninja
  • 串口终端:VS Code Serial Monitor,115200-8-N-1

image.png

image.png

4.2 软件流程图

4.2.1 主程序流程图

image.png

主程序流程图描述了电子时钟从上电到持续运行的整体控制流程。程序启动后,首先调用BOARD_InitHardware()完成板级基础初始化,接着依次初始化IRTC、SysTick定时器和按键GPIO。进入主循环后,程序不断检测两个标志:当SysTick累计达到0.5秒时置位的时间更新标志被触发,则读取IRTC当前时间并通过LPUART4输出;当按键扫描产生事件时,则进入时间调整状态机进行相应处理。处理完毕后回到循环起点,持续运行,实现时间的实时输出与按键响应。

4.2.2 按键扫描流程图(SysTick中断内)

image.png

按键扫描流程图展示了在SysTick 1ms中断中执行的按键去抖与长短按判定逻辑。中断服务程序首先读取SW2(P0_20)和SW3(P0_6)的当前电平,将电平移入4位移位寄存器进行软件去抖。当连续4次采样均为低电平时,判定为稳定按下,记录按下时刻并清除长按标志;当连续4次采样均为高电平时,判定为稳定释放,若此前未触发长按,则产生短按事件。同时,若按键持续按下时间达到或超过1000ms,则置位长按事件,并标记已处理,避免释放时重复触发短按。该流程以1ms为周期循环执行,确保按键响应的实时性与可靠性。

4.2.3 时间调整状态机

image.png

时间调整状态机定义了用户通过按键修改时间的交互逻辑。系统默认处于NORMAL状态,此时正常显示时间。短按SW2依次进入年、月、日、时、分、秒六个调整状态,每个状态对应一个字段的编辑;在任意调整状态下,短按SW3使当前字段值加1,长按SW3使当前字段值减1,并自动处理进位与循环(如月份12后回到1,小时23后回到0等)。当在秒调整状态下再次短按SW2,或在任意调整状态下长按SW2,则将修改后的时间写回IRTC并保存,随后返回NORMAL状态。该状态机确保了时间调整的灵活性与操作的直观性。

4.3 关键代码

IRTC初始化

static void IRTC_ClockInit(void)
{
irtc_config_t irtcConfig;

/* 关键:同时使能FRO16K和OSC32K,防止访问RTC寄存器挂起 */
CLOCK_SetupClk16KClocking(kCLOCK_Clk16KToVbat | kCLOCK_Clk16KToMain);
CLOCK_SetupOsc32KClocking(kCLOCK_Osc32kToVbat | kCLOCK_Osc32kToMain);
CLOCK_EnableClock(kCLOCK_Rtc0);

IRTC_GetDefaultConfig(&irtcConfig);
irtcConfig.clockSelect = kIRTC_Clk32K;
IRTC_Init(RTC, &irtcConfig);

/* 检查时间有效性,复位后保持原时间 */
IRTC_GetDatetime(RTC, &g_currentTime);
if (g_currentTime.year < 2020U || g_currentTime.year > 2100U) {
g_currentTime.year = 2026; g_currentTime.month = 10;
g_currentTime.day = 10; g_currentTime.weekDay = 6;
g_currentTime.hour = 14; g_currentTime.minute = 0;
g_currentTime.second = 0;
IRTC_SetWriteProtection(RTC, false);
IRTC_SetDatetime(RTC, &g_currentTime);
}
}

SysTick中断与按键扫描

void SysTick_Handler(void)
{
g_tick_count++;
if ((g_tick_count % 500U) == 0U) g_time_update_flag = true;
Button_Scan();
}

static void Button_Scan(void)
{
uint32_t sw2_level = (GPIO_PinRead(GPIO0, 20U) == 0U) ? 0U : 1U;
uint32_t sw3_level = (GPIO_PinRead(GPIO0, 6U) == 0U) ? 0U : 1U;

/* SW2去抖与长短按 */
g_sw2_history = ((g_sw2_history << 1) | sw2_level) & 0x0F;
if (g_sw2_history == 0U) {
if (!g_sw2_pressed) {
g_sw2_pressed = true;
g_sw2_press_tick = g_tick_count;
g_sw2_long_handled = false;
}
} else if (g_sw2_history == 0x0F) {
if (g_sw2_pressed) {
if (!g_sw2_long_handled) g_key_event = KEY_EVENT_SW2_SHORT;
g_sw2_pressed = false;
}
}
if (g_sw2_pressed && !g_sw2_long_handled &&
(g_tick_count - g_sw2_press_tick) >= 1000U) {
g_sw2_long_handled = true;
g_key_event = KEY_EVENT_SW2_LONG;
}
/* SW3同理,略 */
}

时间调整状态机处理

case KEY_EVENT_SW2_SHORT:
switch (g_clock_state) {
case CLOCK_STATE_NORMAL:
g_clock_state = CLOCK_STATE_ADJUST_YEAR;
g_adjustTime = g_currentTime;
break;
case CLOCK_STATE_ADJUST_YEAR:
g_clock_state = CLOCK_STATE_ADJUST_MONTH; break;
/* ... 依次切换 ... */
case CLOCK_STATE_ADJUST_SECOND:
IRTC_SetWriteProtection(RTC, false);
IRTC_SetDatetime(RTC, &g_adjustTime);
g_currentTime = g_adjustTime;
g_clock_state = CLOCK_STATE_NORMAL;
break;
}
break;

case KEY_EVENT_SW2_LONG:
if (g_clock_state != CLOCK_STATE_NORMAL) {
IRTC_SetWriteProtection(RTC, false);
IRTC_SetDatetime(RTC, &g_adjustTime);
g_currentTime = g_adjustTime;
g_clock_state = CLOCK_STATE_NORMAL;
}
break;

case KEY_EVENT_SW3_SHORT:
if (g_clock_state != CLOCK_STATE_NORMAL)
AdjustTime(&g_adjustTime, g_clock_state, +1);
break;

case KEY_EVENT_SW3_LONG:
if (g_clock_state != CLOCK_STATE_NORMAL)
AdjustTime(&g_adjustTime, g_clock_state, -1);
break;

五、功能展示

5.1 串口输出

系统启动后,串口终端显示初始化日志和当前时间:

========================================
Electronic Clock - FRDM-MCXN236
IRTC + Button + UART
========================================
[IRTC] Set default: 2026/10/10 14:00:00
System ready.
[2026-10-10 14:00:00] Electronic Clock Running
[2026-10-10 14:00:00] Electronic Clock Running
[2026-10-10 14:00:01] Electronic Clock Running
[2026-10-10 14:00:01] Electronic Clock Running
...

由于SysTick每0.5秒刷新一次,而IRTC精度为1秒,同一秒的时间会连续输出两次。

5.2 时间调整

短按SW2进入调整模式:

SW2 Pressed! 
[SET MODE] Enter ADJUST_YEAR

短按SW3递增当前字段:

SW3 Pressed! 
[SET] YEAR: 2027

长按SW3递减当前字段:

SW3 Pressed! 
[SET] YEAR: 2026

短按SW2切换字段:

SW2 Pressed! 
[SET MODE] Enter ADJUST_MONTH

长按SW2保存并退出:

SW2 Pressed! 
[SET MODE] Time saved to IRTC
[SET MODE] Long press: Exit to NORMAL (saved)
[2026-11-10 14:00:15] Electronic Clock Running

5.3 复位后时间保持

在正常计时状态下,按下开发板上的复位按键SW1,MCU复位重新运行程序。串口输出显示:

[IRTC] Restored: 2026/10/10 14:05:30
System ready.
[2026-10-10 14:05:31] Electronic Clock Running
[2026-10-10 14:05:31] Electronic Clock Running
[2026-10-10 14:05:32] Electronic Clock Running

可以看到,复位后IRTC时间并未重置为默认值,而是从复位前的时刻继续计时,验证了时间保持功能正常。

六、遇到的主要难题及解决方法

6.1 编译器cc1错误

问题:VS Code下构建工程报错cannot execute 'cc1': CreateProcess: No such file or directory。

解决方法:通过MCUXpresso Installer重新安装Arm GNU Toolchain,并确保ARMGCC_DIR环境变量指向工具链根目录。

6.2 IRTC初始化挂起

问题:IRTC初始化后系统挂起,串口仅输出标题后停止。

原因:RTC的CTRL[CLK_SEL]位默认选择FRO16K作为时钟源,若FRO16K未使能,访问RTC寄存器会挂起。

解决方法:在初始化IRTC之前,同时使能FRO16K和OSC32K到VBAT域。

6.3 IRTC秒中断枚举不存在

问题:编译报错'kIRTC_SecondsInterruptEnable' undeclared。

解决方法:改用SysTick定时器产生1ms中断,累计500次后置位时间更新标志。

6.4 SysTick重载值超出24位

问题:SysTick_Config(SystemCoreClock / 2U)失败,因为150MHz/2=75,000,000超出24位上限。

解决方法:改为1ms中断,在中断中累加计数。

6.5 SW2按键无响应

问题:SW3正常,SW2(P0_20)无反应。

原因:pin_mux.c中未配置P0_20的GPIO复用功能。

解决方法:在BOARD_InitPins()中添加P0_20的PORT配置,设为kPORT_MuxAlt0(GPIO功能),并使能内部上拉。

七、心得体会

通过本次Funpack活动,我深入学习了NXP MCX N系列微控制器的开发流程。FRDM-MCXN236开发板配合MCUXpresso SDK和VS Code环境,提供了从底层驱动到应用开发的完整工具链。

在电子时钟项目的开发过程中,我不仅完成了活动任务要求的全部功能,还实现了全字段时间调整、短按/长按双模式按键等增强功能。特别是复位后时间保持功能的实现,让我对IRTC的独立供电域和复位行为有了深刻理解。

调试过程中遇到的cc1编译错误、IRTC初始化挂起、SysTick重载值超限等问题,锻炼了我分析问题和查阅技术资料的能力。通过NXP社区、SDK文档和官方例程,我逐步定位并解决了每一个问题,这种“遇到问题-分析问题-解决问题”的循环是嵌入式开发中最宝贵的经验。

FRDM-MCXN236的丰富外设和MCUXpresso生态让我能够快速搭建原型系统,从IRTC计时到UART通信,再到GPIO按键处理,整个开发流程高效而流畅。期待未来能基于这块开发板探索更多应用场景。

附件下载
frdmmcxn236_electronic_clock.rar
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号