主要參考學習資料:
B站@江協科技
STM32入門教程-2023版 細致講解 中文字幕
開發資料下載鏈接:https://pan.baidu.com/s/1h_UjuQKDX9IpP-U1Effbsw?pwd=dspb
單片機套裝:STM32F103C8T6開發板單片機C6T6核心板 實驗板最小系統板套件科協
目錄
- PWR簡介
- 電源框圖
- 電壓監測(了解)
- 上電復位和掉電復位
- 可編程電壓監測器
- 低功耗模式
- 模式選擇
- 睡眠模式
- 停機模式
- 待機模式
- 函數詳解
- PWR_DeInit函數
- PWR_PVDCmd函數
- PWR_PVDLevelConfig函數
- PWR_WakeUpPinCmd函數
- PWR_EnterSTOPMode函數
- PWR_EnterSTANDBYMode函數
- 標志位函數
- 實驗30 修改主頻
- 接線圖
- system_stm32f10x文件
- 主程序
- 實驗31 睡眠模式+串口發送+接收
- 接線圖
- 主程序
- 實驗32 停機模式+對射式紅外傳感器計次
- 接線圖
- 主程序
- 實驗33 待機模式+實時時鐘
- 接線圖
- 主程序
PWR簡介
- PWR(Power Control)電源控制
- PWR負責管理STM32內部的電源供電部分,可以實現可編程電壓監測器和低功耗模式的功能。
- 可編程電壓監測器(PVD)可以監控VDD電源電壓,當VDD下降到PVD閾值以下或上升到PVD閾值之上時,PVD會觸發中斷,用于執行緊急關閉任務(非本節重點)。
- 低功耗模式包括睡眠模式(Sleep)、停機模式(Stop)和待機模式(Standby),可在系統空閑時降低STM32的功耗,延長設備使用時間。
- 主頻和電流消耗大致呈正比,降低主頻也能顯著降低功耗。
電源框圖
上圖為STM32供電方案,可分為模擬部分供電(VDDA)、數字部分供電(VDD+1.8V)和后備供電。各供電區域負責的設備已列出。
VDDA供電區域主要負責模擬部分的供電,供電正極為VDDA,負極為VSSA。其中AD轉換器還有參考電壓供電引腳VREF+和VREF-,在引腳多的芯片型號會單獨引出,而在F103C8T6中已經在內部分別接到了VDDA和VSSA。
中間部分分為VDD供電區域和1.8V供電區域,VDD經電壓調節器降壓到1.8V提供給1.8V供電區域。后備供電區域中,低電壓檢測器控制開關,在VDD有電時由VDD供電,VDD沒電時由VBAT供電。
電壓監測(了解)
上電復位和掉電復位
當VDD或VDDA電壓過低時,STM32內部電路直接產生復位。電壓小于下限PDR時復位,大于上限POR時解除復位,中間有40mV的遲滯電壓防抖,復位信號Reset低電平有效。POR、PDR和復位滯后時間均可在芯片數據手冊查詢。
可編程電壓監測器
可編程電壓監測器的工作流程與上電/掉電復位類似,但閾值電壓可以手動調節,調節范圍可在芯片數據手冊查詢,一般高于上電/掉電復位電壓。PVD輸出在電壓過低時為1,電壓正常時為0,可在上升/下降沿申請外部中斷提醒程序進行適當處理。
低功耗模式
上表對三種低功耗模式進行了詳細的對比。其中第二列為進入對應模式所需的配置,第三列為進入對應模式后的喚醒方法,后三列為進入對應模式后關閉的電路(電壓調節器相當于1.8V供電區域的電源)。三種模式從上到下,關閉的電路越來越多,也越來越省電,越來越難喚醒。對于具體的省電效果,正常模式和睡眠模式電流消耗(數量級)均在10mA,停機模式為10 μ \mu μA,待機模式為1 μ \mu μA。
模式選擇
執行WFI(Wait For Interrupt)或WFE(Wait For Event)指令后,STM32進入低功耗模式。在觸發低功耗模式之前,配置寄存器進行模式選擇的方式如下圖所示,其中寄存器的每個位已由庫函數封裝好,無需自行配置。
睡眠模式
- 執行完WFI/WFE指令后,STM32進入睡眠模式,程序暫停運行,喚醒后程序從暫停的地方繼續運行。
- SLEEPONEXIT位決定STM32執行完WFI或WFE后,是立刻進入睡眠,還是等STM32從最低優先級的中斷處理程序中退出時進入睡眠。
- 在睡眠模式下,所有的I/O引腳都保持它們在運行模式時的狀態。
- WFI指令進入睡眠模式,可被任意一個NVIC響應的中斷喚醒。
- WFE指令進入睡眠模式,可被喚醒事件喚醒。
停機模式
- 執行完WFI/WFE指令后,STM32進入停機模式,程序暫停運行,喚醒后程序從暫停的地方繼續運行。
- 1.8V供電區域的所有時鐘都被停止,PLL、HSI和HSE被禁止,SRAM和寄存器內容被保留下來。
- 在停機模式下,所有的I/O引腳都保持它們在運行模式時的狀態。
- 當一個中斷或喚醒事件導致退出停機模式時,HSI被選為系統時鐘,因此喚醒后需重新啟動HSE配置主頻為72MHz。
- 當電壓調節器處于低功耗模式下,系統從停機模式退出時,會有一段額外的啟動延時。
- WFI指令進入停機模式,可被任意一個EXTI中斷喚醒。
- WFE指令進入停機模式,可被任意一個EXTI事件喚醒。
待機模式
- 執行完WFI/WFE指令后,STM32進入待機模式,喚醒后程序從頭開始運行。
- 整個1.8V供電區域被斷電,PLL、HSI和HSE也被斷電,SRAM和寄存器內容丟失,只有備份的寄存器和待機電路維持供電。
- 在待機模式下,所有的I/O引腳變為高阻態(浮空輸入)。
- WKUP引腳的上升沿、RTC鬧鐘事件的上升沿、NRST引腳上外部復位、IWDG復位退出待機模式。
函數詳解
PWR_DeInit函數
簡介:恢復缺省配置。
參數:void
PWR_PVDCmd函數
簡介:使能可編程電壓監測器。
參數:使能/失能
PWR_PVDLevelConfig函數
簡介:配置PVD閾值。
參數:閾值
PWR_PVDLevel_2V2/.../9(2.2~2.9V)
PWR_WakeUpPinCmd函數
簡介:使能WKUP引腳(PA0),配合待機模式的WKUP上升沿喚醒。
參數:使能/失能
PWR_EnterSTOPMode函數
簡介:進入停機模式。
參數一:電壓調節器狀態
PWR_Regulator_ON(開啟)
PWR_Regulator_LowPower(低功耗)
參數二:喚醒方式
PWR_STOPEntry_WFI/WFE(中斷/事件)
PWR_EnterSTANDBYMode函數
簡介:進入待機模式。
參數:void
標志位函數
PWR_GetFlagStatus函數
PWR_ClearFlag函數
參數:PWR標志位
PWR_FLAG_WU/SB/PWDO(喚醒/待機/電壓監測器輸出)
實驗30 修改主頻
接線圖
system_stm32f10x文件
system文件提供了兩個外部可調用的函數和一個外部可調用的變量:
- SystemInit函數:配置時鐘樹,復位后在啟動文件中自動調用。
- SystemCoreClock變量:主頻頻率。
- SystemCoreClockUpdate函數:更新主頻頻率。
源文件system_stm32f10x.c中如下部分可更改主頻的宏定義,解除對應的注釋即可選擇想要的系統主頻。其中else之前的部分為VL超值系列可選主頻。當前主頻為72MHz。
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
#endif
若文件為只讀狀態,可以在文件資源管理器中右鍵對應文件更改屬性,取消勾選只讀。
SystemInit()首先啟動HSI并恢復缺省配置,隨后調用SetSysClock()函數會根據不同的宏定義執行相應的SetSysClockToxx()函數完成相應的時鐘配置。
主程序
以下代碼顯示主頻,并以72MHz下的1s為周期閃爍Running字符串。
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"int main(void)
{OLED_Init();OLED_ShowString(1, 1, "SYSCLK:");OLED_ShowNum(1, 8, SystemCoreClock, 8);while(1){OLED_ShowString(2, 1, "Running");Delay_ms(500);OLED_ShowString(2, 1, " ");Delay_ms(500);}
}
此時Running以現實的1s為周期閃爍。
現通過宏定義修改主頻為36MHz:
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
#define SYSCLK_FREQ_36MHz 36000000
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
/* #define SYSCLK_FREQ_72MHz 72000000 */
#endif
此時Running閃爍周期延長一倍。
由于修改主頻會要求很多涉及精準計時的計算做好匹配工作,一般不建議采用。
實驗31 睡眠模式+串口發送+接收
接線圖
主程序
在實驗21 串口發送+接收的基礎上修改。
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"uint8_t RxData;int main(void)
{OLED_Init();Serial_Init();OLED_ShowString(1, 1, "RxDara:");while(1){if (Serial_GetRxFlag()){RxData = Serial_GetRxData();Serial_SendByte(RxData);OLED_ShowHexNum(1, 8, RxData, 2);}//監測低功耗狀態OLED_ShowString(2, 1, "Running");Delay_ms(100);OLED_ShowString(2, 1, " ");Delay_ms(100);//默認配置開啟睡眠模式__WFI();}
}
此時只有當我們向串口發送數據時,Running才會閃爍一次,并回傳一次數據。
程序運行時,第一次循環即進入睡眠狀態,程序停止在WFI指令,CPU睡眠,但各個外設例如USART仍處于工作狀態。USART外設在收到數據時產生中斷喚醒CPU,程序在暫停的地方繼續運行,先進入中斷服務函數,再回到主循環執行一輪循環進入下一次睡眠,如此循環往復。
實驗32 停機模式+對射式紅外傳感器計次
接線圖
主程序
在實驗6 對射式紅外傳感器計次的基礎上修改。
原驅動程序中博主采用了延時100ms消抖,經實測中斷處理時間過長會導致喚醒失敗,因此將中斷服務函數修改如下:
//中斷函數名稱在啟動文件startup_stm32f10x_md.s中規定
void EXTI15_10_IRQHandler(void)
{//檢查中斷掛起標志位if(EXTI_GetITStatus(EXTI_Line14) == SET){//再次判斷引腳電平if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0){CountSensor_Count ++;}EXTI_ClearITPendingBit(EXTI_Line14);}
}
主程序修改如下:
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"int main(void)
{OLED_Init();CountSensor_Init();//開啟PWR時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);OLED_ShowString(1, 1, "Count:");while(1){OLED_ShowNum(1, 7, CountSensor_Get(), 5);//監測低功耗狀態OLED_ShowString(2, 1, "Running");Delay_ms(100);OLED_ShowString(2, 1, " ");Delay_ms(100);//進入停機模式(電壓調節器開啟,中斷喚醒)PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);}
}
此時每遮擋一次對射式紅外傳感器,計數加一,Running閃爍一次。有時閃爍兩次是因為信號抖動導致遮擋和移開均產生中斷信號。
除此之外,復位后Running第一次閃爍時間短暫,后續遮擋Running閃爍變慢,這是由于前文介紹的停機模式喚醒后HSI被選為系統時鐘,只需在進入停機模式之后加上 SystemInit(); \texttt{SystemInit();} SystemInit();重新配置時鐘為HSE即可。
實驗33 待機模式+實時時鐘
接線圖
主程序
在實驗29 實時時鐘的基礎上修改。
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"int main(void)
{OLED_Init();MyRTC_Init();//開啟PWR時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//更改顯示布局//計數值OLED_ShowString(1, 1, "CNT :");//鬧鐘值OLED_ShowString(2, 1, "ALR :");//鬧鐘標志位OLED_ShowString(3, 1, "ALRF:");//設置鬧鐘值為復位10秒后//鬧鐘寄存器只寫不讀,先定義變量存儲鬧鐘值uint32_t Alarm = RTC_GetCounter() + 10;RTC_SetAlarm(Alarm);OLED_ShowNum(2, 6, Alarm, 10);while(1){OLED_ShowNum(1, 6, RTC_GetCounter(), 10);//顯示鬧鐘標志位OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1);//監測低功耗狀態OLED_ShowString(4, 1, "Running");Delay_ms(100);OLED_ShowString(4, 1, " ");Delay_ms(100);//待機前關閉所有外部耗電電路以達到省電目的OLED_Clear();PWR_EnterSTANDBYMode();}
}
待機模式喚醒后程序從頭開始運行,因此該程序現象為復位后OLED閃爍一次,十秒之后觸發鬧鐘事件再次閃爍,往后每隔十秒閃爍一次。
在while循環之前加上 PWR_WakeUpPinCmd(ENABLE); \texttt{PWR\_WakeUpPinCmd(ENABLE);} PWR_WakeUpPinCmd(ENABLE);可實現WKUP引腳(PA0)喚醒,此時每次將該引腳用跳線接到高電平即可觀察到OLED閃爍。