基于 STM32F103C8T6 的无人机低功耗设计与实现

前言

做无人机的都知道,续航是永远的痛。电池就那么大,电机一转电量哗哗掉。很多人把注意力全放在电机和桨叶上,却忽略了飞控主控本身也在持续耗电。别小看这几十毫安的电流,在微型无人机上,主控功耗能占到整机待机功耗的一半以上。

本文以最常用的 STM32F103C8T6为主控,从低功耗的基本概念讲起,到三种低功耗模式的原理,再到无人机场景下的具体代码实现,一步步把飞控的功耗压下去。

一、什么是低功耗?为什么无人机需要它?

1.1 低功耗到底是什么

低功耗不是简单的 "少用电",而是在保证系统功能和响应速度的前提下,通过硬件设计和软件调度,让芯片在不需要全速运行的时候进入省电状态,需要的时候又能快速唤醒

STM32F103C8T6来说,就是合理利用它的三种低功耗模式 ------ 睡眠、停机、待机,配合外设时钟门控,把平均电流降下来。

1.2 无人机低功耗的实际作用

  • 延长待机时间:无人机开机后不一定马上起飞,地面等待阶段主控如果一直满速跑,电池白白消耗。进入低功耗后,待机电流可以从几十毫安降到微安级。
  • 给动力系统留更多电量:主控省下来的电,就是电机能多飞的时间。
  • 降低发热:芯片长时间运行会发热,高温影响 IMU 等传感器精度,低功耗模式能减少发热。
  • 适应微型无人机:微型无人机电池容量只有几百毫安时,主控每省 10mA,可能就多飞几十秒。

二、STM32F103C8T6 的三种低功耗模式

STM32F103 系列有三种低功耗模式,功耗从高到低依次是:睡眠模式 > 停机模式 > 待机模式

表格

模式 核心 时钟 SRAM / 寄存器 唤醒方式 典型电流
睡眠 (Sleep) 停止 外设继续 保持 任意中断 ~10mA
停机 (Stop) 停止 全部停止 保持 外部中断 / RTC ~20μA
待机 (Standby) 停止 全部停止 丢失 (仅备份寄存器) WKUP 引脚 / RTC ~2μA

2.1 睡眠模式(Sleep Mode)

最简单的低功耗模式。CPU 内核停止运行,但所有外设(GPIOUSARTSPII2C 、定时器等)继续工作,SRAM和寄存器内容全部保留。

特点

  • 进入和唤醒最快,唤醒后直接从断点继续执行;
  • 任意中断都能唤醒;
  • 适合短时间、高频次的省电场景。

无人机应用场景:飞控在两次姿态解算之间的空闲期,可以短暂进入睡眠,由定时器中断唤醒。

2.2 停机模式(Stop Mode)

所有 1.8V 区域的时钟停止,PLLHSIHSE 振荡器被关闭,但 SRAM和寄存器内容保持不变。唤醒后需要重新配置系统时钟。

特点

  • 功耗比睡眠低很多(微安级);
  • 唤醒后程序从停止处继续,但要重新初始化时钟;
  • 只能通过外部中断(EXTI)或 RTC 闹钟唤醒。

无人机应用场景:地面待机、任务间歇期,需要保留飞控参数和传感器配置时使用。

2.3 待机模式(Standby Mode)

功耗最低的模式,整个 1.8V 区域断电,PLLHSIHSE 全部关闭,SRAM和寄存器内容丢失,只有备份寄存器和待机电路保持供电。

特点

  • 功耗最低(约 2μA);
  • 唤醒后相当于系统复位,程序从头开始执行;
  • 只能通过 WKUP 引脚(PA0)上升沿、RTC 闹钟、IWDG 复位唤醒。

无人机应用场景:长时间存放、运输过程中,不需要保留运行状态时使用。


三、具体实现代码

3.1 睡眠模式实现

睡眠模式最简单,调用 WFIWait For Interrupt)指令即可。

