Keil、IAR实现处理器复位而变量不被初始化的方法

有时候,我们有这种需求:处理器复位,要求变量不被初始化。

比如:一个实时统计数据的系统,不想因为某种原因(异常原因死机、看门狗等)复位,而丢失正在计数而未来得及保存的数据。

下面就来讲讲在Keil、 IAR编译环境下,实现该功能的方法。

嵌入式专栏

1

处理器复位
处理器复位的方式有很多种,这里结合STM来讲述MCU复位的来源:

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系统与内核复位区别》。

嵌入式专栏

2

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

修饰符是用于限定类型以及类型成员申明的一种符号。如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在系统启动时不初始化变量。

嵌入式专栏

3

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

1.宏定义__no_init

#define __no_init attribute((zero_init))

2.在工程选项中配置__no_init
Project -> Options for Targets -> Target,里面右下有个NoInit,这个就是需要我们配置的区域(可设定某一区域);
图片

3.使用方法
比如定义变量:Cnt_NoInit

__no_init uint16_t Cnt_NoInit;

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

嵌入式专栏

4

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

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

__no_init uint16_t Cnt_NoInit;

嵌入式专栏

5

参考源码
为方便大家学习,这里分享源代码下载地址:
https://pan.baidu.com/s/1hskScba
(公号不支持外链接,请复制链接到浏览器下载)

两个工程名称:
1.STM32F103ZE(Keil)_复位不初始化变量NoInit
2.STM32F103ZE(IAR)_复位不初始化变量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 = 0Cnt_Init = 100
Start…Cnt_NoInit = 1Cnt_Init = 100
Start…Cnt_NoInit = 2Cnt_Init = 100
Start…Cnt_NoInit = 3Cnt_Init = 100

如果不被修饰:

uint16_t Cnt_NoInit;uint16_t Cnt_Init = 100;
如果不被修饰:则会打印如下消息:

Start…Cnt_NoInit = 0Cnt_Init = 100
Start…Cnt_NoInit = 0Cnt_Init = 100
Start…Cnt_NoInit = 0Cnt_Init = 100
Start…Cnt_NoInit = 0Cnt_Init = 100

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

------------ END ------------

<think>好的,我现在需要帮助用户验证他们在汇编代码中定义的共享内存区域是否正确,具体是SHARED_AREA B1 W1这样的定义。首先,我需要回忆一下同汇编器中的共享内存定义方式,比如在ARM、x86或者其他架构中是否有类似的语法。 用户提到的是“SHARED_AREA B1 W1”,这可能是指定义一个名为SHARED_AREA的区域,B1可能代表块1,W1可能代表宽度或大小?或者是某种属性?同的汇编器可能会有同的语法规则,比如GNU汇编器(GAS)和ARM汇编器在段定义上有所同。 接下来,我需要考虑共享内存在汇编中的常见定义方式。通常,共享内存区域需要在链接脚本(linker script)中进行定义,指定其起始地址、大小和属性(如可读、可写、共享)。但在汇编代码中,可能会使用特定的伪指令来声明段,例如使用.section指令,并设置相应的标志。例如,在GNU汇编中,可能会这样写: .section .shared_memory, "aw", %nobits 这里的“aw”表示可分配和可写,“nobits”表示该段占用文件空间,只在内存中存在。但用户给出的语法“B1 W1”看起来像GNU的语法,可能属于特定的汇编器,比如KeilIAR的工具链? 例如,在ARM的汇编器中,使用AREA指令来定义段: AREA SHARED_AREA, DATA, READWRITE, SHARED 这里的DATA表示数据段,READWRITE是访问权限,SHARED表示该段为共享内存。所以用户提到的“B1 W1”可能对应同的参数,比如B1可能是指对齐方式(比如BYTE_ALIGN 1),而W1可能是指段的属性或宽度?或者B和W代表同的选项? 这时候我需要确认同汇编器的具体语法。假设用户使用的是ARM汇编器,那么正确的AREA指令可能需要指定段名、类型、属性和对齐方式等。例如: AREA SHARED_AREA, DATA, SHARED ALIGN 4 这里的DATA是类型,SHARED是属性,表示该段可被多个进程共享。而B1和W1可能是指同的参数,比如B1可能表示对齐方式为字节对齐,但具体的参数需要查阅相关文档。 可能用户将多个参数简写成了B1和W1,但实际语法可能同。例如,B可能代表基地址,W代表宽度或大小。或者B1是块编号,W1是字大小?这需要更多的上下文信息。 为了验证正确性,我需要指出用户当前的写法是否符合特定汇编器的语法。例如,在ARM汇编中,AREA指令的正确用法需要包括段名、类型、属性,例如: AREA SHARED_AREA, DATA, READWRITE, SHARED=1 但确定用户的环境。可能的错误点在于参数顺序或是否缺少必要的属性。例如,是否遗漏了READWRITE属性,或者SHARED的赋值是否正确。 另外,共享内存的正确性仅在于汇编定义,还需要链接脚本中的配置,确保该区域的地址正确,并且被多个进程或线程正确访问。所以可能需要结合链接脚本进行检查。 总结来说,用户的当前定义“SHARED_AREA B1 W1”可能符合标准汇编语法,需要根据具体汇编器的文档进行调整。比如在ARM中正确的定义可能需要使用AREA指令,并明确指定段类型和属性。同时,需要检查链接脚本是否将该段放置在共享内存区域,并设置正确的访问权限。</think>在汇编语言中定义共享内存区域需要根据具体架构和汇编器语法进行正确配置。对于您提到的定义方式$SHARED\_AREA\ B1\ W1$,其正确性需结合以下要点分析: ### 1. 汇编器语法验证 常见汇编器(如GNU as/ARMCC)使用明确的内存区域定义指令: ```assembly ; ARM汇编示例 AREA SHARED_AREA, DATA, READWRITE, SHARED=1 ALIGN 4 ``` - $AREA$:段定义指令 - $DATA$:声明为数据段 - $READWRITE$:设置读写权限 - $SHARED=1$:关键共享属性声明[^1] ### 2. 参数语义分析 您使用的$B1\ W1$参数需要具体解释: - $B1$:可能表示字节对齐(Byte alignment 1),但标准语法应为$ALIGN\ n$ - $W1$:可能表示字宽(Word size),但通常由架构自动确定 ### 3. 链接脚本配合 共享内存需要链接器支持: ```ld MEMORY { SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 1K } SECTIONS { .shared_memory : { *(.shared_area) } > SHARED_RAM } ``` ### 4. 验证建议 通过以下步骤验证定义正确性: 1. 反编译确认内存区域地址范围 2. 使用调试器测试多核/多进程访问 3. 检查MMU/MPU的内存保护配置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值