Forráskód Böngészése

add dual flash blocks function

CoderNotCute 2 éve
szülő
commit
766ad75f7d
5 módosított fájl, 468 hozzáadás és 128 törlés
  1. 147 14
      README.md
  2. 3 20
      tfdb_port.c
  3. 7 20
      tfdb_port.h
  4. 289 50
      tinyflashdb.c
  5. 22 24
      tinyflashdb.h

+ 147 - 14
README.md

@@ -65,45 +65,172 @@ typedef struct _tfdb_index_struct{
 TFDB_Err_Code tfdb_get(const tfdb_index_t *index, uint8_t *rw_buffer, tfdb_addr_t *addr_cache, void* value_to);
 ```
 
-函数功能:从index指向的扇区中获取一个index中指定变量长度的变量,flash头部数据校验出错不会重新初始化flash。  
+函数功能:从`index`指向的扇区中获取一个index中指定变量长度的变量,flash头部数据校验出错不会重新初始化flash。  
 
-参数 index:tfdb操作的index指针。
+参数 `index`:tfdb操作的index指针。
 
-参数 rw_buffer:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用tfdb_port_write或者tfdb_port_read进行写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
+参数 `rw_buffer`:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用`tfdb_port_write`或者`tfdb_port_read`进行读取写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
 
-参数 addr_cache:可以是NULL,或者是地址缓存变量的指针,当addr_cache不为NULL,并且也不为0时,则认为addr_cache已经初始化成功,不再校验flash头部,直接从该addr_cache的地址读取数据。  
+参数 `addr_cache`:可以是`NULL`,或者是地址缓存变量的指针,当`addr_cache`不为`NULL`,并且也不为0时,则认为`addr_cache`已经初始化成功,不再校验flash头部,直接从该`addr_cache`的地址读取数据。  
 
-参数 value_to:要存储数据内容的地址。  
+参数 `value_to`:要存储数据内容的地址。  
 
-返回值:TFDB_NO_ERR成功,其他失败。  
+返回值:`TFDB_NO_ERR`成功,其他失败。  
 
 ```c
 TFDB_Err_Code tfdb_set(const tfdb_index_t *index, uint8_t *rw_buffer, tfdb_addr_t *addr_cache, void* value_from);
 ```
 
+函数功能:在`index`指向的扇区中写入一个index中指定变量长度的变量,flash头部数据校验出错重新初始化flash。  
+
+参数 `index`:tfdb操作的index指针。  
+
+参数 `rw_buffer`:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用`tfdb_port_write`或者`tfdb_port_read`进行读取写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
+
+参数 `addr_cache`:可以是`NULL`,或者是地址缓存变量的指针,当`addr_cache`不为`NULL`,并且也不为0时,则认为`addr_cache`已经初始化成功,不再校验flash头部,直接从该`addr_cache`的地址读取数据。  
+
+参数 `value_from`:要存储的数据内容。  
+
+返回值:`TFDB_NO_ERR`成功,其他失败。  
+
+## TinyFlashDB dual使用示例
+
+tfdb dual api是基于`tfdb_set`和`tfdb_get`封装而成的。`tfdb dual`会调用`tfdb_set`和`tfdb_get`,并且在数据前部添加两个字节的seq,所以在tfdb dual中,最长支持的存储变量长度为253字节。  
+同时,tfdb dual api需要提供两个缓冲区,并且需要是增加两字节变量长度再重新计算的`aligned_value_size`。
+
+```c
+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 = sizeof(my_test_params_t) + 2,
+        },
+        .indexes[1] = {
+                .end_byte     = 0x00,
+                .flash_addr   = 0x08077100,
+                .flash_size   = 256,
+                .value_length = sizeof(my_test_params_t) + 2,
+        },
+};
+
+tfdb_dual_cache_t my_test_tfdb_dual_cache = {0};
+
+void my_test_tfdb_dual_func()
+{
+    uint32_t rw_buffer[32], rw_buffer_bak[32];
+    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 ok\ncache seq1:0x%04x, seq2:0x%04x\naddr1:0x%08x, addr2:0x%08x\n", 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:%d\n", 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 ok\ncache seq1:0x%04x, seq2:0x%04x\naddr1:0x%08x, addr2:0x%08x\n", 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:%d\n", err);
+        }
+
+        memset(&my_test_tfdb_dual_cache, 0, sizeof(my_test_tfdb_dual_cache));   /* 测试无地址缓存读取 */
+    }
+}
+
+```
+
+## TinyFlashDB dual API介绍
+
+```c
+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;
+```
+
+结构体功能:在TinyFlashDB dual中,API的操作都需要指定的参数`index`,该`index`结构体中存储了两个`tfdb_index_t`。
+
+```c
+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);
+```
+
+函数功能:从index指向的扇区中获取一个index中指定变量长度的变量,flash头部数据校验出错不会重新初始化flash。  
+
+参数 `index`:tfdb操作的index指针。
+
+参数 `rw_buffer`:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用`tfdb_port_write`或者`tfdb_port_read`进行读取写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
+
+参数 `rw_buffer_bak`:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用`tfdb_port_write`或者`tfdb_port_read`进行读取写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
+
+参数 `cache`:不可以是`NULL`,必须是`tfdb_dual_cache_t`定义的缓存的指针,当`cache`中数据合法时,则认为`cache`已经初始化成功,直接从该`cache`的flash块和地址读取数据。  
+
+参数 `value_to`:要存储数据内容的地址。  
+
+返回值:`TFDB_NO_ERR`成功,其他失败。  
+
+```c
+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);
+```
+
 函数功能:在index指向的扇区中写入一个index中指定变量长度的变量,flash头部数据校验出错重新初始化flash。  
 
-参数 index:tfdb操作的index指针。  
+参数 `index`:tfdb操作的index指针。  
+
+参数 `rw_buffer`:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用`tfdb_port_write`或者`tfdb_port_read`进行读取写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
 
-参数 rw_buffer:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用tfdb_port_write或者tfdb_port_read进行写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
+参数 `rw_buffer_bak`:写入和读取的缓存,所有flash的操作最后都会将整理后的数据拷贝到该buffer中,再调用`tfdb_port_write`或者`tfdb_port_read`进行读取写入。当芯片对于写入的数据区缓存有特殊要求(例如4字节对齐,256字节对齐等),可以通过该参数将符合要求的变量指针传递给函数使用。至少为4字节长度。  
 
-参数 addr_cache:可以是NULL,或者是地址缓存变量的指针,当addr_cache不为NULL,并且也不为0时,则认为addr_cache已经初始化成功,不再校验flash头部,直接从该addr_cache的地址读取数据。  
+参数 `cache`:不可以是`NULL`,必须是`tfdb_dual_cache_t`定义的缓存的指针,当`cache`中数据合法时,则认为`cache`已经初始化成功,直接从该`cache`的flash块和地址读取数据。  
 
-参数 value_from:要存储的数据内容。  
+参数 `value_from`:要存储的数据内容。  
 
-返回值:TFDB_NO_ERR成功,其他失败。  
+返回值:`TFDB_NO_ERR`成功,其他失败。  
 
 ## TinyFlashDB设计原理
 
-观察上方代码,可以发现TinyFlashDB的操作都需要tfdb_index_t定义的index参数。  
+观察上方代码,可以发现TinyFlashDB的操作都需要`tfdb_index_t`定义的`index`参数。  
 Flash初始化后头部信息为4字节,所以只支持1、2、4、8字节操作的flash:  
-头部初始化时会读取头部,所以函数中rw_buffer指向的数据第一要求至少为4字节,如果最小写入单位是8字节,则为第一要求最少为8字节。  
+头部初始化时会读取头部,所以函数中`rw_buffer`指向的数据第一要求至少为4字节,如果最小写入单位是8字节,则为第一要求最少为8字节。  
 
 |第一字节|第二字节|第三字节|第四字节和其他对齐字节|
 -|-|-|-
 |flash_size高8位字节|flash_size低8位字节|value_length|end_byte|
 
-数据存储时,会根据flash支持的字节操作进行对齐,所以函数中rw_buffer指向的数据第二要求至少为下面函数中计算得出的aligned_value_size个字节:
+数据存储时,会根据flash支持的字节操作进行对齐,所以函数中`rw_buffer`指向的数据第二要求至少为下面函数中计算得出的`aligned_value_size`个字节:
 
 ```c
     aligned_value_size  = index->value_length + 2;/* data + verify + end_byte */