复制代码
#include "stm32f10x.h"

/**
 * @brief  进入睡眠模式
 * @note   任意中断唤醒,唤醒后继续执行
 */
void Enter_Sleep_Mode(void)
{
    // 使能PWR时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    
    // 进入睡眠模式:WFI指令
    __WFI();
    
    // 唤醒后从这里继续执行
}

在飞控主循环中的典型用法:

复制代码
int main(void)
{
    System_Init();       // 系统初始化
    Sensor_Init();       // 传感器初始化
    Motor_Init();        // 电机初始化
    
    while(1)
    {
        if(flight_state == FLYING)
        {
            Attitude_Calculate();   // 姿态解算
            Motor_Control();        // 电机控制
        }
        else
        {
            // 未起飞时,进入睡眠,由1ms定时器中断唤醒
            Enter_Sleep_Mode();
        }
    }
}

3.2 停机模式实现

停机模式需要配置 PWR寄存器,唤醒后要重新配置系统时钟。

复制代码
#include "stm32f10x.h"

/**
 * @brief  进入停机模式
 * @note   外部中断或RTC唤醒,唤醒后需重新配置时钟
 */
void Enter_Stop_Mode(void)
{
    // 使能PWR时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    
    // 进入停机模式,调节器低功耗模式
    PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
    
    // ===== 唤醒后从这里继续 =====
    // 重新配置系统时钟(因为HSE/PLL已关闭)
    SystemInit();
    SystemCoreClockUpdate();
}

配置外部中断唤醒(以 PA1引脚为例,接遥控信号):

复制代码
/**
 * @brief  配置EXTI用于唤醒停机模式
 */
void EXTI_Wakeup_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 使能GPIOA和AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    
    // PA1配置为上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 连接EXTI1到PA1
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
    
    // 配置EXTI1为下降沿触发(遥控信号到来时唤醒)
    EXTI_InitStructure.EXTI_Line = EXTI_Line1;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    // 配置NVIC
    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

// 中断服务函数
void EXTI1_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line1) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line1);
        // 唤醒后执行的操作
    }
}

3.3 待机模式实现

待机模式功耗最低,但唤醒后系统复位。需要配置 WKUP 引脚(PA0 )或 RTC闹钟。

复制代码
#include "stm32f10x.h"

/**
 * @brief  进入待机模式
 * @note   唤醒后系统复位,程序从头执行
 */
void Enter_Standby_Mode(void)
{
    // 使能PWR和BKP时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    
    // 使能对备份区域的访问
    PWR_BackupAccessCmd(ENABLE);
    
    // 清除唤醒标志
    PWR_ClearFlag(PWR_FLAG_WU);
    
    // 使能WKUP引脚(PA0),上升沿唤醒
    PWR_WakeUpPinCmd(ENABLE);
    
    // 进入待机模式
    PWR_EnterSTANDBYMode();
    
    // 注意:待机模式唤醒后不会回到这里,而是从复位向量开始执行
}

在主函数中判断是否是待机唤醒:

复制代码
int main(void)
{
    // 检查是否是从待机模式唤醒
    if(PWR_GetFlagStatus(PWR_FLAG_SB) != RESET)
    {
        // 清除待机标志
        PWR_ClearFlag(PWR_FLAG_SB);
        // 待机唤醒后的特殊处理(比如快速初始化,跳过自检)
        Quick_Init();
    }
    else
    {
        // 正常上电初始化
        Full_Init();
    }
    
    while(1)
    {
        // 主循环
    }
}

3.4 RTC 闹钟唤醒停机模式

无人机在地面待机时,可以用 RTC定时唤醒做状态检测,平时处于停机模式。

复制代码
/**
 * @brief  配置RTC闹钟,N秒后唤醒
 * @param  seconds: 多少秒后唤醒
 */
