Funpack5-4 - 基于FRDM-MCXN236的桌面RTC电子时钟设计
该项目使用了FRDM-MCXN236开发板,实现了桌面RTC电子时钟设计的设计,它的主要功能为:内部RTC记录时间、OLED显示时间、按键设置时间。
标签
嵌入式系统
Funpack活动
OLED显示
电子时钟
小小洋洋
更新2026-10-16
同济大学
10

一、任务活动介绍

Funpack 活动是由硬禾科技联合 DigiKey 发起的“玩成功就全额退”活动。第五季第 4 期所使用的板卡为 NXP FRDM-MCXN236 开发板。本项目选择的任务如下。

任务 1:电子时钟设计【难度 ⭐️⭐️】

基于 FRDM-MCXN236 开发板实现一个电子时钟系统,能够显示当前时间并支持时间设置功能。任务要求包括:

  • 使用 RTC 获取系统时间;
  • 显示当前时、分、秒信息;
  • 使用按键调整时间;
  • 通过串口输出当前时间;
  • 在具备后备供电的条件下,掉电后保持时间。

本项目在基础任务之上增加了日期显示和桌面化交互,使用 0.91 英寸 SSD1306 OLED 同时显示日期与时间。


二、项目描述

image.png

本项目基于 NXP FRDM-MCXN236 开发板实现桌面电子时钟。系统使用内部 IRTC 记录日期和时间,通过 SSD1306 128×32 OLED 显示,并利用板载 SW2、SW3 按键完成时间设置。

项目主要功能包括:

  • 显示年月日和时分秒;
  • 支持按键设置时间及字段闪烁提示;
  • 自动判断闰年和月份天数;
  • 每秒通过串口输出当前时间;
  • RESET 后保持 RTC 继续运行;
  • 支持软件 I²C和硬件 I²C 两种 OLED 驱动方式。

工程基于 MCUXpresso SDK 的 IRTC 示例扩展,采用中断与主循环相结合的结构,具有实现简单、资源占用少和运行稳定等特点。


三、芯片及器件选型

3.1 FRDM-MCXN236 开发板

image.png

FRDM-MCXN236 开发板的核心为 MCX N236 微控制器,主要资源如下。

项目

参数

CPU 内核

Arm Cortex-M33

最高主频

150 MHz

Flash

1 MB 双组 Flash

RAM

352 KB ECC RAM

加速单元

SmartDMA

RTC

带日历功能的内部 IRTC,位于 VBAT 域

调试接口

板载 MCU-Link

扩展接口

Arduino 兼容接口

本项目使用外设

IRTC、GPIO、PORT、LPUART、SysTick、LPI2C2(可选)

MCX N236 的 IRTC 能够直接保存年、月、日、星期、时、分、秒信息,并支持独立 VBAT 域,因此适合实现需要跨系统复位保持计时的电子时钟。

3.2 OLED 显示模块

image.png

本项目使用 0.91 英寸 SSD1306 OLED 模块。

项目

参数

控制芯片

SSD1306

分辨率

128×32

通信方式

I²C

默认 7 位地址

​0x3C​

显存大小

128×32÷8 = 512 字节

显示内容

英文标题、日期、时间及设置状态

OLED 具有体积小、对比度高、无需背光和接口简单等特点,适合作为桌面电子时钟的显示器。


四、方案框图与设计思路

FRDM-MCXN236 RTC电子时钟系统框图

系统以 FRDM-MCXN236 开发板为核心,利用内部 IRTC 完成日期和时间的持续计时。SW3 作为 SET 按键,用于进入、确认或取消时间设置;SW2 作为 PLUS 按键,用于增加当前设置字段。系统通过 GPIO 完成按键扫描与消抖,并由 SysTick 提供稳定的毫秒级时间基准。当前日期和时间通过 SSD1306 OLED 显示,同时经调试串口每秒输出。OLED 默认采用交换引脚后的软件 I²C驱动,也可通过宏定义切换为硬件 LPI2C2。系统 RESET 期间 IRTC 能够继续计时,若需在完全断电后保持时间,则需要为 VBAT 域提供独立后备电源。


五、电路设计

5.1 开发板参考原理图

FRDM-MCXN236 参考原理图可从电子森林资源库的FRDM-MCXN236 开发板页面获取。本项目没有设计独立 PCB,主要使用开发板内部 IRTC、板载按键和扩展接口连接 OLED。

