復(fù)位(stm32f407ZGT6)-屬于中斷操作
共有三種類型的復(fù)位,分別為系統(tǒng)復(fù)位、電源復(fù)位和備份域復(fù)位。

系統(tǒng)復(fù)位
除了時鐘控制寄存器 CSR 中的復(fù)位標(biāo)志和備份域中的寄存器外,系統(tǒng)復(fù)位會將其它全部寄
存器都復(fù)位為復(fù)位值。
只要發(fā)生以下事件之一,就會產(chǎn)生系統(tǒng)復(fù)位:
1. NRST 引腳低電平(外部復(fù)位)
2. 窗口看門狗計數(shù)結(jié)束(WWDG 復(fù)位)
3. 獨立看門狗計數(shù)結(jié)束(IWDG 復(fù)位)
4. 軟件復(fù)位(SW 復(fù)位)(請參見軟件復(fù)位)
5. 低功耗管理復(fù)位(請參見低功耗管理復(fù)位)
NRST 引腳低電平(外部復(fù)位-不可屏蔽的硬件中斷)


這兩個RESET的信號都會連接到NRST,簡單理解就是說當(dāng)RESET輸出低電平小于0.8V并且持續(xù)100ns,stm32就會復(fù)位,先說上圖,這個算是一個手動復(fù)位RC充電電路,上電的瞬間,電容C12兩端電壓可以認(rèn)為是0,RESET會輸出低電平,stm32處于復(fù)位狀態(tài),VCC3.3通過電阻R3給電容充電,當(dāng)電容C12的電壓升高到0.8V以上,stm32退出復(fù)位狀態(tài)進(jìn)入運行狀態(tài)。當(dāng)我們需要手動復(fù)位的時候只需要按下按鈕,讓電容放電,松手后就會重復(fù)上述流程,stm32會進(jìn)行一次復(fù)位。
復(fù)位電路不止這一個
ISP下載復(fù)位
還記得一鍵下載也需要復(fù)位嗎,如下圖(具體原理參考文章鏈接)

JTAG下載復(fù)位

我在使用ST-LINK進(jìn)行下載時,就因為這個RESET,讓我草了個草
stm32中“拔掉jlink”程序無法正確運行,原因是只拔了仿真器與電腦連接的那端,然后把另外端依然接在板子上
當(dāng)JLINK在板子上連接的時候,斷電情況下,會一直把RESET拉低,導(dǎo)致芯片一直處在復(fù)位狀態(tài),所以不能正常工作。
把jlink或stlink和stm32板的的連接排線(jtag 接口)也拔掉,這個時候主板就可以工作了。
由于JLINK內(nèi)部也有電路,調(diào)試時會自動產(chǎn)生一個復(fù)位信號,讓系統(tǒng)恢復(fù)默認(rèn)狀態(tài),然后程序開始運行,因此調(diào)試下程序應(yīng)該都能正常啟動。

補(bǔ)充:
nTRST(上圖的JTRST) 測試系統(tǒng)復(fù)位信號
nRESET(RESET、nrst) 目標(biāo)系統(tǒng)復(fù)位信號\
關(guān)于目標(biāo)系統(tǒng)復(fù)位信號和測試系統(tǒng)復(fù)位信號
測試系統(tǒng)其實指的是目標(biāo)板上JTAG掃描鏈中各個芯片中TAP測試訪問端口和邊界掃描單元等,nTRST也就是指復(fù)位TAP測試訪問端口,不復(fù)位芯片邏輯。而目標(biāo)系統(tǒng)就是目標(biāo)板上除了測試功能以外的正常邏輯了。nRESET連接FPGA的PROG_B(復(fù)位配置邏輯電路,但是這時的復(fù)位是由上位PC機(jī)使用控制軟件通過JTAG電纜或控制器來控制的,條件缺一不可。源自文章
看門狗復(fù)位(WDT)
(此部分參考CMS80F231系列)
看門狗復(fù)位是系統(tǒng)的一種保護(hù)設(shè)置。在正常狀態(tài)下,由程序?qū)⒖撮T狗定時器清零。若出錯,系統(tǒng)處于未知
狀態(tài),看門狗定時器溢出,此時系統(tǒng)復(fù)位??撮T狗復(fù)位后,系統(tǒng)重啟進(jìn)入正常狀態(tài)。
WDT 的計數(shù)器不可被尋址,在上電復(fù)位結(jié)束后程序運行時就開始計數(shù),設(shè)置 WDT 寄存器時建議將 WDT
計數(shù)器清除,以便準(zhǔn)確控制 WDT 的溢出時間。
看門狗復(fù)位的時序如下:
- 看門狗定時器狀態(tài):系統(tǒng)檢測看門狗定時器是否溢出,若溢出,則系統(tǒng)復(fù)位;
- 初始化:所有的系統(tǒng)寄存器被置為默認(rèn)狀態(tài);
- 程序:復(fù)位完成,程序開始從 0000H 運行。
WDT 溢出后復(fù)位 CPU 與所有的寄存器,1 個 Tsys 后程序立即從 0000H 開始執(zhí)行。WDT 復(fù)位不會重新
進(jìn)行上電復(fù)位配置。
WDT 的時鐘源由系統(tǒng)時鐘(SystemClock)提供,WDT 計數(shù)器的計時基本周期為 Tsys。
看門狗的溢出時間可由程序設(shè)置,在 CKCON 寄存器 WDS2-WTS0 兩位可選擇溢出時間。
(stm32f407ZGT6)
此部分原文鏈接:https://blog.csdn.net/weixin_44403365/article/details/116991749
看門狗復(fù)位:獨立看門狗和窗口看門狗。
獨立看門狗
STM32的獨立看門狗由內(nèi)部專門的40Khz低速時鐘驅(qū)動,即主時鐘發(fā)生故障,它也仍然有效,這里我們需要注意獨立看門狗的時鐘不是準(zhǔn)確的40Khz,而是在30~60Khz之間變化的一個時鐘,只是我們估算以40Khz來計算,看門狗對時間要求不是很精確,時鐘有點偏差還是可以接受的。




