复位(stm32f407ZGT6)-属于中断操作
共有三种类型的复位,分别为系统复位、电源复位和备份域复位。
系统复位
除了时钟控制寄存器 CSR 中的复位标志和备份域中的寄存器外,系统复位会将其它全部寄
存器都复位为复位值。
只要发生以下事件之一,就会产生系统复位:
1. NRST 引脚低电平(外部复位)
2. 窗口看门狗计数结束(WWDG 复位)
3. 独立看门狗计数结束(IWDG 复位)
4. 软件复位(SW 复位)(请参见软件复位)
5. 低功耗管理复位(请参见低功耗管理复位)
NRST 引脚低电平(外部复位-不可屏蔽的硬件中断)
这两个RESET的信号都会连接到NRST,简单理解就是说当RESET输出低电平小于0.8V并且持续100ns,stm32就会复位,先说上图,这个算是一个手动复位RC充电电路,上电的瞬间,电容C12两端电压可以认为是0,RESET会输出低电平,stm32处于复位状态,VCC3.3通过电阻R3给电容充电,当电容C12的电压升高到0.8V以上,stm32退出复位状态进入运行状态。当我们需要手动复位的时候只需要按下按钮,让电容放电,松手后就会重复上述流程,stm32会进行一次复位。
复位电路不止这一个
ISP下载复位
还记得一键下载也需要复位吗,如下图(具体原理参考文章链接)
JTAG下载复位
我在使用ST-LINK进行下载时,就因为这个RESET,让我草了个草
stm32中“拔掉jlink”程序无法正确运行,原因是只拔了仿真器与电脑连接的那端,然后把另外端依然接在板子上
当JLINK在板子上连接的时候,断电情况下,会一直把RESET拉低,导致芯片一直处在复位状态,所以不能正常工作。
把jlink或stlink和stm32板的的连接排线(jtag 接口)也拔掉,这个时候主板就可以工作了。
由于JLINK内部也有电路,调试时会自动产生一个复位信号,让系统恢复默认状态,然后程序开始运行,因此调试下程序应该都能正常启动。
补充:
nTRST(上图的JTRST) 测试系统复位信号
nRESET(RESET、nrst) 目标系统复位信号\
关于目标系统复位信号和测试系统复位信号
测试系统其实指的是目标板上JTAG扫描链中各个芯片中TAP测试访问端口和边界扫描单元等,nTRST也就是指复位TAP测试访问端口,不复位芯片逻辑。而目标系统就是目标板上除了测试功能以外的正常逻辑了。nRESET连接FPGA的PROG_B(复位配置逻辑电路,但是这时的复位是由上位PC机使用控制软件通过JTAG电缆或控制器来控制的,条件缺一不可。源自文章
看门狗复位(WDT)
(此部分参考CMS80F231系列)
看门狗复位是系统的一种保护设置。在正常状态下,由程序将看门狗定时器清零。若出错,系统处于未知
状态,看门狗定时器溢出,此时系统复位。看门狗复位后,系统重启进入正常状态。
WDT 的计数器不可被寻址,在上电复位结束后程序运行时就开始计数,设置 WDT 寄存器时建议将 WDT
计数器清除,以便准确控制 WDT 的溢出时间。
看门狗复位的时序如下:
- 看门狗定时器状态:系统检测看门狗定时器是否溢出,若溢出,则系统复位;
- 初始化:所有的系统寄存器被置为默认状态;
- 程序:复位完成,程序开始从 0000H 运行。
WDT 溢出后复位 CPU 与所有的寄存器,1 个 Tsys 后程序立即从 0000H 开始执行。WDT 复位不会重新
进行上电复位配置。
WDT 的时钟源由系统时钟(SystemClock)提供,WDT 计数器的计时基本周期为 Tsys。
看门狗的溢出时间可由程序设置,在 CKCON 寄存器 WDS2-WTS0 两位可选择溢出时间。
(stm32f407ZGT6)
此部分原文链接:https://blog.csdn.net/weixin_44403365/article/details/116991749
看门狗复位:独立看门狗和窗口看门狗。
独立看门狗
STM32的独立看门狗由内部专门的40Khz低速时钟驱动,即主时钟发生故障,它也仍然有效,这里我们需要注意独立看门狗的时钟不是准确的40Khz,而是在30~60Khz之间变化的一个时钟,只是我们估算以40Khz来计算,看门狗对时间要求不是很精确,时钟有点偏差还是可以接受的。
工作原理:
在**键值寄存器(IWDG_KR)**中写入0XCCCC,开始启用独立看门狗,此时计数器开始从其复位值OXFFF递减计数,当计数器计数到末尾0X000的时候,会产生一个复位信号(IWDG_RESET),无论何时,只要寄存器IWDG_KR中被写入0XAAAA,IWDG_RLR中的值就会被重新加载到计数器中从而避免产生看门狗复位。
还有两个寄存器,一个预分频寄存器(IWDG_PR)。该寄存器是用来设置看门狗的时钟分频系数,最低为4,最高位256,虽然是32位寄存器,我们只使用了最低3位。
另一个重装载寄存器。该寄存器用来保存重装载到计数器中的值。该寄存器也是一个 32
位寄存器,但是只有低 12 位是有效的。
**预分频寄存器(IWDG_PR)和重载寄存器(IWDG_RLR)**的写保护 :IWDG_PR和IWDG_RLR寄存器具有写保护功能,要想修改这两个寄存器的值,首先要向IWDG_KR中写入0X5555。以不同的值写入这个寄存器或者重装载(写入0XAAAA)都会重新启动写保护。
独立看门狗由内部低速时钟LSI提供计数时钟,8 位分频,12位计数,需要定期喂狗(重载数值 ReloadCounter),如果计数值减为0了,还没有重载数值,则会响应复位事件。
启动过程:
向IWDG_KR中写入0X5555
通过这一步我们取消了IWDG_PR和IWDG_RLR的写保护,下一步我们设置他们初值。
设置IWDG_PR和IWDG_RLR的初值。
我们计算一下看门狗的喂狗时间(看门狗溢出时间)计算公式
*Tout=((4*2^prer)rlr)/40
其中Tout就是看门狗溢出时间(单位ms),prer是看门狗时钟预分频值(IWDG_PR值),范围为0~7,rlr位看门狗重载值(IWDG_RLR)。比如我们设置prer为4,rlr的值为625,我们就可以计算得到Tout=64*625/40=1000ms,这样,看门狗的溢出时间就是1S,只要在这一秒钟内,有一次吸入0XAAAA到IWDG_KR,就不会导致看门狗复位(写入多次也是可以的)(由于看门狗的时钟不是准确40Khz,所以喂狗不要太晚,以免发生看门狗复位)。
向IWDG_KR中写入0XAAAA
通过这句可以将重载寄存器(IWDG_RLR)中的计数初值载入到看门狗计数器中(也可以时钟该命令喂狗)。
向IWDG_KR中写入0XCCCC
通过这句我们就启动了STM32的看门狗了,使能了看门狗,在程序里面我们就必须间隔一定的时间就喂狗,否则导致程序复位,利用这一点,我们通过一个LED来指示是否复位,验证独立看门狗
举例:正点原子独立看门狗
源文件:
//时间计算(大概):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); //设置IWDG预分频值:设置IWDG预分频值为64
IWDG_SetReload(rlr); //设置IWDG重装载值
//第二步0XAAAA
IWDG_ReloadCounter(); //按照IWDG重装载寄存器的值重装载IWDG计数器
//第三步0xCCCC
IWDG_Enable(); //使能IWDG
}
//喂独立看门狗
void IWDG_Feed(void)
{
IWDG_ReloadCounter();//reload
}
主函数源文件:
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //按键初始化
delay_ms(500); //让人看得到灭
IWDG_Init(4,625); //与分频数为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]用来存储看门狗的计数器值,
随时更新的,每个窗口看门狗计数周期(4096×2^ WDGTB)减 1。当该计数器的值从 0X40 变
为 0X3F 的时候,将产生看门狗复位。
WDGA 位则是看门狗的激活位,该位由软件置 1,以启动看门狗,并且一定要注意的是该
位一旦设置,就只能在硬件复位后才能清零了。
该位中的 EWI 是提前唤醒中断,也就是在快要产生复位的前一段时间(T[6:0]=0X40)来
提醒我们,需要进行喂狗了,否则将复位!因此,我们一般用该位来设置中断,当窗口看门狗
的计数器值减到 0X40 的时候,如果该位设置,并开启了中断,则会产生中断,我们可以在中
断里面向 WWDG_CR 重新写入计数器的值,来达到喂狗的目的。注意这里在进入中断后,必
须在不大于 1 个窗口看门狗计数周期的时间(在 PCLK1 频率为 36M 且 WDGTB 为 0 的条件下,
该时间为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
最后我们要介绍的是状态寄存器(WWDG_SR),该寄存器用来记录当前是否有提前唤醒
的标志。该寄存器仅有位 0 有效,其他都是保留位。当计数器值达到 40h 时,此位由硬件置 1。
它必须通过软件写 0 来清除。对此位写 1 无效。即使中断未被使能,在计数器的值达到 0X40
的时候,此位也会被置 1。
窗口看门狗工作原理:
窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序
背离正常的运行序列而产生的软件故障。除非递减计数器的值在 T6 位(WWDG->CR 的第六位)
变成 0 前被刷新,看门狗电路在达到预置的时间周期时,会产生一个 MCU 复位。在递减计数
器达到窗口配置寄存器(WWDG->CFR)数值之前,如果 7 位的递减计数器数值(在控制寄存器中)
被刷新, 那么也将产生一个 MCU 复位。这表明递减计数器需要在一个有限的时间窗口中被刷
新。他们的关系可以用图 12.1.1 来说明:
图 12.1.1 中,T[6:0]就是 WWDG_CR 的低七位,W[6:0]即是 WWDG->CFR 的低七位。T[6:0]
就是窗口看门狗的计数器,而 W[6:0]则是窗口看门狗的上窗口,下窗口值是固定的(0X40)。
当窗口看门狗的计数器在上窗口值之外被刷新,或者低于下窗口值都会产生复位。
上窗口值(W[6:0])是由用户自己设定的,根据实际要求来设计窗口值,但是一定要确保
窗口值大于 0X40,否则窗口就不存在了。
窗口看门狗的超时公式如下:
Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /Fpclk1;
其中:
Twwdg:WWDG 超时时间(单位为 ms)
Fpclk1:APB1 的时钟频率(单位为 Khz)
WDGTB:WWDG 的预分频系数
T[5:0]:窗口看门狗的计数器低 6 位
根据上面的公式,假设 Fpclk1=36Mhz,那么可以得到最小-最大超时时间表如表 12.1.1 所
示:
窗口看门狗由APB1(RCC_APB1Periph_WWDG)提供计数时钟,2 位分频,7位计数,需要定期喂狗(更新计数值),如果计数值减为0x40了,还未更新计数值,则会响应复位事件。
步骤如下:
1)使能 WWDG 时钟
2)设置窗口值和分频数
3)开启 WWDG 中断并分组
4) 设置计数器初始值并使能看门狗
5) 编写中断服务函数
正点原子程序:
源程序
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);设置IWDG预分频值
WWDG_SetWindowValue(wr);//设置窗口值
WWDG_Enable(WWDG_CNT); //使能看门狗 , 设置 counter .
WWDG_ClearFlag();//清除提前唤醒中断标志位
WWDG_NVIC_Init();//初始化窗口看门狗 NVIC
WWDG_EnableIT(); //开启窗口看门狗中断
}
//重设置WWDG计数器的值
void WWDG_Set_Counter(u8 cnt)
{
WWDG_Enable(cnt);//使能看门狗 , 设置 counter .
}
//窗口看门狗中断服务程序
void WWDG_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占2,子优先级3,组2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //抢占2,子优先级3,组2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);//NVIC初始化
}
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(WWDG_CNT); //当禁掉此句后,窗口看门狗将产生复位
WWDG_ClearFlag(); //清除提前唤醒中断标志位
LED1=!LED1; //LED状态翻转
}
主程序:
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init();
KEY_Init(); //按键初始化
LED0=0;
delay_ms(300);
WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//计数器值为7f,窗口寄存器为5f,分频数为8
while(1)
{
LED0=1;
}
}
软件复位
可通过查看 RCC 时钟控制和状态寄存器 (RCC_CSR),此部分在下边 中的复位标志确定。
要对器件进行软件复位,必须将 Cortex™-M4F 应用中断和复位控制寄存器中的
SYSRESETREQ 位置 1。有关详细信息,请参见 Cortex™-M4F 技术参考手册。
STM32官方已经将软件复位过程给封装好了,即 NVIC_SystemReset() 函数,
NVIC_SystemReset()函数的内容如下:
/**
\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();
}
}
使用软件复位NVIC_SystemReset()函数时,需在该函数之前加上__set_FAULTMASK(1)语句,表示关闭所有中断的意思;
因为在《Cortex-M3权威指南》中有这么一句话提醒我们:从 SYSRESETREQ 被置为有效,到复位发生器执行复位令,往往会有一个延时。在此延时期间,处理器仍然可以响应中断请求。但我们的本意往往是要让此次执行到此为止,不要再做任何其它事情了。所以,最好在发出复位请求前,先把 FAULTMASK 置位,即关闭所有中断。
低功耗管理复位
引发低功耗管理复位的方式有两种:
1. 进入待机模式时产生复位:
此复位的使能方式是清零用户选项字节中的 nRST_STDBY 位。使能后,只要成功执行
进入待机模式序列,器件就将复位,而非进入待机模式。
2. 进入停止模式时产生复位:
此复位的使能方式是清零用户选项字节中的 nRST_STOP 位。使能后,只要成功执行
进入停止模式序列,器件就将复位,而非进入停止模式。
有关用户选项字节的详细信息,请参见 STM32F40x 和 STM32F41x Flash 编程手册,该手
册可从 ST 销售办事处获取,也可以从 ST 网站 www.st.com 获得文档信息。
电源复位
只要发生以下事件之一,就会产生电源复位:
1. 上电/掉电复位(POR/PDR 复位)或欠压 (BOR) 复位
2. 在退出待机模式时
除备份域内的寄存器以外,电源复位会将其它全部寄存器设置为复位值
这些源均作用于 NRST 引脚,该引脚在复位过程中始终保持低电平。RESET 复位入口向量
在存储器映射中固定在地址 0x0000_0004。
芯片内部的复位信号会在 NRST 引脚上输出。脉冲发生器用于保证最短复位脉冲持续时间,
可确保每个内部复位源的复位脉冲都至少持续 20 μs。对于外部复位,在 NRST 引脚处于低
电平时产生复位脉冲。
下面给出上电复位的正常时序:(此部分参考CMS80F231系列,仅供参考)
-上电:系统检测到电源电压上升并等待其稳定;
- 系统初始化:所有的系统寄存器被置为初始值;
- 振荡器开始工作:振荡器开始提供系统时钟;
- 执行程序:上电结束,程序开始运行。
备份域复位
备份域复位会将所有 RTC 寄存器和 RCC_BDCR 寄存器复位为各自的复位值。BKPSRAM 不受
此复位影响。BKPSRAM 的唯一复位方式是通过 Flash 接口将 Flash 保护等级从 1 切换到 0。
只要发生以下事件之一,就会产生备份域复位:
1. 软件复位,通过将 RCC 备份域控制寄存器 (RCC_BDCR) 中的 BDRST 位置 1 触发。
2. 在电源 VDD 和 VBAT 都已掉电后,其中任何一个又再上电。
复位原因
通过查看控制/状态寄存器(RCC_CSR)中的复位状态标志位识别复位事件来源。
先看一下stm32中文参考手册对RCC_CSR寄存器的描述:
复位的标志位-RCC 时钟控制和状态寄存器 (RCC_CSR)
RCC clock control & status register
偏移地址:0x74
复位值:0x0E00 0000,除复位标志只能通过电源复位进行复位以外,其它通过系统复位进
行复位。
访问:0 <=等待周期 <=3,按字、半字和字节访问
连续访问该寄存器时,则插入等待周期。
位 31 LPWRRSTF:低功耗复位标志 (Low-power reset flag)
发生低功耗管理复位时,由硬件置 1。
通过写入 RMVF 位清零。
0:未发生低功耗管理复位
1:发生低功耗管理复位
有关低功耗管理复位的详细信息,请参见低功耗管理复位。
位 30 WWDGRSTF:窗口看门狗复位标志 (Window watchdog reset flag)
发生窗口看门狗复位时,由硬件置 1。
通过写入 RMVF 位清零。
0:未发生窗口看门狗复位
1:发生窗口看门狗复位
位 29 IWDGRSTF:独立看门狗复位标志 (Independent watchdog reset flag)
发生来自 VDD 域的独立看门狗复位时,由硬件置 1。
通过写入 RMVF 位清零。
0:未发生看门狗复位
1:发生看门狗复位
位 28 SFTRSTF:软件复位标志 (Software reset flag)
发生软件复位时,由硬件置 1。
通过写入 RMVF 位清零。
0:未发生软件复位
1:发生软件复位
位 27 PORRSTF:POR/PDR 复位标志 (POR/PDR reset flag)
发生 POR/PDR 复位时,由硬件置 1。
通过写入 RMVF 位清零。
0:未发生 POR/PDR 复位
1:发生 POR/PDR 复位
位 26 PINRSTF:引脚复位标志 (PIN reset flag)
发生来自 NRST 引脚的复位时,由硬件置 1。
通过写入 RMVF 位清零。
0:未发生来自 NRST 引脚的复位
1:发生来自 NRST 引脚的复位
位 25 BORRSTF:BOR 复位标志 (BOR reset flag)
通过软件写入 RMVF 位清零。
发生 POR/PDR 复位或 BOR 复位时,由硬件置 1。
0:未发生 POR/PDR 复位或 BOR 复位
1:发生 POR/PDR 复位或 BOR 复位
位 24 RMVF:清除复位标志 (Remove reset flag)
由软件置 1,用于将复位标志清零。
0:无操作
1:清零复位标志
位 23:2 保留,必须保持复位值。
位 1 LSIRDY:内部低速振荡器就绪 (Internal low-speed oscillator ready)
由硬件置 1 和清零,用于指示内部 RC 40 kHz 振荡器已稳定。在 LSION 位被清零后,
LSIRDY 将在 3 个 LSI 时钟周期后转为低电平。
0:LSI RC 振荡器未就绪
1:LSI RC 振荡器就绪
位 0 LSION:内部低速振荡器使能 (Internal low-speed oscillator enable)
由软件置 1 和清零。
0:LSI RC 振荡器关闭
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获取复位标志的宏:__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清除复位标志的宏:__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)
代码思路
看了对上面对控制/状态寄存器(RCC_CSR)的描述,代码的思路就已经很明显了,通过RCC_CSR寄存器中的复位状态标志位获取复位事件来源,应用层做复位标志位,最后清除复位标志。
定义复位类型枚举
/* 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复位类型
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;//软件复位
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_PORRST;//这是上电复位
}
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) != RESET)
{
ResetStatusFlag = RCC_RESET_FLAG_PINRST;//这是外部RST管脚复位
}
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();//清除复位标志
return ResetStatusFlag;
}
OKK
上边进行了宏封装,不便于理解的话可看下文操作
复位标志位检索/判断什么原因导致的复位
标志位判断的代码由官方库中给定代码如下:
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;
}
当然判断完后,我们需要将复位类型的标志置位以防后期出现重复多次判断
void RCC_ClearFlag(void);//清除复位执行函数
/**
* @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;
}
在使用时,只需要执行如下语句即可:
if(RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
{
//这是上电复位
}
else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
{
//这是外部RST管脚复位
}
else if (RCC_GetFlagStatus(RCC_FLAG_SFTRST)!= RESET)
{
//这是软件复位
}
RCC_ClearFlag();//清除RCC中复位标志
复位函数
void mcuRestart(void)
{
__set_FAULTMASK(1); //关闭所有中断
NVIC_SystemReset(); //复位
}
软件复位实例一
现象:LD5指示灯闪烁5次,3秒后,MCU执行复位操作。
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不亮 ,进入10次,一亮一灭*/
while (index--) {
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_15);//执行一次,改变一次状态
HAL_Delay(500);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(3000);
HAL_NVIC_SystemReset();//复位
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
软件复位实例二(这个实例想看就看哦,引用文章原文)
Freescale ARM Cortex-M系列软复位的使用方法
“复位”这个词对我们搞嵌入式的同志们来说是再熟悉不过了,不过相比于上电复位和硬件管脚复位等我们常见的复位类型来说,软件复位可能对一些初入门道的菜鸟们来说还是比较陌生的东西(记得当初第一次接触软复位的时候,觉着这个东西真的很奇妙,MCU自己对自己进行复位,想想就觉着很有意思,或许一直就是这种好奇的乐趣让我等屌丝对嵌入式的感情越陷越深吧,哈哈),即使对搞嵌入式的老鸟来说,软复位也是一种很有用的手段。
可能说到这,还是有些人犯迷糊,软件复位到底是个什么东西,它到底有什么用。上面提到我们常见的上电复位和外部管脚Reset复位等严格来讲都是MCU被动的复位(看门狗复位我的定义是把它当做硬件复位来对待,因为实际上看门狗也只不过是MCU内部一个独立的外设模块罢了),即外部的触发条件让MCU完成对系统的复位操作,而软件复位则是通过软件触发让MCU自己对自己进行复位,也就是说通过执行某个指令来触发MCU的系统复位。软件复位的好处也是显而易见的,比如可以用在远程代码升级(一般我们做bootloader的时候通常需要让芯片复位等待通信接口的数据,然后代码升级之后一般也会再次触发复位让芯片重新执行新的用户代码,当然有些需要work on-line固件升级的领域除外),或者程序监控(如果监测到某些错误可以触发软件复位让芯片重新执行来消除一些异常,类似于我们手机死机拆电池,呵呵,当然它还达不到彻底断电的效果),当然软件复位也给广大做调试器仿真器的厂家提供了极大的便利,我们会发现调试接口的Reset管脚不接的情况下我们也能正常对芯片进行调试和下载,这就是软件复位的功劳了,哈哈。
说到这里,可能会有些聪明的人提到,我们把PC指针直接指向代码的起始地址让它重头开始执行不就完了,咳咳,的确有点投机取巧,不过这种方法只能是让内核重新执行却无法让这个MCU系统复位,所以其作用还是有限的。而本篇的主角,ARM Cortex-M系列MCU则为此专门划出了一个寄存器用来做这种软件复位功能,即SCB_AIRCR寄存器。我们只需置位该寄存器的SYSRESETREQ位即可把内核送往系统复位发生器的请求线置为有效,从而引起整个芯片系统的复位,当然值得一提的是,这种复位时一种系统复位,会把大多数的外设模块复位(有些个别的外设寄存器只能在上电复位的情况下才能复位),但是却不会复位芯片内部的调试模块,所以我们在IAR或者Keil的调试环境下也可以正常监控和使用软件复位,非常方便。
好了,说了这么多,该上硬菜了,呵呵。我下面直接把在Freescale Cortex-M0+的KL系列上的软件复位的代码贴上来了,其中对SCB_AIRCR寄存器配置中,前面的0x5FA << SCB_AIRCR_VECTKEY_SHIFT为该寄存器的“钥匙”,不得不说ARM将该寄存器保护的很好,还为其配了把钥匙,需要这个钥匙才能对其他的位进行操作,然后置位SYSRESETREQ即可实现软件复位。另外,在这个配置操作的前后,也分别添加了两个__DSB()指令,该指令用来保证在软件复位之前和之后保证内存数据操作都已完成,避免在有些数据还没有来的及更新到目标地址或者目标寄存器前就触发芯片复位从而造成数据丢失,这个还是很重要的,重点提一下。
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();
}
}
下图为我做的一个测试,通过串口发送“A”字符来触发软件复位,效果还是刚刚的,如果大家手头有相应工具不妨试上一试,呵呵。
硬复位和软复位
软复位信号名称中通常包含soft,硬复位信号名称中通常包含hard。
硬复位:通过SOC上的GPIO向IC的复位引脚输入高低电平信号触发复位;(IC初始化时对复位引脚直接操作)
硬复位:
方法一,将TFTLCD 的 RST 同 STM32F1 的 RESET 连接在一起,只要按下开发板的 RESET 键, 就会对 LCD 进行硬复位。
方法二,将TFTLCD 的 RST 同 STM32F1 的 GPIO连接在一起,先拉低,延迟100us,然后
再释放RST(拉高),完成复位。
void LCD_Init(void)
{
SPI1_Init();
LCD_Reset();//直接将LCD的reset引脚接到芯片的nrst引脚,可以实现,中间就是出点问题,就是直接上电就白屏,必须手动按一下复位键,这也不符合上电复位的原理呀
}
void LCD_Reset(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_11);//清0
delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_11);//置1
delay_ms(100);
}
软复位:通过向IC的寄存器中写值触发复位;(某些IC在ESD测试中crash通过复位寄存器进行复位)
硬件复位,即产生脉冲(无论是power on上电时自产生,还是手动、或外部看门狗
产生;高/低电平有效、持续时间、沿等要求,视具体芯片要求而定),加至芯片内各触发
器的Reset端;
软复位,即由用户代码决定复位时机。可能是让程序跳回至起点,或配合片内复位模
块,产生等同于硬件复位的效果(如片内集成了WDT
复位的概念:让赛跑运动员各自回到自己的起跑线。
硬复位:用拖车把运动员给拖到起跑线。文章来源:https://www.toymoban.com/news/detail-821987.html
软复位:运动员自己走到起跑线。文章来源地址https://www.toymoban.com/news/detail-821987.html
到了这里,关于单片机复位详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!