STM8S103停机模式实战:微安级低功耗配置与唤醒设计
2026/9/13 11:07:27 网站建设 项目流程

简介:STM8S103低功耗停机模式实验资源,面向嵌入式初学者与STM8开发者,解决如何配置PWR_CR寄存器、执行停机指令并实现外部中断唤醒的实际问题。资源基于STM8S103F3开发板,包含完整的IAR工程文件与标准外设库,共159个文件,以头文件(.h)、源文件(.c)、链接脚本(.xcl)、编译中间文件(.o/.lst)及调试脚本(.bat/.ewd)为主,另含hex烧录文件与工程配置,便于直接编译、烧录和在线调试。压缩包约3.18MB,结构清晰,适合对照学习低功耗模式的状态保存、中断标志处理与外设关闭流程。已有385人学习下载。通过分析工程源码和实验文档,可掌握STM8S103停机模式下的功耗控制技巧,并延伸至STM8L等系列的低功耗设计,为电池供电项目打下基础。

1. 停机模式不是“断电”,而是让 STM8S103 以微安级电流等待一声中断

一块正常运行的 STM8S103,工作电流随主频和外部负载升高而升高;电池供电场景下,如果 CPU 还在持续取指,系统很难把功耗压下去。实验 25 的停机模式,核心思想不是“降频省电”,而是用一条 STOP 指令把 CPU 和绝大多数外设全部停住,只保留内部 RC 振荡器和唤醒源等待事件,靠这招把整板电流压到几十微安以下。它非常适合遥控器、便携仪表、传感器节点这类对响应不太苛刻但看重电池寿命的应用。下面从 PWR_CR 寄存器、实际代码、电流测量和唤醒跳坑把整个流程讲清楚。

2. STM8S103 停机模式原理:PWR_CR 的 STOP 位与电源域切分

2.1 停机模式的功耗链路与“唯一例外”

在讨论代码前,先理解一件事:STM8S103 停机时并不是所有电压都断开,而是核心电源域和大多数外设时钟被切断。PWR_CR 寄存器中的 STOP 位决定停机使能,内部 RC 振荡器是否保持则由 CLK_ICKR 寄存器控制。

正常运行时主时钟由 HSE 或 HSI 提供,外设通过 CLK_PCKENR 逐位使能时钟。停机模式会把 PCKENR 打开的时钟链路全部断掉,这样 GPIO 等外设保持在上电配置,但不再翻转。CPU 执行到 STOP 指令后,取指停止,程序计数器停在下一条指令处。唤醒后,CPU 从 STOP 之后的指令继续执行,而不是从复位向量开始,这是停机模式和复位最本质的区别。

低功耗模式CPU外设时钟典型唤醒源预期电流
空闲模式暂停可选保持任意中断、复位1 mA 量级
活跃停机停止大部分停止,RC 可保持外部中断、RTC 等数十 μA
停机模式停止全部停止外部中断、复位十几 μA 以下

这里的电流量级是工程估计,实际取决于 LDO 是否关闭和 I/O 静态电流。表中的“停机模式”对应本实验要实现的场景,也是所有模式中功耗最低的一种。

2.2 进入停机前的寄存器序列

很多初次接触的人以为写 PWR_CR 的 STOP 位就立刻停机了,其实它只是“允许停机”,真正的停机动作由 STOP 指令触发。寄存器配置、状态保存、清理中断和停止外设是一个组合动作,少任何一步都可能让系统在进入后立刻被唤醒,甚至因为持续取指导致电流不降。

2.2.1 STM8S103 的 CLK_PCKENR 与 PWR_CR 配置顺序

下面是一段典型的进入停机前初始化代码,基于 SDCC 直接操作寄存器。