工作原理:
在**鍵值寄存器(IWDG_KR)**中寫入0XCCCC,開始啟用獨立看門狗,此時計數(shù)器開始從其復(fù)位值OXFFF遞減計數(shù),當(dāng)計數(shù)器計數(shù)到末尾0X000的時候,會產(chǎn)生一個復(fù)位信號(IWDG_RESET),無論何時,只要寄存器IWDG_KR中被寫入0XAAAA,IWDG_RLR中的值就會被重新加載到計數(shù)器中從而避免產(chǎn)生看門狗復(fù)位。
還有兩個寄存器,一個預(yù)分頻寄存器(IWDG_PR)。該寄存器是用來設(shè)置看門狗的時鐘分頻系數(shù),最低為4,最高位256,雖然是32位寄存器,我們只使用了最低3位。
另一個重裝載寄存器。該寄存器用來保存重裝載到計數(shù)器中的值。該寄存器也是一個 32
位寄存器,但是只有低 12 位是有效的。
**預(yù)分頻寄存器(IWDG_PR)和重載寄存器(IWDG_RLR)**的寫保護(hù) :IWDG_PR和IWDG_RLR寄存器具有寫保護(hù)功能,要想修改這兩個寄存器的值,首先要向IWDG_KR中寫入0X5555。以不同的值寫入這個寄存器或者重裝載(寫入0XAAAA)都會重新啟動寫保護(hù)。
獨立看門狗由內(nèi)部低速時鐘LSI提供計數(shù)時鐘,8 位分頻,12位計數(shù),需要定期喂狗(重載數(shù)值 ReloadCounter),如果計數(shù)值減為0了,還沒有重載數(shù)值,則會響應(yīng)復(fù)位事件。

啟動過程:
向IWDG_KR中寫入0X5555
通過這一步我們?nèi)∠薎WDG_PR和IWDG_RLR的寫保護(hù),下一步我們設(shè)置他們初值。
設(shè)置IWDG_PR和IWDG_RLR的初值。
我們計算一下看門狗的喂狗時間(看門狗溢出時間)計算公式
*Tout=((4*2^prer)rlr)/40
其中Tout就是看門狗溢出時間(單位ms),prer是看門狗時鐘預(yù)分頻值(IWDG_PR值),范圍為0~7,rlr位看門狗重載值(IWDG_RLR)。比如我們設(shè)置prer為4,rlr的值為625,我們就可以計算得到Tout=64*625/40=1000ms,這樣,看門狗的溢出時間就是1S,只要在這一秒鐘內(nèi),有一次吸入0XAAAA到IWDG_KR,就不會導(dǎo)致看門狗復(fù)位(寫入多次也是可以的)(由于看門狗的時鐘不是準(zhǔn)確40Khz,所以喂狗不要太晚,以免發(fā)生看門狗復(fù)位)。
向IWDG_KR中寫入0XAAAA
通過這句可以將重載寄存器(IWDG_RLR)中的計數(shù)初值載入到看門狗計數(shù)器中(也可以時鐘該命令喂狗)。
向IWDG_KR中寫入0XCCCC
通過這句我們就啟動了STM32的看門狗了,使能了看門狗,在程序里面我們就必須間隔一定的時間就喂狗,否則導(dǎo)致程序復(fù)位,利用這一點,我們通過一個LED來指示是否復(fù)位,驗證獨立看門狗
舉例:正點原子獨立看門狗
源文件:
//時間計算(大概):Tout=((4*2^prer)*rlr)/40 (ms).
void IWDG_Init(u8 prer,u16 rlr)
{
//第一步0X5555
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能對寄存器IWDG_PR和IWDG_RLR的寫操作
IWDG_SetPrescaler(prer); //設(shè)置IWDG預(yù)分頻值:設(shè)置IWDG預(yù)分頻值為64
IWDG_SetReload(rlr); //設(shè)置IWDG重裝載值
//第二步0XAAAA
IWDG_ReloadCounter(); //按照IWDG重裝載寄存器的值重裝載IWDG計數(shù)器
//第三步0xCCCC
IWDG_Enable(); //使能IWDG
}
//喂獨立看門狗
void IWDG_Feed(void)
{
IWDG_ReloadCounter();//reload
}
主函數(shù)源文件:
int main(void)
{
delay_init(); //延時函數(shù)初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設(shè)置NVIC中斷分組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
uart_init(115200); //串口初始化為115200
LED_Init(); //初始化與LED連接的硬件接口
KEY_Init(); //按鍵初始化
delay_ms(500); //讓人看得到滅
IWDG_Init(4,625); //與分頻數(shù)為64,重載值為625,溢出時間為1s
LED0=0; //點亮LED0
while(1)
{
if(KEY_Scan(0)==WKUP_PRES)
{
IWDG_Feed();//如果WK_UP按下,則喂狗
}
delay_ms(10);
};
}
窗口看門狗

