1 寫flash意外斷電
在寫flash時突然斷電可能會造成數(shù)據(jù)丟失,為了避免這種情況發(fā)生,我們可以加一層數(shù)據(jù)保護(hù),在上電時檢查數(shù)據(jù)是否正確,如果不正確則使用備份的數(shù)據(jù)
2 內(nèi)部flash還是以STM32F103ZET6為例可在ST官網(wǎng)下載文檔:PM0075
(STM32F10xxx Flash memory microcontrollers)
FLASH的最小擦除單位是扇區(qū),扇區(qū)大小為2K
3 實現(xiàn)數(shù)據(jù)恢復(fù)
3.1 實現(xiàn)原理
-在保存數(shù)據(jù)時,對當(dāng)前數(shù)據(jù)進(jìn)行CRC校驗,把校驗結(jié)果一起寫入FLASH,同時再拷貝一份作為備份數(shù)據(jù)
-在上電加載參數(shù)時,對當(dāng)前數(shù)據(jù)進(jìn)行CRC校驗,對比校驗結(jié)果是否正確,如果不正確則使用備份數(shù)據(jù),正確則不處理
3.1.1 測試數(shù)據(jù)
假設(shè)需要存儲的數(shù)據(jù)是這樣的:
typedef struct
{
uint32_t times_clean;
uint32_t times_error;
uint8_t name[8];
uint32_t crc32;
}test_data_t;
利用影子變量,每隔一定時間來檢查參數(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));
}
此時FLASH中的數(shù)據(jù)應(yīng)該是這個樣子的:
3.2 實現(xiàn)代碼
3.2.1 需要被存儲的數(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校驗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ù)是否出錯,出錯則使用備份數(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 完整的測試代碼
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 測試效果
| /
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é)
這個方法不適合存儲的數(shù)據(jù)超過一個扇區(qū)大小,還需要根據(jù)實際情況來調(diào)整寫入和加載參數(shù)的方式
我們雖不能保證自己的軟件完全沒有BUG,但可以先寫一份軟件測試用例,將需要測試的每一個功能列成TODOLIST,再按照這個清單去自測,這樣就能在自測試發(fā)現(xiàn)并及時修正錯誤,反復(fù)測試多次后,我們再把軟件提交給測試可能會更好一些,工作中遇到困難是讓我們進(jìn)步的,是提醒我們該優(yōu)化自己的工作方法了。
-
存儲器
+關(guān)注
關(guān)注
38文章
7440瀏覽量
163530 -
CRC校驗
+關(guān)注
關(guān)注
0文章
84瀏覽量
15176 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1265瀏覽量
39854 -
Flash單片機(jī)
+關(guān)注
關(guān)注
0文章
111瀏覽量
9379 -
STM32F103ZET6
+關(guān)注
關(guān)注
9文章
67瀏覽量
21069
發(fā)布評論請先 登錄
相關(guān)推薦
評論