image.png

5.2 OLED 连线

本项目使用 0.91 英寸、128×32 分辨率的 SSD1306 OLED。为了方便接线,默认采用 GPIO 模拟软件 I²C,所使用的引脚如下:

OLED 引脚

开发板连接

功能

VCC

3.3 V

模块电源

GND

GND

电源地

SDA

P1_2

软件 I²C 数据线

SCL

P1_0

软件 I²C 时钟线

工程同时保留硬件 LPI2C2 驱动。如需使用硬件 I²C,应按以下方式连接:

OLED 引脚

开发板连接

复用功能

SDA

P4_0

FC2_P0

SCL

P4_1

FC2_P1

软硬件 I²C可通过 board/app.h 中的宏定义选择:

/* 0:软件 I2C;1:硬件 LPI2C2 */
#define BOARD_OLED_USE_HARDWARE_I2C 0

默认值为 0,即使用 P1_2 作为 SDA、P1_0 作为 SCL。修改为 1 后,程序将使用硬件 LPI2C2,此时需要按照硬件外设的固定引脚功能连接 OLED。

5.3 按键分配

按键

MCU 引脚

功能

SW2

P0_20

PLUS,增加当前设置字段

SW3

P0_6

SET,长按进入/取消,短按确认

两个按键均采用低电平有效方式,并启用内部上拉和无源输入滤波。

5.4 RTC 后备供电说明

image.png

IRTC 日历寄存器不受普通软件复位影响,因此按下或按住 RESET 时,RTC 可以继续运行。

当前开发板没有单独连接 VBAT 后备电源,所以“按住 RESET”只能模拟主控停止运行,不能等效为整板断电。要实现真正的完全掉电时间保持,需要根据开发板原理图为 VBAT 域增加纽扣电池或超级电容,根据原理图需断开R76电阻,并在TP32上增加VBAT供电。


六、软件设计与关键代码

6.1 工程结构

工程以 MCUXpresso SDK 的 FRDM-MCXN236 IRTC 示例为基础,主要文件如下。

image.png

文件

功能

​source/irtc.c​

主程序、IRTC 初始化、有效性判断、时间写入和串口输出

​source/clock_app.c/.h​

按键扫描、设置状态机、日历处理、闪烁和界面绘制

​source/ssd1306.c/.h​

SSD1306 驱动、软件 I²C及硬件 LPI2C2 后端

​source/oled_font.h​

6×8 ASCII 字库

​board/hardware_init.c​

按键和 OLED 总线初始化

​board/pin_mux.c/.h​

UART、按键、硬件/软件 I²C 引脚复用

​board/app.h​

OLED 接口模式、引脚和通信参数配置

6.2 软件主流程

FRDM-MCXN236 RTC电子时钟软件流程图

系统上电或 RESET 后,首先初始化系统时钟、GPIO、串口、OLED、SysTick 和内部 IRTC。程序判断 RTC 时间是否有效,若无效则写入默认日期和时间,随后进入 10 ms 周期主循环。正常模式下,系统持续扫描按键,并每秒刷新 OLED 和串口输出;长按 SW3 后进入时间设置模式。设置过程中,SW2 用于增加当前字段,短按 SW3 用于确认并切换字段。当年、月、日、时、分、秒全部设置完成后,程序校验日期并写入 IRTC。设置期间长按 SW3 或 30 秒无操作,系统会放弃修改并返回正常显示模式。

6.3 关键代码

  1. RTC 时间有效性判断与复位保持

程序启动后首先读取 IRTC 状态。如果 RTC 无效或时间字段超出合法范围,则写入默认时间;如果时间有效,则保留原有时间。因此,按下 RESET 后不会重新初始化时间,IRTC 可以继续计时。

static bool APP_TimeIsValid(void)
{
irtc_datetime_t datetime;
uint32_t flags = IRTC_GetStatusFlags(RTC);

if (0U != (flags & (uint32_t)kIRTC_InvalidFlag))
{
return false;
}

IRTC_GetDatetime(RTC, &datetime);

if ((datetime.year < APP_YEAR_MIN) ||
(datetime.year > APP_YEAR_MAX) ||
(datetime.month < 1U) || (datetime.month > 12U) ||
(datetime.day < 1U) ||
(datetime.day > APP_GetDaysOfMonth(datetime.year,
datetime.month)) ||
(datetime.hour > 23U) ||
(datetime.minute > 59U) ||
(datetime.second > 59U))
{
return false;
}

return true;
}