這里我們的 WWDG_CR 只有低八位有效,T[6:0]用來存儲看門狗的計數(shù)器值,
隨時更新的,每個窗口看門狗計數(shù)周期(4096×2^ WDGTB)減 1。當(dāng)該計數(shù)器的值從 0X40 變
為 0X3F 的時候,將產(chǎn)生看門狗復(fù)位。
WDGA 位則是看門狗的激活位,該位由軟件置 1,以啟動看門狗,并且一定要注意的是該
位一旦設(shè)置,就只能在硬件復(fù)位后才能清零了。

該位中的 EWI 是提前喚醒中斷,也就是在快要產(chǎn)生復(fù)位的前一段時間(T[6:0]=0X40)來
提醒我們,需要進(jìn)行喂狗了,否則將復(fù)位!因此,我們一般用該位來設(shè)置中斷,當(dāng)窗口看門狗
的計數(shù)器值減到 0X40 的時候,如果該位設(shè)置,并開啟了中斷,則會產(chǎn)生中斷,我們可以在中
斷里面向 WWDG_CR 重新寫入計數(shù)器的值,來達(dá)到喂狗的目的。注意這里在進(jìn)入中斷后,必
須在不大于 1 個窗口看門狗計數(shù)周期的時間(在 PCLK1 頻率為 36M 且 WDGTB 為 0 的條件下,
該時間為 113us)內(nèi)重新寫 WWDG_CR,否則,看門狗將產(chǎn)生復(fù)位!

最后我們要介紹的是狀態(tài)寄存器(WWDG_SR),該寄存器用來記錄當(dāng)前是否有提前喚醒
的標(biāo)志。該寄存器僅有位 0 有效,其他都是保留位。當(dāng)計數(shù)器值達(dá)到 40h 時,此位由硬件置 1。
它必須通過軟件寫 0 來清除。對此位寫 1 無效。即使中斷未被使能,在計數(shù)器的值達(dá)到 0X40
的時候,此位也會被置 1。
窗口看門狗工作原理:
窗口看門狗(WWDG)通常被用來監(jiān)測由外部干擾或不可預(yù)見的邏輯條件造成的應(yīng)用程序
背離正常的運行序列而產(chǎn)生的軟件故障。除非遞減計數(shù)器的值在 T6 位(WWDG->CR 的第六位)
變成 0 前被刷新,看門狗電路在達(dá)到預(yù)置的時間周期時,會產(chǎn)生一個 MCU 復(fù)位。在遞減計數(shù)
器達(dá)到窗口配置寄存器(WWDG->CFR)數(shù)值之前,如果 7 位的遞減計數(shù)器數(shù)值(在控制寄存器中)
被刷新, 那么也將產(chǎn)生一個 MCU 復(fù)位。這表明遞減計數(shù)器需要在一個有限的時間窗口中被刷
新。他們的關(guān)系可以用圖 12.1.1 來說明:

圖 12.1.1 中,T[6:0]就是 WWDG_CR 的低七位,W[6:0]即是 WWDG->CFR 的低七位。T[6:0]
就是窗口看門狗的計數(shù)器,而 W[6:0]則是窗口看門狗的上窗口,下窗口值是固定的(0X40)。
當(dāng)窗口看門狗的計數(shù)器在上窗口值之外被刷新,或者低于下窗口值都會產(chǎn)生復(fù)位。
上窗口值(W[6:0])是由用戶自己設(shè)定的,根據(jù)實際要求來設(shè)計窗口值,但是一定要確保
窗口值大于 0X40,否則窗口就不存在了。
窗口看門狗的超時公式如下:
Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /Fpclk1;
其中:
Twwdg:WWDG 超時時間(單位為 ms)
Fpclk1:APB1 的時鐘頻率(單位為 Khz)
WDGTB:WWDG 的預(yù)分頻系數(shù)
T[5:0]:窗口看門狗的計數(shù)器低 6 位
根據(jù)上面的公式,假設(shè) Fpclk1=36Mhz,那么可以得到最小-最大超時時間表如表 12.1.1 所
示:

窗口看門狗由APB1(RCC_APB1Periph_WWDG)提供計數(shù)時鐘,2 位分頻,7位計數(shù),需要定期喂狗(更新計數(shù)值),如果計數(shù)值減為0x40了,還未更新計數(shù)值,則會響應(yīng)復(fù)位事件。

