04尚硅谷嵌入式技術(shù)之STM32單片機(jī)(擴(kuò)展篇)1.0_第1頁(yè)
04尚硅谷嵌入式技術(shù)之STM32單片機(jī)(擴(kuò)展篇)1.0_第2頁(yè)
04尚硅谷嵌入式技術(shù)之STM32單片機(jī)(擴(kuò)展篇)1.0_第3頁(yè)
04尚硅谷嵌入式技術(shù)之STM32單片機(jī)(擴(kuò)展篇)1.0_第4頁(yè)
04尚硅谷嵌入式技術(shù)之STM32單片機(jī)(擴(kuò)展篇)1.0_第5頁(yè)
已閱讀5頁(yè),還剩47頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

尚硅谷嵌入式技術(shù)之STM32單片機(jī)(尚硅谷研究院)版本:V1.0電源控制電源控制(PWR:PowerControl)。電源對(duì)電子設(shè)備來(lái)說(shuō)非常重要,它是保證系統(tǒng)穩(wěn)定運(yùn)行的基礎(chǔ)。在保證系統(tǒng)能穩(wěn)定運(yùn)行的同時(shí),對(duì)嵌入式設(shè)備一般又有低功耗的需求。在一些應(yīng)用場(chǎng)合中,對(duì)電子設(shè)備的功耗要求非??量?,如某些傳感器信息采集設(shè)備,僅靠小型的電池提供電源,要求工作長(zhǎng)達(dá)數(shù)年之久,且期間不需要任何維護(hù);由于智慧穿戴設(shè)備的小型化要求,電池體積不能太大導(dǎo)致容量也比較小,所以也很有必要從控制功耗入手,提高設(shè)備的續(xù)行時(shí)間。鑒于低功耗的需求,STM32有專門(mén)的電源管理外設(shè)監(jiān)控電源并管理設(shè)備的運(yùn)行式,確保系統(tǒng)正常運(yùn)行,并盡量降低器件的功耗。電源框圖VDDA供電區(qū)域主要負(fù)責(zé)模擬部分的供電。為了提高轉(zhuǎn)換的精確度,ADC使用一個(gè)獨(dú)立的電源供電,過(guò)濾和屏蔽來(lái)自印刷電路板上的毛刺干擾。主要給A/D轉(zhuǎn)換器,溫度傳感器,復(fù)位模塊,PLL等供電。VDDA是正極,VSSA是負(fù)極。VREF+和VREF是A/D轉(zhuǎn)換器的參考電壓,VREF必須接到VSSA上。對(duì)引腳比較少的芯片,沒(méi)有VREF+和VREF引腳,他們?cè)谛酒瑑?nèi)部與ADC的電源(VDDA)和地(VSSA)相聯(lián)。VDD供電區(qū)域VDD是數(shù)字電的正極,VSS是數(shù)字電的負(fù)極,電壓3.3V。給I/O電路,待機(jī)電路和電壓調(diào)節(jié)器供電。1.8V低電壓區(qū)域從這里可以看出,CPU核心,內(nèi)部存儲(chǔ)器,內(nèi)置的數(shù)字外設(shè)都是工作在1.8V的電壓。1.8V的低電壓是由從電壓調(diào)節(jié)器得到的。后備供電區(qū)域VBAT引腳會(huì)接電池和其他電源,當(dāng)VDD斷電時(shí),可以保存?zhèn)浞菁拇嫫鞯膬?nèi)容和給RTC供電。VBAT腳也為RTC、LSE振蕩器和PC13至PC15供電,這保證當(dāng)主要電源被切斷時(shí)RTC能繼續(xù)工作。切換到VBAT供電由復(fù)位模塊中的掉電復(fù)位功能控制。如果應(yīng)用中沒(méi)有使用外部電池,VBAT必須連接到VDD引腳上。電壓調(diào)節(jié)器復(fù)位后,電壓調(diào)節(jié)器總是工作使能的。有3種不同的工作模式:運(yùn)轉(zhuǎn)模式:調(diào)節(jié)器以正常功耗模式提供1.8V電源(內(nèi)核,內(nèi)存和外設(shè))。停止模式:調(diào)節(jié)器以低功耗模式提供1.8V電源,用于保存寄存器和SRAM的內(nèi)容。待機(jī)模式:調(diào)節(jié)器停止供電。除了備用電路和備份域外,寄存器和SRAM的內(nèi)容全部丟失上電復(fù)位和掉電復(fù)位當(dāng)VDD/VDDA低于指定的限位電壓VPOR/VPDR時(shí),系統(tǒng)保持為復(fù)位狀態(tài),而無(wú)需外部復(fù)位電路。復(fù)位和解除復(fù)位有一個(gè)40mv的遲滯電壓。當(dāng)電壓大于VPOR時(shí)解除復(fù)位,當(dāng)電壓小于VPDR時(shí)進(jìn)入復(fù)位。設(shè)置2個(gè)閾值的作用是當(dāng)電壓在附近抖動(dòng)的時(shí)候防止頻繁的復(fù)位和解除復(fù)位??删幊屉妷簷z測(cè)器(PVD)PVD(Programmablevoltagedetector)。PVD也是監(jiān)測(cè)VDD/VDDA的供電電壓,它的閾值電壓可以編程。用戶可以利用PVD對(duì)VDD電壓與電源控制寄存器(PWR_CR)中的PLS[2:0]位進(jìn)行比較來(lái)監(jiān)控電源。當(dāng)電壓達(dá)到閾值的時(shí)候,不是產(chǎn)生復(fù)位,而是產(chǎn)生中斷??梢栽谥袛囗憫?yīng)函數(shù)內(nèi)做一些緊急處理。低功耗在系統(tǒng)或電源復(fù)位以后,微控制器處于運(yùn)行狀態(tài)。當(dāng)CPU不需繼續(xù)運(yùn)行時(shí),可以利用多種低功耗模式來(lái)節(jié)省功耗,例如等待某個(gè)外部事件時(shí)。用戶需要根據(jù)最低電源消耗、最快速啟動(dòng)時(shí)間和可用的喚醒源等條件,選定一個(gè)最佳的低功耗模式。STM32F10xxx有三種低功耗模式:睡眠模式進(jìn)入睡眠模式通過(guò)執(zhí)行WFI(WaitforInterrupt)或WFE(WaitforEvent)指令進(jìn)入睡眠狀態(tài)。根據(jù)Cortex?M3系統(tǒng)控制寄存器中的SLEEPONEXIT位的值,有兩種選項(xiàng)可用于選擇睡眠模式進(jìn)入機(jī)制:SLEEPNOW:如果SLEEPONEXIT位被清除,當(dāng)WRI或WFE被執(zhí)行時(shí),微制器立即進(jìn)入睡眠模式。SLEEPONEXIT:如果SLEEPONEXIT位被置位,系統(tǒng)從最低優(yōu)先級(jí)的中斷處理程序中退出時(shí),微控制器就立即進(jìn)入睡眠模式。注意:在睡眠模式下,所有的I/O引腳都保持它們?cè)谶\(yùn)行時(shí)的狀態(tài)。退出睡眠模式如果執(zhí)行WFI指令進(jìn)入睡眠模式,任意一個(gè)被嵌套向量中斷控制器響應(yīng)的外設(shè)中斷都能將系統(tǒng)從睡眠模式喚醒。如果執(zhí)行WFE指令進(jìn)入睡眠模式,則一旦發(fā)生喚醒事件時(shí),微處理器都將從睡眠模式退出。停機(jī)(停止)模式停止模式是在Cortex?M3的深睡眠模式基礎(chǔ)上結(jié)合了外設(shè)的時(shí)鐘控制機(jī)制,在停止模式下電壓調(diào)節(jié)器可運(yùn)行在正?;虻凸哪J?。此時(shí)在1.8V供電區(qū)域的的所有時(shí)鐘都被停止,PLL、HSI和HSERC振蕩器的功能被禁止,SRAM和寄存器內(nèi)容被保留下來(lái)。注意:在停止模式下,所有的I/O引腳都保持它們?cè)谶\(yùn)行模式時(shí)的狀態(tài)。進(jìn)入停止模式在停止模式下,通過(guò)設(shè)置電源控制寄存器(PWR_CR)的LPDS位使內(nèi)部調(diào)節(jié)器進(jìn)入低功耗模式,能夠降低更多的功耗。如果正在進(jìn)行閃存編程,直到對(duì)內(nèi)存訪問(wèn)完成,系統(tǒng)才進(jìn)入停止模式。如果正在進(jìn)行對(duì)APB的訪問(wèn),直到對(duì)APB訪問(wèn)完成,系統(tǒng)才進(jìn)入停止模式。退出停止模式待機(jī)模式待機(jī)模式可實(shí)現(xiàn)系統(tǒng)的最低功耗。該模式是在CortexM3深睡眠模式時(shí)關(guān)閉電壓調(diào)節(jié)器。整個(gè)1.8V供電區(qū)域被斷電。PLL、HSI和HSE振蕩器也被斷電。SRAM和寄存器內(nèi)容丟失。只有備份寄存器和待機(jī)電路維持供電。這么說(shuō)吧,能停的全停。注意:在待機(jī)模式下,所有的I/O引腳處于高阻態(tài),除了以下的引腳:復(fù)位引腳(始終有效)當(dāng)被設(shè)置為防侵入或校準(zhǔn)輸出時(shí)的TAMPER引腳被使能的喚醒引腳低功耗案例1:睡眠模式需求描述讓MCU進(jìn)入睡眠模式,然后通過(guò)串口發(fā)送消息來(lái)喚醒MCU退出睡眠模式。觀察LED在進(jìn)入休眠模式后是否仍然開(kāi)啟。軟件設(shè)計(jì)(寄存器)main.c#includestm32f10x.hstm32f10x.h"http://Deviceheader#include"usart.h"#include"Delay.h"voidenter_sleep_mode(void){

