Funpack5-4 - 基于MCXN236实现的时钟demo
该项目使用了MCXN236,实现了mBUS接口实现的墨水屏单词机的设计,它的主要功能为:可录音可播放单词读音,M33强大的低功耗处理能力。
标签
MCXN236
墨水屏单词机
zhjlmt
更新2026-10-19
3

一、项目介绍

基于 NXP FRDM-MCXN236 开发板(Cortex-M33 @150MHz)实现英语单词学习机原型:RTC 电子时钟(走时/设时/掉电保持/按键调时)、20 词词库与自动测验、3.7 寸六色墨水屏驱动、PDM 麦克风录音与 I2S 喇叭播放、RTD 测温。全程在 macOS 上无头开发(west+CMake+armgcc+pyOCD),串口验证、git 全程管理。

二、硬件介绍

FRDM-MCXN236 开发板(MCXN236:1MB Flash/352KB RAM/板载 8MB QSPI/MCU-Link 调试器);

EPD037A02 3.7 寸六色墨水屏(JD7601,32P FPC);

MAX98357A I2S 功放 + 4Ω 喇叭;

PDM 数字麦克风(板载 U7 焊位/LMD2718);

其中MAX98357A使用杜邦线连接到开发板上,六色墨水屏通过绘制的PCB和MCXN236开发板进行连接。

三、方案框图和设计思路

image.png

RTC时钟代码

void clock_app_init(void)
{
/* 1) FRO16K 路由到 VBAT 与 Main 域, 必须先于任何 IRTC 访问 (每次复位后需重配) */
(void)CLOCK_SetupClk16KClocking(kCLOCK_Clk16KToVbat | kCLOCK_Clk16KToMain);
/* 2) IRTC 总线时钟 (门控外设, 先开后访问) */
CLOCK_EnableClock(kCLOCK_Rtc0);


/* 3) 先恢复访问, 确认安全后才允许读时间寄存器 */
if (!regs_ok_single()) {
if (!unlock_wp()) { /* 解锁失败: 隔离, 报告, 不再访问 */ return; }
clear_irq_residual();
delay_ms(2);
if ((IRTC_GetStatusFlags(RTC0) & kIRTC_InvalidFlag) != 0U) {
IRTC_Init(RTC0, &cfg); /* 软复位清 INVAL (计数清零) */
s_need_default = true;
}
}


/* 4) 年份异常 = 真断电/首次上电 → 载默认; 否则 VBAT 保持 → 直接沿用 */
IRTC_GetDatetime(RTC0, &dt);
if (dt.year < 2026U || dt.year > 2099U) {
IRTC_Init(RTC0, &cfg);
load_default_time(); /* 2026-01-01 12:00:00 */
}
}

bool clock_set_hms(uint32_t h, uint32_t m, uint32_t s)
{
if (h > 23U || m > 59U || s > 59U)
return false; /* 越界拒绝 */
if (!ensure_rtc_ok())
return false;


for (int attempt = 0; attempt < 2; attempt++) {
if (!unlock_wp())
return false;
IRTC_GetDatetime(RTC0, &dt);
dt.hour = h; dt.minute = m; dt.second = s;
if (IRTC_SetDatetime(RTC0, &dt) == kStatus_Success) {
/* 回读校验 (写入瞬间秒可能 +1 翻转, 容忍 s 或 s+1) */
IRTC_GetDatetime(RTC0, &chk);
if (chk.hour == h && chk.minute == m &&
(chk.second == s || chk.second == ((s + 1U) % 60U)))
return true;
}
delay_ms(2); /* 可能撞上秒翻转 INVAL 脉冲, 稍候重试 */
}
return false; /* 明确报失败, 不静默 */
}

uint8_t c2 = (GPIO_PinRead(BOARD_SW2_GPIO, BOARD_SW2_GPIO_PIN) == 0) ? 1U : 0U;
if (c2 == st2) cnt2 = 0;
else if (++cnt2 >= 30U) {
st2 = c2; cnt2 = 0;
if (c2 != 0U) g_btn_evt |= 1U; /* bit0 = SW2 按下 */
}


/* 主循环: SW2=时+1, SW3=分+1 */
if ((ev & 1U) != 0U) (void)clock_adjust(1, 0);
if ((ev & 2U) != 0U) (void)clock_adjust(0, 1);


bool clock_adjust(int dh, int dm)
{
IRTC_GetDatetime(RTC0, &dt);
int h = (int)dt.hour + dh, m = (int)dt.minute + dm;
if (h < 0) h += 24; if (h > 23) h -= 24;
if (m < 0) m += 60; if (m > 59) m -= 60;
return clock_set_hms((uint32_t)h, (uint32_t)m, dt.second); /* 秒不变 */
}

墨水屏驱动代码

