首先,个人不太认同使用mcx156的LPTMR模块适配RTC驱动的做法,但是既然群友提到了可以这么做,那就把这个模块适配一下玩玩。
硬件资源
从MCXAP100M96FS6RM.pdf上可以知道,LPTMR模块可以用作1s计时,但是存在一个问题,就是计数器不能设置,只能读。这就导致我们使用此模块适配RTC时,只能通过rtc驱动内部维护的变量来实现计时计算。其结构框图如下:

此处提到了lptmr的计数器无法初始化,只能读取。

驱动适配
KConfig修改
对应芯片的KConfig文件存储位置为:bsp\nxp\mcx\mcxa\frdm-mcxa156\board\Kconfig
我们仅仅需要在该文件的On-chip Peripheral Drivers项目中增加硬件RTC相关配置即可:
menuconfig BSP_USING_RTC config BSP_USING_RTC bool "Enable RTC" select RT_USING_RTC default y if BSP_USING_RTC config BSP_USING_ALARM bool "Enable Alarm" default y endif
SConscript修改
MCXA系列芯片驱动的SConscript文件存储位置为:bsp\nxp\mcx\mcxa\Libraries\drivers\SConscript
由于此文件中之前便有了RTC的配置,因此不需要修改。

驱动文件添加
在SConscript同一目录下添加drv_rtc.c文件,并填入以下内容。
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-02-28 oxlm Initial version.
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <sys/time.h>
#include "fsl_common.h"
#include "fsl_lptmr.h"
#ifdef RT_USING_RTC
struct rtc_device_object
{
rt_rtc_dev_t dev;
time_t ts;
rt_bool_t is_ts_set;
time_t ts_new;
#ifdef BSP_USING_ALARM
struct rt_rtc_wkalarm alarm;
#endif
};
static struct rtc_device_object rtc_dev;
void LPTMR0_IRQHandler(void)
{
LPTMR_ClearStatusFlags(LPTMR0, kLPTMR_TimerCompareFlag);
if(rtc_dev.is_ts_set)
{
rtc_dev.is_ts_set = RT_FALSE;
rtc_dev.ts = rtc_dev.ts_new;
}
rtc_dev.ts++;
#ifdef BSP_USING_ALARM
if(rtc_dev.alarm.enable)
{
rt_alarm_update(&rtc_dev.dev.parent, 0);
}
#endif
/*
* Workaround for TWR-KV58: because write buffer is enabled, adding
* memory barrier instructions to make sure clearing interrupt flag completed
* before go out ISR
*/
__DSB();
__ISB();
}
static rt_err_t mcx_rtc_init(void)
{
lptmr_config_t lptmrConfig;
LPTMR_GetDefaultConfig(&lptmrConfig);
LPTMR_Init(LPTMR0, &lptmrConfig);
LPTMR_SetTimerPeriod(LPTMR0, USEC_TO_COUNT(1000000, 16000U));
LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
EnableIRQ(LPTMR0_IRQn);
LPTMR_StartTimer(LPTMR0);
return RT_EOK;
}
static rt_err_t mcx_rtc_get_time(time_t *ts)
{
if(rtc_dev.is_ts_set)
{
*ts = rtc_dev.ts_new;
}
else
{
*ts = rtc_dev.ts;
}
return RT_EOK;
}
static rt_err_t mcx_rtc_set_time(time_t *ts)
{
rtc_dev.ts_new = *ts;
rtc_dev.is_ts_set = RT_TRUE;
return RT_EOK;
}
#ifdef BSP_USING_ALARM
rt_err_t mcx_rtc_get_alarm(struct rt_rtc_wkalarm *alarm)
{
rt_memcpy(alarm, &rtc_dev.alarm, sizeof(struct rt_rtc_wkalarm));
return RT_EOK;
}
rt_err_t mcx_rtc_set_alarm(struct rt_rtc_wkalarm *alarm)
{
rt_memcpy(&rtc_dev.alarm, alarm, sizeof(struct rt_rtc_wkalarm));
return RT_EOK;
}
#endif
static const struct rt_rtc_ops ops =
{
.init = mcx_rtc_init,
.get_secs = mcx_rtc_get_time,
.set_secs = mcx_rtc_set_time,
#ifdef BSP_USING_ALARM
.get_alarm = mcx_rtc_get_alarm,
.set_alarm = mcx_rtc_set_alarm,
#else
.get_alarm = RT_NULL,
.set_alarm = RT_NULL,
#endif
.get_timeval = RT_NULL,
.set_timeval = RT_NULL,
};
int rt_hw_rtc_init(void)
{
rtc_dev.is_ts_set = RT_FALSE;
rtc_dev.ts = 0;
rtc_dev.dev.ops = &ops;
if (rt_hw_rtc_register(&rtc_dev.dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL) != RT_EOK)
{
rt_kprintf("rtc init failed");
return -RT_ERROR;
}
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_rtc_init);
#endif /*RT_USING_RTC */时钟树修改
在 bsp\nxp\mcx\mcxa\frdm-mcxa156\board\MCUX_Config\board\pin_mux.c中的函数BOARD_InitPins中添加如下代码:
#ifdef BSP_USING_RTC CLOCK_SetClockDiv(kCLOCK_DivLPTMR0, 1u); CLOCK_AttachClk(kFRO12M_to_LPTMR0); CLOCK_SetupFRO16KClocking(kCLKE_16K_SYSTEM | kCLKE_16K_COREMAIN); #endif
至此,代码修改部分已经完成。
开启RTC功能
menuconfig设置


