良许Linux教程网 干货合集 单片机变量不被初始化的实现方法

单片机变量不被初始化的实现方法

最近有读者问了这样一个问题:当看门狗复位之后,是否能够保持复位之前的状态?

这种问题,或者类似的问题,相信很多小伙伴都曾经经历过,特别是有多年单片机开发经验的小伙伴。实际上,现实世界中的许多项目确实对于保持复位之前的状态有着这样的需求。

实现这一目标有多种方法,这里给大家介绍在Keil和IAR编译环境下实现单片机变量不被初始化的方法。

在处理器复位方面,有很多方式可以进行复位。下面结合STM单片机来解析复位的来源:

STM32的复位可以分为三类:系统复位、电源复位和后备域复位

系统复位:

\1. NRST引脚上的低电平(外部复位)

\2. 窗口看门狗计数终止(WWDG复位)

\3. 独立看门狗计数终止(IWDG复位)

\4. 软件复位(SW复位)

\5. 低功耗管理复位

电源复位:

\1. 上电/掉电复位(POR/PDR复位)

\2. 从待机模式中返回

备份区域复位:

\1. 软件复位,备份区域复位可由设置备份域控制寄存器(RCC_BDCR)中的BDRST位产生。

\2. 在VDD和VBAT两者掉电的前提下, VDD或VBAT上电将引发备份区域复位。

请参考《STM32复位来源,Cotrex-M系统与内核复位区别》。

修饰符

实现处理器复位而变量不被初始化方法之前,让我们先了解一下修饰符的知识。

修饰符是用于限定类型以及类型成员申明的一种符号。如C语言中常见的修饰符:

1.static静态修饰符:修饰变量,函数。作用域:变量仅仅在本文件可见,函数在本文件可以被调用;

2.extern声明修饰符:修饰变量,函数。修饰变量时候,变量的声明在外面;

3.const常量修饰符:修饰变量,函数。修饰变量时候,不能被重复赋值,只能放在只读段中;

4.volatile不稳定变量修饰符:这个变量不好翻译,在c中的作用大概有两点意思:A.表示变量是易失的,易变的; B.强制访存操作,防止编译器去优化,告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存。

其实,在C++ JAVA中还有更多:

public公共访问修饰符、private私有访问修饰符、protected保护访问修饰符、friendly、abstract等。

而本文会使用到一个修饰符:

__no_init

虽然这个修饰符不是C语言标准的修饰符,但在Keil、IAR这种集成开发环境中,他们支持这种修饰符。

而本文说的修饰符,修饰的变量位于RAM中:

在默认情况下,编译器会将其变量存放在主RAM中,并在启动时对其进行初始化。而__no_init类型修饰符使编译器把变量放在非易失RAM区中,在启动时也不对它们进行初始化,也就是说__no_init在系统启动时不初始化变量。

Keil中__no_init的配置和使用

在Keil中,__no_init不是标准的修饰符,需要进行配置,配置之后就可以使用了。

1.宏定义__no_init

#define __no_init __attribute__((zero_init))

2.在工程选项中配置__no_init

Project -> Options for Targets -> Target,里面右下有个NoInit,这个就是需要我们配置的区域(可设定某一区域);

image-20231030221107784
image-20231030221107784

3.使用方法

比如定义变量:Cnt_NoInit

__no_init uint16_t Cnt_NoInit;

提示:不能初始化这个变量(也就是定义时不要赋值)。

IAR中中使用__no_init

在IAR中“__no_init”属于是一个关键字,你会发现在使用这个修饰符之后,字体都是关键字颜色。

直接使用即可,类似上面定义一个不被初始化的变量:

__no_init uint16_t Cnt_NoInit;

参考源码

这里给大家分享两个简单的Demo(源码),Keil和IAR工程实现的功能一样。

源代码:

__no_inituint16_t Cnt_NoInit;
uint16_t Cnt_Init = 100;

int main(void)
{
  System_Initializes();
  printf("Start...\n");  //复位打印

  while(1)
  {
    printf("Cnt_NoInit = %d\n", Cnt_NoInit);   //打印变量
    Cnt_NoInit++;

    if(Cnt_NoInit > 1000)
    {
      Cnt_NoInit = 0;
    }

    printf("Cnt_Init = %d\n", Cnt_Init);
    Cnt_Init++;
    if(Cnt_Init > 1000)
    {
      Cnt_Init = 0;
    }

    LED_ON;  
    TIMDelay_Nms(500);
    LED_OFF;
    TIMDelay_Nms(500);
    NVIC_SystemReset(); //系统复位
  }
}

被Cnt_NoInit修饰,则会打印如下消息:

Start...
Cnt_NoInit = 0
Cnt_Init = 100

Start...
Cnt_NoInit = 1
Cnt_Init = 100

Start...
Cnt_NoInit = 2
Cnt_Init = 100

Start...
Cnt_NoInit = 3
Cnt_Init = 100

如果不被修饰:

uint16_t Cnt_NoInit;
uint16_t Cnt_Init = 100;

如果不被修饰:则会打印如下消息:

Start...
Cnt_NoInit = 0
Cnt_Init = 100

Start...
Cnt_NoInit = 0
Cnt_Init = 100

Start...
Cnt_NoInit = 0
Cnt_Init = 100

Start...
Cnt_NoInit = 0
Cnt_Init = 100

相信聪明的你,看了上面例子会明白为什么没有初始化的变量“Cnt_NoInit”在变化,而初始化了的“Cnt_Init”一直不变。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部