标题:
SRAM中的变量,进入掉电真的不变?
[打印本页]
作者:
千早爱音爱玩51
时间:
2025-7-4 21:17
标题:
SRAM中的变量,进入掉电真的不变?
最近学完C语言,在做手持风扇主板,我使用了一个数组和一个计数器读取预设好的档位值,然后赋值给CCAP寄存器,实现改变占空比。
const unsigned char ccapvalues[] = {0x38, 0x33, 0x2E, 0x25, 0x20, 0x16, 0x10, 0x00};
unsigned char ccapcounter;
我读手册时,看到一句话:
时钟停振模式下,CPU 和全部的外设均停止工作,但 SRAM 和 XRAM 中的数据是一直维持不变的。
看到这句话之后,我想既然如此,那我这个计数器的值就不需要保存到eeprom了,毕竟我是软关机,单片机不会完全掉电。
然而当我写完代码之后,实际测试发现,
CCAP计数器在从睡眠恢复之后,无论怎么设置都恢复到0。
即使是尝试将该UCHAR用idata和_at_命令固定在ram中的某个位置,它也会恢复到0。
这似乎不对?不是说进入掉电模式SRAM中的数据不会变吗?由于怎么改代码都得不到正确的结果,我索性就去尝试eeprom读写了
在看过手册之后,写了一段EEPROM的读写函数,我按照原来的思路,把eeprom读取函数放在了SLEEP函数中,唤醒后执行的地方。
void SLEEP(void){
//其他代码
EEPROM_WRITE(ccapcounter);//把计数器的值写入
PCON = 0X02;
/////////////////唤醒后从这里执行/////////////////
//其他代码
EEPROM_READ();//读取EEPROM,赋值给计数器
}
但是这么尝试之后,依然无法观察到从睡眠中唤醒计数器值不会被重置。
最后搞的我都开始怀疑我写的代码了,直接复制手册里的代码之后,发现还是不行,于是我就想着写一段用于验证eeprom读写功能的代码
http://www.stcaimcu.com/thread-18923-1-1.html
不负我所望,在这个代码中,成功验证了我写的EEPROM读写代码是完全正确的,既然可以正确读写,那又为什么计数器值还是被归零呢?我开始怀疑是不是代码其他部分把计数器值重置了。
检查之后也没有发现有哪里会把计数器给重置。
但是我看着睡眠函数里面的读取位置,我突然想到,我在主循环里再执行一遍读取行不行?说做就做,我用了一个用于初始化计数器值的bit量。
bit initccapcounter = 0;
void main(){
//其他代码
while(1){
if(!initccapcounter){
initccapcounter = 1;
EEPROM_READ();
}
结果还真实现了唤醒后计数器不归零的功能。
然后经过查证之后,发现是
在执行完SLEEP函数之后,单片机会从复位向量地址重新执行程序,也就是变量可能在这里重新被初始化了!
也就是说,
在我这个场景下,相当于是刚执行完EEPROM_READ,刚给计数器赋了值,然后你程序就把这个值清空了,所以才表现出了“没有读取成功的现象”!
但是我对这个问题也是一知半解,我不知道为什么我不不论是不给变量初值,还是用_at_把变量定位到IDATA,都没用,不过既然EEPROM已经成功了,我就不在乎这些试过的方案了。
到此为止,总算是解决了变量会被归零的问题。
作者:
rundstedt
时间:
2025-7-5 18:43
你需要修改startup.a51这个文件。
作者:
622323wjl
时间:
2025-7-6 00:24
在手持风扇主板开发中,你遇到的计数器在睡眠模式恢复后归零的问题,本质上是对单片机睡眠模式与程序执行机制理解偏差导致的。尽管数据手册表明SRAM在时钟停振模式下能维持数据,但程序从睡眠唤醒后并非从断点继续执行,而是会从复位向量地址重新启动,这会导致全局变量和静态变量按编译设定的初始值重新初始化(如`ccapcounter`默认归零),即便用`_at_`命令定位到IDATA也无法改变这一初始化逻辑,因为变量初始化是程序启动阶段的固有流程。 你最初尝试将EEPROM读写放在`SLEEP`函数中,误以为唤醒后能直接沿用写入的值,但实际程序重启后会先执行初始化代码,此时EEPROM读取的时机滞后于变量初始化,导致新值被初始值覆盖。而将`EEPROM_READ()`放在主循环的条件判断中(通过`initccapcounter`标记避免重复读取),确保了程序重启后首次进入主循环时立即用EEPROM数据覆盖默认初始化值,从而实现了计数器状态的保持,这是利用程序执行顺序解决了初始化与数据恢复的时序冲突问题。 此案例揭示了单片机睡眠模式下的关键要点:睡眠唤醒本质是一次程序重启,变量会按编译规则重新初始化,SRAM数据维持特性仅适用于未被程序主动修改的场景。因此,涉及状态保持的应用需在程序启动流程中优先完成非易失性数据(如EEPROM)的读取,以覆盖默认初始化值,而非依赖SRAM的被动数据维持机制。这也验证了嵌入式系统开发中“程序执行流程控制”与“硬件特性利用”需协同设计的重要性。
作者:
人中狼
时间:
2025-7-6 10:22
你这是什么单片机啊,休眠唤醒跟复位操作一个效果,那还要休眠干什么,直接断电不就好了
作者:
lzuoxin
时间:
2025-7-6 13:32
进入休眠模式不变,掉电断电会丢失的。
作者:
WL0123
时间:
2025-7-6 14:55
楼主说的问题根本不存在,是楼主没有设置好PCON寄存器。这是用STC8H3K48S2在官方例程基础上改写的测试程序,唤醒后的num值就是休眠前的值。
#include <STC8H.H>
#include "intrins.h"
#define IDL 0x01 //PCON.0
#define PD 0x02 //PCON.1
#define uint unsigned int
#define uchar unsigned char
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
uchar data dis_buf[8];
uchar i;
uint j;
uint num=9990;
void Delay1ms(void) //@11.0592MHz
{
unsigned char data i, j;
i = 15;
j = 90;
do
{
while (--j);
} while (--i);
}
void main()
{
P0M0 = 0x00;
P0M1 = 0x00;
P1M0 = 0x00;
P1M1 = 0x00;
P2M0 = 0x00;
P2M1 = 0x00;
P3M0 = 0x00;
P3M1 = 0x00;
P4M0 = 0x00;
P4M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;
EX0 = 1; //使能INT0中断,用于唤醒MCU
EA = 1;
while (1)
{
dis_buf[0]=table[num/1000%10];
dis_buf[1]=table[num/100%10];
dis_buf[2]=table[num/10%10];
dis_buf[3]=table[num%10];
P0=0x00;
P2&=0xf0;
P2|=~(0x01<<i);
P0=dis_buf[i];
i=++i%4;
if(++j>=1000)
{
j=0;
if(++num>9999)num=0;
}
if(!P33)//key
{
P0=0xff;
P2=0xff;
_nop_();
_nop_();
_nop_();
_nop_();
// PCON = IDL; //MCU进入IDLE模式
PCON = PD; //MCU进入掉电模式
_nop_();
_nop_();
_nop_();
_nop_();
}
Delay1ms();
}
}
void INT0_Isr() interrupt 0//P3.2
{
// 外部中断唤醒 //测试端口
}
复制代码
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1