配置好后按Q键并按Y键保存。
生成工程

添加测试代码
在main.c中添加如下代码。
void user_alarm_callback(rt_alarm_t alarm, time_t timestamp)
{
time_t now = (time_t)0;
struct timeval tv = { 0 };
gettimeofday(&tv, RT_NULL);
now = tv.tv_sec;
rt_kprintf("local time: %.*s", 25U, ctime(&now));
}
void alarm_sample(void)
{
rt_device_t dev = rt_device_find("rtc");
struct rt_alarm_setup setup;
struct rt_alarm * alarm = RT_NULL;
static time_t now;
struct tm p_tm;
if (alarm != RT_NULL)
return;
now = time(NULL) + 1;
gmtime_r(&now,&p_tm);
setup.flag = RT_ALARM_SECOND;
setup.wktime.tm_year = p_tm.tm_year;
setup.wktime.tm_mon = p_tm.tm_mon;
setup.wktime.tm_mday = p_tm.tm_mday;
setup.wktime.tm_wday = p_tm.tm_wday;
setup.wktime.tm_hour = p_tm.tm_hour;
setup.wktime.tm_min = p_tm.tm_min;
setup.wktime.tm_sec = p_tm.tm_sec;
alarm = rt_alarm_create(user_alarm_callback, &setup);
if(RT_NULL != alarm)
{
rt_alarm_start(alarm);
}
}
MSH_CMD_EXPORT(alarm_sample,alarm sample);结果验证
编译后将固件下载至板卡,复位板卡后输入命令alarm_sample并回车,得到如下结果:
sram heap, begin: 0x0x200015dc, end: 0x0x2001dc00 \ | / - RT - Thread Operating System / | \ 5.2.0 build Feb 28 2025 00:37:09 2006 - 2024 Copyright by RT-Thread team using armclang, version: 6210000 MCXA156 HelloWorld msh > msh > msh >alar alarm_sample msh >alarm_sample msh >local time: Thu Jan 1 08:00:02 1970 local time: Thu Jan 1 08:00:03 1970 local time: Thu Jan 1 08:00:04 1970 local time: Thu Jan 1 08:00:05 1970 local time: Thu Jan 1 08:00:06 1970 local time: Thu Jan 1 08:00:07 1970 local time: Thu Jan 1 08:00:08 1970 local time: Thu Jan 1 08:00:09 1970 local time: Thu Jan 1 08:00:10 1970 local time: Thu Jan 1 08:00:11 1970 local time: Thu Jan 1 08:00:12 1970 local time: Thu Jan 1 08:00:13 1970 local time: Thu Jan 1 08:00:14 1970 local time: Thu Jan 1 08:00:15 1970 local time: Thu Jan 1 08:00:16 1970 local time: Thu Jan 1 08:00:17 1970 local time: Thu Jan 1 08:00:18 1970 local time: Thu Jan 1 08:00:19 1970 local time: Thu Jan 1 08:00:20 1970 local time: Thu Jan 1 08:00:21 1970 local time: Thu Jan 1 08:00:22 1970 local time: Thu Jan 1 08:00:23 1970 local time: Thu Jan 1 08:00:24 1970 local time: Thu Jan 1 08:00:25 1970 local time: Thu Jan 1 08:00:26 1970 local time: Thu Jan 1 08:00:27 1970 local time: Thu Jan 1 08:00:28 1970
至此,功能部分验证完成。
最后
正如我一开始所说,这方法仅适用于玩玩,毕竟不是真正的硬件RTC,与其这么干还不如之接弄软件RTC实在。
我要赚赏金