@@ -128,6 +255,12 @@ Flash初始化后头部信息为4字节,所以只支持1、2、4、8字节操
 
 读取数据时也会计算和校验,不通过的话继续读取,直到返回校验通过的最新数据,或者读取失败。  
 
+## TinyFlashDB dual设计原理
+
+数据前部两字节seq只有3种合法值,0x00ff->0x0ff0->0xff00。  
+如此循环往复,通过读取两个block中最新变量的seq来判断哪个flash扇区中存储的是最新值。  
+当最新值存储在第一扇区时,下次写入则会在第二扇区写入,反之亦然。
+
 ## TinyFlashDB移植和配置
 
 ### 移植使用只需要在tfdb_port.c中,编写完成三个接口函数,也要在tfdb_port.h中添加相应的头文件和根据不同芯片修改宏定义

+ 3 - 20
tfdb_port.c

@@ -1,25 +1,7 @@
 /*
- * Copyright (c) 2022, smartmx - smartmx@qq.com
+ * Copyright (c) 2022-2023, smartmx - smartmx@qq.com
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * This file is part of the Tiny Flash DataBase Library.
+ * SPDX-License-Identifier: MIT
  *
  * Change Logs:
  * Date           Author       Notes
@@ -28,6 +10,7 @@
  * 2022-02-12     smartmx      fix bugs, add support for 2 byte write flash
  * 2022-03-15     smartmx      fix bugs, add support for stm32l4 flash
  * 2022-08-02     smartmx      add TFDB_VALUE_AFTER_ERASE_SIZE option
+ * 2023-02-22     smartmx      add dual flash index function
  *
  */
 #include "tfdb_port.h"