/* 进入停机前关闭不需要的外设时钟 */ CLK->PCKENR1 = 0x00; /* 关闭定时器、UART2、SPI、I2C 等外设时钟 */ CLK->PCKENR2 = 0x00; /* 关闭剩余外设时钟 */ /* 允许停机模式:PWR_CR 的 STOP 位置 1 */ PWR->CR |= 0x01; /* 只影响停机使能位,不复位其他电源控制位 */ /* 设置外部中断唤醒引脚,这里以 PA1 为例 */ PA_CR1 |= 0x02; /* PA1 设为带上拉输入 */ PA_CR2 |= 0x02; /* PA1 允许外部中断 */ /* 清除可能残留的中断标志 */ EXTI->CR1 = 0x00; /* 选择下降沿触发,同时避免旧事件直接唤醒 */ /* 执行停机指令 */ halt();

代码逻辑:前两行把外设时钟关闭,这样即使某个外设电平处于开漏状态,也不会产生时钟翻转电流;PWR_CR 的 STOP 位配合后续halt()才真正进入停机。PA1 配置成上拉输入后,下降沿可以产生唤醒信号。halt()是编译器提供的将 STM8 的HALT指令包装起来的函数,不能省略。

参数说明:PCKENR1 和 PCKENR2 分别管两组外设,具体每位对应的模块需要查 STM8S103 数据手册。EXTI->CR1的低两位设置 PA 口的触发条件,这里 0x00 表示下降沿;如果设成上升沿,则按键按下结束才唤醒,不利于快速响应。在停机模式下,外部中断引脚必须配置为输入模式并打开中断使能,否则无法唤醒。

2.3 常见误区:没有清除待处理中断标志会立刻退出停机

外部中断标志如果在上一轮触发后没有清零,执行halt()时系统会认为中断已经到达,CPU 直接从停机状态恢复,表现为“按一次按钮,实际唤醒了两三次”。所以进入停机前应该先读中断状态,写入1清除标志,再执行停机。

还有一类误区是把所有引脚都保持为默认高电平,但外部电路恰好是低电平有效,停机时外部信号把引脚拉低,产生持续灌电流。工程上常用的做法是把未使用引脚全部配置为模拟输入,避免悬空产生不确定电平。

3. 实验 25 工程实战:从唤醒工程到 STM8S103 停机模式代码复现

3.1 KeQianMCU 项目文件里到底有什么

源材料中列出的文件是KeQianMCU_STM8S103F3_Library.Debug.cspy.batKeQianMCU_STM8S103F3_Library.cspy.batKeQianMCU_STM8S103F3_Library.pbd.browse,以及stm8s_tim1.cstm8s_tim5.cstm8s_can.cstm8s_tim2.cstm8s_tim3.cstm8s_i2c.cstm8s_uart2.c。这里的.cspy.bat是 IAR C-SPY 命令行调试入口,不是主程序;真正控制停机行为的是开发者在 main 函数里调用的电源管理序列。

这些.c文件是 ST 标准外设库的驱动源文件,实验 25 不会全部用到,工程默认把用不到的模块文件一起打包,方便后续扩展。实际工程中进入停机模式只需要包含stm8s.h和必要的 GPIO/EXTI 驱动。

3.2 实际可跑的停机模式主流程

在示例工程里,通常会有这样的一个 main 函数。

#include "stm8s.h" /* 全局唤醒标志,中断里置位 */ volatile uint8_t flag_wakeup = 0; void GPIO_Init_Key(void) { /* PD3 配置成外部中断输入 */ PD->DDR &= ~BIT3; /* 输入 */ PD->CR1 |= BIT3; /* 带上拉 */ PD->CR2 |= BIT3; /* 允许外部中断 */ EXTI->CR2 &= ~0x0F; /* 清原有配置 */ EXTI->CR2 |= 0x04; /* PD 上升沿触发 */ } void main(void) { CLK->CKDIVR = 0x03; /* 主频降到 2 MHz,降低待机前功耗 */ GPIO_Init_Key(); while (1) { /* 进入停机前先关闭外设时钟 */ CLK->PCKENR1 = 0x00; CLK->PCKENR2 = 0x00; /* 打开停机允许位 */ PWR->CR |= 0x01; __enable_interrupt(); halt(); /* 停机,等待外部中断唤醒 */ /* 唤醒后恢复外设时钟 */ CLK->PCKENR1 = 0xFF; CLK->PCKENR2 = 0xFF; /* 模拟数据处理 */ flag_wakeup++; } } /* PD3 外部中断服务函数 */ INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6) { EXTI->CR2 &= ~0x0F; /* 清除边沿遗留 */ flag_wakeup = 1; }