启动时根据检测结果决定是否写入默认时间:

retained = APP_TimeIsValid();

if (!retained)
{
APP_LoadDefaultTime();
PRINTF("Cold start detected, default time loaded.\r\n");
}
else
{
PRINTF("Warm reset detected, retained time is kept running.\r\n");
}
  1. 时间设置状态机

系统使用状态机依次设置年、月、日、时、分、秒。每次短按 SW3 后进入下一字段,确认秒字段后返回正常模式,并通知主程序将编辑结果写入 IRTC。

bool APP_AdvanceField(void)
{
bool finished = false;

switch (s_clockMode)
{
case kAppMode_SetYear:
s_clockMode = kAppMode_SetMonth;
break;

case kAppMode_SetMonth:
s_clockMode = kAppMode_SetDay;
break;

case kAppMode_SetDay:
s_clockMode = kAppMode_SetHour;
break;

case kAppMode_SetHour:
s_clockMode = kAppMode_SetMinute;
break;

case kAppMode_SetMinute:
s_clockMode = kAppMode_SetSecond;
break;

case kAppMode_SetSecond:
s_clockMode = kAppMode_Run;
finished = true;
break;

default:
s_clockMode = kAppMode_Run;
break;
}

s_blinkTicks = 0U;
s_blinkVisible = false;

return finished;
}

进入设置模式时,程序先复制当前 RTC 时间,避免在设置过程中直接修改硬件时间:

void APP_EnterSetting(const irtc_datetime_t *current)
{
memcpy(&s_editTime, current, sizeof(s_editTime));
APP_SanitizeDatetime(&s_editTime);

s_clockMode = kAppMode_SetYear;
s_blinkTicks = 0U;
s_blinkVisible = false;
}
  1. 闰年及日期合法性处理

不同月份的天数并不相同,二月还需要判断闰年。程序使用完整的公历规则计算每个月的最大天数,避免出现 2 月 30 日、4 月 31 日等非法日期。

uint8_t APP_GetDaysOfMonth(uint16_t year, uint8_t month)
{
uint8_t days[12] = {
31U, 28U, 31U, 30U, 31U, 30U,
31U, 31U, 30U, 31U, 30U, 31U
};

if ((month < 1U) || (month > 12U))
{
return 31U;
}

if (2U == month)
{
if ((((year % 4U) == 0U) &&
((year % 100U) != 0U)) ||
((year % 400U) == 0U))
{
return 29U;
}
}

return days[month - 1U];
}

增加日期时,根据当前年月确定循环上限:

case kAppMode_SetDay:
{
uint8_t maxDay =
APP_GetDaysOfMonth(s_editTime.year,
s_editTime.month);

s_editTime.day++;

if (s_editTime.day > maxDay)
{
s_editTime.day = 1U;
}
break;
}

小时、分钟和秒则通过取模运算实现自动循环:

s_editTime.hour =
(uint8_t)((s_editTime.hour + 1U) % 24U);

s_editTime.minute =
(uint8_t)((s_editTime.minute + 1U) % 60U);

s_editTime.second =
(uint8_t)((s_editTime.second + 1U) % 60U);
  1. 软件 I²C字节发送

由于 OLED 模块的 SDA、SCL 顺序与硬件 LPI2C2 引脚相反,工程默认使用 GPIO 模拟 I²C。GPIO 输出模式用于拉低总线,输入模式用于释放总线,从而模拟 I²C 的开漏结构。

static void SSD1306_SoftDriveLow(uint32_t pin)
{
BOARD_OLED_SOFT_I2C_GPIO->PDDR |= (1UL << pin);
}

static void SSD1306_SoftRelease(uint32_t pin)
{
BOARD_OLED_SOFT_I2C_GPIO->PDDR &= ~(1UL << pin);
}

发送一个字节后,程序在第九个时钟读取 OLED 返回的 ACK:

static bool SSD1306_SoftWriteByte(uint8_t value)
{
uint32_t bit;
bool acknowledged;

for (bit = 0U; bit < 8U; bit++)
{
if (0U != (value & 0x80U))
{
SSD1306_SoftRelease(
BOARD_OLED_SOFT_I2C_SDA_PIN);
}
else
{
SSD1306_SoftDriveLow(
BOARD_OLED_SOFT_I2C_SDA_PIN);
}

SSD1306_SoftDelay();
if (!SSD1306_SoftRaiseClock())
{
return false;
}

SSD1306_SoftDelay();
SSD1306_SoftDriveLow(
BOARD_OLED_SOFT_I2C_SCL_PIN);

value <<= 1U;
}

/* 第九个时钟读取低电平有效的 ACK */
SSD1306_SoftRelease(
BOARD_OLED_SOFT_I2C_SDA_PIN);
SSD1306_SoftRaiseClock();
SSD1306_SoftDelay();

acknowledged = !SSD1306_SoftRead(
BOARD_OLED_SOFT_I2C_SDA_PIN);

SSD1306_SoftDriveLow(
BOARD_OLED_SOFT_I2C_SCL_PIN);

return acknowledged;
}
  1. OLED 刷新与串口时间输出

IRTC 每秒产生一次中断事件。主循环检测到该事件后重新读取 RTC,刷新 OLED,并通过调试串口输出完整日期和时间。

串口时间格式化代码如下:

static void APP_PrintDatetime(
const irtc_datetime_t *datetime)
{
PRINTF("RTC: %04u-%02u-%02u %02u:%02u:%02u\r\n",
(unsigned int)datetime->year,
(unsigned int)datetime->month,
(unsigned int)datetime->day,
(unsigned int)datetime->hour,
(unsigned int)datetime->minute,
(unsigned int)datetime->second);
}

每秒事件处理代码如下:

if (s_secondTick)
{
s_secondTick = false;
redraw = true;
printTime = true;
}

if (redraw)
{
IRTC_GetDatetime(RTC, &now);
APP_SanitizeDatetime(&now);
}

if (printTime)
{
APP_PrintDatetime(&now);
}

if (redraw && oledReady)
{
APP_Display(&now);
}

串口输出示例:

RTC: 2025-07-25 12:30:45
RTC: 2025-07-25 12:30:46


七、实物演示

  1. 上电显示:上电之后,由于刚上电,默认显示的时间为2025-01-01 00:00:00,然后间隔1s增加时间。

image.png

  1. 设置时间和切换显示模式:长按SW3进入设置模式,设置正确的时候后,即可按最新时间运行。然后可以通过SW2切换显示模式,共三种显示模式。

image.png

image.png

image.png

  1. 长按进入时间设置:进入设置模式后,左上角会显示※标志,对应的设置位,会闪烁显示。设置过程中,按SW2增加对应位,长按可以快速增加。

image.png

  1. 串口输出内容:1s周期发送最新时间。

image.png

  1. RTC测试:由于VBAT没有单独接入电源,我们以按住RESET按键一段时间,观察时间是否正确。测试结果RTC是一直运行的,具体效果查看视频。


八、难点与解决方法

8.1 两个按键完成六个字段设置

问题: 按键数量有限,既要进入设置,又要调整和确认字段,还要避免误触。

解决: 将 SW3 设计为多功能 SET 键:正常模式长按进入、设置模式短按确认、设置模式长按取消;SW2 专门负责增加。当前字段闪烁提供视觉反馈,并加入 30 秒无操作超时。

8.2 日期边界和闰年处理

问题: 日期字段不能简单按 1~31 循环,否则会产生 2 月 30 日等非法时间。

解决: 建立统一的月份天数函数,并使用完整公历闰年规则。修改年、月后立即修正日字段,最终写入前再次进行完整合法性检查。


九、总结

本项目基于 FRDM-MCXN236 实现了具备 OLED 日期时间显示、双按键设置、串口输出、日期校验和 RESET 计时保持功能的桌面 RTC 电子时钟。针对 OLED 引脚顺序问题,系统提供可切换的软硬件 I²C 驱动,后续还可扩展独立 VBAT 供电、闹钟、亮度调节和低功耗等功能。

通过本次实践,掌握了 MCX N236 的 IRTC、GPIO、PORT、LPUART、LPI2C 和 SysTick 等外设的使用方法,也进一步理解了 RTC 后备供电、I²C 开漏总线、按键状态机和日历合法性处理。

附件下载
Funpack5-4-RTC-Desktop-Clockfrdmmcxn236.zip
项目源码(MCUXpresso IDE)
团队介绍
无
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号