1 寫flash意外斷電
在寫flash時(shí)突然斷電可能會(huì)造成數(shù)據(jù)丟失,為了避免這種情況發(fā)生,我們可以加一層數(shù)據(jù)保護(hù),在上電時(shí)檢查數(shù)據(jù)是否正確,如果不正確則使用備份的數(shù)據(jù)
2 內(nèi)部flash還是以STM32F103ZET6為例可在ST官網(wǎng)下載文檔:PM0075
(STM32F10xxx Flash memory microcontrollers)
FLASH的最小擦除單位是扇區(qū),扇區(qū)大小為2K
3 實(shí)現(xiàn)數(shù)據(jù)恢復(fù)
3.1 實(shí)現(xiàn)原理
-在保存數(shù)據(jù)時(shí),對(duì)當(dāng)前數(shù)據(jù)進(jìn)行CRC校驗(yàn),把校驗(yàn)結(jié)果一起寫入FLASH,同時(shí)再拷貝一份作為備份數(shù)據(jù)
-在上電加載參數(shù)時(shí),對(duì)當(dāng)前數(shù)據(jù)進(jìn)行CRC校驗(yàn),對(duì)比校驗(yàn)結(jié)果是否正確,如果不正確則使用備份數(shù)據(jù),正確則不處理
3.1.1 測(cè)試數(shù)據(jù)
假設(shè)需要存儲(chǔ)的數(shù)據(jù)是這樣的:
typedef struct
{
uint32_t times_clean;
uint32_t times_error;
uint8_t name[8];
uint32_t crc32;
}test_data_t;
利用影子變量,每隔一定時(shí)間來檢查參數(shù)是否發(fā)生變化,如果變化了就把最新的數(shù)據(jù)寫入FLASH
if (0 != rt_memcmp(&test_data,&test_data_shadow,sizeof(test_data_t)))
{
uint32_t get_crc = crc32_customized(&test_data_shadow,sizeof(test_data_t)-4);
test_data_shadow.crc32 = get_crc;
stm32_flash_erase(CONFIG_ADDRESS_TEST_DATA,sizeof(test_data_t)*2);
stm32_flash_write(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));
stm32_flash_write(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_shadow,sizeof(test_data_t));
rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
}
此時(shí)FLASH中的數(shù)據(jù)應(yīng)該是這個(gè)樣子的:
3.2 實(shí)現(xiàn)代碼
3.2.1 需要被存儲(chǔ)的數(shù)據(jù)相關(guān)定義
#define CONFIG_ADDRESS_TEST_DATA 0x0807F800
#define CONFIG_HEAT_PARAMETER_DEFAULT
{
.times_clean = 0,
.times_error = 0,
.name = "test",
};
test_data_t test_data = CONFIG_HEAT_PARAMETER_DEFAULT;
test_data_t test_data_shadow = CONFIG_HEAT_PARAMETER_DEFAULT;
test_data_t test_data_bak = CONFIG_HEAT_PARAMETER_DEFAULT;
3.2.2 CRC32校驗(yàn)API,與STM32的硬件CRC結(jié)果相同
#define CONFIG_CRC32_POLY 0x04C11DB7
#define CONFIG_CRC32_INIT_VALUE 0xFFFFFFFF
#define CONFIG_CRC32_OUT_XOR 0x00000000
uint32_t crc32_stm32_hardware(uint8_t *source,uint32_t length)
{
uint32_t crc_value = CONFIG_CRC32_INIT_VALUE;
for (int i =0; i < length; i++)
{
for (int j = 0; j < 8; j++)
{
uint8_t get_bit_value = ((source[i] > > (7 - j) & 1) == 1);
uint8_t get_value = ((crc_value > > 31 & 1) == 1);
crc_value < <= 1;
if (get_value ^ get_bit_value)
{
crc_value ^= CONFIG_CRC32_POLY;
}
}
}
crc_value &= 0xFFFFFFFF;
return (crc_value ^= CONFIG_CRC32_OUT_XOR);
}
3.2.3 上電加載參數(shù),檢查數(shù)據(jù)是否出錯(cuò),出錯(cuò)則使用備份數(shù)據(jù)
void g_check_data(void)
{
stm32_flash_read(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));
stm32_flash_read(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_bak,sizeof(test_data_t));
uint32_t crc_value_cal = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);
rt_kprintf("crc_value_cal[%x], crc_old[%x]rn",crc_value_cal,test_data_shadow.crc32);
if (crc_value_cal != test_data_shadow.crc32)
{
rt_kprintf("test data is invalidrn");
rt_memcpy(&test_data_shadow,&test_data_bak,sizeof(test_data_t)-4);
uint32_t crc_value_bak = crc32_stm32_hardware(&test_data_bak,sizeof(test_data_t)-4);
test_data_shadow.crc32 = crc_value_bak;
rt_memcpy(&test_data_shadow,&test_data_bak,sizeof(test_data_t)-4);
}
rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
}
3.2.4 完整的測(cè)試代碼
int main(void)
{
uint32_t get_crc_first = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);
test_data_shadow.crc32 = get_crc_first;
test_data.crc32 = get_crc_first;
g_check_data();
while (1)
{
if (0 != rt_memcmp(&test_data,&test_data_shadow,sizeof(test_data_t)))
{
uint32_t get_crc = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);
test_data_shadow.crc32 = get_crc;
rt_base_t level;
level = rt_hw_interrupt_disable();
stm32_flash_erase(CONFIG_ADDRESS_TEST_DATA,sizeof(test_data_t)*2);
stm32_flash_write(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));
stm32_flash_write(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_shadow,sizeof(test_data_t));
rt_hw_interrupt_enable(level);
rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
}
rt_thread_mdelay(1000);
}
}
int cmd_flash_protect_test(int argc, char **argv)
{
if (2 == argc)
{
uint32_t get_type = atoi(argv[1]);
if (0 == get_type)
{
g_check_data();
}
else if (1 == get_type)
{
test_data_shadow.times_clean++;
}
}
return 0;
}
MSH_CMD_EXPORT_ALIAS(cmd_flash_protect_test,flash_protect,flash_protect [val]);
4 測(cè)試效果
| /
RT - Thread Operating System
/ | 4.1.1 build Jul 1 2023 21:37:26
2006 - 2022 Copyright by RT-Thread team
crc_value_cal[95663ff9], crc_old[95663ff9]
msh / >flash_protect 1
msh / >need write flash crc[ba0600aa]
old_data: times_clean:[6] times_error:[0] name[test] crc:[95663ff9] old_data end
new_data: times_clean:[7] times_error:[0] name[test] crc:[ba0600aa] new_data end
msh / >flash_protect 0
crc_value_cal[ba0600aa], crc_old[ba0600aa]
5 總結(jié)
這個(gè)方法不適合存儲(chǔ)的數(shù)據(jù)超過一個(gè)扇區(qū)大小,還需要根據(jù)實(shí)際情況來調(diào)整寫入和加載參數(shù)的方式
我們雖不能保證自己的軟件完全沒有BUG,但可以先寫一份軟件測(cè)試用例,將需要測(cè)試的每一個(gè)功能列成TODOLIST,再按照這個(gè)清單去自測(cè),這樣就能在自測(cè)試發(fā)現(xiàn)并及時(shí)修正錯(cuò)誤,反復(fù)測(cè)試多次后,我們?cè)侔衍浖峤唤o測(cè)試可能會(huì)更好一些,工作中遇到困難是讓我們進(jìn)步的,是提醒我們?cè)搩?yōu)化自己的工作方法了。
-
存儲(chǔ)器
+關(guān)注
關(guān)注
38文章
7518瀏覽量
164074 -
CRC校驗(yàn)
+關(guān)注
關(guān)注
0文章
84瀏覽量
15244 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1300瀏覽量
40264 -
Flash單片機(jī)
+關(guān)注
關(guān)注
0文章
111瀏覽量
9423 -
STM32F103ZET6
+關(guān)注
關(guān)注
9文章
67瀏覽量
21167
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論