这段代码里,CLK->CKDIVR = 0x03把主时钟分频到 2 MHz,便于唤醒后快速进入工作。PWR->CR |= 0x01打开停机允许后,halt()会立即把内核时钟停止;唤醒后程序从flag_wakeup++继续执行,因此必须使用volatile修饰标志位,防止编译器把循环优化掉。

参数说明:EXTI->CR2的高/低四位分别对应 PD 和 PE 口。上面0x04设定 PD 口上升沿触发;如果按键接法是按键一端接地、PD3 平时接高电平,则需要用下降沿,对应值要改成 0x08,具体见参考手册中 EXTI_CR2 的位定义。

3.3 为什么不能直接在主循环里反复执行 halt

有的项目出现按键按下后系统总是处于停止状态,是因为唤醒后的中断标志被再次识别。实验 25 的指导思路是:唤醒后的处理逻辑写在halt()之后,而不是全部塞进 ISR。ISR 只负责清除标志和置位,否则未处理的中断事件会让系统在halt()处反复进入退出,功耗反而更高。

3.3.1 用 IAR 命令行调试脚本验证停机点

工程里的KeQianMCU_STM8S103F3_Library.Debug.cspy.bat是 IAR C-SPY 的命令行调试脚本,双击会启动仿真器并加载调试配置。实际调试时,可以在halt()那一行打断点,观察 PC 指针是否停在 HALT 指令地址;如果把断点设在halt()之前,单步执行时会发现执行完 HALT 后无法继续单步,这是正常现象,因为内核时钟已经停止。

如果要在批处理基础上加日志,可以使用-log all让 C-SPY 输出完整寄存器变化记录,便于定位是哪一步没有进入预期状态。

4. 验证停机模式与抖掉“假复位”陷阱:电流实测和唤醒时序

4.1 万用表与示波器的接法

评估停机效果必须量电流。常见做法是万用表选 mA 档,串在电源回路上测整体电流;电流降下来后再切换到 μA 档。如果开发板上有 LDO 和指示灯,测量结果会被外围电路干扰,建议把 STM8S103 的 VDD 脚飞线出来单独供电,跳开 LDO 和 LED。

# 用 IAR 命令行会话启动调试器,观察运行状态 KeQianMCU_STM8S103F3_Library.Debug.cspy.bat -log all

这里的-log all是 C-SPY 输出详细日志的选项,可以观察复位、停机、唤醒时的寄存器变化。测量电流的同时,用示波器观察按键唤醒产生的电平跳变,能确认外部中断信号是否真实到达引脚。

4.1.1 电流档位选择的细节

不要用万用表的自动量程直接测微安级电流,自动切换量程时的内阻变化会产生瞬间压降,可能导致芯片欠压复位。正确做法是:先短接表笔让系统正常运行,再用 mA 档测工作电流,最后切换到 μA 档测停机电流,切换过程要保证 VDD 电压不低于 3.0 V。

4.2 唤醒后“假复位”的几个根源

停机模式唤醒后,PC 会从halt()后一条指令继续执行。如果发现程序总是从头跑,说明唤醒源被配置成了复位源,或者硬件看门狗没有被正确喂狗。

  • 如果 IWDG 没有关,停机时看门狗依然在计数,唤醒瞬间复位,程序从头执行。
  • 如果 PWR_CR 配置的唤醒源和复位源同时使能,可能出现复位行为。
  • 如果内部 RC 唤醒时间较长,电源轨出现跌落,也会触发 BOR 复位,需要检查 VDD 电容,建议 100 nF + 10 μF 搭配。