+ 7 - 20
tfdb_port.h

@@ -1,25 +1,7 @@
 /*
- * Copyright (c) 2022, smartmx - smartmx@qq.com
+ * Copyright (c) 2022-2023, smartmx - smartmx@qq.com
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * This file is part of the Tiny Flash DataBase Library.
+ * SPDX-License-Identifier: MIT
  *
  * Change Logs:
  * Date           Author       Notes
@@ -28,6 +10,7 @@
  * 2022-02-12     smartmx      fix bugs, add support for 2 byte write flash
  * 2022-03-15     smartmx      fix bugs, add support for stm32l4 flash
  * 2022-08-02     smartmx      add TFDB_VALUE_AFTER_ERASE_SIZE option
+ * 2023-02-22     smartmx      add dual flash index function
  *
  */
 #ifndef _TFDB_PORT_H_
@@ -45,6 +28,10 @@ typedef enum
     TFDB_WRITE_ERR,
     TFDB_HDR_ERR,
     TFDB_FLASH_ERR,
+    TFDB_CACHE_ERR,
+    TFDB_SEQ_ERR,
+    TFDB_FLASH1_ERR,
+    TFDB_FLASH2_ERR,
     TFDB_ERR_MAX,
 } TFDB_Err_Code;
 

+ 289 - 50
tinyflashdb.c

