目录
1、理论
众所周知,单片机复位后变量数值会自动初始化,以华大半导体HC32L136为例,具有 7 个复位信号来源,每个复位信号都可以让 CPU 重新运行,绝大多数寄存器会被复位到复位值,程序会从复位向量处开始执行。
- 数字区域上电掉电复位 POR
- 外部 Reset PAD,低电平为复位信号
- WDT 复位
- PCA 复位
- LVD 低电压复位
- Cortex-M0+ SYSRESETREQ 软件复位
- Cortex-M0+ LOCKUP 硬件复位
每个复位源由相应的复位标志进行指示,复位标志均由硬件置位,需要用户软件清零。
华大半导体各区域的复位来源如下图所示:
本篇博客主要讲授华大半导(STM32、C51等单片机均可适用)复位(以看门狗复位为例)后变量数据保存的方法。
这里将用到__not_init属性,其用于变量声明,可禁止系统启动时变量的初始化,有了__not_init属性,编译器只给指定变量分配空间,不会再初始化。
__not_init的两种定义方式如下所示:
-
方式
1:不指定存储位置,由编译器分配
-
__no_init 类型 变量名;
///< 例如:__no_init uint8_t cou_num;
-
方式
2:指定存储位置
-
__no_init 类型 变量名 @地址;
///< 例如:__no_init uint8_t cou_num @0x20000000;
2、实践
实践描述:使用__no_init属性创建一个变量cou_num,其将数据存储在SRAM中,每隔300毫秒自加1并通过串口打印输出数值,当检测到上电复位和按键复位后,变量cou_num数值置为0,在看门狗复位下变量cou_num数值不变。
第1步:配置串口引脚、串口使能和串口中断,代码如下所示:
-
///< 串口引脚配置
-
static void App_PortInit(void)
-
{
-
stc_gpio_cfg_t stcGpioCfg;
-
-
DDL_ZERO_STRUCT(stcGpioCfg);
-
///< 使能GPIO模块时钟
-
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);
-
-
///< 配置PA02端口为URART1_TX
-
stcGpioCfg.enDir = GpioDirOut;
-
Gpio_Init(GpioPortA, GpioPin2, &stcGpioCfg);
-
Gpio_SetAfMode(GpioPortA, GpioPin2, GpioAf1);
-
}
-
-
///< 串口配置
-
static void App_UartCfg(void)
-
{
-
stc_uart_cfg_t stcCfg;
-
-
DDL_ZERO_STRUCT(stcCfg);
-
-
///< 开启UART1外设时钟
-
Sysctrl_SetPeripheralGate(SysctrlPeripheralUart1,TRUE);
-
-
///< UART1初始化
-
stcCfg.enRunMode = UartMskMode3;
///< 模式3
-
stcCfg.enStopBit = UartMsk1bit;
///< 1bit停止位
-
stcCfg.enMmdorCk = UartMskEven;
///< 偶检验
-
stcCfg.stcBaud.u32Baud =
9600;
///< 波特率9600 注意误差
-
stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;
///< 通道采样分频配置
-
stcCfg.stcBaud.u32Pclk = Sysctrl_GetPClkFreq();
///< 获得外设时钟(PCLK)频率值
-
Uart_Init(M0P_UART1, &stcCfg);
///< 串口初始化
-
-
///< UART1中断使能
-
Uart_ClrStatus(M0P_UART1,UartTC);
///< 清发送请求
-
Uart_EnableIrq(M0P_UART1,UartTxIrq);
///< 使能串口发送中断
-
EnableNvic(UART1_IRQn, IrqLevel3, TRUE);
///< 系统中断使能
-
}
-
-
///< UART1中断函数
-
void Uart1_IRQHandler(void)
-
{
-
///< UART1数据发送
-
if(Uart_GetStatus(M0P_UART1, UartTC))
-
{
-
///< 清中断状态位
-
Uart_ClrStatus(M0P_UART1, UartTC);
-
}
-
}
第2步:配置看门狗复位,每隔820毫秒若没有喂狗,则复位,代码如下所示:
-
///< WDT初始化配置
-
static void App_WdtInit(void)
-
{
-
///< 开启WDT外设时钟
-
Sysctrl_SetPeripheralGate(SysctrlPeripheralWdt,TRUE);
-
///< WDT 初始化,喂狗时间:820ms
-
Wdt_Init(WdtResetEn, WdtT820ms);
-
}
第3步:使用__no_init属性定义cou_num变量,将数组存储在SRAM寄存器0x20001000中,代码如下所示:
__no_init uint8_t cou_num @ 0x20001000;
第4步:添加上电复位源和RESET脚复位源检测,当检测到其中之一个复位的时候,cou_num置为0,代码如下所示:
-
int32_t main(
void)
-
{
-
char * data_buf = (
char *)
malloc(
sizeof(
char) *
19);
-
-
///< 串口引脚配置
-
App_PortInit();
-
-
///< 串口配置
-
App_UartCfg();
-
-
///< WDT初始化
-
App_WdtInit();
-
-
///< 启动 WDT
-
Wdt_Start();
-
-
///< 当上电复位或者RESET脚复位后cou_num为0,看门狗复位数值不变
-
if((Reset_GetFlag(ResetFlagMskPor5V) ==
1) || (Reset_GetFlag(ResetFlagMskRstb) ==
1))
-
{
-
cou_num =
0;
-
-
Reset_ClearFlag(ResetFlagMskPor5V);
-
Reset_ClearFlag(ResetFlagMskRstb);
-
}
-
-
while (
1)
-
{
-
cou_num = cou_num +
1;
-
-
delay1ms(
300);
-
-
///< 开启喂狗后,将不会产生复位
-
//Wdt_Feed();
-
-
sprintf(data_buf,
"numerical value:%d\n",cou_num);
-
-
for(
int8_t i =
0;i <
19;i++)
-
{
-
Uart_SendDataIt(M0P_UART1,data_buf[i]);
-
delay1ms(
5);
-
}
-
}
-
}
运行效果如下所示:
可见虽然看门狗每隔820毫秒复位一次,但是cou_num数值不收影响,但是也可以看出cou_num数值中间存在丢失,例如没有打印输出数值3,主要原因是运行到此数时,恰巧看门狗复位,所以串口未来得及打印,但是不影响cou_num计数。
转载:https://blog.csdn.net/m0_38106923/article/details/106108546