4.2.1 用 RST_SR 寄存器确认复位来源

STM8S103 的复位状态寄存器可以区分是外部复位、看门狗复位还是上电复位。唤醒异常复位后,可以在 main 开头读取该寄存器,打印或记录。

uint8_t rst_status = RST->SR; /* 读取复位原因 */ if (rst_status & RST_SR_IWDGF) { /* 说明是独立看门狗溢出复位 */ } RST->SR = 0x00; /* 写清零 */

这段代码检查 IWDGF 位,如果置位,就说明停机期间看门狗没有停止。正常情况下,停机模式下 IWDG 可以保持计数,但如果你希望停机后不被复位,必须在进入停机前刷新一次 IWDG,并把唤醒时间控制在看门狗周期内,或者临时关闭看门狗。

4.3 现象定位表

现象可能原因处理办法
停机电流明显高于 100 μA外设时钟没关完,或 GPIO 悬空遍历关闭 PCKENR;未使用引脚设为模拟输入
按键唤醒后程序从头跑硬件看门狗仍在运行进入停机前先喂狗,或初始化里关闭 IWDG
多次按键才唤醒中断标志残留halt()前清除 EXTI 边沿标志
唤醒后 UART 收到乱码主频恢复时序不对唤醒后重新配置 CLK_CKDIVR 并等待时钟稳定

4.4 一个常见的低功耗设计检查清单

不要在设计里看到“停机”就觉得万事大吉。我见过很多项目在进入停机前把引脚保持为输出低电平,结果外部电路是推挽驱动,两边直接对灌电流。最稳妥的检查顺序是:先关闭外设时钟,再把所有未使用引脚设为模拟输入,最后执行停机指令。每次改完代码后,至少测量 10 组上电和唤醒电流,确认没有偶发高功耗。

5. 进阶:用停机模式配合 RTC/看门狗做周期唤醒,以及从 STM8L 停机程序移植的差异

5.1 定时唤醒的另一种思路

有的项目需要系统每 10 秒采集一次,但只靠外部中断会卡住。STM8S103 的 RTC 没有独立电源域,停机模式下无法像 STM8L 那样保持 RTC 走时;工程上常用带中断输出的外部 RTC 芯片,或者干脆用看门狗定时唤醒。如果非要在内部做周期唤醒,更实际的做法是退回到 idle 低功耗休眠模式,用定时器中断定期唤醒,代价是待机电流会上升到 1 mA 量级。认清这个边界,才不会被测试数据误导。

5.2 STM8L 停机程序移植到 STM8S103 的注意事项

对于从 STM8L 项目迁移过来的代码,例如bend8w5提供的 STM8L 停机程序,需要特别关注 PWR 寄存器地址和位段差异。STM8L 的 PWR_CR1 和 STM8S 的 PWR_CR 不能互换;STM8L 支持 RTC 异步唤醒和多种低功耗模式,而 STM8S103 没有独立的 RTC 电源域,进入停机前必须把外部中断作为唯一唤醒源,或者配合外部看门狗事件。

/* 典型的 STM8L 停机配置,移植到 STM8S103 时需要改寄存器名 */ PWR->CR1 |= 0x01; /* STM8L 写法,在 STM8S103 上应改为 PWR->CR */

这里的关键是把CR1改为CR,并且确认唤醒源引脚在 STM8S103 的 EXTI 映射范围内。如果原来的代码使用 STM8L 的 RTC 事件唤醒,换成 STM8S103 后需要外接 RTC 芯片,或者改用外部中断引脚。

5.3 引脚电平固定是最后一道收尾

发布前建议用示波器确认所有引脚没有浮空抖动。一个很实用的收尾技巧:在进入停机函数末尾加上GPIO_DeInit(),然后重新配置关键引脚,把不需要的引脚全部设置为推挽输出低电平;但外部控制电源的引脚要反着来,避免误关负载。这样整板电流才会全部贡献给真正的负载,而不是被 I/O 口漏电流吃掉。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询