/*1.設(shè)置普通睡眠模式*/

SCB>SCR&=~SCB_SCR_SLEEPDEEP;

/*2.進(jìn)入睡眠模式,等待中斷喚醒*/

__WFI();}intmain(void){usart1_init();

LED_Init();

/*開(kāi)啟LED燈*/

LED_On();

Delay_s(3);

while(1)

{

printf("正常代碼執(zhí)行完畢,5s后進(jìn)入睡眠模式\r\n");

Delay_s(5);

printf("開(kāi)始睡眠\(yùn)r\n");

enter_sleep_mode();

Delay_s(1);

printf("從睡眠模式被喚醒...\r\n");

}}usart.h#ifndef__USART_H#define__USART_H#include"stm32f10x.h"#include"stdio.h"/*初始化串口1*/voidusart1_init(void);/*

通過(guò)串口1發(fā)送數(shù)據(jù):

參數(shù)1:要發(fā)送的數(shù)據(jù)

參數(shù)2:要發(fā)送的數(shù)據(jù)的長(zhǎng)度

*/voidsend_string(uint8_t*str,uint32_tlen);/*

從串口1接收一個(gè)字符

返回值:接收到的字符*/uint8_treceive_char(void);/*

向串口1發(fā)送一個(gè)字符*/voidsend_char(uint8_tc);intfputc(intch,FILE*f);#endifusart.c#include"usart.h"/*初始化串口1*/voidusart1_init(void){

/*1開(kāi)啟時(shí)鐘

1<<14=0x4000*/

/*1.1開(kāi)啟usart1時(shí)鐘*/

RCC>APB2ENR|=RCC_APB2ENR_USART1EN;

/*1.2開(kāi)啟GPIOA時(shí)鐘*/

RCC>APB2ENR|=RCC_APB2ENR_IOPAEN;

/*2配置引腳:PA9(Tx)和PA10(Rx)*/

/*2.1PA9配置為推挽輸出mode=11cnf=10*/

GPIOA>CRH|=(GPIO_CRH_MODE9|GPIO_CRH_CNF9_1);

/*2.2PA10配置為浮空輸入*/

GPIOA>CRH|=GPIO_CRH_CNF10_0;

/*3配置波特率為:*/

USART1>BRR=0x271;

/*4使能usart1的發(fā)送和接收*/

USART1>CR1|=(USART_CR1_TE|USART_CR1_RE);

/*5使能usart1接收中斷

*/

USART1>CR1|=USART_CR1_RXNEIE;

/*6.設(shè)置NVIC*/

NVIC_SetPriorityGrouping(4);

NVIC_SetPriority(USART1_IRQn,2);

NVIC_EnableIRQ(USART1_IRQn);

/*7使能usart1*/

USART1>CR1|=USART_CR1_UE;}/*

通過(guò)串口1發(fā)送數(shù)據(jù):

參數(shù)1:要發(fā)送的數(shù)據(jù)

參數(shù)2:要發(fā)送的數(shù)據(jù)的長(zhǎng)度

*/voidsend_string(uint8_t*str,uint32_tlen){

uint32_ti;

for(i=0;i<len;i++)

{

send_char(str[i]);

}}/*

從串口1接收一個(gè)字符

返回值:接收到的字符*/uint8_treceive_char(void){

/*等待接收緩沖區(qū)非空*/

while((USART1>SR&USART_SR_RXNE)==0)

{

}

returnUSART1>DR;}/*

向串口1發(fā)送一個(gè)字符*/voidsend_char(uint8_tc){

/*等待發(fā)送緩沖區(qū)為空。SR_TXE為1表示已經(jīng)移到移位寄存器,0表示還沒(méi)有*/

while((USART1>SR&USART_SR_TXE)==0)

{

}

/*把要發(fā)送的數(shù)據(jù)寫(xiě)入到數(shù)據(jù)寄存器*/

USART1>DR=c;}/*中斷服務(wù)函數(shù)*/voidUSART1_IRQHandler(void){

if(USART1>SR&USART_SR_RXNE)

{

/*獲取接收到的數(shù)據(jù):只取低8位*/

uint8_tc=USART1>DR&0xff;

/*回顯給上位機(jī)*/

send_char(c);

}

/*可以不用清除中斷標(biāo)志位,因?yàn)樽x取DR寄存器值的時(shí)候會(huì)自動(dòng)清零*/

//USART1>SR&=~USART_SR_RXNE;}/*

printf函數(shù)底層會(huì)調(diào)用fputc

重定向c庫(kù)函數(shù)printf到串口,重定向后可使用printf函數(shù)打印數(shù)據(jù)*/intfputc(intch,FILE*f){

send_char((uint8_t)ch);

return(ch);}LED.h#ifndef__LED_H#define__LED_H#include"stm32f10x.h"/*對(duì)led進(jìn)行初始化*/voidLED_Init(void);/*點(diǎn)亮led*/voidLED_On(void);/*熄滅led*/voidLED_Off(void);/*翻轉(zhuǎn)led的狀態(tài)*/voidLED_Toggle(void);#endifLED.c#include"LED.h"/*對(duì)led進(jìn)行初始化*/voidLED_Init(void){

/*1.使能GPIOA時(shí)鐘*/

RCC>APB2ENR|=RCC_APB2ENR_IOPAEN;//1<<2

/*2.設(shè)置PA0為通用推挽輸出,速度50MHzMODE=11CNF=00*/

GPIOA>CRL|=GPIO_CRL_MODE1;

GPIOA>CRL&=~GPIO_CRL_CNF1;}/*點(diǎn)亮led*/voidLED_On(void){

GPIOA>ODR&=~GPIO_ODR_ODR1;}/*熄滅led*/voidLED_Off(void){

GPIOA>ODR|=GPIO_ODR_ODR1;}/*翻轉(zhuǎn)led的狀態(tài)*/voidLED_Toggle(void){

if(GPIOA>IDR&GPIO_IDR_IDR1)

{

LED_On();

}

else

{

LED_Off();

}}測(cè)試軟件設(shè)計(jì)(HAL庫(kù))STM32CubeMX設(shè)置添加其他代碼uint8_tbuff[100];//串口收到數(shù)據(jù)的時(shí)候回調(diào)這個(gè)函數(shù)voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){

