本帖最后由 slotg 于 2019-12-29 23:48 编辑
学习笔记+适用于STM32F0系列的STM32Cube固件例程
学习笔记+STM32F0系列的STM32Cube固件例程 DMA
程序中有时候会因为程序上的 bug 或是外部杂讯的影响让程序跑飞或是卡死,因此大部份的 MCU 芯片里面都会有 WATCHDOG 看门狗的设计,看门狗就是一个倒数的计数器,当计数器减为 0 时就会对 MCU 做复位 (RESET) 动作,在 STM32F0 里面有2种看门狗分别是 IWDG 与 WWDG。IWDG 是 Independent watchdog 独立看门狗,它使用了内部 LSI 做为时钟驱动,即使主时钟停止仍可以工作。
IWDG_Reset
在固件库 Projects\STM32F030R8-Nucleo\Examples\IWDG\IWDG_Reset 里面的这一个例程:
我们从 main() 回圈开始了解一下流程:
HAL_Init();
/* Configure the system clock to 48 MHz */
SystemClock_Config();
/* Configure LED2 */
BSP_LED_Init(LED2);
在基本的设定之后设定了 使用者按键 为中断触发的模式:
/* Configure User push-button */
BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
由于 IWDG 是使用内部 LSI 时钟驱动,频率约为 40kHz,而这个频率的误差是很大的,为了准确的掌握喂狗时间因此使用 TIM14 去量测 LSI 时钟的真实频率再来设定计数参数:
例程中设定 IWDG 的计数时间为 1秒钟,也就是程序要在 1秒钟的周期内去重新设定计数值(喂狗),否则当计数值减为0之后就会复位 MCU。
/*##-2- Get the LSI frequency: TIM14 is used to measure the LSI frequency ###*/
uwLsiFreq = GetLSIFrequency();
/*##-3- Configure & Start the IWDG peripheral #########################################*/
/* Set counter reload value to obtain 1 sec. IWDG TimeOut.
IWDG counter clock Frequency = uwLsiFreq
Set Prescaler to 32 (IWDG_PRESCALER_32)
Timeout Period = (Reload Counter Value * 32) / uwLsiFreq
So Set Reload Counter Value = (1 * uwLsiFreq) / 32 */
IwdgHandle.Instance = IWDG;
IwdgHandle.Init.Prescaler = IWDG_PRESCALER_32;
IwdgHandle.Init.Reload = (uwLsiFreq / 32);
IwdgHandle.Init.Window = IWDG_WINDOW_DISABLE;
由于 IWDG 的计数时间是 1秒钟,因此在 while(1) 主回圈中我们以 990ms 周期喂狗一次,并反转 LD2 状态。
/* Infinite loop */
while (1)
{
/* Toggle LED2 */
BSP_LED_Toggle(LED2);
/* Insert 990 ms delay */
HAL_Delay(990);
/* Refresh IWDG: reload counter */
if(HAL_IWDG_Refresh(&IwdgHandle) != HAL_OK)
{
/* Refresh Error */
Error_Handler();
}
}
当我们按下使用者键时会产生按键中断,中断函数里关闭了 LD2 显示,并对地址 0x00040001 写入数据 0xFF,由于这个地址的写入动作是无效的因此会产生一个 Hardfault 错误事件。
void EXTI4_15_IRQHandler(void)
{
/* Failure is generated by user, turn LED2 off */
BSP_LED_Off(LED2);
/* As the following address is invalid (not mapped), a Hardfault exception
will be generated with an infinite loop and when the IWDG counter falls to 0
the IWDG reset occurs */
*(__IO uint32_t *) 0x00040001 = 0xFF;
}
在 Hardfault 响应函数中我们放了一个无限回圈让程序回不去。
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
由于程序回不去了因此主回圈就没有办法喂狗,所以在约 1秒钟内 IWDG 会复位 MCU,而在 main() 开始运行的代码中有一个判断语句判断程序的启动原因是否是因为 IWDG 所产生的?是的话点亮 LD2 4秒钟。
/*##-1- Check if the system has resumed from IWDG reset ####################*/
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET)
{
/* IWDGRST flag set: Turn LED2 on */
BSP_LED_On(LED2);
/* Insert 4s delay */
HAL_Delay(4000);
/* Notification done: Turn LED2 off */
BSP_LED_Off(LED2);
}
所以这个例程的动作是平常时 LD2 会以 0.99秒的周期反转状态,当按下使用者按键后 LD2 立即关闭,约 1秒时间内 LD2 亮起 4秒的时间,然后又回到以 0.99秒的周期闪烁。
|