C 闪存编程导致意外复位
我使用的是MCF51EM256飞思卡尔微控制器,我对flash编程有一些问题 为了使我的软件持久化,我试图在辅助闪存中存储一些变量,以便从意外关闭中恢复 有时,当我关闭MCU测试我的工作时,它会不断重置 我必须存储此结构:C 闪存编程导致意外复位,c,embedded,microcontroller,flash-memory,C,Embedded,Microcontroller,Flash Memory,我使用的是MCF51EM256飞思卡尔微控制器,我对flash编程有一些问题 为了使我的软件持久化,我试图在辅助闪存中存储一些变量,以便从意外关闭中恢复 有时,当我关闭MCU测试我的工作时,它会不断重置 我必须存储此结构: // kWh or kVARh Energy accumulator type typedef struct { uint32 Ea_ps; // Energy must be stored in kWh or kVARh uint32 Ea_ng;
// kWh or kVARh Energy accumulator type
typedef struct {
uint32 Ea_ps; // Energy must be stored in kWh or kVARh
uint32 Ea_ng; // All fields must contain POSITIVE values!
uint32 Er_q1;
uint32 Er_q2;
uint32 Er_q3;
uint32 Er_q4;
}kWh_EnergyAcc32;
以下是我的职责:
// This function stores in Flash a given kWh_EnergyAcc64 structure.
void Save_Flash_kWhEnergyAcc(long addr, kWh_EnergyAcc32* Acc) {
// kWhEnergyAcc struct needs 32 bytes in Flash
Flash_Burst(addr, 1, &(Acc->Ea_ps));
Flash_Burst(addr + 4, 1, &(Acc->Ea_ng));
Flash_Burst(addr + 8, 1, &(Acc->Er_q1));
Flash_Burst(addr + 12, 1, &(Acc->Er_q2));
Flash_Burst(addr + 16, 1, &(Acc->Er_q3));
Flash_Burst(addr + 20, 1, &(Acc->Er_q4));
}
#define FLASH_MASS_ERASE_CMD 0x41
#define FLASH_ERASE_CMD 0x40
#define FLASH_PROGRAM_CMD 0x20
#define FLASH_BURST_CMD 0x25
#if (SYSTEM_CLOCK/2) > 12800000 /* 12.8 MHz */
#define FLASH_CLOCK (UINT8)(( (SYSTEM_CLOCK/3200000) -1) | 0x40)
#else
#define FLASH_CLOCK (unsigned char)( (SYSTEM_CLOCK/400000) -1)//<200KHz
#endif
/* Macros to call the function using the different features */
#define Flash_Burst(Address, Size, DataPtr) \
Flash_Cmd((UINT32)Address, (UINT16)Size, (UINT32*)DataPtr, FLASH_BURST_CMD)
UINT8 /*far*/
Flash_Cmd(UINT32 FlashAddress,
UINT16 FlashDataCounter,
UINT32 *pFlashDataPtr,
UINT8 FlashCommand)
{
/* Check to see if FACCERR or PVIOL is set */
if (FSTAT &0x30)
{
/* Clear Flags if set*/
FSTAT = 0x30;
}
if (FlashDataCounter)
{
do
{
/* Wait for the Last Busrt Command to complete */
while(!(FSTAT&FSTAT_FCBEF_MASK)){};/*wait until termination*/
/* Write Data into Flash*/
(*((volatile unsigned long *)(FlashAddress))) = *pFlashDataPtr;
FlashAddress += 4;
pFlashDataPtr++;
/* Write Command */
FCMD = FlashCommand;
/* Put FCBEF at 1 */
FSTAT = FSTAT_FCBEF_MASK;
asm (NOP);
asm (NOP);
asm (NOP);
/* Check if Flash Access Error or Protection Violation Error are Set */
if (FSTAT&0x30)
{
/* If so, finish the function returning 1 to indicate error */
return (1);
}
}while (--FlashDataCounter);
}
/* wait for the last command to complete */
while ((FSTAT&FSTAT_FCCF_MASK)==0){};/*wait until termination*/
/* Return zero to indicate that the function executed OK */
return (0);
}
*
*
*
和闪存编程功能:
// This function stores in Flash a given kWh_EnergyAcc64 structure.
void Save_Flash_kWhEnergyAcc(long addr, kWh_EnergyAcc32* Acc) {
// kWhEnergyAcc struct needs 32 bytes in Flash
Flash_Burst(addr, 1, &(Acc->Ea_ps));
Flash_Burst(addr + 4, 1, &(Acc->Ea_ng));
Flash_Burst(addr + 8, 1, &(Acc->Er_q1));
Flash_Burst(addr + 12, 1, &(Acc->Er_q2));
Flash_Burst(addr + 16, 1, &(Acc->Er_q3));
Flash_Burst(addr + 20, 1, &(Acc->Er_q4));
}
#define FLASH_MASS_ERASE_CMD 0x41
#define FLASH_ERASE_CMD 0x40
#define FLASH_PROGRAM_CMD 0x20
#define FLASH_BURST_CMD 0x25
#if (SYSTEM_CLOCK/2) > 12800000 /* 12.8 MHz */
#define FLASH_CLOCK (UINT8)(( (SYSTEM_CLOCK/3200000) -1) | 0x40)
#else
#define FLASH_CLOCK (unsigned char)( (SYSTEM_CLOCK/400000) -1)//<200KHz
#endif
/* Macros to call the function using the different features */
#define Flash_Burst(Address, Size, DataPtr) \
Flash_Cmd((UINT32)Address, (UINT16)Size, (UINT32*)DataPtr, FLASH_BURST_CMD)
UINT8 /*far*/
Flash_Cmd(UINT32 FlashAddress,
UINT16 FlashDataCounter,
UINT32 *pFlashDataPtr,
UINT8 FlashCommand)
{
/* Check to see if FACCERR or PVIOL is set */
if (FSTAT &0x30)
{
/* Clear Flags if set*/
FSTAT = 0x30;
}
if (FlashDataCounter)
{
do
{
/* Wait for the Last Busrt Command to complete */
while(!(FSTAT&FSTAT_FCBEF_MASK)){};/*wait until termination*/
/* Write Data into Flash*/
(*((volatile unsigned long *)(FlashAddress))) = *pFlashDataPtr;
FlashAddress += 4;
pFlashDataPtr++;
/* Write Command */
FCMD = FlashCommand;
/* Put FCBEF at 1 */
FSTAT = FSTAT_FCBEF_MASK;
asm (NOP);
asm (NOP);
asm (NOP);
/* Check if Flash Access Error or Protection Violation Error are Set */
if (FSTAT&0x30)
{
/* If so, finish the function returning 1 to indicate error */
return (1);
}
}while (--FlashDataCounter);
}
/* wait for the last command to complete */
while ((FSTAT&FSTAT_FCCF_MASK)==0){};/*wait until termination*/
/* Return zero to indicate that the function executed OK */
return (0);
}
有人能看出我做错了什么吗?为什么当我关闭电源以测试持续性时,我的微积分会不断重置?有一种方法可以捕获导致my micro重置的致命异常
首先,我认为这可能是由于在关机期间写入任何闪存地址时出错造成的,之后无法正确读取,但我尝试在闪存写入结束时使用写入已知位置的“魔法字”来检查闪存写入是否已经完成,似乎这不是问题所在
编辑:
编辑2:这是my micro的内存映射:
编辑3:
我在FLASH编程函数中包含了FLASH_时钟定义
我还包含了这个函数来检查不一致的值:
int testIntegrity(kWh_EnergyAcc32 Acc) {
if (Acc.Ea_ps == -1 || Acc.Ea_ng == -1 || Acc.Er_q1 == -1 || Acc.Er_q2 == -1 || Acc.Er_q3 == -1 || Acc.Er_q4 == -1)
return 0;
else return 1;
}
现在,在初始化值后调用此函数,并且LED从不亮起
注:(Acc->Ea_ps==-1)与(Acc->Ea_ps==0xFFFFFF)相同
编辑4:
我的函数SetFlashSectorToZero的代码:
void SetFlashSectorToZero(长地址){
uint32重置值=0x00000000;
int-endSector=addr+1024;
而(地址
- 您无法从当前正在编程的同一闪存阵列执行闪存编程代码。根据此特定设备的手册,它包含双闪存控制器,仅用于在编程另一个闪存阵列的同时编程一个闪存阵列
因此,您必须确保闪存编程代码分配在单独的数组中。您甚至可能需要两个完整的闪存编程代码副本
否则将导致任何随机行为
- 您必须通过写入FxCDIV寄存器来配置闪存预定标器时钟。错误配置的闪存时钟通常会导致程序挂起或CPU重置。时钟需要在150-200kHz范围内。重要提示:您需要余量,因此必须考虑时钟的不准确度,以及由此产生的不准确度你的公车时钟不能被你选择的常数平均整除
- 不用说,在闪存编程期间,如果中断驻留在正在编程的闪存阵列内,或者它们试图访问闪存阵列内的常量/调用代码,则不能运行任何中断
我不知道您是否通过我们的建议解决了问题,但我认为您的代码可以通过以下方式缩小:
static kWh_EnergyAcc32 PhR_ABS_kWh_AccStr;
static long PhR_ABS_kWh_addr = 0x20000;
static long magic_word_addr = 0x203FC;
main() {
uint32 ok_magic_word = 0x87654321;
magic_word = *(uint32*)magic_word_addr;
if (isFirstExecution() || (magic_word != ok_magic_word)) {
EraseFlashSector(PhR_ABS_kWh_addr);
// Writes 0's in all addresses of the flash sector (1024 bytes)
SetFlashSectorToZero(PhR_ABS_kWh_addr);
}
Init_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
if (testIntegrity(PhR_ABS_kWh_AccStr) == 0) {
// Turn on LEDs to show a message
ShowMsgLED(255, 0, 0);
}
while (1) {
getValuesFromSensors(&PhR_ABS_kWh_AccStr);
processValues(&PhR_ABS_kWh_AccStr);
EraseFlashSector(PhR_ABS_kWh_addr);
Save_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
Flash_Burst(magic_word_addr, 1, &ok_magic_word);
}
}
或
您的例程似乎在OnChip闪存上执行操作,而不是在外部设备上。据我所知,您的代码不负责关机。如果在代码擦除所有闪存页时电源消失怎么办?新启动时,您的所有值都将设置为0xffffff
。这是由processValues
管理的吗>函数?如果写入函数在执行过程中停止,则情况会更糟…很抱歉@LP造成混淆。请参阅编辑2。我正在尝试写入第二个闪存阵列。小提示:在OnChip闪存擦除和写入操作期间,您应该禁用中断。是的@LP。首先,我已保护自己不使用来自闪存的不一致写入值,并使用“魔咒"在闪存写入结束时写入到已知位置,问题仍然存在。我没有在帖子中包含代码。将闪存编程代码移动到RAM可能是一个好主意,这样它可以访问两个闪存阵列。这避免了每个闪存阵列中需要两个相同的代码块。但复制到RAM和从RAM执行通常需要一点时间棘手。@jeb不,这从来都不是一个好主意,它更像是一个肮脏的黑客行为。我知道飞思卡尔在各种应用程序注释中都提出了这一点,但这并不是一个真正的专业解决方案,而是他们在意识到自己硬件的局限性后发明的一项肮脏的工作。从RAM运行代码是一项危险的业务。而且,RAM通常更危险比flash更有价值。增加几百字节的程序大小不应该是个问题。我不明白为什么它比从flash运行更危险。而且有足够多的平台,它是flash的唯一解决方案,并且部分能够在flash时处理中断。你是对的,RAM是有价值的,但是通过复制代码,它不会o堆栈是免费的。@jeb主要是因为任何错误都会破坏整个程序,并可能使其无法恢复。但也因为“操作码数组”非常脆弱,任何程序更改都可能使所有内容无效。从历史上看,从RAM运行代码时也会担心EMI,但现在这已经不是什么问题了(尽管出于这个原因,安全标准通常禁止从RAM运行代码)。还要注意的是,无论如何,您都必须将整个例程存储在闪存中,以便在需要时将其复制到堆栈中,这样就根本不会节省太多内存。@LPs I know:-)我为MC56F8006(16KB闪存/2KB RAM)编写了一个引导加载程序MCU和DSP的混合。对于加密固件,我不知道为什么。但是在同一闪存扇区中设置magic_word_addr和PhR_ABS_kWh_addr,并更改(addr by(addr(addr
。您试图写入的闪存地址比取消的扇区地址空间多4个字节。但我不明白为什么。之前(一个月前),我尝试写入了许多未擦除的闪存地址,但从未得到这些重置。
void SetFlashSectorToZero(long addr){
uint32 resetValue = 0x00000000;
int endSector = addr + 1024;
while (addr <= endSector) {
Flash_Burst(addr, 1, &resetValue);
addr = addr + 4;
}
}
static kWh_EnergyAcc32 PhR_ABS_kWh_AccStr;
static long PhR_ABS_kWh_addr = 0x20000;
static long magic_word_addr = 0x203FC;
main() {
uint32 ok_magic_word = 0x87654321;
magic_word = *(uint32*)magic_word_addr;
if (isFirstExecution() || (magic_word != ok_magic_word)) {
EraseFlashSector(PhR_ABS_kWh_addr);
// Writes 0's in all addresses of the flash sector (1024 bytes)
SetFlashSectorToZero(PhR_ABS_kWh_addr);
}
Init_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
if (testIntegrity(PhR_ABS_kWh_AccStr) == 0) {
// Turn on LEDs to show a message
ShowMsgLED(255, 0, 0);
}
while (1) {
getValuesFromSensors(&PhR_ABS_kWh_AccStr);
processValues(&PhR_ABS_kWh_AccStr);
EraseFlashSector(PhR_ABS_kWh_addr);
Save_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
Flash_Burst(magic_word_addr, 1, &ok_magic_word);
}
}
// kWh or kVARh Energy accumulator type
typedef struct {
uint32 Ea_ps; // Energy must be stored in kWh or kVARh
uint32 Ea_ng; // All fields must contain POSITIVE values!
uint32 Er_q1;
uint32 Er_q2;
uint32 Er_q3;
uint32 Er_q4;
uint32 magic_word;
}kWh_EnergyAcc32;
static kWh_EnergyAcc32 PhR_ABS_kWh_AccStr;
static long PhR_ABS_kWh_addr = 0x20000;
static long *magic_word_addr = (uint32 *)(PhR_ABS_kWh_addr-sizeof(uint32));
#define OK_MAGIC_WORD 0x87654321
main() {
if (isFirstExecution() || (*magic_word != OK_MAGIC_WORD)) {
EraseFlashSector(PhR_ABS_kWh_addr);
// Writes 0's in all addresses of the flash sector (1024 bytes)
SetFlashSectorToZero(PhR_ABS_kWh_addr);
}
Init_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
if (testIntegrity(PhR_ABS_kWh_AccStr) == 0) {
// Turn on LEDs to show a message
ShowMsgLED(255, 0, 0);
}
while (1) {
getValuesFromSensors(&PhR_ABS_kWh_AccStr);
processValues(&PhR_ABS_kWh_AccStr);
PhR_ABS_kWh_AccStr.magic_word = OK_MAGIC_WORD;
EraseFlashSector(PhR_ABS_kWh_addr);
Flash_Burst(PhR_ABS_kWh_addr, sizeof(kWh_EnergyAcc32)/4, &PhR_ABS_kWh_AccStr);
}
}