if(huart>Instance==USART1)

{

printf("%c",buff[0]);

HAL_UART_Receive_IT(&huart1,buff,1);//繼續(xù)接收

}}intmain(void){

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

MX_USART1_UART_Init();

/*USERCODEBEGIN2*/

printf("尚硅谷低功耗實(shí)驗(yàn):睡眠模式\r\n");

/*點(diǎn)亮LED*/

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);

//利用串口中斷接收消息

HAL_UART_Receive_IT(&huart1,buff,1);

while(1)

{

HAL_Delay(3000);

printf("正常代碼執(zhí)行完畢,5s后進(jìn)入睡眠模式\r\n");

HAL_Delay(5000);

printf("開(kāi)始睡眠\(yùn)r\n");

/*1.暫停滴答定時(shí)器,防止被自動(dòng)喚醒*/

HAL_SuspendTick();

/*2.進(jìn)入睡眠模式:參數(shù)1:要不要關(guān)閉電壓調(diào)節(jié)器(睡眠模式無(wú)效)參數(shù)2:進(jìn)入指令wfiorwfe*/

HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);

/*3.被喚醒后要恢復(fù)滴答時(shí)鐘*/

HAL_ResumeTick();

HAL_Delay(1);

printf("從睡眠模式被喚醒...\r\n");

}}低功耗案例2:停機(jī)模式需求描述讓MCU進(jìn)入停機(jī)模式。停機(jī)模式需要外部中斷才能喚醒,所以要開(kāi)啟外部中斷。(串口不是外部中斷)硬件電路設(shè)計(jì)我們按下按鍵來(lái)產(chǎn)生外部中斷。軟件設(shè)計(jì)(寄存器)復(fù)制實(shí)驗(yàn)1項(xiàng)目,新增外部中斷相關(guān)配置。使用寄存器實(shí)現(xiàn)。main.c#include"stm32f10x.h"http://Deviceheader#include"usart.h"#include"Delay.h"#include"key.h"#include"LED.h"voidenter_stop_mode(void){

/*1.開(kāi)啟PWR時(shí)鐘*/

RCC>APB1ENR|=RCC_APB1ENR_PWREN;

/*2.清除電源控制器中的PDDS位進(jìn)入深睡眠時(shí),進(jìn)入停機(jī)模式*/

PWR>CR&=~PWR_CR_PDDS;

/*3.設(shè)置電壓調(diào)節(jié)器的模式0:電壓調(diào)節(jié)器開(kāi)啟1:電壓調(diào)節(jié)器處于低功耗模式*/

PWR>CR|=PWR_CR_LPDS;

/*4.

系統(tǒng)控制寄存器的深度睡眠模式*/

SCB>SCR|=SCB_SCR_SLEEPDEEP;

/*5.進(jìn)入睡眠模式,等待被中斷喚醒*/

__WFI();}/**

*@description:從stop模式恢復(fù)后,重新設(shè)置系統(tǒng)時(shí)鐘

*@return{*}

*/voidconfig_system_clock(void){

/*1.使能HSE*/

RCC>CR|=RCC_CR_HSEON;

/*2.由于時(shí)鐘啟用需要一定時(shí)間,需要等待HSE準(zhǔn)備就緒*/

while(!(RCC>CR&RCC_CR_HSERDY))

;

/*3.使能PLL*/

RCC>CR|=RCC_CR_PLLON;

/*4.等待PLL準(zhǔn)備就緒*/

while(!(RCC>CR&RCC_CR_PLLRDY))

;

/*

5.選擇PLL作為系統(tǒng)時(shí)鐘源CFGR_SW位控制

00:HSI作為系統(tǒng)時(shí)鐘;

01:HSE作為系統(tǒng)時(shí)鐘;

10:PLL輸出作為系統(tǒng)時(shí)鐘;

11:不可用。

*/

RCC>CFGR|=RCC_CFGR_SW_1;

RCC>CFGR&=~RCC_CFGR_SW_0;

/*6.等待PLL被選為系統(tǒng)時(shí)鐘源*/

while(!(RCC>CFGR&RCC_CFGR_SWS_PLL))

;}/**

*@description:獲取各個(gè)時(shí)鐘頻率

*/voidget_clock_freq(uint32_t*sys_clock,

uint32_t*AHB_clock,

uint32_t*APB1_clock,

uint32_t*APB2_clock){

/*1.獲取時(shí)鐘源RCC_CFGR_SWS用來(lái)獲取當(dāng)前時(shí)鐘源*/

uint32_tsystem_clock_source=RCC>CFGR&RCC_CFGR_SWS;

/*2.根據(jù)時(shí)鐘源獲取主時(shí)鐘頻率*/

if(system_clock_source==RCC_CFGR_SWS_HSE)

{

*sys_clock=HSE_VALUE;

}

elseif(system_clock_source==RCC_CFGR_SWS_HSI)

{

*sys_clock=HSI_VALUE;

}

elseif(system_clock_source==RCC_CFGR_SWS_PLL)

{

/*倍頻系數(shù)*/

uint32_tmull=((RCC>CFGR&RCC_CFGR_PLLMULL)>>18)+2;

*sys_clock=HSE_VALUE*mull;/*默認(rèn)情況PLL時(shí)用HSE的9倍頻*/

}

/*3.獲取AHB總線時(shí)鐘頻率默認(rèn)情況AHB等于系統(tǒng)頻率*/

uint32_thpre=(RCC>CFGR&RCC_CFGR_HPRE)>>4;

if(hpre&0x8)//有分頻

{

*AHB_clock=*sys_clock>>((hpre&0x7)+1);

}

else

{

//沒(méi)有分頻

*AHB_clock=*sys_clock;

}

/*4.APB1的頻率默認(rèn)是系統(tǒng)頻率2分頻*/

uint32_tppre1=(RCC>CFGR&RCC_CFGR_PPRE1)>>8;

if(ppre1&0x4)//有分頻

{

*APB1_clock=*AHB_clock>>((ppre1&0x3)+1);

}

else

{

//沒(méi)有分頻

*APB1_clock=*AHB_clock;

}

/*4.APB2的頻率默認(rèn)是系統(tǒng)頻率*/

uint32_tppre2=(RCC>CFGR&RCC_CFGR_PPRE2)>>11;

if(ppre2&0x4)//有分頻

{

*APB2_clock=*AHB_clock>>((ppre2&0x3)+1);

}

else

{

//沒(méi)有分頻

*APB2_clock=*AHB_clock;

}}intmain(void){

usart1_init();

key_init();

LED_Init();

LED_On();

Delay_s(1);

while(1)

{

printf("正常代碼執(zhí)行完畢,5s后進(jìn)入停機(jī)模式\r\n");

Delay_s(5);

printf("開(kāi)始進(jìn)入停機(jī)模式,按下按鍵喚醒\r\n");

Delay_ms(1);

enter_stop_mode();

/*獲取喚醒后的時(shí)鐘頻率*/

uint32_tpre_system_clock,pre_AHB_clock,pre_APB1_clock,pre_APB2_clock;

get_clock_freq(&pre_system_clock,&pre_AHB_clock,&pre_APB1_clock,&pre_APB2_clock);

/*

從停機(jī)模式恢復(fù)后,會(huì)啟用HSI(高速內(nèi)部時(shí)鐘8M)作為系統(tǒng)時(shí)鐘,與我們正常使用的有差別的。

所以需要恢復(fù)到HSE(高速外部時(shí)鐘)

*/

config_system_clock();

/*獲取重置后的時(shí)鐘頻率*/

uint32_tpost_system_clock,post_AHB_clock,post_APB1_clock,post_APB2_clock;

get_clock_freq(&post_system_clock,&post_AHB_clock,&post_APB1_clock,&post_APB2_clock);

printf("從停機(jī)模式被喚醒...\r\n");

printf("重置前的時(shí)鐘頻率:pre_system_clock=%d,pre_AHB_clock=%d,pre_APB1_clock=%d,pre_APB2_clock=%d\r\n",

pre_system_clock,

pre_AHB_clock,

pre_APB1_clock,

pre_APB2_clock);

printf("重置后的時(shí)鐘頻率:post_system_clock=%d,post_AHB_clock=%d,post_APB1_clock=%d,post_APB2_clock=%d\r\n",

post_system_clock,

post_AHB_clock,

post_APB1_clock,

post_APB2_clock);

}}key.h#ifndef__KEY_H#define__KEY_H/*初始化按鍵:外部中斷*/voidkey_init(void);#endifkey.c#include"key.h"#include"stm32f10x.h"#include"usart.h"/*初始化按鍵:外部中斷*/voidkey_init(void){