步驟如下:
1)使能 WWDG 時鐘
2)設(shè)置窗口值和分頻數(shù)
3)開啟 WWDG 中斷并分組
4) 設(shè)置計數(shù)器初始值并使能看門狗
5) 編寫中斷服務(wù)函數(shù)
正點原子程序:
源程序
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG時鐘使能
WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT.
WWDG_SetPrescaler(fprer);設(shè)置IWDG預(yù)分頻值
WWDG_SetWindowValue(wr);//設(shè)置窗口值
WWDG_Enable(WWDG_CNT); //使能看門狗 , 設(shè)置 counter .
WWDG_ClearFlag();//清除提前喚醒中斷標(biāo)志位
WWDG_NVIC_Init();//初始化窗口看門狗 NVIC
WWDG_EnableIT(); //開啟窗口看門狗中斷
}
//重設(shè)置WWDG計數(shù)器的值
void WWDG_Set_Counter(u8 cnt)
{
WWDG_Enable(cnt);//使能看門狗 , 設(shè)置 counter .
}
//窗口看門狗中斷服務(wù)程序
void WWDG_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占2,子優(yōu)先級3,組2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //搶占2,子優(yōu)先級3,組2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);//NVIC初始化
}
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(WWDG_CNT); //當(dāng)禁掉此句后,窗口看門狗將產(chǎn)生復(fù)位
WWDG_ClearFlag(); //清除提前喚醒中斷標(biāo)志位
LED1=!LED1; //LED狀態(tài)翻轉(zhuǎn)
}
主程序:
int main(void)
{
delay_init(); //延時函數(shù)初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設(shè)置中斷優(yōu)先級分組為組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
uart_init(115200); //串口初始化為115200
LED_Init();
KEY_Init(); //按鍵初始化
LED0=0;
delay_ms(300);
WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//計數(shù)器值為7f,窗口寄存器為5f,分頻數(shù)為8
while(1)
{
LED0=1;
}
}
軟件復(fù)位
可通過查看 RCC 時鐘控制和狀態(tài)寄存器 (RCC_CSR),此部分在下邊 中的復(fù)位標(biāo)志確定。
要對器件進(jìn)行軟件復(fù)位,必須將 Cortex?-M4F 應(yīng)用中斷和復(fù)位控制寄存器中的
SYSRESETREQ 位置 1。有關(guān)詳細(xì)信息,請參見 Cortex?-M4F 技術(shù)參考手冊。
STM32官方已經(jīng)將軟件復(fù)位過程給封裝好了,即 NVIC_SystemReset() 函數(shù),
NVIC_SystemReset()函數(shù)的內(nèi)容如下:
/**
\brief System Reset
\details Initiates a system reset request to reset the MCU.
*/
__STATIC_INLINE void NVIC_SystemReset(void)
{
__DSB(); /* Ensure all outstanding memory accesses included
buffered write are completed before reset */
SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */
__DSB(); /* Ensure completion of memory access */
for(;;) /* wait until reset */
{
__NOP();
}
}
使用軟件復(fù)位NVIC_SystemReset()函數(shù)時,需在該函數(shù)之前加上__set_FAULTMASK(1)語句,表示關(guān)閉所有中斷的意思;
因為在《Cortex-M3權(quán)威指南》中有這么一句話提醒我們:從 SYSRESETREQ 被置為有效,到復(fù)位發(fā)生器執(zhí)行復(fù)位令,往往會有一個延時。在此延時期間,處理器仍然可以響應(yīng)中斷請求。但我們的本意往往是要讓此次執(zhí)行到此為止,不要再做任何其它事情了。所以,最好在發(fā)出復(fù)位請求前,先把 FAULTMASK 置位,即關(guān)閉所有中斷。
低功耗管理復(fù)位
引發(fā)低功耗管理復(fù)位的方式有兩種:
1. 進(jìn)入待機(jī)模式時產(chǎn)生復(fù)位:
此復(fù)位的使能方式是清零用戶選項字節(jié)中的 nRST_STDBY 位。使能后,只要成功執(zhí)行
進(jìn)入待機(jī)模式序列,器件就將復(fù)位,而非進(jìn)入待機(jī)模式。
2. 進(jìn)入停止模式時產(chǎn)生復(fù)位:
此復(fù)位的使能方式是清零用戶選項字節(jié)中的 nRST_STOP 位。使能后,只要成功執(zhí)行
進(jìn)入停止模式序列,器件就將復(fù)位,而非進(jìn)入停止模式。
有關(guān)用戶選項字節(jié)的詳細(xì)信息,請參見 STM32F40x 和 STM32F41x Flash 編程手冊,該手
冊可從 ST 銷售辦事處獲取,也可以從 ST 網(wǎng)站 www.st.com 獲得文檔信息。
電源復(fù)位
只要發(fā)生以下事件之一,就會產(chǎn)生電源復(fù)位:
1. 上電/掉電復(fù)位(POR/PDR 復(fù)位)或欠壓 (BOR) 復(fù)位
2. 在退出待機(jī)模式時
除備份域內(nèi)的寄存器以外,電源復(fù)位會將其它全部寄存器設(shè)置為復(fù)位值
這些源均作用于 NRST 引腳,該引腳在復(fù)位過程中始終保持低電平。RESET 復(fù)位入口向量
在存儲器映射中固定在地址 0x0000_0004。
芯片內(nèi)部的復(fù)位信號會在 NRST 引腳上輸出。脈沖發(fā)生器用于保證最短復(fù)位脈沖持續(xù)時間,
可確保每個內(nèi)部復(fù)位源的復(fù)位脈沖都至少持續(xù) 20 μs。對于外部復(fù)位,在 NRST 引腳處于低
電平時產(chǎn)生復(fù)位脈沖。
下面給出上電復(fù)位的正常時序:(此部分參考CMS80F231系列,僅供參考)
-上電:系統(tǒng)檢測到電源電壓上升并等待其穩(wěn)定;
- 系統(tǒng)初始化:所有的系統(tǒng)寄存器被置為初始值;
- 振蕩器開始工作:振蕩器開始提供系統(tǒng)時鐘;
- 執(zhí)行程序:上電結(jié)束,程序開始運行。
備份域復(fù)位
備份域復(fù)位會將所有 RTC 寄存器和 RCC_BDCR 寄存器復(fù)位為各自的復(fù)位值。BKPSRAM 不受
此復(fù)位影響。BKPSRAM 的唯一復(fù)位方式是通過 Flash 接口將 Flash 保護(hù)等級從 1 切換到 0。
只要發(fā)生以下事件之一,就會產(chǎn)生備份域復(fù)位:
1. 軟件復(fù)位,通過將 RCC 備份域控制寄存器 (RCC_BDCR) 中的 BDRST 位置 1 觸發(fā)。
2. 在電源 VDD 和 VBAT 都已掉電后,其中任何一個又再上電。
復(fù)位原因
通過查看控制/狀態(tài)寄存器(RCC_CSR)中的復(fù)位狀態(tài)標(biāo)志位識別復(fù)位事件來源。
先看一下stm32中文參考手冊對RCC_CSR寄存器的描述:
復(fù)位的標(biāo)志位-RCC 時鐘控制和狀態(tài)寄存器 (RCC_CSR)
RCC clock control & status register
偏移地址:0x74
復(fù)位值:0x0E00 0000,除復(fù)位標(biāo)志只能通過電源復(fù)位進(jìn)行復(fù)位以外,其它通過系統(tǒng)復(fù)位進(jìn)
行復(fù)位。
訪問:0 <=等待周期 <=3,按字、半字和字節(jié)訪問
連續(xù)訪問該寄存器時,則插入等待周期。