void RTC_Alarm_Wakeup_Init(uint32_t seconds)
{
    // 使能PWR和BKP时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    
    // 使能备份区域访问
    PWR_BackupAccessCmd(ENABLE);
    
    // 复位备份区域
    BKP_DeInit();
    
    // 使能LSE(外部低速晶振32.768kHz)
    RCC_LSEConfig(RCC_LSE_ON);
    while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
    
    // 选择LSE作为RTC时钟源
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    RCC_RTCCLKCmd(ENABLE);
    
    // 等待RTC寄存器同步
    RTC_WaitForSynchro();
    RTC_WaitForLastTask();
    
    // 设置RTC分频:32768 / 32768 = 1Hz
    RTC_SetPrescaler(32767);
    RTC_WaitForLastTask();
    
    // 设置闹钟值:当前时间 + seconds
    RTC_SetAlarm(RTC_GetCounter() + seconds);
    RTC_WaitForLastTask();
    
    // 使能RTC闹钟中断
    RTC_ITConfig(RTC_IT_ALR, ENABLE);
    RTC_WaitForLastTask();
    
    // 配置NVIC
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 配置EXTI17(RTC闹钟连接到EXTI17)
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line17;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

// RTC闹钟中断服务函数
void RTCAlarm_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
    {
        RTC_ClearITPendingBit(RTC_IT_ALR);
        EXTI_ClearITPendingBit(EXTI_Line17);
        // 唤醒后处理
    }
}

四、无人机系统中的低功耗策略

光会进低功耗模式还不够,要结合无人机的实际工作状态来设计策略。

4.1 状态机驱动的功耗管理

复制代码
typedef enum {
    STATE_POWER_ON,    // 刚上电,完整初始化
    STATE_STANDBY,     // 地面待机,停机模式 + RTC周期唤醒
    STATE_READY,       // 已解锁,准备起飞,睡眠模式
    STATE_FLYING,      // 飞行中,全速运行
    STATE_LANDING,     // 降落中
    STATE_LOW_BATTERY  // 低电量,强制降功耗
} DroneState_t;

DroneState_t drone_state = STATE_POWER_ON;

void Power_Management(void)
{
    switch(drone_state)
    {
        case STATE_STANDBY:
            // 关闭不必要的外设时钟
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE);  // 关闭SPI
            RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, DISABLE); // 关闭USART2
            // 进入停机模式,RTC 5秒后唤醒检测状态
            RTC_Alarm_Wakeup_Init(5);
            Enter_Stop_Mode();
            // 唤醒后恢复时钟
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
            RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
            break;
            
        case STATE_READY:
            // 已解锁但未起飞,用睡眠模式,保持快速响应
            Enter_Sleep_Mode();
            break;
            
        case STATE_LOW_BATTERY:
            // 低电量:关闭图传、LED等非必要外设
            GPIO_ResetBits(GPIOB, GPIO_Pin_5);  // 关闭LED
            // 限制电机最大功率
            Motor_SetMaxPower(60);
            break;
            
        default:
            break;
    }
}

4.2 外设时钟门控

STM32F103的每个外设都有独立的时钟开关,不用的外设一定要关掉时钟,这是最基本也是最有效的省电手段。

复制代码
/**
 * @brief  关闭所有未使用的外设时钟
 */
void Disable_Unused_Peripherals(void)
{
    // 关闭APB2上未使用的外设
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_UNUSED, DISABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, DISABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE);  // 如果不用SPI
    
    // 关闭APB1上未使用的外设
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, DISABLE);  // 如果不用TIM2
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, DISABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, DISABLE);  // 如果不用I2C
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, DISABLE);
}

4.3 GPIO 配置优化

未使用的 GPIO不要悬空,配置成模拟输入可以最大限度降低功耗。

复制代码
/**
 * @brief  将未使用的GPIO配置为模拟输入(最低功耗)
 */