/*1開(kāi)啟時(shí)鐘(EXTI和NVIC時(shí)鐘始終開(kāi)啟,無(wú)需手動(dòng)開(kāi)啟)*/

/*1.1開(kāi)啟GPIOF時(shí)鐘(PF10)*/

RCC>APB2ENR|=RCC_APB2ENR_IOPFEN;

/*1.2開(kāi)啟AFIO時(shí)鐘*/

RCC>APB2ENR|=RCC_APB2ENR_AFIOEN;

/*2配置PF10

設(shè)置為輸入下拉(cnf:10mode:00)。另外還需要把ODR寄存器對(duì)應(yīng)的位設(shè)為0*/

GPIOF>CRH&=~GPIO_CRH_MODE10;

GPIOF>CRH|=GPIO_CRH_CNF10_1;

GPIOF>CRH&=~GPIO_CRH_CNF10_0;

GPIOF>ODR&=~GPIO_ODR_ODR10;

/*3

配置AFIO,用于引腳復(fù)用為外部中斷

PF10*/

AFIO>EXTICR[2]|=AFIO_EXTICR3_EXTI10_PF;

/*4.配置EXTI*/

/*4.1PF10的上升沿觸發(fā)中斷

*/

EXTI>RTSR|=EXTI_RTSR_TR10;

/*4.2開(kāi)啟EXTI10線*/

EXTI>IMR|=EXTI_IMR_MR10;

/*5.配置NVIC*/

/*5.1配置中斷優(yōu)先級(jí)組*/

NVIC_SetPriorityGrouping(4);

/*5.2配置中斷優(yōu)先級(jí)*/

NVIC_SetPriority(EXTI15_10_IRQn,2);

/*5.3使能中斷線1510中斷*/

NVIC_EnableIRQ(EXTI15_10_IRQn);}/*中斷服務(wù)函數(shù)*/voidEXTI15_10_IRQHandler(void){

/*清除中斷標(biāo)志*/

EXTI>PR|=EXTI_PR_PR10;}測(cè)試軟件設(shè)計(jì)(HAL庫(kù))復(fù)制實(shí)驗(yàn)2,新增外部中斷相關(guān)配置。STM32CubeMX的配置添加其他代碼intmain(void){

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

MX_USART1_UART_Init();

/*USERCODEBEGIN2*/

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);

printf("尚硅谷低功耗實(shí)驗(yàn):停機(jī)模式HAL庫(kù)\r\n");

while(1)

{

printf("正常任務(wù)執(zhí)行完畢,5s后進(jìn)入停止模式...\r\n");

HAL_Delay(5000);

printf("開(kāi)始進(jìn)入停止模式...\r\n");

//1.暫停滴答定時(shí)器(防止退出停止模式)

HAL_SuspendTick();

//2.進(jìn)入停止模式:

//參數(shù)1:要不要關(guān)電壓調(diào)節(jié)器

//參數(shù)2:進(jìn)入指令:wfi還是wfe

HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON,PWR_STOPENTRY_WFI);

//3.重新配置系統(tǒng)時(shí)鐘(如果不配置時(shí)鐘,串口打印不正常,因?yàn)闀r(shí)鐘頻率變化了,波特率計(jì)算也不對(duì)了)

SystemClock_Config();

//4.恢復(fù)滴答定時(shí)器

HAL_ResumeTick();

printf("系統(tǒng)被喚醒...\r\n");

}}低功耗案例3:待機(jī)模式需求描述寄存器操作進(jìn)入待機(jī)模式。待機(jī)模式的喚醒方式比較有限。我們這次使用WKUP引腳的上升沿喚醒。PA0就是WKUP引腳。當(dāng)然PA0仍然需要工作在下拉輸入模式,只有這樣當(dāng)按鍵按下的時(shí)候才會(huì)有一個(gè)上升沿。由于我們電路中PA0已經(jīng)連接了LED1,所以要產(chǎn)生上升沿需要先用杜邦線連接GND,然后再連接3.3V電源即可。另外也可以使用NRST引腳實(shí)現(xiàn)喚醒。軟件設(shè)計(jì)(寄存器)Copy實(shí)驗(yàn)3,進(jìn)行修改。#include"stm32f10x.h"http://Deviceheader#include"usart.h"#include"Delay.h"#include"led.h"/**

*@description:進(jìn)入待機(jī)模式

*/voidenter_standby_mode(void){

/*1.使能PA0的喚醒功能*/

PWR>CSR|=PWR_CSR_EWUP;/*引腳會(huì)被強(qiáng)制設(shè)置下拉輸入*/

/*3.CPU進(jìn)入深睡眠時(shí)進(jìn)入待機(jī)模式。*/

PWR>CR|=PWR_CR_PDDS;

/*4.

系統(tǒng)控制寄存器的深度睡眠模式*/

SCB>SCR|=SCB_SCR_SLEEPDEEP;

/*5.進(jìn)入待機(jī)模式,等待喚醒*/

__WFI();}intmain(void){

led_init();

led_on();

usart1_init();

/*1.開(kāi)啟PWR時(shí)鐘

*/

RCC>APB1ENR|=RCC_APB1ENR_PWREN;

/*檢測(cè)復(fù)位來(lái)源:按下reset和從待機(jī)恢復(fù)都會(huì)重新執(zhí)行main函數(shù)*/

/*PWR_CSR_SBF是待機(jī)標(biāo)志位:1表示曾經(jīng)待機(jī)過(guò)*/

if(PWR>CSR&PWR_CSR_SBF)

{

printf("待機(jī)喚醒復(fù)位\r\n");

/*1.清除待機(jī)標(biāo)志位,然后才可以進(jìn)入待機(jī),通過(guò)CR_CSBF寫(xiě)1清除*/

PWR>CR|=PWR_CR_CSBF;

/*2.清除喚醒標(biāo)記,通過(guò)CR_CWUF*/

PWR>CR|=PWR_CR_CWUF;

}

else

{

printf("非待機(jī)喚醒復(fù)位\r\n");

}

while(1)

{

printf("正常代碼執(zhí)行完畢,3s后進(jìn)入待機(jī)模式\r\n");

Delay_s(3);

printf("開(kāi)始進(jìn)入待機(jī)模式,所有燈光將關(guān)閉,按下按鍵喚醒\r\n");

Delay_ms(1);

enter_standby_mode();

}}注意:待機(jī)期間是不能程序的,需要喚醒之后再燒錄。所以務(wù)必注意一定要加延時(shí),在這期間燒錄程序,否則一啟動(dòng)就進(jìn)入待機(jī)模式,就沒(méi)有辦法燒錄程序了。軟件設(shè)計(jì)(HAL庫(kù))STM32CubeMX配置添加其他代碼intmain(void){

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

MX_USART1_UART_Init();

//檢測(cè)復(fù)位來(lái)源。當(dāng)從待機(jī)恢復(fù)的時(shí)候,會(huì)重新開(kāi)始執(zhí)行main函數(shù)

if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB)==SET)//從待機(jī)恢復(fù)

{

//清除待機(jī)標(biāo)志位

__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);

printf("\r\n待機(jī)喚醒復(fù)位\r\n");

}