位 31 LPWRRSTF:低功耗復(fù)位標(biāo)志 (Low-power reset flag)
發(fā)生低功耗管理復(fù)位時,由硬件置 1。
通過寫入 RMVF 位清零。
0:未發(fā)生低功耗管理復(fù)位
1:發(fā)生低功耗管理復(fù)位
有關(guān)低功耗管理復(fù)位的詳細(xì)信息,請參見低功耗管理復(fù)位。
位 30 WWDGRSTF:窗口看門狗復(fù)位標(biāo)志 (Window watchdog reset flag)
發(fā)生窗口看門狗復(fù)位時,由硬件置 1。
通過寫入 RMVF 位清零。
0:未發(fā)生窗口看門狗復(fù)位
1:發(fā)生窗口看門狗復(fù)位
位 29 IWDGRSTF:獨立看門狗復(fù)位標(biāo)志 (Independent watchdog reset flag)
發(fā)生來自 VDD 域的獨立看門狗復(fù)位時,由硬件置 1。
通過寫入 RMVF 位清零。
0:未發(fā)生看門狗復(fù)位
1:發(fā)生看門狗復(fù)位
位 28 SFTRSTF:軟件復(fù)位標(biāo)志 (Software reset flag)
發(fā)生軟件復(fù)位時,由硬件置 1。
通過寫入 RMVF 位清零。
0:未發(fā)生軟件復(fù)位
1:發(fā)生軟件復(fù)位
位 27 PORRSTF:POR/PDR 復(fù)位標(biāo)志 (POR/PDR reset flag)
發(fā)生 POR/PDR 復(fù)位時,由硬件置 1。
通過寫入 RMVF 位清零。
0:未發(fā)生 POR/PDR 復(fù)位
1:發(fā)生 POR/PDR 復(fù)位
位 26 PINRSTF:引腳復(fù)位標(biāo)志 (PIN reset flag)
發(fā)生來自 NRST 引腳的復(fù)位時,由硬件置 1。
通過寫入 RMVF 位清零。
0:未發(fā)生來自 NRST 引腳的復(fù)位
1:發(fā)生來自 NRST 引腳的復(fù)位
位 25 BORRSTF:BOR 復(fù)位標(biāo)志 (BOR reset flag)
通過軟件寫入 RMVF 位清零。
發(fā)生 POR/PDR 復(fù)位或 BOR 復(fù)位時,由硬件置 1。
0:未發(fā)生 POR/PDR 復(fù)位或 BOR 復(fù)位
1:發(fā)生 POR/PDR 復(fù)位或 BOR 復(fù)位
位 24 RMVF:清除復(fù)位標(biāo)志 (Remove reset flag)
由軟件置 1,用于將復(fù)位標(biāo)志清零。
0:無操作
1:清零復(fù)位標(biāo)志
位 23:2 保留,必須保持復(fù)位值。
位 1 LSIRDY:內(nèi)部低速振蕩器就緒 (Internal low-speed oscillator ready)
由硬件置 1 和清零,用于指示內(nèi)部 RC 40 kHz 振蕩器已穩(wěn)定。在 LSION 位被清零后,
LSIRDY 將在 3 個 LSI 時鐘周期后轉(zhuǎn)為低電平。
0:LSI RC 振蕩器未就緒
1:LSI RC 振蕩器就緒
位 0 LSION:內(nèi)部低速振蕩器使能 (Internal low-speed oscillator enable)
由軟件置 1 和清零。
0:LSI RC 振蕩器關(guān)閉
1:LSI RC 振蕩器開啟
stm32對CSR寄存器各個位的宏封裝:
/* Flags in the CSR register */
#define RCC_FLAG_LSIRDY ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LSIRDY))) /*!< Internal Low Speed oscillator Ready */
#define RCC_FLAG_LSECSS ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LSECSSD))) /*!< CSS on LSE failure Detection */
#define RCC_FLAG_OBLRST ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_OBLRSTF))) /*!< Options bytes loading reset flag */
#define RCC_FLAG_PINRST ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_PINRSTF))) /*!< PIN reset flag */
#define RCC_FLAG_PORRST ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_PORRSTF))) /*!< POR/PDR reset flag */
#define RCC_FLAG_SFTRST ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_SFTRSTF))) /*!< Software Reset flag */
#define RCC_FLAG_IWDGRST ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_IWDGRSTF))) /*!< Independent Watchdog reset flag */
#define RCC_FLAG_WWDGRST ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_WWDGRSTF))) /*!< Window watchdog reset flag */
#define RCC_FLAG_LPWRRST ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LPWRRSTF))) /*!< Low-Power reset flag */
#define RCC_FLAG_LSERDY ((uint8_t)((CSR_REG_INDEX << 5U) | POSITION_VAL(RCC_CSR_LSERDY))) /*!< External Low Speed oscillator Ready */
stm32獲取復(fù)位標(biāo)志的宏:__HAL_RCC_GET_FLAG(FLAG)
/** @brief Check RCC flag is set or not.
* @param __FLAG__ specifies the flag to check.
* This parameter can be one of the following values:
* @arg @ref RCC_FLAG_HSIRDY HSI oscillator clock ready.
* @arg @ref RCC_FLAG_MSIRDY MSI oscillator clock ready.
* @arg @ref RCC_FLAG_HSERDY HSE oscillator clock ready.
* @arg @ref RCC_FLAG_PLLRDY Main PLL clock ready.
* @arg @ref RCC_FLAG_LSERDY LSE oscillator clock ready.
* @arg @ref RCC_FLAG_LSECSS CSS on LSE failure Detection (*)
* @arg @ref RCC_FLAG_LSIRDY LSI oscillator clock ready.
* @arg @ref RCC_FLAG_OBLRST Option Byte Load reset
* @arg @ref RCC_FLAG_PINRST Pin reset.
* @arg @ref RCC_FLAG_PORRST POR/PDR reset.
* @arg @ref RCC_FLAG_SFTRST Software reset.
* @arg @ref RCC_FLAG_IWDGRST Independent Watchdog reset.
* @arg @ref RCC_FLAG_WWDGRST Window Watchdog reset.
* @arg @ref RCC_FLAG_LPWRRST Low Power reset.
* @note (*) This bit is available in high and medium+ density devices only.
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_RCC_GET_FLAG(__FLAG__) (((((__FLAG__) >> 5U) == CR_REG_INDEX)? RCC->CR :RCC->CSR) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))
stm32清除復(fù)位標(biāo)志的宏:__HAL_RCC_CLEAR_RESET_FLAGS()
/** @brief Set RMVF bit to clear the reset flags.
* The reset flags are RCC_FLAG_PINRST, RCC_FLAG_PORRST, RCC_FLAG_SFTRST,
* RCC_FLAG_IWDGRST, RCC_FLAG_WWDGRST, RCC_FLAG_LPWRRST
*/
#define __HAL_RCC_CLEAR_RESET_FLAGS() (RCC->CSR |= RCC_CSR_RMVF)
代碼思路
看了對上面對控制/狀態(tài)寄存器(RCC_CSR)的描述,代碼的思路就已經(jīng)很明顯了,通過RCC_CSR寄存器中的復(fù)位狀態(tài)標(biāo)志位獲取復(fù)位事件來源,應(yīng)用層做復(fù)位標(biāo)志位,最后清除復(fù)位標(biāo)志。
定義復(fù)位類型枚舉
/* Reset Flag Status */
typedef enum
{
RCC_RESET_FLAG_NONE = 0x00, /*!< None Reset Flag */
RCC_RESET_FLAG_IWDGRST = 0x01, /*!< Independent Watchdog Reset Flag */
RCC_RESET_FLAG_SFTRST = 0x02, /*!< Software Reset Flag */
RCC_RESET_FLAG_PORRST = 0x03, /*!< POR/PDR Reset Flag */
RCC_RESET_FLAG_PINRST = 0x04, /*!< PIN Reset Flag */
RCC_RESET_FLAG_LPWRRST = 0x05, /*!< Low-Power Reset Flag */
RCC_RESET_FLAG_OBLRST = 0x06, /*!< Options Bytes Loading Reset Flag */
RCC_RESET_FLAG_WWDGRST = 0x07 /*!< Window Watchdog Reset Flag */
}RCC_RESET_FLAG_TypeDef;
獲取STM32復(fù)位類型
RCC_RESET_FLAG_TypeDef RCC_ResetFlag_GetStatus(void)
{
RCC_RESET_FLAG_TypeDef ResetStatusFlag = RCC_RESET_FLAG_NONE;
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_IWDGRST;
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_SFTRST;//軟件復(fù)位
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_PORRST;//這是上電復(fù)位
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_PINRST;//這是外部RST管腳復(fù)位
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_LPWRRST;
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_OBLRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_OBLRST;
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_WWDGRST;
}
__HAL_RCC_CLEAR_RESET_FLAGS();//清除復(fù)位標(biāo)志
return ResetStatusFlag;
}
OKK
上邊進(jìn)行了宏封裝,不便于理解的話可看下文操作
復(fù)位標(biāo)志位檢索/判斷什么原因?qū)е碌膹?fù)位
標(biāo)志位判斷的代碼由官方庫中給定代碼如下:
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);//FlagStatus 分為SET和RESET兩種;
/**
* @brief Checks whether the specified RCC flag is set or not.
* @param RCC_FLAG: specifies the flag to check.
*
* For @b STM32_Connectivity_line_devices, this parameter can be one of the
* following values:
* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
* @arg RCC_FLAG_PLLRDY: PLL clock ready
* @arg RCC_FLAG_PLL2RDY: PLL2 clock ready
* @arg RCC_FLAG_PLL3RDY: PLL3 clock ready
* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
* @arg RCC_FLAG_PINRST: Pin reset
* @arg RCC_FLAG_PORRST: POR/PDR reset
* @arg RCC_FLAG_SFTRST: Software reset
* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
* @arg RCC_FLAG_WWDGRST: Window Watchdog reset
* @arg RCC_FLAG_LPWRRST: Low Power reset
*
* For @b other_STM32_devices, this parameter can be one of the following values:
* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
* @arg RCC_FLAG_PLLRDY: PLL clock ready
* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
* @arg RCC_FLAG_PINRST: Pin reset
* @arg RCC_FLAG_PORRST: POR/PDR reset
* @arg RCC_FLAG_SFTRST: Software reset
* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
* @arg RCC_FLAG_WWDGRST: Window Watchdog reset
* @arg RCC_FLAG_LPWRRST: Low Power reset
*
* @retval The new state of RCC_FLAG (SET or RESET).
*/
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG)
{
uint32_t tmp = 0;
uint32_t statusreg = 0;
FlagStatus bitstatus = RESET;
/* Check the parameters */
assert_param(IS_RCC_FLAG(RCC_FLAG));
/* Get the RCC register index */
tmp = RCC_FLAG >> 5;
if (tmp == 1) /* The flag to check is in CR register */
{
statusreg = RCC->CR;
}
else if (tmp == 2) /* The flag to check is in BDCR register */
{
statusreg = RCC->BDCR;
}
else /* The flag to check is in CSR register */
{
statusreg = RCC->CSR;
}
/* Get the flag position */
tmp = RCC_FLAG & FLAG_Mask;
if ((statusreg & ((uint32_t)1 << tmp)) != (uint32_t)RESET)
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}
/* Return the flag status */
return bitstatus;
}
當(dāng)然判斷完后,我們需要將復(fù)位類型的標(biāo)志置位以防后期出現(xiàn)重復(fù)多次判斷
void RCC_ClearFlag(void);//清除復(fù)位執(zhí)行函數(shù)
/**
* @brief Clears the RCC reset flags.
* @note The reset flags are: RCC_FLAG_PINRST, RCC_FLAG_PORRST, RCC_FLAG_SFTRST,
* RCC_FLAG_IWDGRST, RCC_FLAG_WWDGRST, RCC_FLAG_LPWRRST
* @param None
* @retval None
*/
void RCC_ClearFlag(void)
{
/* Set RMVF bit to clear the reset flags */
RCC->CSR |= CSR_RMVF_Set;
}
在使用時,只需要執(zhí)行如下語句即可:
if(RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
{
//這是上電復(fù)位
}
else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
{
//這是外部RST管腳復(fù)位
}
else if (RCC_GetFlagStatus(RCC_FLAG_SFTRST)!= RESET)
{
//這是軟件復(fù)位
}
RCC_ClearFlag();//清除RCC中復(fù)位標(biāo)志
復(fù)位函數(shù)
void mcuRestart(void)
{
__set_FAULTMASK(1); //關(guān)閉所有中斷
NVIC_SystemReset(); //復(fù)位
}
軟件復(fù)位實例一
現(xiàn)象:LD5指示燈閃爍5次,3秒后,MCU執(zhí)行復(fù)位操作。
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t index = 10;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* 效果0.5s亮,0.5s不亮 ,進(jìn)入10次,一亮一滅*/
while (index--) {
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_15);//執(zhí)行一次,改變一次狀態(tài)
HAL_Delay(500);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(3000);
HAL_NVIC_SystemReset();//復(fù)位
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
軟件復(fù)位實例二(這個實例想看就看哦,引用文章原文)
Freescale ARM Cortex-M系列軟復(fù)位的使用方法
“復(fù)位”這個詞對我們搞嵌入式的同志們來說是再熟悉不過了,不過相比于上電復(fù)位和硬件管腳復(fù)位等我們常見的復(fù)位類型來說,軟件復(fù)位可能對一些初入門道的菜鳥們來說還是比較陌生的東西(記得當(dāng)初第一次接觸軟復(fù)位的時候,覺著這個東西真的很奇妙,MCU自己對自己進(jìn)行復(fù)位,想想就覺著很有意思,或許一直就是這種好奇的樂趣讓我等屌絲對嵌入式的感情越陷越深吧,哈哈),即使對搞嵌入式的老鳥來說,軟復(fù)位也是一種很有用的手段。
可能說到這,還是有些人犯迷糊,軟件復(fù)位到底是個什么東西,它到底有什么用。上面提到我們常見的上電復(fù)位和外部管腳Reset復(fù)位等嚴(yán)格來講都是MCU被動的復(fù)位(看門狗復(fù)位我的定義是把它當(dāng)做硬件復(fù)位來對待,因為實際上看門狗也只不過是MCU內(nèi)部一個獨立的外設(shè)模塊罷了),即外部的觸發(fā)條件讓MCU完成對系統(tǒng)的復(fù)位操作,而軟件復(fù)位則是通過軟件觸發(fā)讓MCU自己對自己進(jìn)行復(fù)位,也就是說通過執(zhí)行某個指令來觸發(fā)MCU的系統(tǒng)復(fù)位。軟件復(fù)位的好處也是顯而易見的,比如可以用在遠(yuǎn)程代碼升級(一般我們做bootloader的時候通常需要讓芯片復(fù)位等待通信接口的數(shù)據(jù),然后代碼升級之后一般也會再次觸發(fā)復(fù)位讓芯片重新執(zhí)行新的用戶代碼,當(dāng)然有些需要work on-line固件升級的領(lǐng)域除外),或者程序監(jiān)控(如果監(jiān)測到某些錯誤可以觸發(fā)軟件復(fù)位讓芯片重新執(zhí)行來消除一些異常,類似于我們手機(jī)死機(jī)拆電池,呵呵,當(dāng)然它還達(dá)不到徹底斷電的效果),當(dāng)然軟件復(fù)位也給廣大做調(diào)試器仿真器的廠家提供了極大的便利,我們會發(fā)現(xiàn)調(diào)試接口的Reset管腳不接的情況下我們也能正常對芯片進(jìn)行調(diào)試和下載,這就是軟件復(fù)位的功勞了,哈哈。
說到這里,可能會有些聰明的人提到,我們把PC指針直接指向代碼的起始地址讓它重頭開始執(zhí)行不就完了,咳咳,的確有點投機(jī)取巧,不過這種方法只能是讓內(nèi)核重新執(zhí)行卻無法讓這個MCU系統(tǒng)復(fù)位,所以其作用還是有限的。而本篇的主角,ARM Cortex-M系列MCU則為此專門劃出了一個寄存器用來做這種軟件復(fù)位功能,即SCB_AIRCR寄存器。我們只需置位該寄存器的SYSRESETREQ位即可把內(nèi)核送往系統(tǒng)復(fù)位發(fā)生器的請求線置為有效,從而引起整個芯片系統(tǒng)的復(fù)位,當(dāng)然值得一提的是,這種復(fù)位時一種系統(tǒng)復(fù)位,會把大多數(shù)的外設(shè)模塊復(fù)位(有些個別的外設(shè)寄存器只能在上電復(fù)位的情況下才能復(fù)位),但是卻不會復(fù)位芯片內(nèi)部的調(diào)試模塊,所以我們在IAR或者Keil的調(diào)試環(huán)境下也可以正常監(jiān)控和使用軟件復(fù)位,非常方便。
好了,說了這么多,該上硬菜了,呵呵。我下面直接把在Freescale Cortex-M0+的KL系列上的軟件復(fù)位的代碼貼上來了,其中對SCB_AIRCR寄存器配置中,前面的0x5FA << SCB_AIRCR_VECTKEY_SHIFT為該寄存器的“鑰匙”,不得不說ARM將該寄存器保護(hù)的很好,還為其配了把鑰匙,需要這個鑰匙才能對其他的位進(jìn)行操作,然后置位SYSRESETREQ即可實現(xiàn)軟件復(fù)位。另外,在這個配置操作的前后,也分別添加了兩個__DSB()指令,該指令用來保證在軟件復(fù)位之前和之后保證內(nèi)存數(shù)據(jù)操作都已完成,避免在有些數(shù)據(jù)還沒有來的及更新到目標(biāo)地址或者目標(biāo)寄存器前就觸發(fā)芯片復(fù)位從而造成數(shù)據(jù)丟失,這個還是很重要的,重點提一下。
void software_reset(void)
{
__DSB(); /* Ensure all outstanding memory accesses includedbuffered write are completed before reset */
SCB_AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_SHIFT) |
SCB_AIRCR_SYSRESETREQ_MASK);
__DSB(); /* Ensure completion of memory access */
while(1); /* wait until reset */
}
/********************************************************************/
int main (void)
{
char ch;
printf("\nRunning the platinum project.\n");
while(1)
{
ch = in_char();
out_char(ch);
if(ch=='A')
software_reset();
}
}
下圖為我做的一個測試,通過串口發(fā)送“A”字符來觸發(fā)軟件復(fù)位,效果還是剛剛的,如果大家手頭有相應(yīng)工具不妨試上一試,呵呵。

