[APM32F0] APM32F003应用实例: 在keil MDK中定义非初始化(noini)变量的实际应用

[复制链接]
1945|2
 楼主| david-lau 发表于 2024-8-8 17:44 | 显示全部楼层 |阅读模式
本帖最后由 david-lau 于 2024-8-8 17:53 编辑

在单片机编程中,变量未初始化是一个常见的问题,有时会导致程序出现一些难以预料的行为。不过,在某些情况下,这种"未初始化"的特性也可能会带来一些意想不到的好处。本文将探讨APM32F003微控制器中,未初始化变量得的一些实际应用场景。

APM32F003 RAM的特殊初始化机制

APM32F003是一款基于ARM Cortex-M0+内核的32位微控制器。对于RAM存储区有一个特殊的初始化机制,如果芯片保持不断电,APM32F003的RAM在复位或休眠唤醒时并不一定要是全部初始化为0,还可以保留之前的值。这意味着未初始化的变量会保留上次程序运行时的值。

这种行为可能会在某些情况下引起困扰,但如果善加利用,也可以派上用场。让我们看看几个实际应用例子:

1. 状态机实现

在实现状态机时,未初始化的状态变量可以用来表示系统的初始状态。程序上电后,状态变量的值就是未定义的,恰好可以用来标识系统刚刚启动的初始状态。这样就无需在代码中专门定义一个"初始状态"变量。
  1. typedef enum {
  2.     STATE_INIT,
  3.     STATE_RUN,
  4.     STATE_STOP,
  5.     STATE_POWERON
  6. } system_state_t;

  7. system_state_t current_state __attribute__((section("NoInit"), zero_init));

  8. void main() {
  9.     // 上电后,current_state自动处于初始状态STATE_INIT
  10.     while (1) {
  11.         switch (current_state) {
  12.             case STATE_INIT:
  13.                 // 初始化逻辑
  14.                 current_state = STATE_RUN;
  15.                 break;
  16.             case STATE_RUN:
  17.                 // 运行逻辑
  18.                 break;
  19.             case STATE_STOP:
  20.                 // 停止逻辑
  21.                 break;
  22.             case STATE_POWERON:
  23.                 // 上电逻辑
  24.                 break;
  25.             default:
  26.                 // 未知状态处理
  27.                 break;
  28.         }
  29.     }
  30. }



2. 断电恢复

在需要断电恢复的场景中,未初始化的变量可以用来判断系统是否刚刚从断电状态恢复。例如,你可以将一个标志位变量设置为未初始化状态,在恢复逻辑中检查该变量的值,来决定是否需要执行特殊的恢复操作。
  1. uint16_t power_restored_flag __attribute__((section("NoInit"), zero_init));

  2. void main() {
  3.     // 检查power_restored_flag是否处于未初始化状态
  4.     if (power_restored_flag != 0x55AA) {
  5.         // 执行断电恢复逻辑
  6.         power_restored_flag = 0x55AA; // 设置标志位为特定值
  7.     } else {
  8.         // 正常运行逻辑
  9.     }
  10. }

3. 低功耗模式

在低功耗模式下,MCU会进入睡眠状态,只有在特定事件发生时才会被唤醒。未初始化的变量可以用来标记唤醒原因,避免在唤醒后需要重新初始化状态。

  1. uint8_t wakeup_source __attribute__((section("NoInit"), zero_init));

  2. void main() {
  3.     while (1) {
  4.         // 正常运行逻辑
  5.         enter_low_power_mode();

  6.         // 检查唤醒源
  7.         if (wakeup_source == 0xFF) { // 未初始化时为0xFF
  8.             // 由定时器唤醒
  9.         } else if (wakeup_source == 0x55) {
  10.             // 由外部中断唤醒
  11.         } else {
  12.             // 未知唤醒源
  13.         }

  14.         // 根据唤醒源执行相应的恢复操作
  15.         switch (wakeup_source) {
  16.             case 0xFF:
  17.                 // 定时器唤醒恢复逻辑
  18.                 break;
  19.             case 0x55:
  20.                 // 外部中断唤醒恢复逻辑
  21.                 break;
  22.             default:
  23.                 // 未知唤醒源处理逻辑
  24.                 break;
  25.         }

  26.         // 重置唤醒源标志
  27.         wakeup_source = 0;
  28.     }
  29. }

4. 关闭只能复位才能关闭的外设

某些外设,比如独立看门狗(IWDT,WWDT)在正常运行过程中无法被关闭,只能通过复位才能关闭。但在某些情况下,我们需要在程序运行时关闭这些外设,比如在系统需要进入低功耗或出现故障时。

利用未初始化变量的特性,我们可以通过检查变量的值来判断系统是否刚刚复位,从而决定是否需要关闭IWDT等外设。
  1. uint16_t iwdt_disabled_flag __attribute__((section("NoInit"), zero_init));

  2. void main() {
  3.     // 检查iwdt_disabled_flag是否处于未初始化状态
  4.     if (iwdt_disabled_flag != 0x55aa) {
  5.         // 启动独立看门狗(IWDT)
  6.         IWDT_Init(freqLIRC);
  7.     }

  8.     // 正常运行逻辑
  9.     while (1) {
  10.         // 其他工作...

  11.         if (sleep_flag) {
  12.             // 进入睡眠模式
  13.             iwdt_disabled_flag = 0x55aa; // 设置标志位,下次不启动IWDT
  14.             enter_low_power_mode();
  15.         }
  16.     }
  17. }


以上几个例子展示了如何利用APM32F003 RAM未初始化的特性来简化代码逻辑,提高程序的健壮性。当然,在实际应用中还需要结合具体需求来权衡使用。合理利用这一特性,可以让你的APM32F003项目获得意想不到的收益。

如下是在Keil中的具体用法:1,修改SCT文件:
  1. ; *************************************************************
  2. ; *** Scatter-Loading Description File generated by uVision ***
  3. ; *************************************************************

  4. LR_IROM1 0x00000000 0x00008000  {    ; load region size_region
  5.   ER_IROM1 0x00000000 0x00008000  {  ; load address = execution address
  6.    *.o (RESET, +First)
  7.    *(InRoot$Sections)
  8.    .ANY (+RO)
  9.    .ANY (+XO)
  10.   }
  11. <font color="#ff0000">  RW_IRAM1 0x20000000 UNINIT 0x00000100  { ;no init section
  12.    *(NoInit)
  13.   }</font>
  14.   RW_IRAM2 0x20000100 0x00000F00  {                ;all other RW data
  15.    .ANY(+RW +ZI)
  16.   }
  17. }
2,在代码中声明变量:

  1. unsigned char NI_charVar __attribute__( ( section( "NoInit"),zero_init) ) ;


caigang13 发表于 2024-8-9 07:55 来自手机 | 显示全部楼层
所以在定一个变量时最好同时赋值一个初值。
dalong-168 发表于 2024-8-9 11:41 | 显示全部楼层
学习了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

42

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部