在工作中用某款单品机,由于芯片进待机模式唤醒后,芯片会复位,导致数据丢失,所以需要通过keil修改SRAM区域大小,将数据存储在SRAM特定区域,复位后特定区域的数据不丢失。
代码.h文件
typedef struct {
// 油位百分比
uint8_t oil_level;
// 运行时间统计
unsigned long cumulative_run_time;
// 锁定状态 (true=锁定)
bool lock_state;
// 按键状态
enum {
KEY_EVENT_NONE ,
KEY_EVENT_PRESS,
KEY_EVENT_RELEASE,
KEY_EVENT_PRESS_X1,
KEY_EVENT_PRESS_X2,
KEY_EVENT_PRESS_X3,
KEY_EVENT_PRESS_X4,
KEY_EVENT_PRESS_X5,
KEY_EVENT_PRESS_LONG,
KEY_EVENT_PRESS_LONG_LONG,
} key_state;
// 软件系统
enum {
SW_BOOTING, // 启动中
SW_IDLE, // 空闲
SW_OPERATIONAL, // 运行中
SW_ERROR // 错误
} sw_state;
// 输出控制
uint16_t out_shift;
// 输出标志
bool out_one_shift;
// 电源管理
enum {
CHARGE_OFF, // 未充电
CHARGE_IN_PROGRESS, // 充电中
CHARGE_COMPLETE // 充电完成
} charge_state;
// 电源开关 (true=通电)
bool power_on;
// 电池电量 (0-100%)
uint8_t battery_level;
// 故障系统
struct {
uint32_t bat_low : 1; // 低电压
uint32_t oil_low : 1; // 低油位
uint32_t fire_otp : 1; //
uint32_t heater_open : 1; // 负载开路
uint32_t heater_short : 1; // 负载短路
uint32_t reserved : 27; // 保留位
} fault;
uint8_t *test; // 测试256个字节是不是都可以正常存储
} device_manage;
#define TARGET_ADDRESS 0x20007B00
.c代码
device_manage * const my_device = (device_manage *)TARGET_ADDRESS;
uint8_t test_data[220] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
211, 212, 213, 214, 215, 216, 217, 218, 219, 220
};
device_manage device_data = {
.oil_level = 85, // 油位85%
.cumulative_run_time = 100, // 累计运行
.lock_state = false, // 未锁定
.key_state = KEY_EVENT_NONE, // 无按键事件
.sw_state = SW_IDLE, // 系统空闲
.out_shift = 0x00FF, // 输出档位
.out_one_shift = false, // 未触发调整
.charge_state = CHARGE_OFF, // 无充电
.power_on = true, // 电源开启
.battery_level = 95, // 电池电量95%
.fault = { // 故障状态
.bat_low = 0, // 无低电压故障
.oil_low = 0, // 无低油位故障
.fire_otp = 0, // 无过吸故障
.heater_open = 0, // 无负载开路故障
.heater_short = 0, // 无负载短路故障
.reserved = 0 // 保留位清零
},
.test = test_data,
};
static void POW_DOWN_Delay_test(uint32_t ms)
{
while (ms--)
{
for (uint32_t i = 0; i < 89; i++)
{
for (uint32_t j = 0; j < 50; j++)
{
__NOP();
}
}
}
}
static void init_sys(void)
{
Set_sys_Frequency(64000000);
PWRCU_PB4WakeupCmd(DISABLE);
}
static void Enter_Sleep(void)
{
PWRCU_PB4WakeupConfig(NEGATIVE_EDGE, ENABLE); // PB4 rising edge wakeup
PWRCU_PB4WakeupDeb_Config(7, ENABLE); // PB4 wakeup debounce
PWRCU_WakeupPIn_IntEnt_Cmd(ENABLE);
PWRCU_PB4WakeupCmd(ENABLE);
while (PMU_Get_Flag(WUP_PB4_FLAG | VCC33LVDF))
{
// PMU_Clear_Flag(WUP_PB4_FLAG | VCC33LVDF);
PMU_Clear_Flag(0xffffffff);
PMU_Clear_Plug_Flag(0xffffffff);
AM_DEBUG_LOG("clear WUP_PB4_FLAG | VCC33LVDF!\r\n");
}
while (1)
{
AM_DEBUG_LOG("enter sleep!\r\n");
PMU_Clear_Flag(0xffffffff);
PMU_Clear_Plug_Flag(0xffffffff);
PWRCU_PowerDown();
};
}
static void Exit_Sleep(void)
{
init_sys();
}
const char* key_state_to_str(int state) {
switch(state) {
case 0: return "KEY_EVENT_NONE";
case 1: return "KEY_EVENT_PRESS";
case 2: return "KEY_EVENT_RELEASE";
case 3: return "KEY_EVENT_PRESS_X1";
case 4: return "KEY_EVENT_PRESS_X2";
case 5: return "KEY_EVENT_PRESS_X3";
case 6: return "KEY_EVENT_PRESS_X4";
case 7: return "KEY_EVENT_PRESS_X5";
case 8: return "KEY_EVENT_PRESS_LONG";
case 9: return "KEY_EVENT_PRESS_LONG_LONG";
default: return "UNKNOWN_KEY_STATE";
}
}
const char* sw_state_to_str(int state) {
switch(state) {
case 0: return "SW_BOOTING";
case 1: return "SW_IDLE";
case 2: return "SW_OPERATIONAL";
case 3: return "SW_ERROR";
default: return "UNKNOWN_SW_STATE";
}
}
const char* charge_state_to_str(int state) {
switch(state) {
case 0: return "CHARGE_OFF";
case 1: return "CHARGE_IN_PROGRESS";
case 2: return "CHARGE_COMPLETE";
default: return "UNKNOWN_CHARGE_STATE";
}
}
// 打印设备管理结构体的所有数据
void print_device_manage(const device_manage *dev) {
AM_DEBUG_LOG("\n=== 设备状态报告 ===\n");
// 基本状态
AM_DEBUG_LOG("油位: %d%%\n", dev->oil_level);
AM_DEBUG_LOG("累计运行时间: %lu \n", dev->cumulative_run_time);
AM_DEBUG_LOG("锁定状态: %s\n", dev->lock_state ? "已锁定" : "未锁定");
// 按键状态
AM_DEBUG_LOG("按键状态: %s\n", key_state_to_str(dev->key_state));
// 软件状态
AM_DEBUG_LOG("软件状态: %s\n", sw_state_to_str(dev->sw_state));
// 输出控制
AM_DEBUG_LOG("输出档位: 0x%04X\n", dev->out_shift);
AM_DEBUG_LOG("触发调整: %s\n", dev->out_one_shift ? "已触发" : "未触发");
// 电源管理
AM_DEBUG_LOG("充电状态: %s\n", charge_state_to_str(dev->charge_state));
AM_DEBUG_LOG("电源开关: %s\n", dev->power_on ? "开启" : "关闭");
AM_DEBUG_LOG("电池电量: %d%%\n", dev->battery_level);
// 故障系统
AM_DEBUG_LOG("故障状态:\n");
AM_DEBUG_LOG(" - 低电压: %s\n", dev->fault.bat_low ? "是" : "否");
AM_DEBUG_LOG(" - 低油位: %s\n", dev->fault.oil_low ? "是" : "否");
AM_DEBUG_LOG(" - 过吸故障: %s\n", dev->fault.fire_otp ? "是" : "否");
AM_DEBUG_LOG(" - 负载开路: %s\n", dev->fault.heater_open ? "是" : "否");
AM_DEBUG_LOG(" - 负载短路: %s\n", dev->fault.heater_short ? "是" : "否");
AM_DEBUG_LOG("\n测试数组内容:\n");
for (int i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++) // 或者直接使用数组元素个数
{
AM_DEBUG_LOG("%d ", dev->test[i]);
if ((i + 1) % 10 == 0) AM_DEBUG_LOG("\n");
}
AM_DEBUG_LOG("========================\n");
}
void copy_to_address(device_manage *src_ptr,device_manage *dest_ptr)
{
// 将绝对地址转换为指向device_manage类型的指针
memcpy(dest_ptr, src_ptr, sizeof(device_manage));
}
int main(void)
{
uint32_t i = 0;
uint32_t j = 1;
// Initialize the system
init_sys();
Init_Uart_Printf();
AM_DEBUG_LOG("System start!\r\n");
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = LPWUP_POR_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
AM_DEBUG_LOG("NVIC\r\n");
i = PWRCU_ReadBackupRegister(PWRCU_BAKREG_0);
if(i)
{
copy_to_address((device_manage *)TARGET_ADDRESS,&device_data); //唤醒读出
}
AM_DEBUG_LOG("Backup register value: 0x%08X\r\n", i);
i = 10;
print_device_manage(&device_data);
for (int k = 0; k < sizeof(test_data) / sizeof(test_data[0]); k++)
{
device_data.test[k] += 1; // 每个元素 +1
AM_DEBUG_LOG("%d ", device_data.test[k]);
if ((k + 1) % 10 == 0) AM_DEBUG_LOG("\n");
}
POW_DOWN_Delay_test(2000);
device_data.cumulative_run_time += j;
device_data.battery_level += j;
device_data.oil_level += j;
while (1)
{
// Write the value to the backup register
PWRCU_WriteBackupRegister(PWRCU_BAKREG_0, i);
copy_to_address(&device_data,(device_manage *)TARGET_ADDRESS); //睡眠前保存
// Enter sleep
Enter_Sleep();
// never reach here
Exit_Sleep();
AM_DEBUG_LOG("exit sleep\r\n");
AM_DEBUG_LOG("Backup register value: 0x%08lX\r\n", PWRCU_ReadBackupRegister(PWRCU_BAKREG_0));
}
}
void LPWUP_POR_IRQHandler(void)
{
if (PMU_Get_Flag(WUP_PB4_FLAG))
{
AM_DEBUG_LOG("PB4 wakeup!\r\n");
// PMU_Clear_Flag(WUP_PB4_FLAG | VCC33LVDF);
PMU_Clear_Flag(0xffffffff);
}
}
这里的问题在于uint8_t *test; // 这是一个指针,指向 test_data 数组。
test 存储的是 test_data 的地址(如 0x20000000),而不是数组数据本身。memcpy 只会复制指针的值(地址),而不会复制指针指向的数据(test_data 数组)。
当执行 copy_to_address()时,
copy_to_address(&device_data, (device_manage *)TARGET_ADDRESS);
memcpy 会把 device_data 的所有成员(包括 test 指针)复制到 TARGET_ADDRESS,但 test 指向的 test_data 数组不会被复制,只会复制指针值(地址)。掉电后,test_data 区域的数据会全部丢失(0x20000000 或其他地址),复位后,又重新分配地址赋值,所以打印数据不是每次唤醒后,数组数据都加1
log信息如下:
=== 设备状态报告 ===
油位: 96%
累计运行时间: 111
锁定状态: 未锁定
按键状态: KEY_EVENT_NONE
软件状态: SW_IDLE
输出档位: 0x00FF
触发调整: 未触发
充电状态: CHARGE_OFF
电源开关: 开启
电池电量: 106%
故障状态:
- 低电压: 否
- 低油位: 否
- 过吸故障: 否
- 负载开路: 否
- 负载短路: 否
测试数组内容:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
101 102 103 104 105 106 107 108 109 110
111 112 113 114 115 116 117 118 119 120
121 122 123 124 125 126 127 128 129 130
131 132 133 134 135 136 137 138 139 140
141 142 143 144 145 146 147 148 149 150
151 152 153 154 155 156 157 158 159 160
161 162 163 164 165 166 167 168 169 170
171 172 173 174 175 176 177 178 179 180
181 182 183 184 185 186 187 188 189 190
191 192 193 194 195 196 197 198 199 200
201 202 203 204 205 206 207 208 209 210
211 212 213 214 215 216 217 218 219 220
========================
2 3 4 5 6 7 8 9 10 11
12 13 14 15 16 17 18 19 20 21
22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40 41
42 43 44 45 46 47 48 49 50 51
52 53 54 55 56 57 58 59 60 61
62 63 64 65 66 67 68 69 70 71
72 73 74 75 76 77 78 79 80 81
82 83 84 85 86 87 88 89 90 91
92 93 94 95 96 97 98 99 100 101
102 103 104 105 106 107 108 109 110 111
112 113 114 115 116 117 118 119 120 121
122 123 124 125 126 127 128 129 130 131
132 133 134 135 136 137 138 139 140 141
142 143 144 145 146 147 148 149 150 151
152 153 154 155 156 157 158 159 160 161
162 163 164 165 166 167 168 169 170 171
172 173 174 175 176 177 178 179 180 181
182 183 184 185 186 187 188 189 190 191
192 193 194 195 196 197 198 199 200 201
202 203 204 205 206 207 208 209 210 211
212 213 214 215 216 217 218 219 220 221
这里的修正方法:
1、直接存储数组数据
修改 device_manage,让 test 直接包含数组数据(而不是指针):
typedef struct {
// ... 其他成员 ...
uint8_t test[256 -36]; // 测试256个字节是不是都可以正常存储,因为程序中有可能有其他模块用到这部分SRAM,比如BROM 启动的时候
} device_manage;