硬復(fù)位和軟復(fù)位
軟復(fù)位信號名稱中通常包含soft,硬復(fù)位信號名稱中通常包含hard。
硬復(fù)位:通過SOC上的GPIO向IC的復(fù)位引腳輸入高低電平信號觸發(fā)復(fù)位;(IC初始化時對復(fù)位引腳直接操作)
硬復(fù)位:
方法一,將TFTLCD 的 RST 同 STM32F1 的 RESET 連接在一起,只要按下開發(fā)板的 RESET 鍵, 就會對 LCD 進(jìn)行硬復(fù)位。

方法二,將TFTLCD 的 RST 同 STM32F1 的 GPIO連接在一起,先拉低,延遲100us,然后
再釋放RST(拉高),完成復(fù)位。
void LCD_Init(void)
{
SPI1_Init();
LCD_Reset();//直接將LCD的reset引腳接到芯片的nrst引腳,可以實現(xiàn),中間就是出點問題,就是直接上電就白屏,必須手動按一下復(fù)位鍵,這也不符合上電復(fù)位的原理呀
}
void LCD_Reset(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_11);//清0
delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_11);//置1
delay_ms(100);
}
軟復(fù)位:通過向IC的寄存器中寫值觸發(fā)復(fù)位;(某些IC在ESD測試中crash通過復(fù)位寄存器進(jìn)行復(fù)位)
硬件復(fù)位,即產(chǎn)生脈沖(無論是power on上電時自產(chǎn)生,還是手動、或外部看門狗
產(chǎn)生;高/低電平有效、持續(xù)時間、沿等要求,視具體芯片要求而定),加至芯片內(nèi)各觸發(fā)
器的Reset端;
軟復(fù)位,即由用戶代碼決定復(fù)位時機(jī)??赡苁亲尦绦蛱刂疗瘘c,或配合片內(nèi)復(fù)位模
塊,產(chǎn)生等同于硬件復(fù)位的效果(如片內(nèi)集成了WDT
復(fù)位的概念:讓賽跑運動員各自回到自己的起跑線。
硬復(fù)位:用拖車把運動員給拖到起跑線。文章來源:http://www.zghlxwxcb.cn/news/detail-821987.html
軟復(fù)位:運動員自己走到起跑線。文章來源地址http://www.zghlxwxcb.cn/news/detail-821987.html
到了這里,關(guān)于單片機(jī)復(fù)位詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!