Closed WangShuoran closed 5 months ago
而且在README中提到的代码块中:
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)),
},
};
这两个flash_addr都指向了一个块,此时如果tfdb_port中实现的tfdb_port_erase将全把上面数据擦除了,我感觉不应该是这样。所以想问下这个dual API作用是什么?
您好!
我在找单片机KV资源块管理的过程中,看到您开源的库。在看README及部分源码的时候,有一些问题:
- 在tfdb_index_t中的flash_addr应该最好填入物理块的起始地址,flash_size应该最好要填入物理块的大小吧(块Block指被擦除的最小单位),否则的话如果第二次修改数据将可能导致把其他数据清除掉,是吗?
- 如果这样的话假如一个单片机有6个模块,用原生的tfdb_set来操作时是不是必须要手动管理数据,保证数据在下次修改过程中不要断电丢失?
- 因此您提供了第二类API(dual API),您讲到tfdb dual会调用tfdb_set和tfdb_get,并且在数据前部添加两个字节的seq,所以在tfdb dual中,最长支持的存储变量长度为253字节。这是不是指dual API维护两个数据块(两个数据块完成数据相互更新),但是一般Flash数据块都要大于256字节的,您这里最长变量支持253代表一个块只能用256字节,那就至少有一半空间没有用到(我相信应该不是这么理解的)。那么意义到底是什么,维护两个数据块的原因到底是什么?
而且在README中提到的代码块中:
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)), }, };
这两个flash_addr都指向了一个块,此时如果tfdb_port中实现的tfdb_port_erase将全把上面数据擦除了,我感觉不应该是这样。所以想问下这个dual API作用是什么?
这个只是一个例子,tfdb_dual必须使用两个可以分开擦除的不同扇区。我使用的是ch32v203,最小擦除单位256字节。所以实例没问题,你需要根据自己的单片机进行调整。
建议你可以写一个测试例程,使用tfdb_set或者tfdb_dual_set后,把整个扇区内容打印出来,循环往复,就可以理解tfdb的工作原理。
谢谢您的快速回复,我的理解如下,并马上根据您的建议写例程实测理解,主要现在是在综合考虑。
其中您对我问题的两个回答我都看了,其中我复述一下,再马上执行您的建议进行理解:
您好! 我在找单片机KV资源块管理的过程中,看到您开源的库。在看README及部分源码的时候,有一些问题:
- 在tfdb_index_t中的flash_addr应该最好填入物理块的起始地址,flash_size应该最好要填入物理块的大小吧(块Block指被擦除的最小单位),否则的话如果第二次修改数据将可能导致把其他数据清除掉,是吗?
- 如果这样的话假如一个单片机有6个模块,用原生的tfdb_set来操作时是不是必须要手动管理数据,保证数据在下次修改过程中不要断电丢失?
- 因此您提供了第二类API(dual API),您讲到tfdb dual会调用tfdb_set和tfdb_get,并且在数据前部添加两个字节的seq,所以在tfdb dual中,最长支持的存储变量长度为253字节。这是不是指dual API维护两个数据块(两个数据块完成数据相互更新),但是一般Flash数据块都要大于256字节的,您这里最长变量支持253代表一个块只能用256字节,那就至少有一半空间没有用到(我相信应该不是这么理解的)。那么意义到底是什么,维护两个数据块的原因到底是什么?
- 是的,flash_size就是一个物理块的大小,就是这个块被擦除的最小单位。
- 是的,tfdb_set在用完一整个扇区后悔执行擦除操作,这时断电会导致数据丢失。可以使用tfdb_dual来完成这个操作。
- 存储的变量长度为253字节,不是扇区长度为253字节。例如,扇区为4096个字节,存储的数据长度为253字节,则你每次写入flash的长度为253 + 2(seq) + 1(和校验) + 1(end_byte)+ 对齐字节(如果需要)。tfdb会从扇区起始地址开始写入,一直往后写,写到扇区剩余长度不满足单次写入的长度时,擦除从头写。这个时候断电,擦除扇区数据丢失,但是另一个扇区数据还在,所以可以实现断电不丢失之前的数据。
其中第三点您回答的是value_length的TFDB_DUAL_VALUE_LENGTH(sizeof(my_test_params_t))中my_test_params_t的大小为253字节,而不是flash_size的限制是这么大。在程序工作中,会将value_length的数据在flash_size依次存放,直到不能整除。才会进行擦除处理。
tfdb_dual必须使用两个可以分开擦除的不同扇区,和我对KV库理解相同。您看看上述理解是否正确?
tfdb_dual必须使用两个可以分开擦除的不同扇区,和我对KV库理解相同。您看看上述理解是否正确?
是的,还有一个减去扇区头部信息(4字节或者8字节)后不能整除,其他没有问题。
也就是您在上面讲到TinyFlashDB设计原理,其中一个块分为几个value_length空间,每个value_length都有图片上提到扇区头部信息(4字节或者8字节),是这样的吧?
没有,只有扇区头部有,后面写入的数据不包括头部
谢谢,确实应该名词叫扇区,叫块是不对的,您这么说我就理解了。其中README中提到各种4字节的限制也是来源于此的吧?
是的。
我当前移植完成了,其中TFDB_WRITE_UNIT_BYTES是每个写入的单元长度,但是TFDB_VALUE_AFTER_ERASE_SIZE这个是代表什么呢? 闪存擦除后的值大小,不大于TFDB_WRITE_UNIT_BYTES大小,但是很难找到应用在哪里,想请问设计这个值作用在哪里呢?
TFDB_WRITE_UNIT_BYTES
是flash写入的单位,有的flash支持一次写入一个字节,有的flash只能一次写入4字节,这个时候软件需要将写入的地址对齐。
TFDB_VALUE_AFTER_ERASE_SIZE
是表示有的flash擦除后的值的判断单位。
有的flash擦除后是每4个字节的值为0xF5F9BDA9
,这个时候TFDB_VALUE_AFTER_ERASE_SIZE
为4,TFDB_VALUE_AFTER_ERASE
为0xF5F9BDA9
。
有的flash擦除后是2个字节的0x39e3
,这个时候TFDB_VALUE_AFTER_ERASE_SIZE
为2,TFDB_VALUE_AFTER_ERASE
为0x39e3
。
一般的flash没有加密。TFDB_VALUE_AFTER_ERASE_SIZE
为1,TFDB_VALUE_AFTER_ERASE
为0xff
,即可。
谢谢您,我继续移植,移植完成我关闭这个ISSUE,并立志整理后开源我的移植过程,再次感谢
您好,我在使用中发现一个问题,和您一起探讨下: 在主要的四个函数tfdb_get/tfdb_set/tfdb_dual_get/tfdb_dual_set中的rw_buffer是至少4字节,但是例程中:
uint8_t test_buf[TFDB_ALIGNED_RW_BUFFER_SIZE(2,1)]; /*aligned_value_size*/
其中TFDB_ALIGNED_RW_BUFFER_SIZE的实现为:
#define TFDB_ALIGNED_RW_BUFFER_SIZE(VALUE_LENGTH, ALIGNED_SIZE) ((VALUE_LENGTH + 1 + ALIGNED_SIZE) / (ALIGNED_SIZE))
带入数据得到(2+1+1)/2=2,而此时少于4字节,而且我认为这个应该由我们底层实现,或者让用户注意,而在README的例程并没有遵守这个概念吧?
PS:我认为其中VALUE_LENGTH是值的长度,比如uint16_t就是2字节;其中ALIGNED_SIZE是对齐的长度,由TFDB_WRITE_UNIT_BYTES决定;不知道是否正确?
tfdb_dual_set 和 tfdb_dual_get 遵守下面的宏定义
#define TFDB_DUAL_ALIGNED_RW_BUFFER_SIZE(VALUE_LENGTH, ALIGNED_SIZE) ((VALUE_LENGTH + 3 + ALIGNED_SIZE) / (ALIGNED_SIZE))
#define TFDB_DUAL_VALUE_LENGTH(VALUE_LENGTH) (VALUE_LENGTH + 2)
PS:我认为其中VALUE_LENGTH是值的长度,比如uint16_t就是2字节;其中ALIGNED_SIZE是对齐的长度,由TFDB_WRITE_UNIT_BYTES决定;不知道是否正确?
不是的,VALUE_LENGTH是你存储变量的长度,ALIGNED_SIZE是你定义变量的对齐宽度,uint32_t 就是 4, uint16_t 就是 2,uint8_t 就是 1。
这个不在底层实现的原因是:
tfdb_dual_set 和 tfdb_dual_get 遵守下面的宏定义
#define TFDB_DUAL_ALIGNED_RW_BUFFER_SIZE(VALUE_LENGTH, ALIGNED_SIZE) ((VALUE_LENGTH + 3 + ALIGNED_SIZE) / (ALIGNED_SIZE)) #define TFDB_DUAL_VALUE_LENGTH(VALUE_LENGTH) (VALUE_LENGTH + 2)
PS:我认为其中VALUE_LENGTH是值的长度,比如uint16_t就是2字节;其中ALIGNED_SIZE是对齐的长度,由TFDB_WRITE_UNIT_BYTES决定;不知道是否正确?
不是的,VALUE_LENGTH是你存储变量的长度,ALIGNED_SIZE是你定义变量的对齐宽度,uint32_t 就是 4, uint16_t 就是 2,uint8_t 就是 1。
这个不在底层实现的原因是:
- 51等平台不支持局部可变长度变量的定义。
- 有些平台写入flash时对数据的ram缓存地址有要求。
- 可以更快更大长度的写入数据,像easyflash很多时候会只写4个字节,重复的调用写入。比起作为一个整体写入,很浪费时间。
我重复下,就是如果我存放一个uint16_t数组,在VALUE_LENGTH的值为数组个数*类型长度,而ALIGNED_SIZE代表类型长度。
然后您给出了不少于4字节不应该由库实现
的原因,因为存在兼容问题以及其他代价。
所以,我的问题是,在README中要求四个函数tfdb_get/tfdb_set/tfdb_dual_get/tfdb_dual_set中的rw_buffer是至少4字节,但例程经过查看是两个字节,到底以谁为准? 详细来说,如果以个函数tfdb_get/tfdb_set/tfdb_dual_get/tfdb_dual_set中的rw_buffer是至少4字节为准,那么就需要例程中test_buf至少4字节,这个我们调用实现并不难做到;或者以test_buf可以两字节,那么就在rw_buffer不应该以4字节为准。
这个不在底层实现的原因是:
- 51等平台不支持局部可变长度变量的定义。
- 有些平台写入flash时对数据的ram缓存地址有要求。
- 可以更快更大长度的写入数据,像easyflash很多时候会只写4个字节,重复的调用写入。比起作为一个整体写入,很浪费时间。
其中第三点您提到的意思是如果rw_buffer可以一次性完成更多数据的写入,而第二点提到一次性写入Flash的数据大小存在上限,这都感觉和rw_buffer是否至少4字节或者不限大小无关。我要重新描述我的提问,以防你误解,我问的是关于rw_buffer以什么数据为准,这涉及到空间申请,申请不够大小的数组空间会引起越界。 其中第一点,您提到在51等平台不支持局部可变长度变量的定义,也就是可变长数组,这个我倒是有点理解;我希望我能详细描述您表达的意思,看看是否正确:就是基于我们必须申请固定长数组,所以必须大于4字节并满足上面提到的TFDB_DUAL_VALUE_LENGTH空间要求(两者选多的那个),这是不是可以理解README的例程在某些情况可用,但实际运用时,如果是固定数组长度,长度必须大于4字节并满足TFDB_DUAL_VALUE_LENGTH空间要求(两者选多的那个)?
是的,最少满足4个字节或者8个字节。之前的确写漏了,现在宏定义已经更新。
好的,我看到您仅仅更新到tinyflashdb.h中:
那README中的应该会用新的计算方式:
那么我重新计算uint8_t test_buf[TFDB_ALIGNED_RW_BUFFER_SIZE(2,1)];
发现:(2+3+1)/(1)=6,结果正确
是的,最少满足4个字节或者8个字节。之前的确写漏了,现在宏定义已经更新。
请问您这个是的代表我前面两个回答理解都正确吗?
我认为我的ALIGNED_SIZE代表类型长度的理解并不正确,因为此时你RAEDME依然保持
uint8_t test_buf[TFDB_ALIGNED_RW_BUFFER_SIZE(2,1)];
而且,例程中的test_value的类型是uint16_t类型,所以是我理解错了还是README依然需要修改?
您可以告诉我正确的对参数的理解,如果您这段时间没空,告诉我理解后,我综合修改后提交PR吧,这些适合在PR中讨论
如果ALIGNED_SIZE确实代表类型长度,此时依照最新的宏定义,VALUE_LENGTH最小和ALIGNED_SIZE相同,此时满足下面曲线
会无限逼近于2他,也是不满足条件的 (这个回答是我对另外一种回答的思考)
类似于定义一个buf时:
uint16_t test_buf [ TFDB_ALIGNED_RW_BUFFER_SIZE(2, sizeof(uint16_t) ];
uint32_t test_buf [ TFDB_ALIGNED_RW_BUFFER_SIZE(2, sizeof(uint32_t) ];
uint8_t test_buf [ TFDB_ALIGNED_RW_BUFFER_SIZE(2, sizeof(uint8_t) ];
Readme中没有uint16_t定义 rw_buf 吧?
这个是你用来定义缓存区大小的类型的大小。
具体在这里,这里提到存储数据是2字节,那么依照下面我的问题和您的回答: 可以得到后面的这个是指变量的类型长度吧,那么下面README中提到的这个1就不对吧?
test_buf 是使用的 uint8_t 定义的,1 是没问题的。test_value是uint16_t定义的,这个参数和 test_value无关。 这个参数不是你存储的变量的长度,这个参数是你定义 rw_buffer 所使用的类型的长度。
好的,那么我们的test_buf所使用的变量类型是基于什么设置的呢?应该不是可以顺便设置的吧?
是不是基于TFDB_WRITE_UNIT_BYTES是flash写入的单位来设置的,上面你提到有的flash支持一次写入一个字节,有的flash只能一次写入4字节,这个时候软件需要将写入的地址对齐。
这个是看客户自己想怎么定义就怎么定义的。结合编译器特性等,能定义出满足硬件使用条件的即可。
谢谢,再确认下在VALUE_LENGTH的值为tfdb_get的最后一个参数长度,如果存放的是数组数据,那么空间为数组个数*类型长度?
就是
sizeof()
那个变量的值。
uint16_t test[100]; 那么value_length就是200.
我已经测试可用,是否可以生成个版本,把上面这个问题解决的代码包进去
目前版本还没更新,但是代码已经在主线里了,不需要在意这个吧,后面还有个tfdb_dual_get_pre没写好,写完后才会更新版本到0.0.8。
嗯嗯,不在意的,您先修改
您好!
我在找单片机KV资源块管理的过程中,看到您开源的库。在看README及部分源码的时候,有一些问题: