keer_zu 发表于 2025-8-19 23:56

关于stm32L051进入stop模式后被rtc的alarm中断唤醒的执行顺序问题

<h2>问题:</h2>
<p>`stm32l051执行以下代码进入stop模式:<br />
void Enter_Stop_Mode(void)<br />
{<br />
// 1. 配置RTC为唤醒源<br />
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);</p>
<p>// 2. 挂起SysTick<br />
HAL_SuspendTick();</p>
<p>// 3. 进入Stop模式<br />
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);</p>
<p>// 4. 唤醒后重新配置时钟<br />
SystemClock_Config();<br />
HAL_ResumeTick();<br />
}<br />
如果被rtc的alarm中断唤醒,请问是先执行RTC的中断函数RTC_IRQHandler,还是 紧接着HAL_PWR_EnterSTOPMode之后的代码:<br />
// 4. 唤醒后重新配置时钟<br />
SystemClock_Config();<br />
HAL_ResumeTick();`</p>

keer_zu 发表于 2025-8-19 23:58

<p>这个问题涉及到STM32低功耗模式的中断唤醒机制。</p>
<p><strong>答案是:先执行RTC的中断服务函数(RTC_IRQHandler),然后再返回到 <code>HAL_PWR_EnterSTOPMode</code> 函数之后的代码(即 <code>SystemClock_Config()</code>)继续执行。</strong></p>
<p>下面是详细的执行流程和原理分析:</p>
<h3>唤醒过程详细流程</h3>
<ol>
<li>
<p><strong>进入Stop模式</strong>:</p>
<ul>
<li>当代码执行 <code>HAL_PWR_EnterSTOPMode()</code> 中的 <code>WFI</code> (Wait For Interrupt) 指令后,内核停止执行,芯片进入低功耗的Stop模式。</li>
</ul>
</li>
<li>
<p><strong>RTC Alarm事件发生</strong>:</p>
<ul>
<li>RTC计时到达设定的闹钟时间,触发了Alarm事件。</li>
</ul>
</li>
<li>
<p><strong>产生中断请求</strong>:</p>
<ul>
<li>RTC Alarm事件会置位相应的中断标志位,并向NVIC(嵌套向量中断控制器)发出一个中断请求。</li>
</ul>
</li>
<li>
<p><strong>芯片唤醒</strong>:</p>
<ul>
<li>这个中断请求会将芯片从Stop模式中唤醒。内核退出睡眠状态,时钟恢复。</li>
</ul>
</li>
<li>
<p><strong>执行中断服务程序</strong>:</p>
<ul>
<li>唤醒后,内核的第一要务是处理中断。NVIC会检查中断源,并跳转到对应的<strong>中断向量表</strong>,找到 <code>RTC_IRQHandler</code> 函数的地址并开始执行。</li>
<li>在 <code>RTC_IRQHandler</code> 内部,通常会调用 <code>HAL_RTC_AlarmIRQHandler</code> 这个函数来处理Alarm中断,它会清除中断标志位并调用您事先设置好的回调函数(例如 <code>HAL_RTC_AlarmAEventCallback</code>)。</li>
</ul>
</li>
<li>
<p><strong>退出中断服务程序</strong>:</p>
<ul>
<li>中断服务程序执行完毕后,使用 <code>BX LR</code> 指令返回。这个返回地址,正是当初执行 <code>WFI</code> 指令后,程序计数器(PC)保存的下一条指令的地址。</li>
</ul>
</li>
<li>
<p><strong>继续执行主程序</strong>:</p>
<ul>
<li>从中断返回后,程序流程就回到了 <code>HAL_PWR_EnterSTOPMode</code> 函数内部(紧跟在 <code>WFI</code> 指令之后)。该函数会执行一些必要的清理工作后也返回。</li>
<li>最终,代码继续执行 <code>Enter_Stop_Mode</code> 函数中 <code>HAL_PWR_EnterSTOPMode</code> 之后的代码,即重新配置系统时钟 <code>SystemClock_Config()</code> 和恢复SysTick <code>HAL_ResumeTick()</code>。</li>
</ul>
</li>
</ol>
<h3>为什么需要这样设计?</h3>
<p>这种“先中断,后主流程”的设计是中断系统的标准行为,非常合理:</p>
<ul>
<li><strong>实时性</strong>:中断的核心目的是为了立即响应一个紧急事件。确保中断服务程序最先执行,可以最快地对事件做出处理(例如,记录唤醒时间、设置一个标志位、执行一个关键操作)。</li>
<li><strong>安全性</strong>:<code>HAL_PWR_EnterSTOPMode</code> 之后的代码(尤其是 <code>SystemClock_Config()</code>)通常涉及对时钟树的重要配置。确保在配置时钟之前,所有可能依赖旧时钟的中断都已经处理完毕,可以避免潜在的错误。</li>
</ul>
<h3>代码编写建议</h3>
<p>在您的代码中,通常会在RTC Alarm中断服务程序(或其回调函数)中做一些“轻量级”的操作,例如设置一个全局的标志变量 <code>rtc_wakeup_flag = 1;</code>。</p>
<p>而在主循环或 <code>Enter_Stop_Mode</code> 函数之后,你再检查这个标志位,来决定后续要执行哪些具体的任务(例如读取传感器数据并发送)。</p>
<pre><code class="language-c">// 定义一个全局变量
volatile uint8_t rtc_wakeup_flag = 0;

// RTC Alarm回调函数(在中断上下文被调用)
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
rtc_wakeup_flag = 1; // 仅仅设置标志位,不执行耗时操作
}

void Enter_Stop_Mode(void)
{
// ... 进入Stop模式前的配置 ...
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 从这里开始,是唤醒后执行的地方
SystemClock_Config();
HAL_ResumeTick();

// 检查唤醒标志,执行后续应用逻辑
if(rtc_wakeup_flag == 1) {
    rtc_wakeup_flag = 0;
    // 执行你的主要任务,例如:
    Read_Sensor();
    Send_Data();
}

// 任务执行完后,可以再次进入Stop模式
// Enter_Stop_Mode();
// 注意:在实际应用中,这可能会是一个循环,或者由主循环控制。
}
</code></pre>
<h3>总结</h3>
<table>
<thead>
<tr>
<th align="left">顺序</th>
<th align="left">执行内容</th>
<th align="left">上下文</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">1</td>
<td align="left"><code>HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);</code></td>
<td align="left">主程序</td>
</tr>
<tr>
<td align="left">2</td>
<td align="left"><strong>(芯片进入Stop模式)</strong></td>
<td align="left">-</td>
</tr>
<tr>
<td align="left">3</td>
<td align="left">RTC Alarm事件发生,芯片唤醒</td>
<td align="left">硬件事件</td>
</tr>
<tr>
<td align="left"><strong>4</strong></td>
<td align="left"><strong><code>RTC_IRQHandler</code> / <code>HAL_RTC_AlarmIRQHandler</code> / <code>HAL_RTC_AlarmAEventCallback</code></strong></td>
<td align="left"><strong>中断服务程序</strong></td>
</tr>
<tr>
<td align="left">5</td>
<td align="left"><code>SystemClock_Config();</code></td>
<td align="left">主程序(<code>Enter_Stop_Mode</code>函数)</td>
</tr>
<tr>
<td align="left">6</td>
<td align="left"><code>HAL_ResumeTick();</code></td>
<td align="left">主程序(<code>Enter_Stop_Mode</code>函数)</td>
</tr>
</tbody>
</table>
页: [1]
查看完整版本: 关于stm32L051进入stop模式后被rtc的alarm中断唤醒的执行顺序问题