ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

GD32F407VET6低功耗实验:三种模式代码与实测功耗对比

GD32F407VET6低功耗实验:三种模式代码与实测功耗对比 简介GD32F407VET6低功耗实验工程源码面向嵌入式开发者、单片机学习者及从事低功耗产品设计的工程师。资源基于兆易创新Cortex-M4内核MCU完整演示待机、睡眠、停止等低功耗模式的进入与唤醒机制包含PMU电源管理、时钟配置、中断唤醒等关键模块的初始化与调试逻辑。工程采用标准库函数编写代码结构清晰适合参考学习或直接移植到实际项目。资源包共88个文件以h头文件、c源文件为主另含启动文件、工程配置文件、链接脚本及编译列表等覆盖从工程创建到调试烧录的完整链路压缩包大小425KB轻量易用。已有268人学习下载适合正在研究GD32低功耗特性、需要快速搭建实验环境的开发者。通过该工程可掌握低功耗模式切换、唤醒源配置及功耗优化思路为电池供电类产品开发提供实用参考。 手头这块GD32F407VET6核心板放了小半年之前一直拿它跑各种性能测试直到最近做电池供电的小项目才认真把低功耗实验补上。这篇就来记录一下GD32F407VET6单片机低功耗实验的完整过程包括睡眠模式、深度睡眠模式、待机模式的代码实现、实测功耗数据以及几个很容易踩的坑。如果你正在做低功耗设计或者想看看M4内核能把功耗压到多低这篇应该能帮你少走点弯路。很多人一提低功耗就想到stm8s003这类微安级的小芯片确实追求极限续航时8位机有天然优势。但GD32F407VET6这种Cortex-M4平台做低功耗实验同样有价值它有512KB Flash和192KB RAM外设资源丰富完全可以在跑完任务后休眠把待机电流压到微安级。对于智能家居传感器节点、便携采集设备这类既要性能又要省电的场景M4平台的低功耗设计思路更实用也更能锻炼对时钟、外设、中断的整体把控能力。1. 低功耗实验整体思路与模式选型1.1 为什么拿GD32F407VET6做低功耗实验GD32F407VET6是一颗100引脚的M4内核芯片外设齐全跑个RTOS、处理点音频数据、挂个屏都绰绰有余。但高性能不等于不能省电MCU的功耗瓶颈往往不在内核本身而在“用完后有没有把该关的东西关掉”。低功耗实验的意义就在于此同样的芯片有人待机几十毫安有人能做到几微安差距基本都在低功耗模式的选择和状态配置上。我做过一个简单估算一颗1000mAh的电池如果设备待机电流是10mA理论上只能撑100小时差不多4天多如果压到10uA理论上能撑10万小时以上约11年。当然这是理论值但方向很清楚——待机功耗直接决定产品的续航上限。这个实验适合三类人一是正在用GD32做项目的开发者想把手头设备的待机功耗降下来二是刚接触低功耗设计的嵌入式新手想搞清楚睡眠、深度睡眠、待机到底什么区别三是准备做电池供电产品的硬件工程师需要一份可参考的代码和实测数据。整篇内容我都基于GD32F4xx标准外设库来写你用STM32F4的HAL库也能对照思路只是函数名不同。1.2 GD32F407VET6的三种低功耗模式怎么选GD32F407VET6的低功耗模式和市面上多数Cortex-M4芯片类似分成三档模式内核状态外设状态典型唤醒源唤醒后行为适合场景睡眠模式内核停止外设时钟保持任意中断/事件从休眠点继续执行需要频繁快速唤醒深度睡眠模式内核停止高频时钟停外设大多关闭RTC、外部中断、WKUP从休眠点继续需重配时钟需保留RAM数据待机模式完全掉电绝大部分电路断电WKUP引脚、RTC闹钟、复位相当于系统复位从头执行极低功耗休眠选型时主要看三个维度唤醒响应时间、RAM数据是否需要保留、最低功耗要求。睡眠模式唤醒最快几微秒就能继续跑但省电有限因为外设时钟还活着。深度睡眠模式把高频时钟关掉RAM内容还在唤醒后要花点时间重新锁相环。待机模式最省电但代价是RAM里的内容全部丢失程序重启。这次的实验我打算把三种模式都实现一遍再分别测电流这样你就能直观看到每一个模式到底省了多少电。测试平台很简单一块GD32F407VET6核心板加一个ST-Link测电流还需要一个能读微安的万用表这个后面细说。2. 硬件准备与开发环境搭建2.1 硬件清单与测量接线硬件方面我手里这套配置很基础GD32F407VET6核心板一块ST-Link/V2调试器一个也可以用DAP数字万用表一个建议有uA档杜邦线若干USB转TTL模块一个调试时打印日志用这里有一个特别关键的点测低功耗电流的时候千万不要直接把万用表并到电源上测一定要串联进去。我习惯的做法是把核心板的电源跳线帽取下来把万用表红黑表笔串进供电回路里相当于让整个板的电流都流过万用表。具体接线我是这样做的万用表拨到电流档红表笔插A或者uA/mA口黑表笔插COM口。然后短接表笔给板上电容充一下电再接入电路。为什么要先短接因为上电瞬间电容充电电流很大直接接上去可能会打火花也容易让万用表保险丝熔断。测全速运行电流时用mA档等降到低功耗模式后再切uA档不然小电流在mA档上读数会非常不准确。引脚分配方面不同厂家的核心板略有差异我手头这块的接法是这样的功能引脚说明状态LEDPE2低电平点亮作为运行状态指示唤醒按键PA0连接WKUP0上升沿唤醒待机模式调试串口PA9/PA10用于打印日志实验时可断开GPIO接法不一定完全一样你拿到板子先看原理图确认LED和按键接在哪个脚再把代码里的引脚宏改掉就行。2.2 Keil工程和时钟初始化要点开发环境我用的是Keil MDK加GD32F4xx标准外设库。如果你手头有GD32官方例程建议直接从它的GPIO工程改省去自己搭工程的时间。没有的话新建工程后需要做几件事芯片型号选择GD32F407VE或者直接选GD32F407系列对应型号宏定义里加上GD32F407根据固件库要求配置添加GD32F4xx标准外设库的源码路径重点是gd32f4xx_gpio.c、gd32f4xx_rcu.c、gd32f4xx_pmu.c这几个文件如果是从STM32F4工程移植过来要注意GD32的库函数命名差异很大比如ST的HAL_GPIO_WritePin对应GD32的gpio_bit_set时钟使能从__HAL_RCC_GPIOA_CLK_ENABLE变成了rcu_periph_clock_enable(RCU_GPIOA)。直接搬代码会报一堆错但改起来也不难对照着查就行。初始化代码很简单LED和按键的部分如下#include gd32f4xx.h void board_init(void) { /* 使能GPIO时钟 */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOE); /* PE2推挽输出驱动LED */ gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2); gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); /* PA0浮空输入作为唤醒按键 */ gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0); }这里提醒一下GD32F4xx的GPIO初始化用的是gpio_mode_set和gpio_output_options_set的组合不像51单片机直接写P0口。初次上手可能不习惯但逻辑很清楚先设模式输入还是输出再设输出类型推挽还是开漏和速度。3. 三种低功耗模式的代码实现与关键细节3.1 睡眠模式最简单也很好理解睡眠模式是所有低功耗模式里最温和的内核停止执行指令但所有外设时钟都还在跑。打个比方就像你在工位上闭目养神但电脑屏幕还亮着风扇还转着电话一响你马上就能接。唤醒源是任意中断速度非常快。代码实现很直接主循环里调用pmu_to_sleepmode传入WFI还是WFE是关键。WFIWait For Interrupt靠中断唤醒WFEWait For Event靠事件唤醒我们一般用WFI就够了void sleep_mode_test(void) { /* 配置PA0外部中断用于唤醒 */ gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0); exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_SOURCE_GPIOA, EXTI_SOURCE_PIN0); exti_interrupt_flag_clear(EXTI_0); nvic_irq_enable(EXTI0_IRQn, 0, 0); while (1) { gpio_bit_set(GPIOE, GPIO_PIN_2); /* LED亮 */ delay_ms(500); gpio_bit_reset(GPIOE, GPIO_PIN_2); /* LED灭 */ /* 进入睡眠模式等待外部中断唤醒 */ pmu_to_sleepmode(PMU_SLEEP_WFI); /* 唤醒后从这里继续执行 */ } } void EXTI0_IRQHandler(void) { if (SET exti_interrupt_flag_get(EXTI_0)) { exti_interrupt_flag_clear(EXTI_0); } }这个代码有个很隐蔽的坑如果中断处理函数里没有清除中断标志唤醒后处理器会立刻再次进入中断然后再次睡眠看起来就像“没醒过来”。所以exti_interrupt_flag_clear一定要有。另外一个细节是Cortex-M4内核里有个SLEEPONEXIT位位于系统控制寄存器SCB-SCR。如果这个位被置1那么中断服务程序退出后CPU会立刻重新进入睡眠这不是我们想要的效果。保险起见可以在进入睡眠前把它清零SCB-SCR ~SCB_SCR_SLEEPONEXIT_Msk;3.2 深度睡眠模式把高频时钟关干净深度睡眠模式比睡眠模式省得多内核停止后系统的高频时钟也停了只有低速时钟比如LSI、LSE还可以运行。RAM的内容会保留这是它和待机模式最大的区别。用电脑类比的话睡眠模式是息屏待机深度睡眠模式就是合上盖子进休眠了。进入深度睡眠前最好把用不到的外设时钟关掉。我见过不少人在这个模式里功耗还是很高就是因为外设模块一个个都还开着。虽然深度睡眠下大多数外设时钟已经停止但先关掉更稳妥也能降低唤醒后的功耗void deepsleep_mode_test(void) { /* 关闭不需要的外设时钟 */ rcu_periph_clock_disable(RCU_GPIOB); rcu_periph_clock_disable(RCU_GPIOC); rcu_periph_clock_disable(RCU_GPIOD); /* 配置PA0为WKUP唤醒源 */ gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0); pmu_wakeup_pin_enable(PMU_WAKEUP_PIN0); /* 进入深度睡眠模式 */ pmu_to_deepsleepmode(PMU_LDO_NORMAL, PMU_DEEPSLEEP, PMU_DEEPSLEEP_WFI); /* 唤醒后从这里继续执行但系统时钟需要重新配置 */ system_clock_config(); board_init(); }这里有个重点深度睡眠唤醒后芯片不会自动恢复高速时钟配置你需要手动重新初始化和PLL相关的时钟设置。我的做法是在唤醒后调用system_clock_config()把时钟树重新建立起来。如果不做这一步代码执行起来会特别慢甚至有些外设配置会乱了套。很多人会忽略GPIO引脚在低功耗模式下的状态。深度睡眠期间GPIO是保持之前输出状态的如果你某个引脚输出高电平但外部电路恰好是低电平有效就会有电流倒灌的问题。正确做法是在进入深度睡眠之前把所有不需要保持的GPIO设置为模拟输入或者下拉输入让引脚不产生任何漏电路径。3.3 待机模式最彻底的断电待机模式是三种模式里最省电的但代价也最明显——整个芯片相当于被强行断电了SRAM和寄存器内容全部丢失唤醒后程序从头开始执行。这个模式和深度睡眠有本质区别深度睡眠至少RAM里的数据还在待机模式就差等于关机重开了。进入待机模式的代码更短但要注意唤醒源的配置void standby_mode_test(void) { /* 配置PA0为WKUP唤醒源 */ gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0); pmu_wakeup_pin_enable(PMU_WAKEUP_PIN0); /* 进入待机模式唤醒后芯片复位程序从main重新开始 */ pmu_to_standbymode(PMU_LDO_NORMAL, PMU_STANDBY, PMU_STANDBY_WFI); while (1) { } }不同版本的GD32固件库对pmu_to_standbymode的参数定义可能有差异你写代码之前先打开gd32f4xx_pmu.h看枚举定义以你本地库为准。待机模式唤醒源除了WKUP引脚还可以用RTC闹钟唤醒。这个适合做定时采集类的设备比如每隔1小时醒一次发数据再睡。RTC走的是备份域电源待机模式下依然能工作所以定时唤醒完全没问题。还有一个非常有用的技巧由于待机唤醒后就是复位那你如何在代码里区分“这次是刚上电”还是“从待机唤醒”答案是查标志位if (SET pmu_flag_get(PMU_FLAG_STANDBY)) { /* 说明是从待机模式唤醒的 */ pmu_flag_clear(PMU_FLAG_STANDBY); }这个标志位在芯片复位后依然保留直到软件清除。利用它可以在初始化时跳过某些耗时的自检流程或者恢复特定的业务状态。4. 功耗实测与数据对比4.1 电流测量的正确流程测低功耗电流是个精细活我自己的流程是先把板上跟MCU无关的耗电模块摘掉比如USB转串口芯片、电源指示灯跳帽。这些器件不管MCU睡不睡都在耗电会把真实数据完全淹没。将万用表拨到电流档串联到主板供电回路里。先测全速运行模式的电流用mA档记录基准数据。烧录进入睡眠模式的程序等LED熄灭后切到uA档等读数稳定。分别烧录深度睡眠和待机模式的程序重复测量。测量时有个细节容易忽略每次切换模式前都要重新上电。因为待机模式下芯片基本没电流万用表的uA档内阻又比较大如果直接从运行状态切到待机状态电路上电瞬间可能复位或者触发保护。我习惯的做法是关电源切换程序再重新上电等稳定后读数。还有测量的时间要给够。低功耗模式不是一进去电流就立刻稳定有些外设的放电过程需要几秒钟计数器也会在低功耗下不停翻转。我每次都会等至少10秒再读数多测几次取平均值。4.2 实测数据表与逐项解读我这次是在去掉板载USB转串口芯片、LED跳帽的条件下测的外部8MHz晶振保留供电3.3V系统时钟正常配置GD32F407VET6支持高主频运行。数据如下模式实测电流说明全速运行32.6mA主频全开外设时钟大部分使能睡眠模式8.4mA内核停止外设时钟仍运行深度睡眠模式82uA高频时钟停止保留RAM待机模式4.7uA全芯片断电仅保留唤醒逻辑看到这个数据你就明白为什么我总是强调“选对模式”了。睡眠模式省电效果其实很有限只降到了8mA级别对于电池供电应用来说还是太奢侈。真正有质的飞跃是深度睡眠模式一下子跳到几十微安已经是功耗敏感的级别。待机模式更加夸张只有几微安基本可以忽略不计。深度睡眠和待机之间相差十几倍代价就是RAM数据存不存得住。如果你需要设备醒来后还能使用之前缓存的传感器数据那就必须用深度睡眠如果只是定时醒过来发个数据然后继续睡待机模式是最优解。四个模式的数据也解释了为什么低功耗设计要考虑“占空比”。假设一个设备每小时工作1秒工作电流30mA其余时间都处于待机模式4.7uA那么平均电流大约是30mA1/3600 0.0047mA3599/3600算下来大概是0.013mA左右一颗1000mAh电池能撑7万多小时即8年以上。这就是低功耗设计的核心逻辑。5. 常见问题与排查技巧实录5.1 唤醒后程序卡死或者乱跑我调试睡眠模式时就遇到过按了按键唤醒串口打印出来的数据全是乱的程序像是在异常状态里跑。排查下来有两个原因。第一个是中断标志没清干净。外部中断触发唤醒后中断服务函数如果没清挂起标志CPU会反复进入中断表现为程序卡在中断里出不来。解决办法我已经在上面代码里写了进中断第一件事就是exti_interrupt_flag_clear。第二个是唤醒后时钟没恢复。深度睡眠模式唤醒后芯片不会自动把PLL拉回高速状态。如果你的代码直接从休眠点继续跑但此时系统时钟还处于低速甚至未稳定状态就会出现串口乱码、定时器不准之类的问题。解决方法是唤醒后显式调用时钟配置函数把系统时钟和总线时钟重新初始化。5.2 功耗一直下不去比手册标称高几十倍这个是最常见的问题几乎每个人第一次测低功耗都会怀疑人生。明明待机模式标称几微安实测却是几百微安甚至毫安级差距大得让人想砸板子。我的排查顺序是这样先看板子上的其他芯片。USB转串口、稳压芯片、电源指示灯、电平转换芯片这些只要存在就在耗电。我把板载USB转串口芯片从板上摘掉之后待机电流立刻降了一个数量级。再看GPIO引脚。所有悬空的输入引脚在低功耗下都可能产生漏电流。解决方法是把未使用的GPIO统一配置为模拟输入模式代码一行就能搞定gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_ALL);再看调试接口。Cortex-M4的调试模块在睡眠状态下默认是保持工作的如果调试器还连着功耗就会高不少。调试完拔掉调试器再测数据会正常很多。其实这个排查过程本身就是低功耗设计的一部分软件设好了模式硬件上的每一本文还有配套的精品资源点击获取
返回列表