else

{

printf("\r\n非待機(jī)喚醒復(fù)位\r\n");

}

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);

while(1)

{

printf("正常代碼執(zhí)行完畢,5s后進(jìn)入待機(jī)模式...\r\n");

HAL_Delay(5000);

//1.清除喚醒標(biāo)記位

__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

//2.使能PA0的待機(jī)喚醒功能

HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);

//3.停止滴答定時(shí)器

HAL_SuspendTick();

//4.進(jìn)入待機(jī)模式

HAL_PWR_EnterSTANDBYMode();

}}RTC(實(shí)時(shí)時(shí)鐘)RTC簡(jiǎn)介RTC(RealTimeClock,實(shí)時(shí)時(shí)鐘)。是一個(gè)掉電后仍然可以繼續(xù)運(yùn)行的獨(dú)立定時(shí)器。RTC模塊擁有一個(gè)連續(xù)計(jì)數(shù)的計(jì)數(shù)器,在相應(yīng)的軟件配置下,可以提供時(shí)鐘日歷的功能。修改計(jì)數(shù)器的值可以重新設(shè)置當(dāng)前時(shí)間和日期RTC還包含用于管理低功耗模式的自動(dòng)喚醒單元。RTC實(shí)質(zhì)是一個(gè)掉電后還繼續(xù)運(yùn)行的定時(shí)器,從定時(shí)器的角度來(lái)看,相對(duì)于通用定時(shí)器TIM外設(shè),它的功能十分簡(jiǎn)單,只有計(jì)時(shí)功能(也可以觸發(fā)中斷)。但其高級(jí)指出也就在于掉電之后還可以正常運(yùn)行。掉電能運(yùn)行,不是說(shuō)它不需要電能,而是它有一套專門(mén)的供電系統(tǒng)。掉電繼續(xù)運(yùn)行這里所說(shuō)的掉電是指的VDD掉電。VDD掉電后,由VBAT負(fù)責(zé)給后備區(qū)域供電。VBAT由電路中的紐扣電池來(lái)供電,一般的電路設(shè)計(jì)中都會(huì)加上備用電池。只要這個(gè)電池有電,則后備區(qū)域就一直會(huì)有電供應(yīng)。RTC功能框圖RTC的時(shí)鐘源RTC有3路時(shí)鐘來(lái)源:HSE(8MHz)/128,LSE(32.768KHz),LSI(40KHz)。其中,如果使用HSE或LSI的話,當(dāng)主電源掉電的話,這兩個(gè)始終都會(huì)受到影響,RTC就無(wú)法正常工作。所以,一般的通用做法是使用LSE。2個(gè)原因:一是LSE不受主電源掉電的影響,二是它的頻率是我們都是選擇32768Hz,正好是2^15,分頻容易實(shí)現(xiàn)。APB1接口用來(lái)和APB1總線相連。此單元還包含一組16位寄存器,可通過(guò)APB1總線對(duì)其進(jìn)行讀寫(xiě)操作。APB1接口由APB1總線時(shí)鐘驅(qū)動(dòng),用來(lái)與APB1總線連接。通過(guò)APB1接口可以訪問(wèn)RTC的相關(guān)寄存器(預(yù)分頻值,計(jì)數(shù)器值,鬧鐘值)。RTC預(yù)分頻模塊這個(gè)模塊是RTC預(yù)分頻模塊,屬于后備區(qū)域,VDD掉電后,可以在VBAT下繼續(xù)運(yùn)行。包含了一個(gè)20位的可編程分頻器(RTC預(yù)分頻器)。它可編程產(chǎn)生1秒的RTC時(shí)間基準(zhǔn)TR_CLK。如果在RTC_CR寄存器中設(shè)置了相應(yīng)的允許位,則在每個(gè)TR_CLK周期中RTC產(chǎn)生一個(gè)中斷(秒中斷)。32位可編程計(jì)數(shù)器這個(gè)模塊也屬于后備區(qū)域,是一個(gè)32位的可編程計(jì)數(shù)器,可被初始化為當(dāng)前的系統(tǒng)時(shí)間。一個(gè)32位的時(shí)鐘計(jì)數(shù)器,按秒鐘計(jì)算,可以記錄4294967296秒,約合136年左右,作為一般應(yīng)用,這已經(jīng)是足夠了的。中斷從圖中可以看到一共有3個(gè)中斷:秒中斷:每計(jì)時(shí)1s產(chǎn)生一次中斷。計(jì)數(shù)器溢出中斷。136年才會(huì)產(chǎn)生溢出,一般用不上。RTC鬧鐘中斷。RCT_CN和RTC_ALR會(huì)比較相等,如果相等表示鬧鐘時(shí)間到,會(huì)產(chǎn)生鬧鐘中斷。備份寄存器(BKP)備份寄存器介紹備份寄存器簡(jiǎn)介BKP(backupregister,備份寄存器)。備份寄存器是42個(gè)16位的寄存器,可用來(lái)存儲(chǔ)84個(gè)字節(jié)的用戶應(yīng)用程序數(shù)據(jù)。他們處在備份域里,當(dāng)VDD電源被切斷,他們?nèi)匀挥蒝BAT維持供電。當(dāng)系統(tǒng)在待機(jī)模式下被喚醒,或系統(tǒng)復(fù)位或電源復(fù)位時(shí),他們也不會(huì)被復(fù)位。此外,BKP控制寄存器用來(lái)管理侵入檢測(cè)和RTC校準(zhǔn)功能。復(fù)位后,對(duì)備份寄存器和RTC的訪問(wèn)被禁止,并且備份域被保護(hù)以防止可能存在的意外的寫(xiě)操作。執(zhí)行以下操作可以使能對(duì)備份寄存器和RTC的訪問(wèn):通過(guò)設(shè)置寄存器RCC_APB1ENR的PWREN和BKPEN位來(lái)打開(kāi)電源和后備接口的時(shí)鐘電源控制寄存器(PWR_CR)的DBP位來(lái)使能對(duì)后備寄存器和RTC的訪問(wèn)侵入檢測(cè)功能當(dāng)TAMPER(PC13)引腳上的信號(hào)從0變成1或者從1變成0(取決于備份控制寄存器BKP_CR的TPAL位),會(huì)產(chǎn)生一個(gè)侵入檢測(cè)事件。侵入檢測(cè)事件將所有數(shù)據(jù)備份寄存器內(nèi)容清除。設(shè)置BKP_CSR寄存器的TPIE位為"1",當(dāng)檢測(cè)到侵入事件時(shí)就會(huì)產(chǎn)生一個(gè)中斷。RTC校準(zhǔn)為方便測(cè)量,RTC時(shí)鐘可以經(jīng)64分頻輸出到侵入檢測(cè)引腳TAMPER上。通過(guò)設(shè)置RTC校驗(yàn)寄存器(BKP_RTCCR)的CCO位來(lái)開(kāi)啟這一功能。備份數(shù)據(jù)寄存器用來(lái)存儲(chǔ)用戶數(shù)據(jù)。一共42個(gè)16位寄存器(大容量和互聯(lián)型是42個(gè),中小容量產(chǎn)品是20個(gè)),可以存84個(gè)字節(jié)的數(shù)據(jù)。其他寄存器RTC時(shí)鐘校準(zhǔn)寄存器,備份控制寄存器,備份控制/狀態(tài)寄存器。參考手冊(cè)49頁(yè)。備份寄存器案例:讀寫(xiě)備份寄存器需求描述基于寄存器操作。程序啟動(dòng)后向后備寄存器寫(xiě)入值,按下按鍵1從備份寄存器讀出數(shù)據(jù),驗(yàn)證是否一致。Reset設(shè)備,然后按下按鍵1是否能讀到數(shù)據(jù),驗(yàn)證掉電不丟失。關(guān)機(jī),再啟動(dòng),然后按下按鍵1是否能讀到數(shù)據(jù),驗(yàn)證掉電不丟失。硬件電路設(shè)計(jì)軟件設(shè)計(jì)(寄存器)main.c#include"stm32f10x.h"http://Deviceheader#include"usart.h"#include"Delay.h"#include"key.h"#include"bkp.h"intmain(void){

