国民技术N32G45X低功耗问题之更改全局变量和栈在RAM中的地址

最近项目中用的MCU是国民技术的N32G45X,MCU的资源很丰富,RAM也很大,配套的库和例程也比较全,用起来还是不错的,但是在调试低功耗的时候还是费了不少劲。

首先由于项目低功耗需要,只能选择停机模式2(STOP2)。根据数据手册的描述,在此模式下外部低速时钟开启, RTC运行, R-SRAM保持,所有I/O状态保持,独立看门狗处于关闭状态,25℃的条件下电流为10个微安。

用的这款MCU的RAM是144KB,分为SRAM(128KB)和R-SRAM( Retention SRAM – 16KB),如果不考虑低功耗的话资源还是很充足的,R-SRAM的总线地址与 SRAM 是连续相接的,不用做特别的区分。但是在STOP2模式下SRAM 数据不能保持,R-SRAM 可以保持数据。这也就意味着全局变量和栈必须放在R-SRAM中。栈的作用是用于局部变量、函数形参、函数调用时的现场保护和返回地址,以及进入中断函数前和中断嵌套等的开销,如果栈内容丢失,退出STOP2模式后找不到函数入口,就无法运行。

进入STOP2模式的函数是:

//while(1)中直接调用
PWR_EnterSTOP2Mode(PWR_STOPENTRY_WFI);

//通过函数调用
void Pwr_Test(void)
{
	PWR_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
}

如上所示,使用官方的STOP例程main函数while(1)里面直接调用进入STOP2模式的函数,唤醒后可以继续进入低功耗,但是如果在while(1)中通过函数调用唤醒后就会运行异常,就是因为栈内容丢失。

keil中更改栈的地址可以通过以下两种方式:

一:直接在启动文件修改:

在这里插入图片描述

二:通过分散加载文件.sct修改

①先把未修改之前编译出来的sct文件复制出来,直接在文件中修改

将:

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

LR_IROM1 0x0800A000 0x00040000  {    ; load region size_region
  ER_IROM1 0x0800A000 0x00040000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00024000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

修改为:

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

LR_IROM1 0x0800A000 0x00040000  {    ; load region size_region
  ER_IROM1 0x0800A000 0x00040000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00024000  {  ; RW data
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x20022000 UNINIT 0x00001000 {  ; STACK ADDRESS
  .ANY (+RW +ZI)
  startup_n32g45x.o (STACK)
  }
}

堆没有修改,因为项目中除了sota升级部分,没有用到malloc来动态获取内存;sct文件中的栈大小要跟启动文件中的大小一致。

②在keil中Linker中选定刚才修改后的sct文件
在这里插入图片描述
更改栈地址编译后查看map文件会发现,栈的地址已经更改成功了,堆栈空间不连续,栈向下生长,范围0x2002200至0x2002300。

    __heap_base                              0x20000880   Data           0  startup_n32g45x.o(HEAP)
    __heap_limit                             0x20001880   Data           0  startup_n32g45x.o(HEAP)
    //
    //通过__attribute__((at(XXXXXXXX)))定义的内容
    //
    __initial_sp                             0x20023000   Data           0  startup_n32g45x.o(STACK)

而在未修改栈地址时生成的map文件可以看到堆栈地址空间是连续的,堆向上生长,栈向下生长,所以堆栈溢出就是堆和栈的空间起了冲突。跟STM32一样,如下:

//未修改栈地址时
    __heap_base                              0x20000b30   Data           0  startup_n32g45x.o(HEAP)
    __heap_limit                             0x20002b30   Data           0  startup_n32g45x.o(HEAP)
    __initial_sp                             0x20004b30   Data           0  startup_n32g45x.o(STACK)

指定全局变量在RAM中的地址

可以用__attribute__((at(0x20020000)))来指定全局变量在RAM中的地址,但是编译后会发现ZI-Data特别大
在这里插入图片描述
在这里插入图片描述
查看map文件中Image component sizes部分会发现有很多的填充字节:
在这里插入图片描述
编译之后的文件ZI-Data大小为141704字节,其中填充字节为125110字节,占绝大部分。

keil用户手册
Keil官网上的用户手册中对__attribute__((at(address))) 生成的内容描述如下:

const int x1 __attribute__((at(0x10000))) = 10; /* RO */
int x2 __attribute__((at(0x12000))) = 10; /* RW */
int x3 __attribute__((at(0x14000))) = 0; /* RVCT 3.1 and earlier: RW.
                                          * RVCT 4.0 and later: ZI. */
int x4 __attribute__((at(0x16000))); /* ZI */

由于我通过__attribute__((at(address))) 定义的全局变量并没有初始化,所以这个全局变量编译后是放在ZI-Data中;而编译器为了把这个变量定义到address这个地址,也就在address这个地址之前产生了大量的填充字节。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值