Tiny Flash Database pour MCU.
Dans le développement quotidien de microcontrôleurs, certaines informations sont toujours nécessaires. À l'heure actuelle, une solution pour le stockage flash microcontrôleur est nécessaire. Actuellement, il existe de nombreuses solutions pour le stockage du microcontrôleur, telles que: EasyFlash, FlashDB, OSAL_NV, etc. Leurs programmes sont très importants et ne valent pas la peine de stocker peu de variables. Et il est rare de considérer les erreurs d'écriture flash.
Dans les produits réels, l'écriture flash de produits embarqués peut être affectée par divers facteurs (panne de courant inattendu et alimenté par batterie, température, etc.) et n'est pas très stable. Une fois qu'une erreur se produit, cela entraînera une série de problèmes de produit.
Contrairement à de nombreuses autres bases de données de type KV, chaque variable qui doit être stockée dans TinyflashDB sera allouée à un secteur de flash de microcontrôleur distinct, et la longueur de la variable est immuable.
Par conséquent, TinyflashDB ne convient que pour le stockage de plusieurs variables clés (telles que: IAP Jump Flag, Temps de sortie du système, etc.), et ne convient pas au stockage de données à grande échelle (le stockage de données à grande échelle peut utiliser Easyflash, etc.).
TinyflashDB a été conçu pour tenir compte de l'impact des erreurs d'écriture, de la poursuite des garanties de sécurité dans sa capacité, de l'occupation des ressources autant que possible (moins de 1 Ko Code occupe) et de l'universalité que possible (peut être porté vers des ordinateurs 8 bits tels que 51, STM32L4 série qui ne peuvent pas être écrits dans les ordinateurs inverses, certains microcontrôlers incendiaires et les ordinateurs incrustés et les ordinateurs ordinaires 32 bit).
const tfdb_index_t test_index = {
. end_byte = 0x00 ,
. flash_addr = 0x4000 ,
. flash_size = 256 ,
. value_length = 2 ,
}; /* c99写法,如果编译器不支持,可自行改为c89写法 */
tfdb_addr_t addr = 0 ; /*addr cache*/
uint8_t test_buf [ TFDB_ALIGNED_RW_BUFFER_SIZE ( 2 , 1 )]; /*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:%xn" , addr );
}
addr = 0 ; /* reset addr cache, to see tfdb_get. */
result = tfdb_get ( & test_index , test_buf , & addr , & test_value );
if ( result == TFDB_NO_ERR )
{
printf ( "get ok, addr:%x, value:%xn" , addr , test_value );
}
} 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 ;Fonction de structure: Dans TinyflashDB, les opérations API nécessitent l'indice de paramètre spécifié. Cette structure d'index stocke l'adresse du flash, la taille du flash, la longueur des variables stockées et le drapeau d'extrémité. Ces informations seront vérifiées lors de la lecture du secteur Flash.
TFDB_Err_Code tfdb_get ( const tfdb_index_t * index , uint8_t * rw_buffer , tfdb_addr_t * addr_cache , void * value_to ); Fonction Fonction: Obtenez une variable avec une longueur de variable spécifiée dans l'index du secteur pointé par index . L'erreur de vérification des données de l'en-tête flash ne réinitialisera pas le flash.
index des paramètres: pointeur d'index pour l'opération TFDB.
Paramètre rw_buffer : écriture et lisez le cache. Toutes les opérations flash copieront enfin les données triées dans le tampon, puis appellent tfdb_port_write ou tfdb_port_read pour la lecture et l'écriture. Lorsque la puce a des exigences spéciales pour le cache de la zone de données écrits (tel que l'alignement de 4 octets, l'alignement de 256 octets, etc.), le pointeur variable qui répond aux exigences peut être transmis à la fonction d'utilisation via ce paramètre. Au moins 4 octets de longueur.
Paramètre addr_cache : peut être NULL ou un pointeur vers la variable de cache d'adresse. Lorsque addr_cache n'est pas NULL et n'est pas 0, addr_cache est considéré comme ayant été initialisé avec succès, ne vérifiez plus l'en-tête flash et les données sont lues directement à partir de l'adresse de addr_cache .
Paramètre value_to : l'adresse pour stocker le contenu des données.
Valeur de retour: TFDB_NO_ERR succède, d'autres échouent.
TFDB_Err_Code tfdb_set ( const tfdb_index_t * index , uint8_t * rw_buffer , tfdb_addr_t * addr_cache , void * value_from ); Fonction Fonction: Écrivez une variable avec une longueur de variable spécifiée dans l'index dans le secteur pointé par index , et l'erreur de vérification des données de l'en-tête flash est erronée et le flash est réinitialisé.
index des paramètres: pointeur d'index pour l'opération TFDB.
Paramètre rw_buffer : écriture et lisez le cache. Toutes les opérations flash copieront enfin les données triées dans le tampon, puis appellent tfdb_port_write ou tfdb_port_read pour la lecture et l'écriture. Lorsque la puce a des exigences spéciales pour le cache de la zone de données écrits (tel que l'alignement de 4 octets, l'alignement de 256 octets, etc.), le pointeur variable qui répond aux exigences peut être transmis à la fonction d'utilisation via ce paramètre. Au moins 4 octets de longueur.
Paramètre addr_cache : peut être NULL ou un pointeur vers la variable de cache d'adresse. Lorsque addr_cache n'est pas NULL et n'est pas 0, addr_cache est considéré comme ayant été initialisé avec succès, ne vérifiez plus l'en-tête flash et les données sont lues directement à partir de l'adresse de addr_cache .
Paramètre value_from : le contenu des données à stocker.
Valeur de retour: TFDB_NO_ERR succède, d'autres échouent.
L'API TFDB Dual est encapsulée basée sur tfdb_set et tfdb_get . tfdb dual appellera tfdb_set et tfdb_get et ajoutera deux octets de SEQ à l'avant des données, donc dans le double TFDB, la longueur de variable de stockage la plus longue est de 253 octets.
Dans le même temps, l'API Dual TFDB doit fournir deux tampons, et il doit être aligned_value_size augmente la longueur de la variable à deux octets, puis la recalcule.
typedef struct _my_test_params_struct
{
uint32_t aa [ 2 ];
uint8_t bb [ 16 ];
} my_test_params_t ;
my_test_params_t my_test_params = {
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18
};
tfdb_dual_index_t my_test_tfdb_dual = {
. indexes [ 0 ] = {
. end_byte = 0x00 ,
. flash_addr = 0x08077000 ,
. flash_size = 256 ,
. value_length = TFDB_DUAL_VALUE_LENGTH ( sizeof ( my_test_params_t )),
},
. indexes [ 1 ] = {
. end_byte = 0x00 ,
. flash_addr = 0x08077100 ,
. flash_size = 256 ,
. value_length = TFDB_DUAL_VALUE_LENGTH ( sizeof ( my_test_params_t )),
},
};
tfdb_dual_cache_t my_test_tfdb_dual_cache = { 0 };
void my_test_tfdb_dual_func ()
{
uint32_t rw_buffer [ TFDB_DUAL_ALIGNED_RW_BUFFER_SIZE ( TFDB_DUAL_VALUE_LENGTH ( sizeof ( my_test_params_t )), 4 )];
uint32_t rw_buffer_bak [ TFDB_DUAL_ALIGNED_RW_BUFFER_SIZE ( TFDB_DUAL_VALUE_LENGTH ( sizeof ( my_test_params_t )), 4 )];
TFDB_Err_Code err ;
for ( uint8_t i = 0 ; i < 36 ; i ++ )
{
err = tfdb_dual_get ( & my_test_tfdb_dual , ( uint8_t * ) rw_buffer , ( uint8_t * ) rw_buffer_bak , & my_test_tfdb_dual_cache , & my_test_params );
if ( err == TFDB_NO_ERR )
{
printf ( "read okncache seq1:0x%04x, seq2:0x%04xnaddr1:0x%08x, addr2:0x%08xn" , my_test_tfdb_dual_cache . seq [ 0 ], my_test_tfdb_dual_cache . seq [ 1 ], my_test_tfdb_dual_cache . addr_cache [ 0 ], my_test_tfdb_dual_cache . addr_cache [ 1 ]);
}
else
{
printf ( "read err:%dn" , err );
}
my_test_params . aa [ 0 ] ++ ;
my_test_params . aa [ 1 ] ++ ;
for ( uint8_t i = 0 ; i < 16 ; i ++ )
{
my_test_params . bb [ i ] ++ ;
}
memset ( & my_test_tfdb_dual_cache , 0 , sizeof ( my_test_tfdb_dual_cache )); /* 测试无地址缓存写入 */
err = tfdb_dual_set ( & my_test_tfdb_dual , ( uint8_t * ) rw_buffer , ( uint8_t * ) rw_buffer_bak , & my_test_tfdb_dual_cache , & my_test_params );
if ( err == TFDB_NO_ERR )
{
printf ( "write okncache seq1:0x%04x, seq2:0x%04xnaddr1:0x%08x, addr2:0x%08xn" , my_test_tfdb_dual_cache . seq [ 0 ], my_test_tfdb_dual_cache . seq [ 1 ], my_test_tfdb_dual_cache . addr_cache [ 0 ], my_test_tfdb_dual_cache . addr_cache [ 1 ]);
}
else
{
printf ( "write err:%dn" , err );
}
memset ( & my_test_tfdb_dual_cache , 0 , sizeof ( my_test_tfdb_dual_cache )); /* 测试无地址缓存读取 */
}
} typedef struct _tfdb_dual_index_struct
{
tfdb_index_t indexes [ 2 ];
} tfdb_dual_index_t ;
typedef struct _tfdb_dual_cache_struct
{
tfdb_addr_t addr_cache [ 2 ];
uint16_t seq [ 2 ];
} tfdb_dual_cache_t ; Fonction de structure: Dans le double Tinyflashdb, les opérations API nécessitent l' index de paramètre spécifié, index stocke deux tfdb_index_t .
TFDB_Err_Code tfdb_dual_get ( const tfdb_dual_index_t * index , uint8_t * rw_buffer , uint8_t * rw_buffer_bak , tfdb_dual_cache_t * cache , void * value_to );Fonction Fonction: Obtenez une variable avec une longueur de variable spécifiée dans l'index du secteur pointé par index. L'erreur de vérification des données de l'en-tête flash ne réinitialisera pas le flash.
index des paramètres: pointeur d'index pour l'opération TFDB.
Paramètre rw_buffer : écriture et lisez le cache. Toutes les opérations flash copieront enfin les données triées dans le tampon, puis appellent tfdb_port_write ou tfdb_port_read pour la lecture et l'écriture. Lorsque la puce a des exigences spéciales pour le cache de la zone de données écrits (tel que l'alignement de 4 octets, l'alignement de 256 octets, etc.), le pointeur variable qui répond aux exigences peut être transmis à la fonction d'utilisation via ce paramètre. Au moins 4 octets de longueur.
Paramètre rw_buffer_bak : Écrivez et lisez le cache. Toutes les opérations flash copieront enfin les données triées dans le tampon, puis appellent tfdb_port_write ou tfdb_port_read pour la lecture et l'écriture. Lorsque la puce a des exigences spéciales pour le cache de la zone de données écrits (tel que l'alignement de 4 octets, l'alignement de 256 octets, etc.), le pointeur variable qui répond aux exigences peut être transmis à la fonction d'utilisation via ce paramètre. Au moins 4 octets de longueur.
cache de paramètre: ne peut pas être NULL , ce doit être un pointeur mis en cache défini par tfdb_dual_cache_t . Lorsque les données cache sont légales, il est considéré que cache a été initialisé avec succès et que les données sont lues directement à partir du bloc flash et de l'adresse cache .
Paramètre value_to : l'adresse pour stocker le contenu des données.
Valeur de retour: TFDB_NO_ERR succède, d'autres échouent.
TFDB_Err_Code tfdb_dual_set ( const tfdb_dual_index_t * index , uint8_t * rw_buffer , uint8_t * rw_buffer_bak , tfdb_dual_cache_t * cache , void * value_from );Fonction Fonction: Écrivez une variable avec une longueur de variable spécifiée dans l'index dans le secteur pointé par index, et l'erreur de vérification des données de l'en-tête flash est erronée et le flash est réinitialisé.
index des paramètres: pointeur d'index pour l'opération TFDB.
Paramètre rw_buffer : écriture et lisez le cache. Toutes les opérations flash copieront enfin les données triées dans le tampon, puis appellent tfdb_port_write ou tfdb_port_read pour la lecture et l'écriture. Lorsque la puce a des exigences spéciales pour le cache de la zone de données écrits (tel que l'alignement de 4 octets, l'alignement de 256 octets, etc.), le pointeur variable qui répond aux exigences peut être transmis à la fonction d'utilisation via ce paramètre. Au moins 4 octets de longueur.
Paramètre rw_buffer_bak : Écrivez et lisez le cache. Toutes les opérations flash copieront enfin les données triées dans le tampon, puis appellent tfdb_port_write ou tfdb_port_read pour la lecture et l'écriture. Lorsque la puce a des exigences spéciales pour le cache de la zone de données écrits (tel que l'alignement de 4 octets, l'alignement de 256 octets, etc.), le pointeur variable qui répond aux exigences peut être transmis à la fonction d'utilisation via ce paramètre. Au moins 4 octets de longueur.
cache de paramètre: ne peut pas être NULL , ce doit être un pointeur mis en cache défini par tfdb_dual_cache_t . Lorsque les données cache sont légales, il est considéré que cache a été initialisé avec succès et que les données sont lues directement à partir du bloc flash et de l'adresse cache .
Paramètre value_from : le contenu des données à stocker.
Valeur de retour: TFDB_NO_ERR succède, d'autres échouent.
En observant le code ci-dessus, vous pouvez constater que les opérations TinyflashDB nécessitent index définis par tfdb_index_t .
Une fois le flash initialisé, les informations d'en-tête sont de 4 octets, donc seuls les opérations 1, 2, 4 et 8 octets sont prises en charge:
L'en-tête sera lu lorsque l'en-tête est initialisé, donc la première exigence pour les données indiquées par rw_buffer dans la fonction est d'au moins 4 octets. Si l'unité d'écriture minimale est de 8 octets, la première exigence est d'au moins 8 octets.
| Premier octet | Deuxième octet | Octet 3 | Quatrième octet et autres octets alignés |
|---|---|---|---|
| flash_size octets 8 bits supérieurs | Flash_Size Low 8 bits octets | Value_Length | end_byte |
Lorsque les données sont stockées, elles seront alignées en fonction des opérations d'octets prises en charge par Flash, de sorte que les deuxièmes données indiquées par rw_buffer dans la fonction doivent être au moins aligned_value_size octets calculés dans la fonction suivante:
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| First Value_Length octets | Value_Length + 1 octet | Value_Length + 2 octets | Autres octets alignés |
|---|---|---|---|
| Contenu de données Value_From | Value_From Sum Vérification | end_byte | end_byte |
Après chaque écriture, il sera lu pour la vérification. Si la vérification échoue, elle continuera d'essayer d'écrire à l'adresse suivante. Jusqu'à ce que le nombre maximum d'écritures (tfdb_write_max_retry) soit atteint ou que l'erreur de vérification de l'en-tête soit mauvaise.
Lors de la lecture des données, il sera également calculé et vérifié. S'il ne passe pas, il continuera de lire tant que les dernières données qui ont réussi la vérification ne seront pas renvoyées ou que la lecture échoue.
Il n'y a que 3 valeurs juridiques pour le SEQ à deux octets à l'avant des données, 0x00ff-> 0x0ff0-> 0xff00.
Ce cycle se répète, en lisant le SEQ de la dernière variable dans les deux blocs, nous pouvons déterminer quel secteur flash est la dernière valeur stockée dans le secteur des flashs.
Lorsque la dernière valeur est stockée dans le premier secteur, la prochaine écriture sera écrite dans le deuxième secteur, et vice versa.
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 ); /* 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.
* if it's over 1 byte, please be care of little endian or big endian. */
#define TFDB_VALUE_AFTER_ERASE 0xff
/* The size of TFDB_VALUE_AFTER_ERASE, only support 1 / 2 / 4.
* This value must not bigger than TFDB_WRITE_UNIT_BYTES. */
#define TFDB_VALUE_AFTER_ERASE_SIZE 1
/* 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 ;Après avoir supprimé les informations d'impression de débogage, la ressource occupe comme suit:
Options d'optimisation de compilation Keil -O2
Code ( inc . data ) RO Data RW Data ZI Data Debug Object Name
154 0 0 0 0 2621 tfdb_port . o
682 0 0 0 0 4595 tinyflashdb . oOptions d'optimisation de compilation GCC -OS
. text . tfdb_port_read
0x00000000000039b4 0x1a ./ Drivers / TFDB / tfdb_port . o
0x00000000000039b4 tfdb_port_read
. text . tfdb_port_erase
0x00000000000039ce 0x46 ./ Drivers / TFDB / tfdb_port . o
0x00000000000039ce tfdb_port_erase
. text . tfdb_port_write
0x0000000000003a14 0x5c ./ Drivers / TFDB / tfdb_port . o
0x0000000000003a14 tfdb_port_write
. text . tfdb_check
0x0000000000003a70 0x56 ./ Drivers / TFDB / tinyflashdb . o
0x0000000000003a70 tfdb_check
. text . tfdb_init
0x0000000000003ac6 0x56 ./ Drivers / TFDB / tinyflashdb . o
0x0000000000003ac6 tfdb_init
. text . tfdb_set
0x0000000000003b1c 0x186 ./ Drivers / TFDB / tinyflashdb . o
0x0000000000003b1c tfdb_set
. text . tfdb_get
0x0000000000003ca2 0x11c ./ Drivers / TFDB / tinyflashdb . o
0x0000000000003ca2 tfdb_get Routine de migration en métal nu, RT-thread peut être utilisé en référence:
STM32F429IGT6
CH583
Groupe de communication QQ: 562090553