usart1_init();

printf("尚硅谷后備寄存器實(shí)驗(yàn)基于寄存器實(shí)現(xiàn)...\r\n");

key_init();

/*初始化RTC和后備寄存器*/

rtc_bkp_init();

/*向后備寄存器寫(xiě)入值,只寫(xiě)一次,注釋掉。然后測(cè)試rest和掉電重啟數(shù)據(jù)是否丟失*/

//BKP>DR1=9999;

while(1)

{

}}/*按鍵按下時(shí)的中斷服務(wù)函數(shù)*/voidEXTI15_10_IRQHandler(void){

printf("DK1=%d\r\n",BKP>DR1);

/*清除中斷標(biāo)志*/

EXTI>PR|=EXTI_PR_PR10;}bkp.h#ifndef__BKP_H#define__BKP_H#include"stm32f10x.h"voidrtc_bkp_init(void);#endifbkp.c#include"bkp.h"/**

*@description:初始化rtc和后備寄存器

*@return{*}

*/voidrtc_bkp_init(void){

/*1.使能PWR時(shí)鐘*/

RCC>APB1ENR|=RCC_APB1ENR_PWREN;

/*

2.允許先RTC寄存器和后備寄存器寫(xiě)入數(shù)據(jù)PWR_CR_DBP位

0:禁止寫(xiě)入RTC和后備寄存器

1:允許寫(xiě)入RTC和后備寄存器

*/

PWR>CR|=PWR_CR_DBP;

/*3.使能BKP時(shí)鐘*/

RCC>APB1ENR|=RCC_APB1ENR_BKPEN;

/*4.如果已經(jīng)向DR1寄存器寫(xiě)入過(guò)值,證明備份域已經(jīng)復(fù)位過(guò)??梢灾苯臃祷亓?/

if(BKP>DR1)return;

/*5.復(fù)位備份域*/

RCC>BDCR|=RCC_BDCR_BDRST;

/*6.解除備份域復(fù)位*/

RCC>BDCR&=~RCC_BDCR_BDRST;

/*7.使能RTC時(shí)鐘*/

RCC>BDCR|=RCC_BDCR_RTCEN;

/*8.使能LSE*/

RCC>BDCR|=RCC_BDCR_LSEON;

/*

9.使用LSE(外部低俗時(shí)鐘)作為RTC時(shí)鐘

00:無(wú)時(shí)鐘;

01:LSE振蕩器作為RTC時(shí)鐘;

10:LSI振蕩器作為RTC時(shí)鐘;

11:HSE振蕩器在128分頻后作為RTC時(shí)鐘。

如果要修改時(shí)鐘源:

1.先復(fù)位后備域(所有數(shù)據(jù)丟失)復(fù)位后變?yōu)?0

2.這兩位必須同時(shí)更改.如果先改高位,則低位就無(wú)法更改了

一旦RTC時(shí)鐘選定,在備份域復(fù)位之前就不能再更改了。

*/

RCC>BDCR|=(0x1<<8);}軟件設(shè)計(jì)(HAL庫(kù))STM32CubeMX配置添加其他代碼//按鍵按下下時(shí)的中斷處理函數(shù)voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){

if(GPIO_Pin==GPIO_PIN_10)

{

uint32_tdata=HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1);

printf("從后備寄存器讀取的值是:%d\r\n",data);

}}intmain(void){

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

MX_RTC_Init();

MX_USART1_UART_Init();

//向后備寄存器寫(xiě)入值,為了測(cè)試,只寫(xiě)一次,第一次寫(xiě)入之后,注釋掉此代碼

//HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,1314);

while(1)

{

}}RTC實(shí)驗(yàn)RTC案例1:使用鬧鐘喚醒待機(jī)模式需求描述執(zhí)行完畢正常代碼之后,讓MCU進(jìn)入待機(jī)模式,設(shè)置鬧鐘,自動(dòng)讓MCU從待機(jī)模式中被喚醒。可以用led點(diǎn)亮熄滅顯示是否喚醒。應(yīng)用場(chǎng)景:比如設(shè)計(jì)一個(gè)野外溫度自動(dòng)采集的設(shè)備,規(guī)定每小時(shí)采集一次溫度,就可以定義一個(gè)1小時(shí)的鬧鐘,定時(shí)喚醒,采集溫度,采集完進(jìn)入待機(jī)模式,可以大大降低設(shè)備功耗。軟件設(shè)計(jì)(寄存器)main.c#include"stm32f10x.h"http://Deviceheader#include"usart.h"#include"Delay.h"#include"led.h"#include"alarm.h"/**

*@description:進(jìn)入待機(jī)模式

*/voidenter_standby_mode(void){

/*1.使能PA0的喚醒功能*/

//PWR>CSR|=PWR_CSR_EWUP;/*引腳會(huì)被強(qiáng)制設(shè)置下拉輸入*/

/*3.CPU進(jìn)入深睡眠時(shí)進(jìn)入待機(jī)模式。*/

PWR>CR|=PWR_CR_PDDS;

/*4.

系統(tǒng)控制寄存器的深度睡眠模式*/

SCB>SCR|=SCB_SCR_SLEEPDEEP;

/*5.進(jìn)入待機(jī)模式,等待喚醒*/

__WFI();}intmain(void){

LED_Init();

LED_On();

usart1_init();

Alarm_Init();

/*1.開(kāi)啟PWR時(shí)鐘

*/

RCC>APB1ENR|=RCC_APB1ENR_PWREN;

/*檢測(cè)復(fù)位來(lái)源:按下reset和從待機(jī)恢復(fù)都會(huì)重新執(zhí)行main函數(shù)*/

/*PWR_CSR_SBF是待機(jī)標(biāo)志位:1表示曾經(jīng)待機(jī)過(guò)*/

if(PWR>CSR&PWR_CSR_SBF)

{

/*1.清除待機(jī)標(biāo)志位,然后才可以進(jìn)入待機(jī),通過(guò)CR_CSBF寫(xiě)1清除*/

PWR>CR|=PWR_CR_CSBF;

/*2.清除喚醒標(biāo)記,通過(guò)CR_CWUF*/

PWR>CR|=PWR_CR_CWUF;

}

while(1)

{

printf("溫度測(cè)試完畢,5s后進(jìn)入待機(jī)....\r\n");

Delay_s(5);

printf("開(kāi)始進(jìn)入待機(jī)模式,所有燈光將關(guān)閉...\r\n");

/*設(shè)置20s后執(zhí)行的鬧鐘*/

Alarm_SetXSecond(20);

Delay_ms(1);

enter_standby_mode();

}}alarm.h#ifndef__ALARM_H#define__ALARM_H#include"stm32f10x.h"voidAlarm_Init(void);voidAlarm_SetXSecond(uint32_tx);#endifalarm.c#include"alarm.h"/**

*@description:鬧鐘的初始化

*@return{*}

