做电池供电的小东西省电永远是第一优先级。我最近用STC8G1K08这颗8脚单片机做了一个10秒周期上报的温湿度节点要求待机电流足够低但又不能全靠外部信号来唤醒——设备得自己每隔10秒醒来一次采完数据再睡回去。折腾了一圈最后把STOP模式配合定时器唤醒用起来待机电流做到1μA出头延时误差通过校准控制在0.1%以内。这篇文章把这套方案从头到尾梳理一遍包括寄存器配置、完整代码、电流实测对比以及我在测试过程中踩过的几个坑。先说结论STC8G1K08的STOP模式并不是“睡死过去”芯片内部还有一个约32KHz的低速IRC可以在睡眠状态下继续工作喂给定时器计数溢出后把芯片唤醒。这比靠外部中断唤醒可控得多适合定时采样、定时上报这类需要“自己定闹钟”的场景。文章里所有电流数据都是基于我手头的一块SOP8封装裸板实测的不同批次芯片会有差异但量级有参考价值。1. 低功耗产品的电池焦虑为什么“睡眠定时唤醒”是刚需做电池供电的小设备最常见的死法就是待机电流压不下去。你费尽心思把工作状态下的电流从10mA优化到5mA结果设备一天只工作几秒钟剩下23小时59分都在傻等待机电流哪怕只是几十μA电池寿命照样被拖垮。真正的瓶颈从来不是工作时耗电而是“不工作时还在耗电”。STC8G1K08这颗芯片的省钱逻辑就在这里。它的Flash有8KBSRAM 1KB价格很低封装又能做得很小SOP8只有8个引脚非常适合传感器节点、遥控器、智能家居里的微功耗模块这类场景。但小封装不等于低功耗可以白拿你必须让它进入睡眠状态否则主频一跑电流就是毫安级电池根本扛不住。STC8G1K08提供了三种“省电档位”正常运行模式CPU全速跑外设都开着电流毫安级。IDLE空闲模式CPU停止外设时钟继续运行中断可以随时唤醒电流从毫安降到一毫安左右。STOP停止模式主时钟停振CPU和外设基本全部停止电流降到微安级是真正的深度睡眠。我自己的项目要求就是设备绝大多数时间处于STOP模式每10秒醒来一次读取传感器把数据发出去然后继续睡。这里有个核心问题——STOP模式下怎么定时醒来你可能第一反应是外部中断唤醒。外部中断确实能把芯片从STOP模式拉起来但它的本质是“等人叫”IO口出个边沿信号才醒。对于“每10秒自己醒一次”这种需求外部中断根本派不上用场。市面上有些方案会用外部RTC芯片比如PCF85063这类靠RTC的闹钟输出去触发单片机的INT引脚精度确实高但也多了颗芯片多了成本还多了编程量。STC8G1K08内部其实自带一个解决方案让定时器在STOP模式下继续跑。这个功能藏得比较深不少人第一次看手册会忽略掉但它恰恰是这里最实用的一招——不需要外部RTC不需要额外的硬件一颗芯片就能实现“定时闹钟”。下面从时钟机制开始讲搞清楚定时器为什么能“在睡眠中工作”你就理解整个方案了。2. STOP模式下定时器为什么还能跑内部低速IRC是幕后功臣很多人对STOP模式的印象是“芯片全停了什么都不跑”这个理解不准确。STC8G1K08进入STOP后停掉的是主时钟内部高速IRC或者外部晶振CPU的时钟也随之停了但芯片内部还有一个低速时钟可以继续工作这就是约32KHz的内部低速IRC。这个内部低速IRC本身就是为了低功耗场景准备的。它不需要外部晶振上电就存在频率很低功耗也很低。关键在于芯片硬件允许把这路32KHz时钟直接喂给定时器让定时器在STOP模式下继续计数。计数溢出后产生一个中断事件把MCU从STOP状态唤醒。这个机制的开关是一个叫TSTOP的寄存器地址0x86。把TSTOP的bit0T0STOP置1定时器0就允许在STOP模式下运行bit1和bit2分别对应定时器1和定时器2。如果这个位是0定时器在STOP模式下就是死的芯片一旦睡过去就只能靠外部中断拉起来。TSTOP寄存器地址0x86的位定义大致如下位名称功能bit0T0STOPT0在STOP模式下继续计数/唤醒使能bit1T1STOPT1在STOP模式下继续计数/唤醒使能bit2T2STOPT2在STOP模式下继续计数/唤醒使能bit3-bit7-保留需要特别说明的是在STOP模式下定时器的时钟源被硬件自动切换到内部低速IRC而不是沿用主时钟。这意味着你进入STOP之前不需要额外去改定时器的时钟源选择只要打开TSTOP对应的位就行。也因此定时器在STOP模式下的计时单位是“32K时钟周期”不是主频周期。把IDLE模式和STOP模式放在一起对比你会更清楚怎么选对比项IDLE模式STOP模式CPU停止停止主时钟高速IRC继续运行停止内部低速IRC32K运行运行若TSTOP使能外设时钟继续运行停止电流量级3.3V实测约0.8mA约0.5μA纯STOP唤醒方式任意中断外部中断/定时器溢出适用场景短时等待UART接收等待长时间定时睡眠看到区别了吧IDLE模式下外设还在跑所以电流下不去但响应快STOP模式下外设全停电流直接少了三个数量级代价是唤醒后主时钟要重新建立需要等几个微秒。对于10秒级别的定时上报场景这个代价完全可以忽略。原理清楚了下面就是寄存器配置和最小代码先把“1秒醒一次”跑通。3. 寄存器配置与最小代码先把“1秒醒一次”跑起来整个方案的核心思路是用定时器0做1秒溢出一次的低速定时每溢出一次就唤醒一次然后靠软件变量累加攒够10次再执行真正的任务。为什么不直接让定时器一次睡10秒因为16位定时器在32KHz时钟下最大计数是65536对应时间只有65536/327682秒。想要10秒必须用软件计数器做二级累加。配置步骤拆开来看其实就四件事把定时器0配置为16位自动重装载模式初值设为0x8000即65536-32768这样它从初值数到65536溢出正好经历32768个时钟周期在32KHz下就是1秒。打开T0中断使能ET01和总中断EA1因为唤醒的本质是中断事件。把TSTOP的bit0置1允许T0在STOP模式下继续运行。启动定时器TR01然后进入STOP模式。这里有一个容易忽略的细节进入STOP模式的指令PCON | 0x02执行之后芯片并不一定马上停因为Keil C51编译器在开启优化时可能会把后面的指令预取到流水线里。稳妥的做法是在设置STOP位之后加两个_nop_()确保芯片真正睡下去。唤醒后程序从PCON|0x02这一行的下一条指令继续执行那些NOP也充当了“缓冲垫”。最小可跑代码长这样#include STC8G.h #include intrins.h #define TSTOP 0x86 volatile unsigned char wake_count 0; void t0_isr(void) interrupt 1 { wake_count; // 每溢出一次说明定时器走过了1秒 } void goto_sleep(void) { PCON | 0x02; // 进入STOP模式 _nop_(); _nop_(); } void main(void) { // T0配置16位自动重装载初值0x8000对应32768个计数 TMOD 0xF0; TMOD | 0x00; // 16位自动重装载 AUXR 0x7F; // T0x12 0保持12T模式 TL0 0x00; // 初值低字节 TH0 0x80; // 初值高字节0x8000 32768 TSTOP 0x01; // 允许T0在STOP模式下运行 TR0 1; // 启动定时器0 ET0 1; // 开T0中断 EA 1; // 开总中断 while (1) { goto_sleep(); // 睡眠等待T0溢出唤醒 if (wake_count 10) { wake_count 0; // 在这里执行你的实际任务采样、发送等 } } }这里解释一下为什么初值是0x800016位定时器从0x8000数到0xFFFF再溢出计数个数是65536-3276832768正好对应内部低速IRC的32768Hz所以理论时间是1秒。注意这只是“理论”因为内部低速IRC的实际频率会有偏差具体怎么校准下一节专门讲。跑通这段代码后用逻辑分析仪或者示波器抓任何一个翻转引脚你应该能看到大约1秒一次的电平变化。我实测的情况是第一次跑的时候间隔是1.01秒左右偏差不大但确实存在。4. 让“精准延时”名副其实误差来源、校准方法与唤醒延迟补偿标题里的“精准延时”不是随便写的但要把“精准”做实得先搞清楚误差从哪来。这是我在实测中总结出的误差来源按影响从大到小排列内部低速IRC的频率偏差。这是最大的误差源。内部32KHz并不保证每颗芯片都是精确的32768Hz出厂校准值会有1%左右偏差不同温度下也会漂。测下来10秒间隔误差可能在±100ms量级。唤醒后的恢复延迟。Timer溢出后芯片要从STOP状态把主时钟拉起来这需要时间再加上中断响应和主循环的判断整体可能有几十微秒到上百微秒。指令执行时间和中断服务程序长度。虽然很短但在毫秒级延时时就不能忽略。校准方法是反向推算实际频率。我是这么做的先把wake_count阈值设成1让LED每次唤醒都翻转一次。用逻辑分析仪抓LED引脚测出方波的周期。假如测出来是2.02秒说明两次唤醒间隔是1.01秒也就是实际频率比32768Hz高了1%。反推实际32K频率实际频率 32768 × 1.01 33095.68 ≈ 33096Hz要得到真正的1秒间隔计数个数应该是33096那初值就是65536-3309632440。把初值从0x8000改成0x7EB8重新烧录实测误差马上就下来了。我实测校准后的10秒间隔误差在10ms以内大约是0.1%级别。再提一个别人不太会告诉你的点唤醒延迟的补偿。定时器溢出后芯片不会立刻执行到你的业务代码中间有主时钟恢复、中断响应、中断返回、主循环判断这几道工序。用示波器抓GPIO翻转能测出这个“延迟尾巴”大概是多少。比如你原本想要1000ms唤醒一次实测是1000.08ms多出来的0.08ms就是唤醒延迟。如果要做毫秒级精准延时就把这个值从定时器计数里扣掉计数个数 目标时间 × 实际频率 - 延迟时间 × 实际频率。但做秒级延时时这个补偿只有几个32K计数基本可以忽略我建议秒级场景不用管毫秒级场景才需要考虑。校准过程中还有一个容易被“精度”迷惑的地方内部IRC的频率会随温度漂移。你在25℃校准到0.1%到了50℃可能又漂了0.5%。如果产品工作温度范围很大又要求很高的计时精度那就只能外接32.768K晶振或者外部RTC芯片内部低速IRC解决不了这个问题。但对大多数传感器上报类应用校准后的精度足够用。下面是校准前后的实测对比用的同一块板子同一个芯片10秒唤醒间隔状态定时器初值实测间隔误差未校准0x800010.11秒1.1%校准后0x7EB810.01秒0.1%校准的代价只是一次额外测试性价比很高强烈建议每次批产前至少抽几颗做频率统计看看分布再决定要不要在产线上做个体校准。5. 电流实测对比与测量中的几个大坑电流数据是很多人关心的。我用的测量方法是3.3V稳压电源供电万用表串联进电源回路测平均电流动态电流用10Ω采样电阻加示波器来看唤醒瞬间的波形。测的是裸板下载器拔掉板上没有任何指示灯。实测数据整理如下3.3V供电主频11.0592MHz工作状态实测电流备注正常全速运行while(1)空转约2.1mA未开外设IDLE模式约0.8mA外设时钟还在跑STOP模式未使能定时器约0.5μAIO口全部处理好STOP模式 T0定时唤醒约1.1μA32K低速IRC在运行10秒周期唤醒含采样任务约2.5μA平均电流几个关键结论STOP模式比IDLE模式省电约三个数量级差距极其明显。纯STOP和STOP定时唤醒之间多出来的大约0.6μA就是内部32K振荡器的工作电流。这个代价换来自动唤醒能力非常划算。如果整机平均电流能做到5μA以下用一颗CR2032纽扣电池约220mAh容量理论续航在5年左右已经能满足绝大多数小产品的电池寿命预期。数据好看但如果你照着测很可能测不出这么低的数字。原因在于测量和硬件处理上有几个坑我一个个说。第一个坑IO口悬空漏电。这是最容易被忽视的。STOP模式下如果某个IO是悬空的电平不确定输入缓冲电路会持续产生漏电几μA甚至十几μA就这么没了比你唤醒定时器多耗的电还多。处理办法是进入STOP前把所有不用的IO设置成推挽输出低电平或者高阻输入反正不能让引脚悬在空中。第二个坑万用表内阻。很多万用表的μA档内阻有几百欧甚至1kΩ当电流只有几个μA时压降很小没问题但设备醒来工作的那几毫秒电流可能有几mA压降瞬间变大可能导致MCU供电电压跌破复位阈值直接复位重启。我吃过这个亏用某款万用表μA档测10秒唤醒节点死活测不到唤醒后的传感器数据最后发现是万用表内阻在唤醒瞬间把电压拉掉了。换个mA档或者用电流探头就好了。测低功耗动态电流更推荐串联小电阻配合示波器看波形而不是只信万用表的数字。第三个坑板子上的隐性耗电。开发板上的电源指示灯、LDO静态电流、下载器芯片的漏电全都会算进你的“待机电流”里。我一开始直接在开发板上测死活压不到2μA以下后来查出来是板载LED在耗电。真正做低功耗测量必须用最小系统或者至少把跳线帽、指示灯处理掉。第四个坑外设没关干净。片上的ADC、比较器、串口如果处于工作状态在STOP模式下也可能漏电。进入STOP前最好显式关闭这些外设的电源位。以ADC为例ADC_CONTR amp; 0x7F可以关掉ADC电源。串口如果没用到相关引脚也设成普通IO处理。第五个坑看门狗的问题。STC8G的看门狗在STOP模式下一般是停止计数的但我建议你在自己的板子上验证一下手册上的说法因为不同型号行为可能不一样。如果看门狗在睡眠期间继续跑而你睡眠周期比看门狗溢出周期长那醒来之前芯片就已经复位了整个定时逻辑就乱了。睡眠前把看门狗处理干净长周期比如分钟级唤醒才稳妥。6. 完整例程10秒周期唤醒的采样节点与平均电流推算最后给出一个可以直接改来用的完整例程。硬件连接很简单STC8G1K08的最小系统VCC接3.3VGND接GNDP3.3接一个LED串个限流电阻用来指示任务执行。传感器部分可以用ADC或者IO读取这里用LED翻转代替任务执行重点展示睡眠框架。完整代码如下#include STC8G.h #include intrins.h #ifndef TSTOP #define TSTOP 0x86 #endif sbit LED P3^3; volatile unsigned char wake_count 0; void t0_isr(void) interrupt 1 { wake_count; } void goto_sleep(void) { PCON | 0x02; _nop_(); _nop_(); } void main(void) { // IO初始化P3.3推挽输出其他引脚输出低电平避免悬空漏电 P3M0 0x08; P3M1 0x00; P3 0x00; // 关闭不用的模拟外设降低睡眠漏电 ADC_CONTR 0x7F; // 关闭ADC电源 // 如果有比较器、串口同样显式关闭 // T0初始化16位自动重装载 TMOD 0xF0; TMOD | 0x00; AUXR 0x7F; // 校准后的初值0x7EB8对应1秒实际32K频率约33096Hz TL0 0xB8; TH0 0x7E; TSTOP 0x01; // T0在STOP模式下继续运行 TR0 1; ET0 1; EA 1; while (1) { goto_sleep(); // 每隔约10秒执行一次 if (wake_count 10) { wake_count 0; LED ~LED; // 在这里读取传感器、发送数据处理完自动回到睡眠 } } }代码本身不复杂但有几个点值得说明wake_count用volatile声明因为它在中断里被修改编译器优化时不会把它搬到寄存器里导致判断失效。中断服务程序里只做wake_count不要放复杂业务。唤醒后停留在中断里越久平均电流就越高。实际业务放到主循环判断后执行。初值改成校准后的0x7EB8之后10秒间隔误差已经很小。未校准的板子直接用0x8000也能工作但延时精度会差一个百分点左右。现在来算平均电流验证这套方案到底能不能撑起一颗纽扣电池。假设工作电流3mA每次醒来后执行采样和发送需要10ms睡眠电流1.1μA睡眠周期10秒。平均电流大概是3mA×0.01s/10s 1.1μA 3μA 1.1μA 4.1μA。用CR2032的220mAh容量来算理论寿命是220mAh/4.1μA大约是5.4万小时折合6年左右还没算电池自放电。如果任务更短比如2ms平均电流可以压到2μA以下。这个量级对很多电池供电产品来说已经非常舒服了。如果你还想继续压电流优先级最高的是检查IO漏电和外设漏电把睡眠电流从1.5μA压到0.7μA是可能的其次是把醒来后的工作时间尽量压缩因为那10ms的3mA虽然短但在平均电流里占比不小。另外如果多个唤醒周期可以合并成更长的睡眠周期比如从10秒改成60秒平均电流还能更低但实时性会变差需要按业务取舍。这套方案我已经在一个温湿度上报节点上跑了一个多月每次唤醒都很稳定没有丢过定时。比例程更关键的是睡眠前的IO状态检查和测量方法这两块做不好代码再正确数据也难看。如果你也想在STC8G1K08上做低功耗建议先按这篇文章的最小代码跑通定时唤醒再拿万用表和示波器量一轮电流把睡眠电流压到1μA级别后面的产品化就顺了。
企业数字化 ERP 产品动态
相关推荐
从零复刻 TinyWebServer —— 环境准备与最小 TCP 服务端 第 1 篇:从零复刻 TinyWebServer —— 环境准备与最小 TCP 服务端 原项目:qinguoyi/TinyWebServer 复刻仓库:L2501031968/ccTinyWebServer 完整 20 章教程:仓库内 docs/TinyWebServer-Recreation.md 为什么写这个系列
TinyWebSer… · 2026/9/27 6:18:29
STM32参考方案资源地图:从官方到开源社区的高效检索指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 6:18:23
1个真实案例一文搞懂生成静态网站报价与防黑逻辑 1个真实案例一文搞懂生成静态网站报价与防黑逻辑 网站被黑挂马不知道怎么办?别慌,这行干久了,这种噩梦场景见得太多。很多老板花大价钱做了动态站,结果后台一不留神被拖库,首页瞬间变成色情广告,域名直接被搜索引擎屏蔽,恢复起来费时费力还丢脸。其实… · 2026/9/27 6:18:23
凡科网站登录入口怎么选?3个细节避开黑产陷阱 凡科网站登录入口怎么选?3个细节避开黑产陷阱 你的网站是不是突然弹出了赌博广告,或者页面代码里莫名其妙多了几行JS?这时候别慌,90%的独立站长第一反应是“我被黑了”,但往往忽略了一个最基础的源头: 登录入口的安全设计… · 2026/9/27 7:00:09
做访问的公司网站用5个免费工具解决没人看难题 做访问的公司网站用5个免费工具解决没人看难题 网站上线三个月,后台访问数还是个位数,这种憋屈感谁懂?我见过太多老板花大几万做了个精美官网,结果连个访客影子都没有,钱白花不说,心里还堵得慌。别急,今天不聊那些虚头巴脑的理论,直接上干货,教你怎… · 2026/9/27 7:00:09
B04_高阶函数泛型委托与DSL Android 基础补强 B04|把 Kotlin 高阶函数、泛型、委托和 DSL 连成一条线 摘要:《第一行代码》的 Kotlin 课堂分散在多个章节。本文用文章筛选规则这一小场景,解释函数参数、类型参数、行为委托和带接收者 Lambda 如何共同降低重复࿰… · 2026/9/27 7:00:09
yolo下载地址 一、先搞清楚:你想下载哪个 YOLO?
YOLO 系列并不是由一个团队统一维护的,不同版本分属不同作者/公司。下载前先认准"官方仓库",避免下到第三方修改版: 版本 维护方 状态 YOLOv1~v3 Joseph Redmon (dark… · 2026/9/27 6:59:26
SVG-edit 代码重构演进史:从巨型单体 JS 到模块化 SVG Canvas 架构 前端图形学 【免费下载链接】svgedit Powerful SVG-Editor for your browser 项目地址: https://gitcode.com/gh_mirrors/sv/svgedit 点击查看 免费下载 SVG-edit 曾经把整个编辑器塞进 svg-editor.js(界面)与 svgcanvas.js(其余… · 2026/9/27 6:59:26
python-安装失败之错误代码0x80070643修复教程 起因是很长时间没用python,然后某次后面整理本机的语言在一块,导致系统识别不到,原以为把系统变量改了就万事大吉,然后各种小问题,后面在一次次微调(实则懒得卸了重装),失误将正在安… · 2026/9/27 6:59:26
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01