void GPIO_Analog_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能所有GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | 
                           RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
    
    // 所有引脚配置为模拟输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  // 模拟输入,无上下拉
    
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    
    // 关闭JTAG,释放PA13/PA14/PA15/PB3/PB4(如果不用JTAG调试)
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
}

注意 :配置模拟输入前,先确认哪些引脚已经被外设使用,不要把正在用的引脚也配成模拟输入。实际项目中应该逐个引脚配置,而不是直接**GPIO_Pin_All**。


五、实测效果参考

STM32F103C8T6 最小系统板(外部 8MHz 晶振 + 32.768kHz晶振)为例,不同模式下的实测电流:

表格

工作状态 电流 说明
全速运行(72MHz) ~30mA 所有外设开启
睡眠模式 ~15mA CPU 停止,外设运行
停机模式 ~25μA 仅 SRAM 保持
待机模式 ~3μA 仅备份寄存器

可以看到,从全速运行到待机模式,电流差了整整 10000 倍。在无人机地面待机场景下,使用停机模式 + RTC 周期唤醒,平均电流可以控制在几十微安,待机时间从几小时延长到几个月。


六、注意事项

  1. 停机模式唤醒后必须重新配置系统时钟,因为 HSE 和 PLL 都停了,不重新配置的话系统会用内部 8MHz HSI 运行,性能大幅下降。
  2. 待机模式唤醒相当于复位,SRAM 数据全部丢失,重要参数要存在备份寄存器或 Flash 中。
  3. 调试接口注意:进入低功耗模式后,SWD/JTAG 调试器可能无法连接,这是正常现象,复位后即可恢复。
  4. 唤醒源要提前配置好:进入低功耗前必须配置好唤醒中断,否则就真的 "睡死" 过去了,只能硬复位。
  5. 传感器上电时序:停机模式下传感器也断电了,唤醒后要重新初始化 IMU、GPS 等传感器,不能直接读数据。

总结

STM32F103C8T6 的低功耗设计核心就是三句话:该跑的时候全速跑,该睡的时候彻底睡,外设不用就关时钟。在无人机系统中,结合飞行状态机,在地面待机时用停机模式、准备起飞时用睡眠模式、飞行中用外设时钟门控,就能在不影响飞行性能的前提下,把主控功耗降到最低。

低功耗优化是个细活,需要用万用表实际测量每个模式的电流,找到耗电大户,再针对性优化。希望这篇文章能帮到正在做无人机低功耗设计的朋友。

相关推荐
hrw_embedded1 小时前
Keil开发与STM32Cube开发的差异点总结(未完待续)
stm32·单片机·嵌入式硬件
Brilliantwxx1 小时前
【STM32】RCC与时钟树 深度详解
stm32·单片机·嵌入式硬件·架构
YOLO数据集集合2 小时前
工地开放危险区域检测数据集 | 工地安全 开放区域检测 临边防护9050期
安全·无人机·工地·工地安全·工地检测·危险区域监测
天空'之城2 小时前
单片机基础核心知识点汇总(二十九)
单片机·嵌入式硬件
Rsingstarzengjx2 小时前
第二十六章 Linux 蜂鸣器实验笔记
linux·驱动开发·笔记·stm32·嵌入式硬件·stm32mp157
一路往蓝-Anbo2 小时前
第七篇:以太网设计 —— 从 RMII 时序到变压器隔离的深度拆解
stm32·单片机·嵌入式硬件·嵌入式·stm32硬件设计
jianqiang.xue3 小时前
ESP-IDF保姆级入门22|WiFi联网与网络编程全解:STA/AP双模式/TCP-UDP Socket/HTTP服务端/自动重连,掌握工业级可靠网络通信
人工智能·stm32·单片机·mcu·物联网·51单片机·iot
恒锐丰科技林技术员7 小时前
EG2302 半桥驱动芯片详解:高压半桥高性价比栅极驱动方案
经验分享·嵌入式硬件·硬件工程
会不再投降21910 小时前
初识嵌入式历史发展与生态
stm32