@@ -1,25 +1,7 @@
 /*
- * Copyright (c) 2022, smartmx - smartmx@qq.com
+ * Copyright (c) 2022-2023, smartmx - smartmx@qq.com
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * This file is part of the Tiny Flash DataBase Library.
+ * SPDX-License-Identifier: MIT
  *
  * Change Logs:
  * Date           Author       Notes
@@ -28,6 +10,7 @@
  * 2022-02-12     smartmx      fix bugs, add support for 2 byte write flash
  * 2022-03-15     smartmx      fix bugs, add support for stm32l4 flash
  * 2022-08-02     smartmx      add TFDB_VALUE_AFTER_ERASE_SIZE option
+ * 2023-02-22     smartmx      add dual flash index function
  *
  */
 #include "tinyflashdb.h"
@@ -191,12 +174,12 @@ start:
                 if ((rw_buffer[aligned_value_size - 1] == (TFDB_VALUE_AFTER_ERASE & 0x000000ff)))
                 {
 #if (TFDB_VALUE_AFTER_ERASE_SIZE == 2)||(TFDB_VALUE_AFTER_ERASE_SIZE == 4)
-                    if ((rw_buffer[aligned_value_size - 2] == ((TFDB_VALUE_AFTER_ERASE>>8) & 0x000000ff)))
+                    if ((rw_buffer[aligned_value_size - 2] == ((TFDB_VALUE_AFTER_ERASE >> 8) & 0x000000ff)))
                     {
 #endif  /* TFDB_VALUE_AFTER_ERASE_SIZE == 2 */
 #if TFDB_VALUE_AFTER_ERASE_SIZE == 4
-                        if ((rw_buffer[aligned_value_size - 3] == ((TFDB_VALUE_AFTER_ERASE>>16) & 0x000000ff)) &&
-                                (rw_buffer[aligned_value_size - 4] == ((TFDB_VALUE_AFTER_ERASE>>24) & 0x000000ff)))
+                        if ((rw_buffer[aligned_value_size - 3] == ((TFDB_VALUE_AFTER_ERASE >> 16) & 0x000000ff)) &&
+                                (rw_buffer[aligned_value_size - 4] == ((TFDB_VALUE_AFTER_ERASE >> 24) & 0x000000ff)))
                         {
 #endif  /* TFDB_VALUE_AFTER_ERASE_SIZE == 4 */
                             /* find value addr success */
@@ -301,25 +284,28 @@ init:
             goto end;
         }
     }
-    else if (*addr_cache == 0)
-    {
-        /* addr_cache is not set */
-        goto start;
-    }
     else
     {
-        /* addr_cache is set */
-        TFDB_DEBUG("    addr_cache is set\n");
-        find_addr = *addr_cache + aligned_value_size;
-        if (find_addr > (index->flash_addr + index->flash_size - aligned_value_size))
+        if (*addr_cache == 0)
         {
-            /* the flash is fill */
-            TFDB_DEBUG("    the flash is fill\n");
-            goto init;
+            /* addr_cache is not set */
+            goto start;
         }
         else
         {
-            goto set;
+            /* addr_cache is set */
+            TFDB_DEBUG("    addr_cache is set\n");
+            find_addr = *addr_cache + aligned_value_size;
+            if (find_addr > (index->flash_addr + index->flash_size - aligned_value_size))
+            {
+                /* the flash is fill */
+                TFDB_DEBUG("    the flash is fill\n");
+                goto init;
+            }
+            else
+            {
+                goto set;
+            }
         }
     }
 end:
@@ -385,12 +371,12 @@ start:
                 if ((rw_buffer[aligned_value_size - 1] == (TFDB_VALUE_AFTER_ERASE & 0x000000ff)))
                 {
 #if (TFDB_VALUE_AFTER_ERASE_SIZE == 2)||(TFDB_VALUE_AFTER_ERASE_SIZE == 4)
-                    if ((rw_buffer[aligned_value_size - 2] == ((TFDB_VALUE_AFTER_ERASE>>8) & 0x000000ff)))
+                    if ((rw_buffer[aligned_value_size - 2] == ((TFDB_VALUE_AFTER_ERASE >> 8) & 0x000000ff)))
                     {
 #endif  /* TFDB_VALUE_AFTER_ERASE_SIZE == 2 */
 #if TFDB_VALUE_AFTER_ERASE_SIZE == 4
-                        if ((rw_buffer[aligned_value_size - 3] == ((TFDB_VALUE_AFTER_ERASE>>16) & 0x000000ff)) &&
-                                (rw_buffer[aligned_value_size - 4] == ((TFDB_VALUE_AFTER_ERASE>>24) & 0x000000ff)))
+                        if ((rw_buffer[aligned_value_size - 3] == ((TFDB_VALUE_AFTER_ERASE >> 16) & 0x000000ff)) &&
+                                (rw_buffer[aligned_value_size - 4] == ((TFDB_VALUE_AFTER_ERASE >> 24) & 0x000000ff)))
                         {
 #endif  /* TFDB_VALUE_AFTER_ERASE_SIZE == 4 */
                             /* find value addr success */
@@ -471,23 +457,276 @@ verify:
             goto end;
         }
     }
-    else if (*addr_cache == 0)
-    {
-        /* addr_cache is not set */
-        goto start;
-    }
     else
     {
-        find_addr = *addr_cache;
-        result = tfdb_port_read(find_addr, rw_buffer, aligned_value_size);
-        if (result != TFDB_NO_ERR)
+        if (*addr_cache == 0)
         {
-            TFDB_DEBUG("    read err\n");
-            goto end;
+            /* addr_cache is not set */
+            goto start;
+        }
+        else
+        {
+            find_addr = *addr_cache;
+            result = tfdb_port_read(find_addr, rw_buffer, aligned_value_size);
+            if (result != TFDB_NO_ERR)
+            {
+                TFDB_DEBUG("    read err\n");
+                goto end;
+            }
+            goto verify;
         }
-        goto verify;
     }
 end:
     TFDB_DEBUG("tfdb_get:%d\n", result);
     return result;
 }
+
+/**
+ * judge which seq is new.
+ *
+ * @param seq the pointer to seq[2] buffer.
+ *
+ * @return uint8_t which seq is new, 0xff means all seq is illegal.
+ */
+static uint8_t tfdb_dual_judge(uint16_t *seq)
+{
+    /* seq range: 0x00ff -> 0x0ff0 -> 0xff00 */
+    switch (seq[0])
+    {
+    case 0xff00:
+        if (seq[1] == 0x00ff)
+        {
+            return 1;
+        }
+        else
+        {
+            return 0;
+        }
+    case 0x0ff0:
+        if (seq[1] == 0xff00)
+        {
+            return 1;
+        }
+        else
+        {
+            return 0;
+        }
+    case 0x00ff:
+        if (seq[1] == 0x0ff0)
+        {
+            return 1;
+        }
+        else
+        {
+            return 0;
+        }
+    default:
+        if ((seq[1] == 0x00ff) || (seq[1] == 0x0ff0) || (seq[1] == 0xff00))
+        {
+            return 1;
+        }
+        break;
+    }
+    return 0xff;
+}
+
+/**
+ * judge which seq is new.
+ *
+ * @param seq the pointer to seq[2] buffer.
+ *
+ * @return uint8_t which seq is new, 0xff means all seq is illegal.
+ */
+static uint16_t tfdb_dual_get_next_seq(uint16_t seq)
+{
+    /* seq range: 0x00ff -> 0x0ff0 -> 0xff00 */
+    if (seq == 0x00ff)
+    {
+        return 0x0ff0;
+    }
+    else if (seq == 0x0ff0)
+    {
+        return 0xff00;
+    }
+    else
+    {
+        return 0x00ff;
+    }
+}
+
+/**
+ * set data in flash and save the addr and seq to cache.
+ *
+ * @param index the data manage index.
+ * @param rw_buffer buffer to store prepared read data or write data.
+ * @param rw_buffer_bak buffer to store prepared read data or write data.
+ * @param cache the pointer to addr which is user offered, which will save read addr and seq.
+ * @param value_from the pointer to buffer which is user offered that need to save.
+ *
+ * @return TFDB_Err_Code
+ */
+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)
+{
+    TFDB_Err_Code rresult = TFDB_NO_ERR;
+    TFDB_Err_Code result[2];
+    uint8_t judge_state;
+
+    if (cache != NULL)
+    {
+        judge_state = tfdb_dual_judge(cache->seq);
+
+        TFDB_DEBUG("tfdb_dual_judge:%d\n", judge_state);
+
+        /* usually, we just read value once during the initializing. */
+        if (judge_state == 0xff)
+        {
+            result[0] = tfdb_get(&index->indexes[0], rw_buffer, &(cache->addr_cache[0]), rw_buffer_bak);
+            if (result[0] == TFDB_NO_ERR)
+            {
+                cache->seq[0] = (rw_buffer_bak[0] << 8) | (rw_buffer_bak[1]);
+                tfdb_memcpy(value_to, &(rw_buffer_bak[2]), index->indexes[0].value_length - 2);
+            }
+            else
+            {
+                cache->seq[0] = 0;
+            }
+
+            result[1] = tfdb_get(&index->indexes[1], rw_buffer, &(cache->addr_cache[1]), rw_buffer_bak);
+            if (result[1] == TFDB_NO_ERR)
+            {
+                cache->seq[1] = (rw_buffer_bak[0] << 8) | (rw_buffer_bak[1]);
+            }
+            else
+            {
+                cache->seq[1] = 0;
+            }
+
+            judge_state = tfdb_dual_judge(cache->seq);
+            if (judge_state == 1)
+            {
+                tfdb_memcpy(value_to, &(rw_buffer_bak[2]), index->indexes[1].value_length - 2);
+            }
+            else if (judge_state == 0xff)
+            {
+                rresult = TFDB_SEQ_ERR;
+            }
+        }
+        else
+        {
+            result[judge_state] = tfdb_get(&index->indexes[judge_state], rw_buffer, &(cache->addr_cache[judge_state]), rw_buffer_bak);
+            if (result[judge_state] == TFDB_NO_ERR)
+            {
+                tfdb_memcpy(value_to, &(rw_buffer_bak[2]), index->indexes[judge_state].value_length - 2);
+            }
+            else
+            {
+                /* block not right, read another block. */
+                judge_state = 1 - judge_state;
+                result[judge_state] = tfdb_get(&index->indexes[judge_state], rw_buffer, &(cache->addr_cache[judge_state]), rw_buffer_bak);
+                if (result[judge_state] == TFDB_NO_ERR)
+                {
+                    tfdb_memcpy(value_to, &(rw_buffer_bak[2]), index->indexes[judge_state].value_length - 2);
+                }
+                else
+                {
+                    /* two blocks are all not right, failed. */
+                    rresult = TFDB_READ_ERR;
+                }
+            }
+        }
+    }
+    else
+    {
+        rresult = TFDB_CACHE_ERR;
+    }
+
+    return rresult;
+
+}
+
+/**
+ * set data in flash and save the addr and seq to cache.
+ *
+ * @param index the data manage index.
+ * @param rw_buffer buffer to store prepared read data or write data.
+ * @param rw_buffer_bak buffer to store prepared read data or write data.
+ * @param cache the pointer to addr which is user offered, which will save read addr and seq.
+ * @param value_from the pointer to buffer which is user offered that need to save.
+ *
+ * @return TFDB_Err_Code
+ */
+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)
+{
+    TFDB_Err_Code rresult = TFDB_NO_ERR;
+    TFDB_Err_Code result[2];
+    uint8_t judge_state;
+    uint16_t write_seq;
+
+    if (cache != NULL)
+    {
+        judge_state = tfdb_dual_judge(cache->seq);
+
+        TFDB_DEBUG("tfdb_dual_judge:%d\n", judge_state);
+
+        /* usually, we just read value once during the initializing. */
+        if (judge_state != 0xff)
+        {
+write:
+            write_seq = tfdb_dual_get_next_seq(cache->seq[judge_state]);
+            judge_state = 1 - judge_state;  /* we need to write in another flash block. */
+
+            rw_buffer_bak[0] = (uint8_t)(write_seq >> 8);
+            rw_buffer_bak[1] = (uint8_t)write_seq;
+
+            tfdb_memcpy(&(rw_buffer_bak[2]), value_from, index->indexes[judge_state].value_length - 2);
+
+            result[judge_state] = tfdb_set(&index->indexes[judge_state], rw_buffer, &(cache->addr_cache[judge_state]), rw_buffer_bak);
+            if (result[judge_state] == TFDB_NO_ERR)
+            {
+                cache->seq[judge_state] = write_seq;
+            }
+            else
+            {
+                /* block is error, do not write to another block, for keeping the old data safe.
+                 * if you want to write in another block, please use tfdb_set directly. */
+                rresult = judge_state + TFDB_FLASH1_ERR;
+            }
+        }
+        else
+        {
+            result[0] = tfdb_get(&index->indexes[0], rw_buffer, &(cache->addr_cache[0]), rw_buffer_bak);
+            if (result[0] == TFDB_NO_ERR)
+            {
+                cache->seq[0] = (rw_buffer_bak[0] << 8) | (rw_buffer_bak[1]);
+            }
+            else
+            {
+                cache->seq[0] = 0;
+            }
+
+            result[1] = tfdb_get(&index->indexes[1], rw_buffer, &(cache->addr_cache[1]), rw_buffer_bak);
+            if (result[1] == TFDB_NO_ERR)
+            {
+                cache->seq[1] = (rw_buffer_bak[0] << 8) | (rw_buffer_bak[1]);
+            }
+            else
+            {
+                cache->seq[1] = 0;
+            }
+
+            judge_state = tfdb_dual_judge(cache->seq);
+            if (judge_state == 0xff)
+            {
+                judge_state = 0;    /* first write 0 block. */
+            }
+
+            goto write;
+        }
+    }
+    else
+    {
+        return TFDB_CACHE_ERR;
+    }
+
+    return rresult;
+}

