ATtiny816(MegaTinyCore)待机模式比较器中断唤醒失效求助
ATtiny816待机模式下比较器唤醒方案(Arduino + MegaTinyCore)
问题背景
在Arduino平台使用MegaTinyCore驱动ATtiny816时,需实现基于内置比较器的压电敲击检测休眠唤醒方案。已完成比较器配置(可正常触发tapInterrupt中断),但将芯片置于待机模式后,比较器中断无法触发。
查阅ATtiny816手册得知:待机模式下AC默认禁用,即使设置ACn.CTRLA的RUNSTDBY位为1,若无其他模块请求CLK_PER时钟,AC不会更新状态寄存器,仅能更新事件和引脚输出,无法产生中断。尝试过PIT轮询(响应性不足)、启用RTC(未解决问题),现需解决待机模式下比较器持续触发中断且低功耗的配置方法,并明确RUNSTDBY位的作用。
解决方案
一、核心模块选择原理
要实现待机模式下比较器触发中断,有两种低功耗方案:
- 事件系统唤醒(最优低功耗):利用ATtiny816事件系统,让比较器触发事件直接唤醒芯片,无需
CLK_PER时钟,功耗最低。 - 保持
CLK_PER运行(兼容原有中断逻辑):启用低功耗模块(如RTC PIT)持续请求CLK_PER,让AC能更新状态寄存器并产生中断,功耗略高于事件方案,但适配现有中断代码。
优先推荐事件系统方案,符合低功耗设计需求。
二、Arduino具体配置代码
方案1:事件系统唤醒(最优低功耗)
此方案无需依赖CLK_PER,直接通过比较器事件触发芯片唤醒,中断逻辑在唤醒后执行:
#include <Comparator.h> #include <avr/io.h> #include <avr/sleep.h> void tapInterrupt() { // 唤醒后的中断处理逻辑 // 示例:触发LED、读取传感器等 } void setup() { // 1. 配置比较器参数 Comparator.input_p = comparator::in_p::in0; Comparator.reference = comparator::ref::vref_0v55; Comparator.input_n = comparator::in_n::vref; Comparator.hysteresis = comparator::hyst::large; Comparator.init(); // 2. 绑定比较器输出到事件系统,设置事件为唤醒源 EVSYS.CHANNEL0 = EVSYS_CHANNEL0_AC0_OUT_gc; // AC0输出映射到事件通道0 EVSYS.USER_WAKEUP = EVSYS_USER_CHANNEL0_gc; // 事件通道0作为芯片唤醒源 // 3. 配置AC待机运行+低功耗模式,启用事件输出 AC_t& AC_struct = Comparator.getPeripheral(); AC_struct.CTRLA |= AC_LPMODE_bm | AC_RUNSTDBY_bm; AC_struct.EVCTRL |= AC_EVOUT_bm; // 4. 启用待机模式唤醒功能 SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc | SLPCTRL_SEN_bm; // 5. 启动比较器 Comparator.start(); // 6. 配置唤醒后执行的中断(可选,根据需求调整引脚) attachInterrupt(digitalPinToInterrupt(0), tapInterrupt, RISING); } void loop() { // 进入待机模式 wdt_disable(); set_sleep_mode(SLEEP_MODE_STANDBY); sleep_enable(); sleep_cpu(); sleep_disable(); // 唤醒后执行逻辑(可直接调用tapInterrupt或自定义) tapInterrupt(); }
方案2:保持CLK_PER运行(兼容原有中断)
若需保留原比较器中断逻辑,需启用RTC PIT持续请求CLK_PER,确保AC能更新状态寄存器产生中断:
#include <Comparator.h> #include <avr/io.h> #include <avr/sleep.h> void tapInterrupt() { // 原有比较器中断处理逻辑 } void setup() { // 1. 配置比较器(保留原有逻辑) Comparator.input_p = comparator::in_p::in0; Comparator.reference = comparator::ref::vref_0v55; Comparator.input_n = comparator::in_n::vref; Comparator.hysteresis = comparator::hyst::large; Comparator.init(); Comparator.attachInterrupt(tapInterrupt, RISING); // 2. 配置AC待机运行+低功耗模式 AC_t& AC_struct = Comparator.getPeripheral(); AC_struct.CTRLA |= AC_LPMODE_bm | AC_RUNSTDBY_bm; // 3. 配置RTC PIT持续运行,请求CLK_PER while (RTC.STATUS > 0); // 等待寄存器同步 RTC.CLKSEL = RTC_CLKSEL_INT32K_gc; // 使用内部32kHz超低功耗振荡器 RTC.PITCTRLA = RTC_PERIOD_CYC1_gc | RTC_PITEN_bm; // 最短周期持续运行 RTC.CTRLA = RTC_RUNSTDBY_bm; // RTC在待机模式保持运行 // 4. 启动比较器 Comparator.start(); } void loop() { // 进入待机模式 wdt_disable(); set_sleep_mode(SLEEP_MODE_STANDBY); sleep_enable(); sleep_cpu(); sleep_disable(); }
注:此方案RTC PIT持续运行会增加少量功耗,但远低于芯片正常工作模式。
三、RUNSTDBY位的实际作用
ACn.CTRLA中的RUNSTDBY位核心作用:
- 允许比较器在待机(Standby)模式下继续运行,而非被硬件自动关闭。
- 它无法单独维持
CLK_PER时钟:若没有其他模块请求CLK_PER,AC仅能执行基础比较操作,更新引脚输出和事件信号,但无法更新自身状态寄存器(如ACn.STATUS),因此无法触发硬件中断。 - 仅当
CLK_PER时钟运行时,AC才能更新状态寄存器并产生中断;若使用事件系统唤醒,则无需CLK_PER,RUNSTDBY仅需保证AC在待机模式下能生成事件信号。
内容的提问来源于stack exchange,提问作者KG-DROID
相关产品推荐
相关产品推荐