*/voidAlarm_Init(void){

/*1.使能PWR時(shí)鐘*/

RCC>APB1ENR|=RCC_APB1ENR_PWREN;

/*2.使能BKP時(shí)鐘*/

RCC>APB1ENR|=RCC_APB1ENR_BKPEN;

/*

3.允許向RTC寄存器和后備寄存器寫(xiě)入數(shù)據(jù)PWR_CR_DBP位

0:禁止寫(xiě)入RTC和后備寄存器

1:允許寫(xiě)入RTC和后備寄存器

*/

PWR>CR|=PWR_CR_DBP;

/*4.使能RTC時(shí)鐘*/

RCC>BDCR|=RCC_BDCR_RTCEN;

/*5.使能LSE外部低速時(shí)鐘*/

RCC>BDCR|=RCC_BDCR_LSEON;

/*6.等待RTC進(jìn)入初始化狀態(tài)(上次的寫(xiě)操作已經(jīng)完成)*/

while(!(RTC>CRL&RTC_CRL_RTOFF))

;

/*7.進(jìn)入配置模式.

允許配置RTC的計(jì)數(shù)器,預(yù)分頻系數(shù)和鬧鐘寄存器。*/

RTC>CRL|=RTC_CRL_CNF;

/*

8.設(shè)置RTC預(yù)分頻系數(shù):我們產(chǎn)生一個(gè)1s的信號(hào),讓計(jì)數(shù)器每s加1

時(shí)鐘頻率是32768Hz,所以預(yù)分頻系數(shù)應(yīng)該是=0x7FFF

*/

RTC>PRLH=0x0;

RTC>PRLL=0x7FFF;

/*9.退出配置模式。前面的數(shù)據(jù)才會(huì)真正更新到寄存器中*/

RTC>CRL&=~RTC_CRL_CNF;}/**

*@description:設(shè)置一個(gè)x秒之后到時(shí)的鬧鐘

*/voidAlarm_SetXSecond(uint32_tx){

/*1.先清除鬧鐘標(biāo)志*/

RTC>CRL&=~RTC_CRL_ALRF;

/*2.等待RTC進(jìn)入初始化狀態(tài)*/

while(!(RTC>CRL&RTC_CRL_RTOFF))

;

/*3.進(jìn)入配置模式*/

RTC>CRL|=RTC_CRL_CNF;

/*4.等待秒標(biāo)志位*/

while(!(RTC>CRL&RTC_CRL_SECF))

;

/*5.RTC計(jì)數(shù)器寄存器的值。我們可以從1開(kāi)始計(jì)數(shù),則大概136年才會(huì)溢出(32位計(jì)數(shù)器)*/

RTC>CNTH=0;

RTC>CNTL=0;

/*6.設(shè)置鬧鐘:

*/

x=1;

RTC>ALRH=(x>>16)&0xFFFF;

RTC>ALRL=x&0xFFFF;

/*7.退出配置模式*/

RTC>CRL&=~RTC_CRL_CNF;}軟件設(shè)計(jì)(HAL庫(kù))STM32CubeMX配置添加其他代碼rtc.c中添加代碼/*USERCODEBEGIN1*/voidAlarm_SetXSecond(uint32_tx){

/*

獲取當(dāng)前時(shí)間*/

RTC_TimeTypeDeftimeTypeDef={0};

HAL_RTC_GetTime(&hrtc,&timeTypeDef,RTC_FORMAT_BIN);

/*設(shè)置鬧鐘*/

RTC_AlarmTypeDefsAlarm={0};

sAlarm.AlarmTime.Hours=timeTypeDef.Hours;

sAlarm.AlarmTime.Minutes=timeTypeDef.Minutes;

sAlarm.AlarmTime.Seconds=timeTypeDef.Seconds+x1;

HAL_RTC_SetAlarm(&hrtc,&sAlarm,RTC_FORMAT_BIN);}/*USERCODEEND1*/main.cintmain(void){

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

MX_RTC_Init();

MX_USART1_UART_Init();

//檢測(cè)復(fù)位來(lái)源。當(dāng)從待機(jī)恢復(fù)的時(shí)候,會(huì)重新開(kāi)始執(zhí)行main函數(shù)

if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB)==SET)//從待機(jī)恢復(fù)

{

//清除待機(jī)標(biāo)志位

__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);

//清除喚醒標(biāo)記

__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

}

while(1)

{

printf("正常代碼執(zhí)行完畢,5s后進(jìn)入待機(jī)模式...\r\n");

HAL_Delay(5000);

printf("開(kāi)始進(jìn)入待機(jī)模式,20s后被喚醒...\r\n");

//1.設(shè)置鬧鐘:20s后到時(shí)

Alarm_SetXSecond(20);

//2.停止滴答定時(shí)器

HAL_SuspendTick();

//3.進(jìn)入待機(jī)模式

HAL_PWR_EnterSTANDBYMode();

}}RTC案例2:實(shí)時(shí)時(shí)鐘(掉電不丟失)需求描述顯示時(shí)間。通過(guò)串口把時(shí)間發(fā)送給電腦顯示。即使關(guān)機(jī)很多天,再啟動(dòng)后也能正確顯示時(shí)間。軟件設(shè)計(jì)(寄存器)main.c#include"stm32f10x.h"http://Deviceheader#include"usart.h"#include"Delay.h"#include"led.h"#include"rtc_datetime.h"intmain(void){

usart1_init();

printf("尚硅谷實(shí)時(shí)時(shí)鐘實(shí)驗(yàn).....\r\n");

RTC_Init();

/*在window的powshell獲取unix時(shí)間戳命令:[int][double]::Parse((GetDateUFormat%s))*/

/*設(shè)置一次就行了,以后時(shí)鐘就可以正常運(yùn)行了*/

//RTC_SetUnixTimestampSecond(1700478670);

DateTimeDefTypedateTime;

while(1)

{

RTC_GetDateTime(&dateTime);

printf("%04d年%02d月%02d日%02d:%02d:%02d\r\n",

dateTime.year,

dateTime.month,

dateTime.day,

dateTime.hour,

dateTime.mintue,

dateTime.second);

Delay_s(1);

}}rtc_datetime.h#ifndef__RTC_DATETIME_C#define__RTC_DATETIME_C#include"stm32f10x.h"#include"time.h"typedefstruct{

uint16_tyear;

uint8_tmonth;

uint8_tday;

uint8_thour;

uint8_tmintue;

uint8_tsecond;}DateTimeDefType;voidRTC_Init(void);voidRTC_SetUnixTimestampSecond(uint32_tunixTimestampSecond);voidRTC_GetDateTime(DateTimeDefType*dateTime);#endifrtc_realtime.c#include"rtc_datetime.h"/**

*@description:rtc的初始化

*@return{*}

