摘要:在單片機(jī)日常開(kāi)發(fā)中,總會(huì)需要存儲(chǔ)一些信息,這時(shí)就需要使用單片機(jī)FLASH存儲(chǔ)的方案,目前單片機(jī)存儲(chǔ)的方案有很多如:EASYFLASH、FLASHDB、OSAL_NV等等方案,他們程序都非常大,在存儲(chǔ)不多的變量時(shí)不值得。而且現(xiàn)有方案的代碼中很少有考慮到flash寫(xiě)入出錯(cuò)的情況。
在實(shí)際產(chǎn)品中,嵌入式產(chǎn)品flash寫(xiě)入可能會(huì)受各種因素影響(電池供電、意外斷電、氣溫等)從而并不是很穩(wěn)定,一旦出現(xiàn)錯(cuò)誤,會(huì)導(dǎo)致產(chǎn)品一系列問(wèn)題。
一、TinyFlashDB設(shè)計(jì)理念
不同于其他很多的KV型數(shù)據(jù)庫(kù),TinyFlashDB每一個(gè)需要存儲(chǔ)的變量都會(huì)分配一個(gè)單獨(dú)的單片機(jī)flash扇區(qū),變量長(zhǎng)度不可變。
TinyFlashDB在設(shè)計(jì)時(shí)就考慮了寫(xiě)入錯(cuò)誤的影響,追求力所能及的安全保障、資源占用方面盡可能的縮小(不到1kb代碼占用)、盡可能的通用性(可以移植到51等8位機(jī),無(wú)法逆序?qū)懭氲?a target="_blank">stm32L4系列,某些flash加密的單片機(jī)和其他普通32位機(jī)上)。
二、TinyFlashDB使用示例
?
const?tfdb_index_t?test_index?=?{ ????.end_byte?=?0x00, ????.flash_addr?=?0x4000, ????.flash_size?=?256, ????.value_length?=?2, }; tfdb_addr_t?addr?=?0;?/*addr?cache*/ uint8_t?test_buf[4];?/*aligned_value_size*/ uint16_t?test_value; void?main() { ????TFDB_Err_Code?result; ????result?=?tfdb_set(&test_index,?test_buf,?&addr,?&test_value); ????if(result?==?TFDB_NO_ERR) ????{ ????????printf("set?ok,?addr:%x ",?addr); ????} ????result?=?tfdb_get(&test_index,?test_buf,?&addr,?&test_value); ????if(result?==?TFDB_NO_ERR) ????{ ????????printf("get?ok,?addr:%x,?value:%x ",?addr,?test_value); ????} }
?
三、TinyFlashDB API介紹
?
typedef?struct?_tfdb_index_struct{ ????tfdb_addr_t?????flash_addr;/*?the?start?address?of?the?flash?block?*/ ????uint16_t????????flash_size;/*?the?size?of?the?flash?block?*/ ????uint8_t?????????value_length;/*?the?length?of?value?that?saved?in?this?flash?block?*/ ????uint8_t?????????end_byte;?/*?must?different?to?TFDB_VALUE_AFTER_ERASE?*/ ????/*?0x00?is?recommended?for?end_byte,?because?almost?all?flash?is?0xff?after?erase.?*/ }tfdb_index_t;
?
結(jié)構(gòu)體功能:在TinyFlashDB中,API的操作都需要指定的參數(shù)index,該index結(jié)構(gòu)體中存儲(chǔ)了flash的地址,flash的大小,存儲(chǔ)的變量的長(zhǎng)度,結(jié)束標(biāo)志位。在讀取flash扇區(qū)時(shí)會(huì)去校驗(yàn)此信息。
?
TFDB_Err_Code?tfdb_get(const?tfdb_index_t?*index,?uint8_t?*rw_buffer,?tfdb_addr_t?*addr_cache,?void*?value_to);
?
函數(shù)功能:從index指向的扇區(qū)中獲取一個(gè)index中指定變量長(zhǎng)度的變量,flash頭部數(shù)據(jù)校驗(yàn)出錯(cuò)不會(huì)重新初始化flash。
參數(shù) index:tfdb操作的index指針。
參數(shù) rw_buffer:寫(xiě)入和讀取的緩存,所有flash的操作最后都會(huì)將整理后的數(shù)據(jù)拷貝到該buffer中,再調(diào)用tfdb_port_write或者tfdb_port_read進(jìn)行寫(xiě)入。當(dāng)芯片對(duì)于寫(xiě)入的數(shù)據(jù)區(qū)緩存有特殊要求(例如4字節(jié)對(duì)齊,256字節(jié)對(duì)齊等),可以通過(guò)該參數(shù)將符合要求的變量指針傳遞給函數(shù)使用。至少為4字節(jié)長(zhǎng)度。
參數(shù) addr_cache:可以是NULL,或者是地址緩存變量的指針,當(dāng)addr_cache不為NULL,并且也不為0時(shí),則認(rèn)為addr_cache已經(jīng)初始化成功,不再校驗(yàn)flash頭部,直接從該addr_cache的地址讀取數(shù)據(jù)。
參數(shù) value_to:要存儲(chǔ)數(shù)據(jù)內(nèi)容的地址。
返回值:TFDB_NO_ERR成功,其他失敗。
?
TFDB_Err_Code?tfdb_set(const?tfdb_index_t?*index,?uint8_t?*rw_buffer,?tfdb_addr_t?*addr_cache,?void*?value_from);
?
函數(shù)功能:在index指向的扇區(qū)中寫(xiě)入一個(gè)index中指定變量長(zhǎng)度的變量,flash頭部數(shù)據(jù)校驗(yàn)出錯(cuò)重新初始化flash。
參數(shù) index:tfdb操作的index指針。
參數(shù) rw_buffer:寫(xiě)入和讀取的緩存,所有flash的操作最后都會(huì)將整理后的數(shù)據(jù)拷貝到該buffer中,再調(diào)用tfdb_port_write或者tfdb_port_read進(jìn)行寫(xiě)入。當(dāng)芯片對(duì)于寫(xiě)入的數(shù)據(jù)區(qū)緩存有特殊要求(例如4字節(jié)對(duì)齊,256字節(jié)對(duì)齊等),可以通過(guò)該參數(shù)將符合要求的變量指針傳遞給函數(shù)使用。至少為4字節(jié)長(zhǎng)度。
參數(shù) addr_cache:可以是NULL,或者是地址緩存變量的指針,當(dāng)addr_cache不為NULL,并且也不為0時(shí),則認(rèn)為addr_cache已經(jīng)初始化成功,不再校驗(yàn)flash頭部,直接從該addr_cache的地址讀取數(shù)據(jù)。
參數(shù) value_from:要存儲(chǔ)的數(shù)據(jù)內(nèi)容。
返回值:TFDB_NO_ERR成功,其他失敗。
四、TinyFlashDB設(shè)計(jì)原理
觀察上方代碼,可以發(fā)現(xiàn)TinyFlashDB的操作都需要tfdb_index_t定義的index參數(shù)。
Flash初始化后頭部信息為4字節(jié),所以只支持1、2、4、8字節(jié)操作的flash:頭部初始化時(shí)會(huì)讀取頭部,所以函數(shù)中rw_buffer指向的數(shù)據(jù)第一要求至少為4字節(jié),如果最小寫(xiě)入單位是8字節(jié),則為第一要求最少為8字節(jié)。
第一字節(jié) | 第二字節(jié) | 第三字節(jié) | 第四字節(jié)和其他對(duì)齊字節(jié) |
---|---|---|---|
flash_size高8位字節(jié) | flash_size低8位字節(jié) | value_length | end_byte |
數(shù)據(jù)存儲(chǔ)時(shí),會(huì)根據(jù)flash支持的字節(jié)操作進(jìn)行對(duì)齊,所以函數(shù)中rw_buffer指向的數(shù)據(jù)第二要求至少為下面函數(shù)中計(jì)算得出的aligned_value_size個(gè)字節(jié):
?
????aligned_value_size??=?index->value_length?+?2;/*?data?+?verify?+?end_byte?*/ ? #if?(TFDB_WRITE_UNIT_BYTES==2) ????/*?aligned?with?TFDB_WRITE_UNIT_BYTES?*/ ????aligned_value_size?=?((aligned_value_size?+?1)?&?0xfe); #elif?(TFDB_WRITE_UNIT_BYTES==4) ????/*?aligned?with?TFDB_WRITE_UNIT_BYTES?*/ ????aligned_value_size?=?((aligned_value_size?+?3)?&?0xfc); #elif?(TFDB_WRITE_UNIT_BYTES==8) ????/*?aligned?with?TFDB_WRITE_UNIT_BYTES?*/ ????aligned_value_size?=?((aligned_value_size?+?7)?&?0xf8); #endif
?
前value_length個(gè)字節(jié) | 第value_length+1字節(jié) | 第value_length+2字節(jié) | 其他對(duì)齊字節(jié) |
---|---|---|---|
value_from數(shù)據(jù)內(nèi)容 | value_from的和校驗(yàn) | end_byte | end_byte |
每次寫(xiě)入后都會(huì)再讀取出來(lái)進(jìn)行校驗(yàn),如果校驗(yàn)不通過(guò),就會(huì)繼續(xù)在下一個(gè)地址寫(xiě)入。指導(dǎo)達(dá)到最大寫(xiě)入次數(shù)(TFDB_WRITE_MAX_RETRY)或者頭部校驗(yàn)錯(cuò)誤。
讀取數(shù)據(jù)時(shí)也會(huì)計(jì)算和校驗(yàn),不通過(guò)的話繼續(xù)讀取,直到返回校驗(yàn)通過(guò)的最新數(shù)據(jù),或者讀取失敗。
五、TinyFlashDB移植和配置
移植使用只需要在tfdb_port.c中,編寫(xiě)完成三個(gè)接口函數(shù),也要在tfdb_port.h中添加相應(yīng)的頭文件和根據(jù)不同芯片修改宏定義
?
TFDB_Err_Code?tfdb_port_read(tfdb_addr_t?addr,?uint8_t?*buf,?size_t?size); TFDB_Err_Code?tfdb_port_erase(tfdb_addr_t?addr,?size_t?size); TFDB_Err_Code?tfdb_port_write(tfdb_addr_t?addr,?const?uint8_t?*buf,?size_t?size);
?
所有的配置項(xiàng)都在tfdb_port.h中
?
/*?use?string.h?or?self?functions?*/ #define?TFDB_USE_STRING_H???????????????1 #if?TFDB_USE_STRING_H ????#include?"string.h" ????#define?tfdb_memcpy?memcpy ????#define?tfdb_memcmp?memcmp ????#define?TFDB_MEMCMP_SAME?0 #else ????#define?tfdb_memcpy ????#define?tfdb_memcmp ????#define?TFDB_MEMCMP_SAME #endif #define?TFDB_DEBUG??????????????????????????printf /*?The?data?value?in?flash?after?erased,?most?are?0xff,?some?flash?maybe?different.?*/ #define?TFDB_VALUE_AFTER_ERASE??????????????0xff /*?the?flash?write?granularity,?unit:?byte ?*?only?support?1(stm32f4)/?2(CH559)/?4(stm32f1)/?8(stm32L4)?*/ #define?TFDB_WRITE_UNIT_BYTES???????????????8?/*?@note?you?must?define?it?for?a?value?*/ /*?@note?the?max?retry?times?when?flash?is?error?,set?0?will?disable?retry?count?*/ #define?TFDB_WRITE_MAX_RETRY????????????????32 /*?must?not?use?pointer?type.?Please?use?uint32_t,?uint16_t?or?uint8_t.?*/ typedef?uint32_t????tfdb_addr_t;
?
六、移植到STM32單片機(jī)
項(xiàng)目地址:https://github.com/smartmx/TFDB/tree/main
使用一下命令克隆Demo,裸機(jī)移植例程
?
git?clone?-b?raw?https://github.com/smartmx/TFDB.git
?
?
評(píng)論
查看更多