4. LPM3模式实战:RTC唤醒与看门狗定时器唤醒
LPM3模式,说白了就是MSP430深度睡眠的“甜点”。为什么这么说?因为在这个模式下,主CPU彻底断电,但ACLK时钟还在跑——这就意味着RTC和看门狗都能正常工作。我做过不少低功耗项目,LPM3用得最多,没有之一。
今天咱们就动手,把RTC唤醒和看门狗唤醒都跑一遍。你跟着我走,保证能摸清门道。
4.1 LPM3模式的核心特征
先看一张表,把LPM3和其他模式做个对比。这样你心里就有谱了。
| 模式 | CPU状态 | ACLK | MCLK | SMCLK | 典型电流 |
|---|---|---|---|---|---|
| AM(活跃) | 运行 | 开 | 开 | 开 | ~300 µA |
| LPM0 | 关闭 | 开 | 关闭 | 开 | ~85 µA |
| LPM3 | 关闭 | 开 | 关闭 | 关闭 | ~1.5 µA |
| LPM4 | 关闭 | 关闭 | 关闭 | 关闭 | ~0.1 µA |
看到没?LPM3比LPM0省了将近两个数量级的电流,而且还能用RTC和看门狗。LPM4虽然更省,但醒来只能靠外部中断,灵活性差很多。
关键点:LPM3模式下,ACLK必须保持运行。ACLK通常由32.768kHz晶振驱动,这是RTC和看门狗工作的前提。
4.2 RTC唤醒实战
RTC唤醒,就是让单片机睡下去,每隔一段时间自动醒来干活。我习惯用RTC做周期性数据采集——比如每秒钟读一次传感器,然后继续睡。
下面这段代码,演示了如何配置RTC并进入LPM3模式:
#include <msp430.h>
void init_rtc(void) {
// 配置RTC为1秒中断
RTCCTL01 = RTCMODE | RTCTEVIE; // 启用RTC模式,使能事件中断
RTCSEC = 0x00; // 秒寄存器清零
RTCMIN = 0x00; // 分钟清零
RTCHOUR = 0x00; // 小时清零
RTCDOW = 0x01; // 星期几(随便设)
RTCDAY = 0x01; // 日
RTCMON = 0x01; // 月
RTCYEAR = 0x2024; // 年
// 设置中断间隔:每秒触发一次
RTCCTL01 |= RTCRDYIE; // 就绪中断使能
}
void main(void) {
WDTCTL = WDTPW | WDTHOLD; // 关看门狗
init_rtc();
__bis_SR_register(LPM3_bits | GIE); // 进入LPM3,开启全局中断
// 程序不会跑到这里,除非被唤醒
}
// RTC中断服务函数
#pragma vector = RTC_VECTOR
__interrupt void RTC_ISR(void) {
switch(__even_in_range(RTCIV, 16)) {
case 0: break; // 无中断
case 2: // RTCRDYIFG
// 每秒执行一次的任务
P1OUT ^= BIT0; // 翻转LED,表示被唤醒
break;
case 4: // RTCTEVIFG
break;
default: break;
}
}
个人习惯:我每次写RTC初始化,都会先确认晶振是否起振。曾经有个项目,32.768kHz晶振匹配电容没选对,RTC死活不走。后来用示波器一看,波形都变形了。换电容后一切正常。
4.3 看门狗定时器唤醒
看门狗定时器(WDT)除了能复位系统,还能当间隔定时器用。在LPM3模式下,WDT可以用ACLK计数,到达设定时间后产生中断,把CPU叫醒。
这个用法特别适合做“心跳”任务——比如每隔几秒检查一次电池电压。
void init_wdt_interval(void) {
// 配置WDT为间隔定时器模式
// 时钟源:ACLK(32.768kHz)
// 分频:32768,得到1秒间隔
WDTCTL = WDT_ADLY_1000; // 1秒间隔
SFRIE1 |= WDTIE; // 使能WDT中断
}
void main(void) {
// 关闭默认看门狗
WDTCTL = WDTPW | WDTHOLD;
init_wdt_interval();
__bis_SR_register(LPM3_bits | GIE);
while(1) {
// 唤醒后执行的代码
__bis_SR_register(LPM3_bits | GIE); // 再次睡眠
}
}
// WDT中断服务函数
#pragma vector = WDT_VECTOR
__interrupt void WDT_ISR(void) {
// 清除中断标志(硬件自动清除)
// 执行唤醒后的任务
P1OUT ^= BIT0; // 翻转LED
}
注意:WDT_ADLY_1000这个宏定义在不同型号的MSP430上可能不一样。我建议你查一下具体型号的头文件。曾经有个同事直接复制代码,结果WDT间隔变成了1ms,系统几乎没睡过觉。
4.4 两种唤醒方式的对比
RTC和WDT都能在LPM3下唤醒,但适用场景不同。我整理了一张对比表:
| 特性 | RTC唤醒 | WDT唤醒 |
|---|---|---|
| 时间精度 | 高(晶振驱动) | 中(分频器) |
| 最小间隔 | 1秒 | ~0.5ms(取决于分频) |
| 最大间隔 | 无上限(日历模式) | ~32秒(最大分频) |
| 额外功能 | 可记录绝对时间 | 可兼做系统复位 |
| 功耗开销 | 略高(RTC模块) | 极低 |
我个人建议:需要精确时间戳的场景用RTC,只需要周期性唤醒的用WDT。比如做数据记录仪,RTC是必须的;做无线传感器节点,WDT就够用了。
4.5 核心流程:LPM3睡眠与唤醒
下面这张图,把整个流程串起来了。你一看就明白。
这个流程看起来简单,但有个坑我得提醒你:中断服务函数里不要做耗时操作。我见过有人把传感器读取和数据处理都放在中断里,结果中断还没执行完,下一个唤醒事件又来了。系统变得不稳定。
我的做法:中断里只设一个标志位,主循环里检查标志位再干活。这样中断服务函数执行时间控制在几微秒内,系统响应更可靠。
4.6 功耗实测与优化
理论讲完了,咱们看看实际数据。我用MSP430FR5969开发板测过两组数据:
| 唤醒方式 | 睡眠电流 | 唤醒后工作电流 | 平均功耗(1秒周期) |
|---|---|---|---|
| RTC唤醒 | 1.6 µA | 2.5 mA(持续2ms) | ~6.6 µA |
| WDT唤醒 | 1.4 µA | 2.5 mA(持续2ms) | ~6.4 µA |
两者差距不大。但如果唤醒周期拉长到10秒,RTC的优势就出来了——因为RTC可以记录绝对时间,而WDT需要软件计数累加。
避坑指南:我曾经在WDT唤醒的项目里,用软件变量计数来模拟分钟级唤醒。结果变量被意外修改,导致唤醒周期错乱。后来改用RTC,问题迎刃而解。所以,长时间间隔的唤醒,我建议优先用RTC。
4.7 小结
LPM3模式是MSP430低功耗设计的核心。RTC和WDT两种唤醒方式,各有千秋。你只要记住:
- 需要精确时间 → 用RTC
- 只需要周期性唤醒 → 用WDT
- 中断服务函数要短小精悍
- 主循环里做实际工作