EEPROM参数被意外修改,如何定位问题
本帖最后由 gnail092 于 2021-1-14 13:04 编辑之前设置过的参数,在调试程序的过程中,发现某个结构体变量存放在EEPROM中的一些参数被意外修改,但检验码还是对的,且另一份备份区也是同样的现象,
存储该结构体变量的函数,按照正常逻辑是只有通讯设置和界面设置才会调用该函数,怀疑是程序跑飞,刚好是执行到存储该结构体变量的函数的地方,只有这样检验和才会仍然是对的,备份区的参数保存也被同时修改,
现在不能判断是硬件问题还是软件问题,而且这个现象很难重现,不知道该如何定位问题?大家有没有出现过类似问题的,分享一下经验。
我再补充一下另一个现象,内部RTC时钟也出现过两次被改写,时间被改写到之前的时间了,按道理是不会操作写RTC的,也是只有通讯和界面设置才会被改写的。 按描述现象 99.9999% 是软件问题 dykwai1 发表于 2021-1-14 08:35
按描述现象 99.9999% 是软件问题
应该如何排查这种问题 每次操作eeprom时串口/usb/网口输出调试信息
jtag条件断点定位 这个好办,先看下这个参数的地址
然后在EEPROM的写数据函数里增加一段判断代码,写该地址时中断下来,然后就可以慢慢找了。。。
AWEN2000 发表于 2021-1-14 10:23
这个好办,先看下这个参数的地址
然后在EEPROM的写数据函数里增加一段判断代码,写该地址时中断下来,然后 ...
你是说仿真进去吗,这个现象是不知道什么时候出来的,你仿真不一定能出现 仔细检查所有的写这个地址的代码,大概率是参数传错了 本帖最后由 gnail092 于 2021-1-14 10:47 编辑
dykwai1 发表于 2021-1-14 10:12
每次操作eeprom时串口/usb/网口输出调试信息
jtag条件断点定位
“每次操作eeprom时串口/usb/网口输出调试信息”
你说的操作eeprom时输出调试信息,应该是指输出包含的是写入什么参数的信息吧
”jtag条件断点定位“
能否说一下具体怎么操作,一直挂着仿真并不一定能出现 这种问题,最好是打印输出,就像楼上有位说的一样。
在所有操作eeprom的地方,打印信息。
比如操作的来源,是面板还是通讯或者内部操作。
如果你的eeprom被改写参数地址比较固定,那就关注这一段就可以了。 操作eeprom的时候有没有关中断?会不会是被中断打断导致? simplorer 发表于 2021-1-14 11:42
这种问题,最好是打印输出,就像楼上有位说的一样。
在所有操作eeprom的地方,打印信息。
比如操作的来源, ...
通讯和界面设置才会进入到写这些参数的函数,假如现象出现时通过打印输出发现确实被调用了该函数,又如何排查为什么会被调用,正常流程是不会被调用的 appl33205 发表于 2021-1-14 12:59
操作eeprom的时候有没有关中断?会不会是被中断打断导致?
被中断打断也不至于把其它地址的数据改写,关键是这一片数据的校验也是对的呀 解決小概率 bug 的重點是想辦法提高概率。
譬如改變環境、嘗試不同的操作組合。實在提高不了的話,就增加機器數量,弄幾十上百台老化。
整體上,出現的概率提高了之後,通過不斷的增加調試打印就可以定位問題了。 本帖最后由 dukelec 于 2021-1-14 22:51 编辑
gnail092 发表于 2021-1-14 13:00
通讯和界面设置才会进入到写这些参数的函数,假如现象出现时通过打印输出发现确实被调用了该函数,又如何 ...
可以通過 printf 把 backtrace 打印出來(函數的調用關系)。
GCC 自帶 backtrace 方法。
譬如我這個隊列程序,打開檢查之後,每次操作都檢查一下隊列的完整性,如果檢查出異常,就会報告錯誤,並打印出當前函數調用關系,看是誰調用導致的。請參考最下面 list_check 函數:
https://github.com/dukelec/cdnet/blob/master/utils/cd_list.c 本帖最后由 gnail092 于 2021-1-14 22:57 编辑
dukelec 发表于 2021-1-14 22:43
解決小概率 bug 的重點是想辦法提高概率。
譬如改變環境、嘗試不同的操作組合。實在提高不了的話,就增加機 ...
因为某种原因导致程序跑飞,会不会存在PC指针可能会跳转到用户程序区任何一个地方,从而执行了写参数函数或写RTC函数导致了我目前的现象,且不会进入Hardfault,喂狗也正常,且能够继续正常运行。现在还无法知道当时单片机有没有复位的现象。 本帖最后由 dukelec 于 2021-1-14 23:11 编辑
gnail092 发表于 2021-1-14 22:56
因为某种原因导致程序跑飞,会不会存在PC指针可能会跳转到用户程序区任何一个地方,从而执行了写参数函数 ...
不排除,但可能性小,總有機會跑飛死機重啟的,一次都不死的話,估計不是跑飛。你也可以改變環境溫度、電壓等環境,看概率有沒有變化,如果變化,則可以減小代碼 bug 的可能性。
backtrace 也可以檢查出是否跑飛導致。
因為 backtrace 会告訴你是從哪個函數的哪一行語句跳轉過來(多級函數都會打印出來),可以是軟件 jump 或是硬件中斷,通過追溯可以判斷是代碼 bug 還是跑飛。 对eeprom写操作进行调试信息打桩,先收集足够多的信息,再对信息进行分析整理,找出出现问题的大致方向,比如写eeprom的方法不可重入?多任务写冲突?等等,一次只改动一个地方,可能性逐个排除 本帖最后由 gnail092 于 2021-1-15 09:37 编辑
dukelec 发表于 2021-1-14 23:06
不排除,但可能性小,總有機會跑飛死機重啟的,一次都不死的話,估計不是跑飛。你也可以改變環境溫度、電 ...
你这个一时半会不会用,我先通过在程序中置标志来确定是不是从通讯或显示操作那里调用过来的,如果没有判断到相应的标志说明是程序跑飞过来的,但也有可能先跑飞到通讯或显示操作那里,再调用了写参数函数 99%的人遇到99%无法排查的软件难题都会推责任给软件跑飞。实际上,软件跑飞到底是怎样的,有多少人见过,或者多少人能证实?{:lol:} 无问西东 发表于 2021-1-15 14:49
99%的人遇到99%无法排查的软件难题都会推责任给软件跑飞。实际上,软件跑飞到底是怎样的,有多少人见过,或 ...
你觉得具体是怎么造成的 无问西东 发表于 2021-1-15 14:49
99%的人遇到99%无法排查的软件难题都会推责任给软件跑飞。实际上,软件跑飞到底是怎样的,有多少人见过,或 ...
见过跑飞的,看门狗直接重启MCU了 EEPROM和RTC是不是挂在同一个IIC总线上了? zqf441775525 发表于 2021-1-15 22:49
EEPROM和RTC是不是挂在同一个IIC总线上了?
不是,RTC是MCU内部的 楼主的MCU开BOD或者外部低电压复位监控这样的电路功能了吗?
程序跑飞的可能性是绝对客观存在的,特别是在电压不稳定的关机掉电和开机上电过程中,如果没有做好欠压保护,很容易在这个时候中招,特别是如果电源的功率余量太大,短暂断电之后(实际此时MCU还没有彻底断电),又再次恢复供电,会死得很难看。 ilikemcu 发表于 2021-1-16 10:13
楼主的MCU开BOD或者外部低电压复位监控这样的电路功能了吗?
程序跑飞的可能性是绝对客观存在的,特别是在 ...
没有,只有掉电检测电路,12V转3.3V,检测12V掉电信号,3.3V输出有个大电容,检测到掉电时刻需在EEPROM中保存数据,现在想把这个3.3V电容加大再试一下。监控电源芯片一定要加的吗 以前用atmege8的时候, 如果没有使用CPU的电源监控功能, EEPROM在上下电的时候很容易丢失. 因为程序在乱跑 qinxg 发表于 2021-1-16 16:14
以前用atmege8的时候, 如果没有使用CPU的电源监控功能, EEPROM在上下电的时候很容易丢失. 因为程序在乱跑 ...
这个掉电检测就是通过MCU的低电压检测机制来实现的,程序检测到掉电信号后就会对一些重要函数就不会去执行了,但没有像楼上说的在外部加个电源监测一旦检测到低电压就强制复位的功能 gnail092 发表于 2021-1-16 12:35
没有,只有掉电检测电路,12V转3.3V,检测12V掉电信号,3.3V输出有个大电容,检测到掉电时刻需在EEPROM中 ...
你这个大电容的缓慢放电,加速没有使用合适的BOD监控,估计是这个问题的主因,把BOD监控用起来吧,否则即使你保存完EE的数据,此时电容的余电还没有放完,你的MCU接下来在电容的电放完之前,它的运行就是完全不受控的。 BOD监控如何用?是检测到掉电,然后把所有中断关闭同时死循环?还是只是简单的复位?复位的话 岂不是又进入问题点? 本帖最后由 gnail092 于 2021-1-17 14:20 编辑
ilikemcu 发表于 2021-1-17 10:39
你这个大电容的缓慢放电,加速没有使用合适的BOD监控,估计是这个问题的主因,把BOD监控用起来吧,否则即 ...
1、掉电检测功能就是采用单片机内部的一路比较器电路 CMP0的功能, CMP0的输入就是给单片机供电的LDO前级的12V分压过来的,设定检测电压阀值,通过CMP0 中断产生掉电事件置掉电标志,掉电时进行EEPROM数据操作,而程序主循环检测到掉电标志后大部分函数就不会去执行了,当然此时程序还是一直在跑的。
2、单片机手册有一句话这样描述:内置上电复位和下电复位,在低于阈值(约1.8V)时,器件处于复位状态。不必再使用外部复位电路。这个电路是始终打开的,不需要另外设置。
附件是手册中单片机电源的相关说明。
单片机只要在它的工作电源范围内应该就不会出现不可控的现象,当低于1.8V时就会进入下电复位,我的理解是否正确? gnail092 发表于 2021-1-17 14:16
1、掉电检测功能就是采用单片机内部的一路比较器电路 CMP0的功能, CMP0的输入就是给单片机供电的LDO前级 ...
我自己的应用是开BOD监控,然后再操作EEPROM时,不管读写,都要先读取BOD标志,如果标志被置位,则清零,延时后再判,必须连续若干次一直没有检测到BOD标志置位情况,才认为当前电源电压可靠,然后再去操作EEPROM,做了这个处理之后,EEPROM基本可以高枕无忧了。在此之前,偶尔会有零星的掉数据,虽然都是个案,但是客户投诉起来也是蛮头疼,现在世界终于安静了。 ilikemcu 发表于 2021-1-18 10:27
我自己的应用是开BOD监控,然后再操作EEPROM时,不管读写,都要先读取BOD标志,如果标志被置位,则清零, ...
那你的BOD也是在主循环中去读取该标志,并不是BOD产生后单片机就被复位了,当单片机电压掉到BOD电压阀值之后程序是不是还在跑的 gnail092 发表于 2021-1-18 12:22
那你的BOD也是在主循环中去读取该标志,并不是BOD产生后单片机就被复位了,当单片机电压掉到BOD电压阀值 ...
在操作EEPROM的子程序的开头部分去读BOD标志,主要就是让这种关键程序处理前做判别。这个具体看你的程序要求咯 ilikemcu 发表于 2021-1-18 16:05
在操作EEPROM的子程序的开头部分去读BOD标志,主要就是让这种关键程序处理前做判别。这个具体看你的程序 ...
为啥不检测后直接复位呢? kttest 发表于 2021-1-19 15:57
为啥不检测后直接复位呢?
如果此时电压不稳,你复位了,也干不了啥事,等待它电压回复到合理可靠的工作范围,就直接进入工作了。 ilikemcu 发表于 2021-1-19 15:59
如果此时电压不稳,你复位了,也干不了啥事,等待它电压回复到合理可靠的工作范围,就直接进入工作了。 ...
你上面提到过MCU没有开BOD,掉电时程序跑飞的情况,如果BOD检测到后并没有强复位,程序还在运行状态,那不是程序跑飞的情况还会存在 dukelec 发表于 2021-1-14 23:06
不排除,但可能性小,總有機會跑飛死機重啟的,一次都不死的話,估計不是跑飛。你也可以改變環境溫度、電 ...
再次请教一下,之前碰到的程序跑飞都是程序bug的地方(比如数组越界,野指针)直接触发异常中断进入Hardfault,而且仿真时很容易出现,这次现象感觉是跑飞到其它用户程序中执行造成数据被修改,后面有没有进入Hardfault也不清楚,概率也极低,这两者之间的原因有什么区别吗 gnail092 发表于 2021-1-30 15:46
再次请教一下,之前碰到的程序跑飞都是程序bug的地方(比如数组越界,野指针)直接触发异常中断进入Hardf ...
一切皆有可能
等你找出問題原因才能知道
怎麼找,已經說了,暫無補充
你之所以覺得我說的 backtrace 的方法一時半會搞不定,是因爲你平時選擇用 windows 而不是 linux,僅此而已,你的選擇,我也沒招,只能穿越回幾年前,重新選擇。 这类问题我很有经验, 以前AVR低功耗产品不能开BOD时就遇到过, 用了极特殊软件方法解决了, 现在用的是 stm8的eeprom带lock和unlcok要方便不少.
解决原理不难, 就是要在解锁和擦除/写入之间多加一重或者多重判断, 并且滞后写入过程, 大概流程如下
1. eep.chk=0x5a; 执行初始化;eep.chk^=0xff; eep.idle =eep_idle;这个idle不是定义成0, 可以是0xa0之类的, 后续程序读到eep.chk不是0xa5的话, 说话代码初始化都没执行完过就跑飞了
2. 主程序想写入数据时, 并不在此时, 此地直接写入, 而是先判断eep.chk是否正确后再装备好写入状态机,
enum
{
eep_idle = 0xaa,
eep_unlock,
eep_write
};
if (eep.chk == 0xa5 && eep.sta==eep_idle)
{
eep.write_addr = xxx;
eep.write_buf = yyy;
eep.write_bytes = zzz;
eep.sta++; //实际效果是eep.sta = eep_unlock;
}
3. 然后有专门的eep写入函数状态机, 并不在同一时刻顺序完成解锁,擦除/写入, 防止直接跑飞到这里
if (eep.chk != 0xa5)
{
eep.sta = 0;
// system_reset();
return;
}
switch(eep.sta)
{
case eep_idle: break;
case eep_unlock: eeprom_unlock(); if (eep.chk!=0xa5) eeprom_lock(); else eep.sta++; break;
case eep_write: if (eep.chk!=0xa5||eep.sta!=eep_write) {eeprom_lock(); break;} eeprom_write(eep.write_addr, eep.write_buf, eep.write_bytes); eep_lock(); eep.sta -= 2; break;// 我的程序实际上每个字节写入都重新检测了一次, 但这里举例就直接写了个连续写入的函数
default: eep.sta = 0; system_reset();
}
大体就是这样了, 以上我临时打的代码, 并非已用程序的复制粘贴, 主要是说明思路, 如果有笔误 自己理解正确就行, 别较真 eep.sta不直接使用赋值, 可以防止 跑飞产生直接满足状态情况的, 如果写入滞后时间允许的话, 最好是在主程序决定写入, 到实际unlock中间再加入一个延时状态
可以有效防止断电 上电电压不稳定时, 初始化也执行, 但最终还是跑飞到主程序开启写入状态机的位置, 有足够延迟的话, 上电跑飞WDT应该就起作用了, 断电也会撑到设备没电自然不可能写入了.. 别人做的产品,参数三天两头丢, 我做产品, 还一次没丢过{:lol:} 以前也发现有EEPROM数据改写的情况,后来这样避免:
1.EEPROM 最好使用独立 IIC接口.
2.EEPROM 的WP引脚 在读写一瞬间才解除写保护, 写完立刻保护住.
3.EEPROM数据带CRC校验, 并且数据备份2份,一份在MCU Flash.一份在EEPROM第二页.万一CRC校验出错厚立即复写恢复. 两份数据表相互备份,每份都有crc检验,读取的时候第一份正确的就用这个数据,不正确读备份数据表,然后复制回主表。
这样不怕掉电,总有一份数据是正确的,对多丢失上一次的记录,不会更多。 本帖最后由 gnail092 于 2021-2-4 10:15 编辑
snoopyzz 发表于 2021-2-1 20:12
eep.sta不直接使用赋值, 可以防止 跑飞产生直接满足状态情况的, 如果写入滞后时间允许的话, 最好是在主程序 ...
感谢回复,有疑问再次请教。
1、判断到eep.chk或eep.sta不对时,说明是程序跑飞了,是不是都应该进行软复位操作了。
2、我想最根本解决还是要找到导致程序跑飞的原因,因为程序任何地方都可能被非正常进入执行,不光是EEPROM操作,上面提到的还有RTC写操作,或者继电器动作控制等等。而且你这个办法有个前提是单片机内部EEPROM带有lock和unlcok,因为没有执行unlcok而直接飞到执行写的地方就不会成功。如果是外部EEPROM,我可以通过WP来控制,但如果是内部RTC或者继电器输出控制这种直接写就可以的话,程序直接跑飞到RTC写函数或继电器控制函数,这种办法效果就不明显了。
qwe2231695 发表于 2021-2-3 14:28
以前也发现有EEPROM数据改写的情况,后来这样避免:
1.EEPROM 最好使用独立 IIC接口.
程序跑飞到执行写数据的地方就可能会导致,flash和eeprom的两份数据一起写 gnail092 发表于 2021-2-4 10:12
感谢回复,有疑问再次请教。
1、判断到eep.chk或eep.sta不对时,说明是程序跑飞了,是不是都应该进行软复 ...
上电 断电过程, 如果电源曲线缓慢, 跑飞是难免的, 所以 WDT一定要加, 可以要快, 我的WDT都是20ms左右(正常1~4ms就喂一次)...
avr当时的eeprom没有lock和unlock但有个严格的时序要求,
我占用了一个通用寄存器的一个bit来做lock, 利用代码判断跳转的执行时间不同来超过这个时序大小, 实现了 lock功能
至于你说的RTC之类的, 你可以从写入值上判断下, 一般跑飞造成的写入值总会有越界的, 另外如果RTC内部带写保护寄存器就可以实现我说的方法 snoopyzz 发表于 2021-2-4 10:23
上电 断电过程, 如果电源曲线缓慢, 跑飞是难免的, 所以 WDT一定要加, 可以要快, 我的WDT都是20ms左右(正 ...
你的意思跑飞之后靠看门狗自己给他复位,我现在有个地方不明白,比方说程序跑飞到eeprom写函数后,执行完后还会继续执行不是照样可以执行到看门狗喂狗的地方吗,现在不知道有没有进hardfault中断。
RTC写函数内部也有个开启写使能的步骤,IO口操作函数内部也是有个开启写使能的操作的,你的意思是吧这个写使能操作也拿出来分步骤去执行了。 gnail092 发表于 2021-2-4 10:50
你的意思跑飞之后靠看门狗自己给他复位,我现在有个地方不明白,比方说程序跑飞到eeprom写函数后,执行完 ...
你自己根据实际需求决定 是否复位, 我不是写复位的代码 但加了//吗 还有问题的话,就是古董单片机存在的跑飞现象, stm32等现代单片机没有.写数据前需要加多点if判断,判断是正常开机,而不是跑飞进入. 会不会是页回卷 不太清楚你的备份是有保存操作的时候将结构体直接复制还是怎么样,有可能是数组越界的问题,看看map文件你这个结构体之前有没数组之类的。 本帖最后由 gnail092 于 2021-2-9 20:25 编辑
zhuozz 发表于 2021-2-9 16:32
不太清楚你的备份是有保存操作的时候将结构体直接复制还是怎么样,有可能是数组越界的问题,看看map文件你 ...
在写EEPROM函数内部将参数保存在指定的地址和备份地址各一份,跟数组越界没关系,就算数组越界也只是影响这个参数变量的RAM值,怎么会把EEPROM内的数据写了,又没有进行过写EEPROM操作 最后怎么解决的 BOD 等自带的电压复位可能不一定有效,有个纯软的办法,在写EEPROM的函数内多判断 程序是否是正常开机进入。
uint16_t _boot_flag= 0;
uint16_t _boot_flag1= 0;
main()
{
uint16_t _boot_flag= 0x55aa;
uint16_t _boot_flag1= 0xbbcc;
}
void eeprom_write()
{
if(_boot_flag == 0x55aa && _boot_flag1 == 0xbbcc)
{
}
else
{
// 亲测,电压不稳时能跑到这里来{:sweat:}
}
} qwe2231695 发表于 2021-11-21 03:07
BOD 等自带的电压复位可能不一定有效,有个纯软的办法,在写EEPROM的函数内多判断 程序是否是正常开机进入 ...
电压不稳时,boot_flag和boot_flag1为什么数据变了 qwe2231695 发表于 2021-11-21 03:07
BOD 等自带的电压复位可能不一定有效,有个纯软的办法,在写EEPROM的函数内多判断 程序是否是正常开机进入 ...
理念蛮好
在硬件考虑周到情况下
也是一种补充
咸蛋超人2 发表于 2021-11-25 09:53
电压不稳时,boot_flag和boot_flag1为什么数据变了
是PC指针 直接飞到函数里面去了。 如果有了变量判断,飞到附近,做判断后会被return,而不会继续运行 uint16_t _boot_flag= 0x55aa;
uint16_t _boot_flag1= 0xbbcc;初始化复一次就行了吗 qwe2231695 发表于 2021-11-25 11:17
是PC指针 直接飞到函数里面去了。 如果有了变量判断,飞到附近,做判断后会被return,而不会继续运行 ...
uint16_t _boot_flag= 0x55aa;
uint16_t _boot_flag1= 0xbbcc;初始化复一次就行了吗 咸蛋超人2 发表于 2021-11-25 11:28
uint16_t _boot_flag= 0x55aa;
uint16_t _boot_flag1= 0xbbcc;初始化复一次就行了吗
是啊,只有经过这里就认为是正常开机,你也可以分2个地方初始化,这样更加保险。
电压不稳定的时候,pc指针可能是随机值,这样导致随机执行代码。 qwe2231695 发表于 2021-11-25 12:03
是啊,只有经过这里就认为是正常开机,你也可以分2个地方初始化,这样更加保险。
电压不稳定的时候,pc ...
好的 谢谢
页:
[1]