这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 细说BSS段SRAM清零与否

共1条 1/1 1 跳转至

细说BSS段SRAM清零与否

院士
2026-09-03 15:08:27     打赏

细说BSS段SRAM清零与否

在大多数项目里面,我们并不需要关心变量在SRAM中的状态。当MCU每次上电复位后,我们编写的程序也都会给全局变量进行初始化,防止SRAM中的脏数据污染了我们的变量,导致程序异常跑飞等。不是这个技术知识点不重要,而是Keil等编译工具链已经为我们准备好了一套常用、稳定的SRAM内存初始化策略。不过,也有一些时候,我们更希望SRAM的使用更贴近项目,依项目背景而定制!这也是本帖子的分享目的。

Keil中的BSS段

在Keil编译环境下,BSS段在编译后记录着”未初始化或显式初始化为0的全局变量与静态变量“。这些内容需要在MCU上电复位后由MCU进行清零操作,以保证获得确定的0值。

Keil启动流程

我们以我平时常用的STM32F407VET6芯片为例。它在上电复位后,第一步的操作是初始化堆栈指针,然后就开始跑SystemInit(),紧接着就是跳转到__main()函数。而在__main()函数里面会把所有RAM清空,再加载全局变量和静态变量,之后MCU才会跑到我们自已编写的main()函数里面。我们查看startup_stm32f407xx.s文件就可以在复位回调函数里面看到:

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

Keil中的BSS段不被清零的方法

在默认情况下,BSS段在上电复位的时候会被MCU清零,但项目需求要求在上电复位时无需清零,这时该怎么办呢?Keil编译工具提供了两种方法来定制化解决这个问题。

编译器GUI设置

打开Keil编译器Option设置,将IRAM区域中的NoInit勾选,即表示在__main函数中此块IRAM区域不会被强制初始化为零。

Keil选项IRAM配置01.jpg

我这里为示意,把全部的IRAM空间都列为了”非初始化“,大家按自已的项目需求而设定。

特别注意:

  1. 用此种方法需要勾选Use Memory Layout from Target Dialog选项,否则无效。后面我们也会讲到。

  2. RAM1-3是片外RAM,下方的IRAM1-2才是片上RAM。不要设置错误与混淆。

sct分散加载文件

刚刚的GUI配置方法设置非常简单,但它有它的局限性——它只能配置两块IRAM区域。这在我们实际项目应用中,很难满足定制化与复杂的项目需求。这时就需要依靠sct分散加载文件了。

对于修改sct文件来关闭RAM初始化清零的操作也非常简单,只需要在RAM的定义声明上面添加UNINIT关键字即可。

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08005000 0x0001B000  {    ; load region size_region
  ER_IROM1 0x08005000 0x0001B000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20002000 UNINIT 0x00020000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

特别注意:

  1. 如果使用sct分散文件加载的方式,一定要把Use Memory Layout from Target Dialog选项勾选掉

Keil选项IRAM配置02.jpg

实验验证

有了理论基础,又有硬件环境,所以一定要上手验证一下。于是我编写了下述代码:

uint8_t txbuf[16] = {0};    /* 全局变量数组 */

int main(void)
{
    uint32_t i;
    hw_init();
    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_3);
    SysTick_Config(SystemCoreClock / 1000);

    debug_out(txbuf, 16);

    time_tick = 1000;
    while(time_tick > 0)
    {
        ;
    }

    for (i = 0; i < 16; i++)
    {
        txbuf[i] = i;
    }

    debug_out(txbuf, 16);

    while (1)
    {

    }
}

上述代码的执行逻辑如下:

  1. 先打印全局变量数组txbuf[16]的16字节数据内容

  2. 等待1秒时间间隔

  3. 再将全局变量数组txbuf[16]依次写入0-15

  4. 再打印全局变量数组txbuf[16]的16字节数据内容

根据上面的理论学习,如果我未勾选NoInit的话,全局数组txbuf[16]应该会在上电复位后由编译器对其清零操作。而勾选NoInit后,全局数组txbuf[16]在上电复位后极可能是脏数据。实验结果会与结论保持一致吗?一起来看看我的串口打印内容

Keil选项IRAM配置03.jpg

串口打印内容一共分为6行:

  1. 第1行为未勾选NoInit时的上电打印数据,可以看到全部为0,即编译器已经将其做了清零操作;

  2. 第2行为手动初始化后打印的结果,也符合预期;

  3. 第3行为勾选NoInit时的上电打印数据,果然没有初始化清零操作了,看上去就是脏数据;

  4. 第4行为手动初始化后打印的结果,符合预期;

  5. 第5行为按reset按键后程序重新运行的打印数据。可以看到是上一次初始化的结果,再次验证了编译器未清零操作;

  6. 第6行为手动初始化后打印的结果,符合预期;

总结

在一些无须清零RAM的应用场景,或者需要保留RAM的应用场景下,Keil提供给了程序员控制BSS段的RAM清零与否的有效手段。大家根据自已的项目需求而参与设计实现吧!




关键词: BSS     BSS段     清零    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]