+ 22 - 24
tinyflashdb.h

@@ -1,25 +1,7 @@
 /*
- * Copyright (c) 2022, smartmx - smartmx@qq.com
+ * Copyright (c) 2022-2023, smartmx - smartmx@qq.com
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * This file is part of the Tiny Flash DataBase Library.
+ * SPDX-License-Identifier: MIT
  *
  * Change Logs:
  * Date           Author       Notes
@@ -28,6 +10,7 @@
  * 2022-02-12     smartmx      fix bugs, add support for 2 byte write flash
  * 2022-03-15     smartmx      fix bugs, add support for stm32l4 flash
  * 2022-08-02     smartmx      add TFDB_VALUE_AFTER_ERASE_SIZE option
+ * 2023-02-22     smartmx      add dual flash index function
  *
  */
 #ifndef _TINY_FLASH_DB_H_
@@ -39,10 +22,10 @@
 
 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 */
+    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;
 
@@ -50,4 +33,19 @@ extern TFDB_Err_Code tfdb_get(const tfdb_index_t *index, uint8_t *rw_buffer, tfd
 
 extern TFDB_Err_Code tfdb_set(const tfdb_index_t *index, uint8_t *rw_buffer, tfdb_addr_t *addr_cache, void *value_from);
 
+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;
+
+extern 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);
+
+extern 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);
+
 #endif