STM32-電源控制、低功耗模式
STM32的電源框圖
STM32的工作電壓(VDD)為2.0~3.6V。通過內建的電壓調節器提供所需的1.8V電源。 當主電源VDD掉電後,通過VBAT腳為實時時鐘(RTC)和備份暫存器提供電源。
下面是STM32的電源框圖:
注意:框圖中的VDDA和VSSA必須分別聯到VDD和VSS。
獨立的A/D轉換器供電和參考電壓
為了提高轉換的精確度,ADC使用一個獨立的電源供電,過濾和遮蔽來自印刷電路板上的毛刺干擾。
- ADC的電源引腳為VDDA;
- 獨立的電源地VSSA。
如果有VREF-引腳(根據封裝而定),它必須連線到VSSA。同時,為了確保輸入為低壓時獲得更好精度,使用者可以連線一個獨立的外部參考電壓ADC到VREF+和VREF-腳上。在VREF+的電壓範圍為2.4V~VDDA。
電池備份區域
使用電池或其他電源連線到VBAT腳上,當VDD斷電時,可以儲存備份暫存器的內容和維持RTC的功能。
VBAT腳也為RTC、LSE振盪器和PC13至PC15供電,這保證當主要電源被切斷時RTC能繼續工作。切換到VBAT供電由復位模組中的掉電覆位功能控制。 如果應用中沒有使用外部電池,VBAT必須連線到VDD引腳上。
電壓調節器
復位後調節器總是使能的。根據應用方式它以3種不同的模式工作:
- 執行模式:調節器以正常功耗模式提供1.8V電源(核心,記憶體和外設);
- 停止模式:調節器以低功耗模式提供1.8V電源,以儲存暫存器和SRAM的內容;
- 待機模式:調節器停止供電。除了備用電路和備份域外,暫存器和SRAM的內容全部丟失。
STM32的低功耗模式
很多微控制器有低功耗模式,STM32也不例外。在系統或者電源復位後,微控制器出於執行狀態之下,HCLK為CPU提供時鐘,核心執行程式碼。當CPU不需要繼續執行時,可以利用多種低功耗模式來節省功耗,例如等待某個事件觸發。
低功耗模式分類
STM32有三種低功耗模式:
- 睡眠模式:Cortex-M3核心停止,所有外設包括Cortex-M3核心的外設,如NVIC、系統時鐘(SysTick)等仍在執行;
- 停止模式:所有時鐘都已停止。
- 待機模式:1.8V核心電源關閉。
在執行模式下,可以通過下面方式降低功耗:
- 降低系統時鐘:
在執行模式下,通過對預分頻暫存器進行程式設計,可以降低任意一個系統時鐘(SYSCLK、HCLK、PCLK1、PCLK2)的速度。進入睡眠模式前,也可以利用預分頻器來降低外設的時鐘;
- 關閉APB和AHB總線上未被使用的外設時鐘:
在執行模式下,任何時候都可以通過停止為外設和記憶體提供時鐘(HCLK和PCLKx)來減少功耗。 為了在睡眠模式下更多地減少功耗,可在執行WFI或WFE指令前關閉所有外設的時鐘。 通過設定AHB外設時鐘使能暫存器 (RCC_AHBENR)、APB2外設時鐘使能暫存器(RCC_APB2ENR)和APB1外設時鐘使能暫存器(RCC_APB1ENR)來開關各個外設模組的時鐘。
睡眠模式
在睡眠模式下,Cortex-M3核心停止,所有外設包括Cortex-M3核心的外設,如NVIC、系統時鐘(SysTick)等仍在執行,也就是說:所有的I/O引腳都保持它們在執行模式時的狀態。
該模式喚醒所需的時間最短,因為沒有時間損失在中斷的進入或退出上。
停止模式
停止模式是在Cortex™-M3的深睡眠模式基礎上結合了外設的時鐘控制機制,在停止模式下電壓調節器可執行在正常或低功耗模式。此時在1.8V供電區域的的所有時鐘都被停止,但1.8V供電區域仍通電,PLL、HSI和HSE RC振盪器的功能被禁止,SRAM和暫存器內容被保留下來。
在停止模式下,所有的I/O引腳都保持它們在執行模式時的狀態。
待機模式
待機模式可實現系統的最低功耗。該模式是在Cortex-M3深睡眠模式時關閉電壓調節器。整個1.8V供電區域被斷電。PLL、HSI和HSE振盪器也被斷電。SRAM和暫存器內容丟失。只有備份的暫存器和待機電路維持供電。
在待機模式下,所有的I/O引腳處於高阻態,除了以下的引腳:
- 復位引腳(始終有效);
- 當被設定為防侵入或校準輸出時的TAMPER引腳;
- 被使能的喚醒引腳。
電源控制相關配置暫存器
電源控制暫存器(PWR_CR)
作用:掉電深度睡眠位的設定(停止模式和待機模式)。
電源控制/狀態暫存器(PWR_CSR)
作用:使能WKUP引腳用於待機模式喚醒、WUF喚醒標誌位。
電源控制相關配置庫函式
- 2個模式進入函式
- void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry);
- void PWR_EnterSTANDBYMode(void);
作用:前者進入停機狀態,後者進入待機狀態。
- 2個使能函式
- void PWR_WakeUpPinCmd(FunctionalState NewState);
- void PWR_BackupAccessCmd(FunctionalState NewState);
作用:前者使能WK_UP引腳喚醒(正常模式下,WK_UP引腳作為普通IO口,待機模式下設定成喚醒功能),後者使能BKP後備區域訪問使能。
- 2個狀態位函式
- FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG);
- void PWR_ClearFlag(uint32_t PWR_FLAG);
作用:前者獲取電源控制的狀態位,後者清除相應的狀態位。
- 2個核心指令函式
- __WFI();
- __WFE();
作用:CM3核心的WFI(等待中斷)、WFE(等待事件)指令(定義在:core_cm3.h)。
待機模式的一般步驟
例項要求:實現同一個引腳PA0引腳(WK_UP引腳),正常模式下,長按3秒進入待機模式;待機模式下,長按3秒待機喚醒。
- 使能電源時鐘。呼叫函式:RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
- 設定WK_UP引腳作為喚醒源。呼叫函式:PWR_WakeUpPinCmd(ENABLE);
- 進入待機模式。呼叫函式:void PWR_EnterSTANDBYMode(void),執行設定SLEEPDEEP位,設定PDDS位,執行WFI指令。
#define WKUP_KD PAin(0) //PA0 檢測是否外部WK_UP按鍵按下
- void Sys_Standby(void)
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR外設時鐘
- PWR_WakeUpPinCmd(ENABLE); //使能喚醒管腳功能
- PWR_EnterSTANDBYMode(); //進入待命(STANDBY)模式
- }
- //系統進入待機模式
- void Sys_Enter_Standby(void)
- {
- RCC_APB2PeriphResetCmd(0X01FC,DISABLE); //復位所有IO口
- Sys_Standby();
- }
- //檢測WKUP腳的訊號
- //返回值1:連續按下3s以上
- // 0:錯誤的觸發
- u8 Check_WKUP(void)
- {
- u8 t=0; //記錄按下的時間
- LED0=0; //亮燈DS0
- while(1)
- {
- if(WKUP_KD)
- {
- t++; //已經按下了
- delay_ms(30);
- if(t>=100) //按下超過3秒鐘
- {
- LED0=0; //點亮DS0
- return 1; //按下3s以上了
- }
- }else
- {
- LED0=1;
- return 0; //按下不足3秒
- }
- }
- }
- //中斷,檢測到PA0腳的一個上升沿.
- //中斷線0線上的中斷檢測
- void EXTI0_IRQHandler(void)
- {
- EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE10上的中斷標誌位
- if(Check_WKUP())//關機?
- {
- Sys_Enter_Standby();
- }
- }
- //PA0 WKUP喚醒初始化
- void WKUP_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- EXTI_InitTypeDef EXTI_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);//使能GPIOA和複用功能時鐘
- GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0; //PA.0
- GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPD;//上拉輸入
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
- //使用外部中斷方式
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //中斷線0連線GPIOA.0
- EXTI_InitStructure.EXTI_Line = EXTI_Line0; //設定按鍵所有的外部線路
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //設外外部中斷模式:EXTI線路為中斷請求
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿觸發
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;
- EXTI_Init(&EXTI_InitStructure); // 初始化外部中斷
- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按鍵所在的外部中斷通道
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先佔優先順序2級
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //從優先順序2級
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
- NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器
- if(Check_WKUP()==0) Sys_Standby(); //不是開機,進入待機模式
- }
- int main(void)
- {
- delay_init(); //延時函式初始化
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設定中斷優先順序分組為組2:2位搶佔優先順序,2位響應優先順序
- uart_init(115200); //串列埠初始化為115200
- LED_Init(); //LED埠初始化
- WKUP_Init(); //待機喚醒初始化
- LCD_Init(); //LCD初始化
- POINT_COLOR=RED;
- LCD_ShowString(30,50,200,16,16,"Warship STM32");
- LCD_ShowString(30,70,200,16,16,"WKUP TEST");
- LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
- LCD_ShowString(30,110,200,16,16,"2014/1/14");
- while(1)
- {
- LED0=!LED0;
- delay_ms(250);
- }
- }
STM32控制程式分析
WKUP_Init()函式:初始化GPIO、外部中斷等準備工作。
由於WK_UP鍵有兩個功能:正常模式下,長按3秒進入待機模式;待機模式下,長按3秒退出待機模式。
本案例中的實現是:
- 正常模式下,將WK_UP鍵設定成外部中斷,一旦按下,進入中斷處理函式,在中斷處理函式中進行3秒的時間判斷是否進入待機模式;
- void WKUP_Init(void)
- {
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);//使能GPIOA和複用功能時鐘
- GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0; //PA.0
- GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPD;//上拉輸入
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
- //使用外部中斷方式
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //中斷線0連線GPIOA.0
- EXTI_Init(&EXTI_InitStructure); // 初始化外部中斷
- NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器
- }
- void EXTI0_IRQHandler(void)
- {
- EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE10上的中斷標誌位
- if(Check_WKUP())//關機?
- {
- Sys_Enter_Standby();
- }
- }
- 待機模式下,設定WK_UP鍵用來退出待機模式,一旦按下,退出待機模式,重新執行main主函式,在主函式中進行3秒的時間判斷,如果沒有3秒,重新進入待機模式,否則繼續正常執行。
- void WKUP_Init(void)
- {
- //初始化過程
- if(Check_WKUP()==0) Sys_Standby(); //不是開機,進入待機模式
- }
而其中進入待機模式的程式程式碼塊,就是之前的待機模式的一般步驟:
- void Sys_Standby(void)
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR外設時鐘
- PWR_WakeUpPinCmd(ENABLE); //使能喚醒管腳功能
- PWR_EnterSTANDBYMode(); //進入待命(STANDBY)模式
- }