*/voidRTC_Init(void){

/*1.使能PWR時(shí)鐘*/

RCC>APB1ENR|=RCC_APB1ENR_PWREN;

RCC>BDCR&=~RCC_BDCR_RTCEN;

/*

2.使用LSE(外部低速時(shí)鐘)作為RTC時(shí)鐘

00:無(wú)時(shí)鐘;

01:LSE振蕩器作為RTC時(shí)鐘32.768KHz;

10:LSI振蕩器作為RTC時(shí)鐘;

11:HSE振蕩器在128分頻后作為RTC時(shí)鐘。

*/

RCC>BDCR|=RCC_BDCR_RTCSEL_1;

RCC>BDCR|=RCC_BDCR_RTCSEL_0;

/*3.使能BKP時(shí)鐘*/

RCC>APB1ENR|=RCC_APB1ENR_BKPEN;

/*

4.允許向RTC寄存器和后備寄存器寫(xiě)入數(shù)據(jù)PWR_CR_DBP位

0:禁止寫(xiě)入RTC和后備寄存器

1:允許寫(xiě)入RTC和后備寄存器

*/

PWR>CR|=PWR_CR_DBP;

/*5.使能RTC時(shí)鐘*/

RCC>BDCR|=RCC_BDCR_RTCEN;

/*6.使能LSE*/

RCC>BDCR|=RCC_BDCR_LSEON;

/*7.等待RTC進(jìn)入初始化狀態(tài)(上次的寫(xiě)操作已經(jīng)完成)*/

while(!(RTC>CRL&RTC_CRL_RTOFF))

;

/*8.進(jìn)入配置模式.

允許配置RTC的計(jì)數(shù)器,預(yù)分頻系數(shù)和鬧鐘寄存器。*/

RTC>CRL|=RTC_CRL_CNF;

/*

9.設(shè)置RTC預(yù)分頻系數(shù):我們產(chǎn)生一個(gè)1s的信號(hào),讓計(jì)數(shù)器每s加1

時(shí)鐘頻率是32768Hz,所以預(yù)分頻系數(shù)應(yīng)該是=0x7FFF

*/

RTC>PRLH=0x0;

RTC>PRLL=0x7FFF;

/*10.退出配置模式。前面的數(shù)據(jù)才會(huì)真正更新到寄存器中*/

RTC>CRL&=~RTC_CRL_CNF;}/**

*@description:把計(jì)數(shù)器的值設(shè)置位unix時(shí)間戳

*/voidRTC_SetUnixTimestampSecond(uint32_tunixTimestampSecond){

/*1.等待RTC進(jìn)入初始化狀態(tài)*/

while(!(RTC>CRL&RTC_CRL_RTOFF))

;

/*2.進(jìn)入配置模式*/

RTC>CRL|=RTC_CRL_CNF;

/*3.等待秒標(biāo)志位*/

while(!(RTC>CRL&RTC_CRL_SECF))

;

/*4.設(shè)置位unix時(shí)間戳*/

RTC>CNTH=unixTimestampSecond>>16;

RTC>CNTL=unixTimestampSecond&0xFFFF;

/*5.退出配置模式*/

RTC>CRL&=~RTC_CRL_CNF;}/**

*@description:獲取年月日時(shí)分秒

*@param{DateTimeDefType}*dateTime年月日時(shí)分秒會(huì)存儲(chǔ)到這個(gè)結(jié)構(gòu)體中

*/voidRTC_GetDateTime(DateTimeDefType*dateTime){

/*1.等待寄存器同步*/

while(!(RTC>CRL&RTC_CRL_RSF))

;

/*2.讀取計(jì)數(shù)器中的值*/

uint32_tsecond=RTC>CNTH<<16|RTC>CNTL;

/*3.根據(jù)傳入的秒得到一個(gè)時(shí)間結(jié)構(gòu)體

*/

structtm*timeinfo=localtime(&second);

/*年是由1900開(kāi)始計(jì)算的兩位年份,所以需要再加個(gè)1900*/

dateTime>year=timeinfo>tm_year+1900;

/*月份是從0開(kāi)始,所以+1*/

dateTime>month=timeinfo>tm_mon+1;

dateTime>day=timeinfo>tm_mday;

dateTime>hour=timeinfo>tm_hour;

dateTime>mintue=timeinfo>tm_min;dateTime>second=timeinfo>tm_sec;}軟件設(shè)計(jì)(HAL庫(kù))復(fù)制上個(gè)項(xiàng)目,適當(dāng)做些修改。STM32CubeMX設(shè)置添加其他代碼intmain(void){

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

MX_RTC_Init();

MX_USART1_UART_Init();

RTC_DateTypeDefdateTypeDef;

RTC_TimeTypeDeftimeTypeDef;

while(1)

{

//一定要先獲取時(shí)間,再獲取日期。獲取時(shí)間的時(shí)候會(huì)對(duì)日期進(jìn)行操作,比如增加一天

HAL_RTC_GetTime(&hrtc,&timeTypeDef,RTC_FORMAT_BIN);

HAL_RTC_GetDate(&hrtc,&dateTypeDef,RTC_FORMAT_BIN);

printf("20%02d年%02d月%02d日%02d:%02d:%02d\r\n",

dateTypeDef.Year,

dateTypeDef.Month,

dateTypeDef.Date,

timeTypeDef.Hours,

timeTypeDef.Minutes,

timeTypeDef.Seconds);

HAL_Delay(1000);

}}看門(mén)狗什么是看門(mén)狗看門(mén)狗是一種計(jì)時(shí)硬件電路。當(dāng)系統(tǒng)環(huán)境比較惡劣復(fù)雜的時(shí)候,程序可能會(huì)出現(xiàn)一些不可預(yù)料的錯(cuò)誤,導(dǎo)致程序卡死,崩潰等情況??撮T(mén)狗就可以及時(shí)的重置程序,使程序重新從頭開(kāi)始運(yùn)行。STM32的看門(mén)狗STM32有2個(gè)看門(mén)狗,獨(dú)立看門(mén)狗和窗口看門(mén)狗。獨(dú)立看門(mén)狗號(hào)稱寵物狗,窗口看門(mén)狗號(hào)稱警犬。獨(dú)立看門(mén)狗IWDG(Independentwatchdog,獨(dú)立看門(mén)狗)。獨(dú)立看門(mén)狗用通俗一點(diǎn)的話來(lái)解釋就是一個(gè)12位的遞減計(jì)數(shù)器,當(dāng)計(jì)數(shù)器的值從某個(gè)值一直減到0的時(shí)候,系統(tǒng)就會(huì)產(chǎn)生一個(gè)復(fù)位信號(hào),即IWDG_RESET。如果在計(jì)數(shù)沒(méi)減到0之前,刷新了計(jì)數(shù)器的值的話,那么就不會(huì)產(chǎn)生復(fù)位信號(hào),這個(gè)刷新計(jì)數(shù)器值的動(dòng)作就是我們經(jīng)常說(shuō)的喂狗。計(jì)數(shù)器時(shí)鐘獨(dú)立看門(mén)狗的時(shí)鐘由獨(dú)立的RC振蕩器LSI提供,即使主時(shí)鐘發(fā)生故障它仍然有效,非常獨(dú)立(叫獨(dú)立看門(mén)狗的由來(lái))。LSI的頻率一般在30~60KHZ之間,根據(jù)溫度和工作場(chǎng)合會(huì)有一定的漂移,我們一般取40KHZ,所以獨(dú)立看門(mén)狗的定時(shí)時(shí)間并不一定非常精確,只適用于對(duì)時(shí)間精度要求比較低的場(chǎng)合。預(yù)分頻寄存器這里存儲(chǔ)著我們想要的預(yù)分頻值。12位遞減計(jì)數(shù)器獨(dú)立看門(mén)狗的計(jì)數(shù)器是一個(gè)12位的遞減計(jì)數(shù)器,最大值為0XFFF,當(dāng)計(jì)數(shù)器減到0時(shí),會(huì)產(chǎn)生一個(gè)復(fù)位信號(hào):IWDG_RESET,讓程序重新啟動(dòng)運(yùn)行,如果在計(jì)數(shù)器減到0之前刷新了計(jì)數(shù)器的值的話,就不會(huì)產(chǎn)生復(fù)位信號(hào),重新刷新計(jì)數(shù)器值的這個(gè)動(dòng)作我們俗稱喂狗。重裝載寄存器重裝載寄存器是一個(gè)12位的寄存器,里面裝著要刷新到計(jì)數(shù)器的值,這個(gè)值的大小決定著獨(dú)立看門(mén)狗的溢出時(shí)間。這里的超時(shí)時(shí)間是指如果超過(guò)這個(gè)時(shí)間不喂狗,就會(huì)產(chǎn)生復(fù)位信號(hào)。超時(shí)時(shí)間的計(jì)算公式:T注意:時(shí)鐘頻率是40KHz。預(yù)分頻系數(shù)只有表中固定的這些值(3位PR值決定)。RL的值就是重裝載寄存器中的值。計(jì)數(shù)器的初始值。鍵寄存器鍵寄存器IWDG_KR獨(dú)立看門(mén)狗的一個(gè)控制寄存器,只能寫(xiě)入015位,讀永遠(yuǎn)是0。三種控制方式,往這個(gè)寄存器寫(xiě)入下面三個(gè)不同的值有不同的效果。寫(xiě)入鍵寄存器的值作用0xCCCC軟件啟動(dòng)看門(mén)狗。若啟用了硬件看門(mén)狗,則此命令無(wú)效??撮T(mén)狗一旦啟動(dòng),無(wú)法停止。0xAAAA重裝載計(jì)數(shù)器的值加入到計(jì)數(shù)器中。(喂狗)0x5555允許訪問(wèn)IWDG_PR(預(yù)分頻計(jì)數(shù)器)和IWDG_RLR(重裝載計(jì)數(shù)器)寄存器。其他值開(kāi)啟IWDG_PR(預(yù)分頻計(jì)數(shù)器)和IWDG_RLR(重裝載計(jì)數(shù)器)寄存器的寫(xiě)保護(hù)。狀態(tài)寄存器

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論