static void pins_init(void)
{
CLOCK_EnableClock(kCLOCK_Gpio1);
CLOCK_EnableClock(kCLOCK_Port1);
#if WM_EPD_ON_MIKROBUS
/* GPIO5/PORT5 属常开域,无时钟门控 */
#endif

gpio_pin_config_t out = {kGPIO_DigitalOutput, 1};
gpio_pin_config_t in = {kGPIO_DigitalInput, 0};
GPIO_PinInit(EPD_DC_GPIO, EPD_DC_PIN, &out);
GPIO_PinInit(EPD_RST_GPIO, EPD_RST_PIN, &out);
GPIO_PinInit(EPD_BUSY_GPIO, EPD_BUSY_PIN, &in);

/* FC3: P1_0=SDO P1_1=SCK → Alt2 */
PORT_SetPinMux(PORT1, 0u, kPORT_MuxAlt2);
PORT_SetPinMux(PORT1, 1u, kPORT_MuxAlt2);
PORT_SetPinMux(PORT1, 2u, kPORT_MuxAlt2);

#if WM_EPD_ON_MIKROBUS
GPIO_PinInit(EPD_CS_GPIO, EPD_CS_PIN, &out);
PORT_SetPinMux(PORT1, EPD_CS_PIN, kPORT_MuxAsGpio);
PORT_SetPinMux(PORT5, EPD_DC_PIN, kPORT_MuxAsGpio);
PORT_SetPinMux(PORT5, EPD_RST_PIN, kPORT_MuxAsGpio);
PORT_SetPinMux(PORT5, EPD_BUSY_PIN, kPORT_MuxAsGpio);
GPIO_PinWrite(EPD_CS_GPIO, EPD_CS_PIN, 1); /* 默认未选中 */
#else
PORT_SetPinMux(PORT1, 3u, kPORT_MuxAlt2); /* P1_3 = FC3_PCS0 硬件片选 */
#endif
}

static void spi_init(void)
{
CLOCK_AttachClk(kFRO12M_to_FLEXCOMM3);
CLOCK_SetClkDiv(kCLOCK_DivFlexcom3Clk, 1u);

lpspi_master_config_t cfg = {0};
cfg.baudRate = EPD_SPI_CLK_HZ;
cfg.bitsPerFrame = 8u;
cfg.cpol = kLPSPI_ClockPolarityActiveHigh;
cfg.cpha = kLPSPI_ClockPhaseFirstEdge;
cfg.direction = kLPSPI_MsbFirst;
cfg.pcsToSckDelayInNanoSec = 50;
cfg.lastSckToPcsDelayInNanoSec = 50;
cfg.betweenTransferDelayInNanoSec = 50;
LPSPI_MasterInit(EPD_SPI, &cfg, 12000000u); /* 源时钟 FRO 12M */
}

四、调试软件

调试链:west 拉取 GitHub 版 SDK → CMake/Ninja 编译(armgcc 14.2)→ pyOCD 烧录 → 串口验证(RYCOM/Web Serial 网页/Python 脚本三套)。


上电 → BOARD_InitHardware → 外设初始化(GPIO/IRTC/PDM/墨水屏) → shell 主循环

中断: RTC 4Hz(按键去抖+走时节拍) / PDM 水位(录音填充)


关键代码(clock_app.c):RTC 只在时间无效时才 Init(IRTC_Init 会清计数),否则跳过以保持时间;按键用 kPORT_InputBufferEnable 显式开输入缓冲(MCX N 复位默认关)。


五、功能展示图

六、bug问题

1. 坏固件把 SWD 挂死(VBAT 总线停摆跨复位)→ 按住 SW3 上电进引导模式恢复;此后先开时钟再访问外设。

2. printf 无输出 → 本 SDK 只重定向 PRINTF/GETCHAR 宏。

3. 按键读错 → PORT 输入缓冲复位默认关闭,须显式使能。

4. PDM 数据错 → FIFO 为 32bit 宽、PCM 左对齐高位,须读 uint32 取高 16 位。

5. RTC 读出 2112 年 → 增加年份有效范围校验。

七、心得体会

最深的教训是 MCX N 的"时钟门控":任何外设寄存器访问前必须先开总线时钟,否则总线挂死连调试器都失效,只能断电+引导模式自救。其次是善用例程源码——printf 重定向、FIFO 位宽这些细节手册里难找,例程一看便知。无头开发流程(脚本化构建+烧录+串口自动验证+git 粒度提交)让每次迭代可复现、可回退,是多人协作与长期维护的正确姿势。硬件选型上"数字接口优先"(PDM/I2S 而非模拟)大幅简化了信号链。遗留:PDM 数据流 100ms 停止问题待对照官方 EDMA 例程继续定位。

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