电子说
后台有许多读者留言说先弄点干货。今天应读者要求,我们先来一篇干货。大家有什么要求,欢迎留言,关于MCU的应用、测试要求,我们都会尽量满足。
在上两期文章和视频中,为了公平起见,所有的MCU使用的是同一个工程程序,(不同的MCU,时钟和GPIO的配置略有不同,使用宏定义区分MCU),除了使用滴答时钟和基本GPIO操作外,没有任何抗干扰手段,全靠MCU内部自身的抗干扰能力进行的测试。结果,只有芯源CW32MCU没有彻底死机外,其它均有死机现象。
这种死机现象,在我们实际开发产品时,是禁止发生的。为了对付这种干扰,除了硬件上有些技术对策,那软件上又有些什么呢?
当然是我们最熟悉的看门狗了。“看门狗”这个神器在“古老的年代”51时期,那是没有的,需要在外面加一个“昂贵”的芯片来实现。当然,现在新时代,所有的ARM MCU基本上都标配了看门狗外设。
看门狗是啥,我们来看一下,STM32芯片的用户手册,关于看门狗的介绍。
▲图1
这里我们就不详细展开其内容了。直接来看核心代码。
//摘要: /* 系统时钟,使用内部高速HSI倍数,系统时钟为48M。 */ //Programed by Cache.Lee 2023.1.4 #include "stm32f0xx.h" #include "stm32f0xx_gpio.h" //GPIOA #define SEGA GPIO_Pin_10 #define SEGB GPIO_Pin_9 #define SEGC GPIO_Pin_8 //GPIOB #define SEGD GPIO_Pin_14 #define SEGE GPIO_Pin_15 //GPIOA #define SEGF GPIO_Pin_11 #define SEGG GPIO_Pin_12 //GPIOB #define SEGDP GPIO_Pin_13 //num:需要显示的数字,no:0显示左边数码管,1显示右边数码管 void SEG_DisplayNum(unsigned int num, unsigned int no) { GPIO_ResetBits(GPIOA,0xffff);//关段码、位码 GPIO_ResetBits(GPIOB,0xffff);//关段码、位码 switch(num) //开段码 { case 0: //ABCDEF GPIO_SetBits(GPIOA,SEGA|SEGB|SEGC|SEGF); GPIO_SetBits(GPIOB,SEGD|SEGE); break; case 1: //BC GPIO_SetBits(GPIOA,SEGB|SEGC); break; case 2: //ABDEG GPIO_SetBits(GPIOA,SEGA|SEGB|SEGG); GPIO_SetBits(GPIOB,SEGD|SEGE); break; case 3: //ABCDG GPIO_SetBits(GPIOA,SEGA|SEGB|SEGC|SEGG); GPIO_SetBits(GPIOB,SEGD); break; case 4://BCFG GPIO_SetBits(GPIOA,SEGF|SEGB|SEGC|SEGG); break; case 5://ACDFG GPIO_SetBits(GPIOA,SEGA|SEGC|SEGG|SEGF); GPIO_SetBits(GPIOB,SEGD); break; case 6: //ACDEFG GPIO_SetBits(GPIOA,SEGA|SEGC|SEGG|SEGF); GPIO_SetBits(GPIOB,SEGD|SEGE); break; case 7: //ABC GPIO_SetBits(GPIOA,SEGA|SEGB|SEGC); break; case 8: //ABCDEFG GPIO_SetBits(GPIOA,SEGA|SEGB|SEGC|SEGG|SEGF); GPIO_SetBits(GPIOB,SEGD|SEGE); break; case 9: //ABCDFG GPIO_SetBits(GPIOA,SEGA|SEGB|SEGC|SEGG|SEGF); GPIO_SetBits(GPIOB,SEGD); break; case 10: //DP 显示DP GPIO_SetBits(GPIOB,SEGDP); break; default: break; } if(no==1) GPIO_SetBits(GPIOB,GPIO_Pin_12); else GPIO_SetBits(GPIOB,GPIO_Pin_11);//关位码 } void GPIOInit(void) { GPIO_InitTypeDef GPIO_InitStructure; //数码管断码位码 IO初始化 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; GPIO_Init(GPIOB, &GPIO_InitStructure); } int main(void) { unsigned long i; unsigned int num=0; for(i=0;i<60000;i++); //上电延时 GPIOInit(); /* IWDG timeout equal to 250 ms (the timeout may varies due to LSI frequency dispersion) */ /* Enable write access to IWDG_PR and IWDG_RLR registers */ IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); /* IWDG counter clock: LSI/32 */ IWDG_SetPrescaler(IWDG_Prescaler_32); /* Set counter reload value to obtain 250ms IWDG TimeOut. Counter Reload Value = 250ms/IWDG counter clock period = 250ms / (LSI/32) = 0.25s / (LsiFreq/32) = LsiFreq/(32 * 4) = LsiFreq/128 */ IWDG_SetReload(40000/128); /* Reload IWDG counter */ IWDG_ReloadCounter(); /* Enable IWDG (the LSI oscillator will be enabled by hardware) */ IWDG_Enable(); SEG_DisplayNum(10,1); for(i=0;i<60000;i++); while(1) { num++; if(num>=100)num=0; SEG_DisplayNum(num/10,0); for(i=0;i<60000;i++); //延时 SEG_DisplayNum(num%10,1); for(i=0;i<60000;i++); //延时 SEG_DisplayNum(num/10,0); for(i=0;i<60000;i++); //延时 IWDG_ReloadCounter(); //喂狗 SEG_DisplayNum(num%10,1); for(i=0;i<60000;i++); //延时 SEG_DisplayNum(num/10,0); for(i=0;i<60000;i++); //延时 SEG_DisplayNum(num%10,1); for(i=0;i<60000;i++); //延时 IWDG_ReloadCounter(); //喂狗 } }
这里的代码与上期代码不同,我们使用官方标准库来重新编写。其中数码管的动态扫描没有使用滴答时钟,而是在主程序中直接用延时来完成。区别于之前的代码,我们增加了独立看门狗的功能。看门狗的喂狗操作在MAIN函数的大循环里,数码管的动态扫描中实现。
当程序发生死机时,MAIN函数的大循环将暂停运行,数码管随机显示最近一次数值,不进行动态扫描,所以,只有一位数码管显示。同时,喂狗暂停。当看门狗时间到,将发生看门狗复位操作,系统将重新复位运行。这样程序就实现了看门狗复位功能。
在实验中,由于打狗棒电压干扰的威力巨大,STM32芯片被打坏了几个引脚。驱动A、F、G的端口功能异常,而且芯片略烫,应该是引脚被打坏了。但不影响下载,其它位码显示正常。
除了看门狗复位,还有一种软件复位方式。当MCU发生硬件失效时,会进入Hardfault中数函数。Hardfault是优先级别为-1的固定类型中断,无需初始化设置。常常在MCU死机时,不知明的会进入Hardfault中断。因此,在Hardfault中断函数中,添加软件复位功能也是一种防死机现象的方法。代码如下:
void HardFault_Handler(void) { unsigned int j; /* Go to infinite loop when Hard Fault exception occurs */ while (1) { SEG_DisplayNum(10,0); for(j=0;j<60000;j++); for(j=0;j<60000;j++); for(j=0;j<60000;j++); for(j=0;j<60000;j++); for(j=0;j<60000;j++); for(j=0;j<60000;j++); NVIC_SystemReset(); } }
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !