Compare commits

..

2 Commits

Author SHA1 Message Date
朱天龙 (Armink) 8ad7d04740
Merge pull request #113 from wu1045718093/stable-v4.x
Fix gc error when ENV full(No dirty block)
5 years ago
“teng.wu” 1fa1f838c7 Fix gc error when ENV full(No dirty block) 5 years ago

@ -4,8 +4,6 @@
## 1、介绍[English](#1-introduction) ## 1、介绍[English](#1-introduction)
> **提示** :从 EasyFlash V4.1 后,基于 EasyFlash 全新设计开发的 [FlashDB](https://github.com/armink/FlashDB) 开源项目正式上线新集成了时序数据库、多分区管理多数据库实例等功能也从一定程度上提升了整体性能欢迎关注https://github.com/armink/FlashDB 。同时,现有的 EasyFlash 也会继续维护。
[EasyFlash](https://github.com/armink/EasyFlash)是一款开源的轻量级嵌入式Flash存储器库方便开发者更加轻松的实现基于Flash存储器的常见应用开发。非常适合智能家居、可穿戴、工控、医疗、物联网等需要断电存储功能的产品资源占用极低支持各种 MCU 片上存储器。该库主要包括 **三大实用功能** [EasyFlash](https://github.com/armink/EasyFlash)是一款开源的轻量级嵌入式Flash存储器库方便开发者更加轻松的实现基于Flash存储器的常见应用开发。非常适合智能家居、可穿戴、工控、医疗、物联网等需要断电存储功能的产品资源占用极低支持各种 MCU 片上存储器。该库主要包括 **三大实用功能**
- **ENV** 快速保存产品参数,支持 **写平衡(磨损平衡)** 及 **掉电保护** 功能 - **ENV** 快速保存产品参数,支持 **写平衡(磨损平衡)** 及 **掉电保护** 功能
@ -20,13 +18,7 @@ EasyFlash不仅能够实现对产品的 **设定参数** 或 **运行日志**
非常适合应用在小型的不带文件系统的产品中,方便开发人员快速定位、查找系统发生崩溃或死机的原因。同时配合[EasyLogger](https://github.com/armink/EasyLogger)(我开源的超轻量级、高性能C日志库它提供与EasyFlash的无缝接口)一起使用轻松实现C日志的Flash存储功能。 非常适合应用在小型的不带文件系统的产品中,方便开发人员快速定位、查找系统发生崩溃或死机的原因。同时配合[EasyLogger](https://github.com/armink/EasyLogger)(我开源的超轻量级、高性能C日志库它提供与EasyFlash的无缝接口)一起使用轻松实现C日志的Flash存储功能。
### 1.1 两种 ENV 模式 ### 1.1、V4.0 NG 模式
目前 ENV 功能有两种主要模式,一种为 V4.0 带来的 **NG** 模式,还有一种为延续 V3.0 的 **legacy** 模式
#### 1.1.1、V4.0 引入的 NG 模式
> 对应源码文件为: `ef_env.c`
自 2019 年春节后EasyFlash 经过 4 年多的迭代,结合众多开发者的需求及建议,终于发布了 V4.0 版本,该版本中的 ENV 功能被命名为 **NG** (Next Generation) 模式,这是一个完全重构的新版本,具有以下新特性: 自 2019 年春节后EasyFlash 经过 4 年多的迭代,结合众多开发者的需求及建议,终于发布了 V4.0 版本,该版本中的 ENV 功能被命名为 **NG** (Next Generation) 模式,这是一个完全重构的新版本,具有以下新特性:
@ -41,26 +33,6 @@ EasyFlash不仅能够实现对产品的 **设定参数** 或 **运行日志**
V4.0 设计及内部原理V4.0 迁移指南等更多内容请继续阅读下面的 [文档章节](#3文档) V4.0 设计及内部原理V4.0 迁移指南等更多内容请继续阅读下面的 [文档章节](#3文档)
> **注意** :个别 Flash 存在无法逆序写入的问题,例如 STM32L4 片内 Flash所以无法使用 NG 模式,这种情况下建议使用 V3.0 的 legacy 模式
#### 1.1.2、延续 V3.0 的 legacy 模式
> 对应源码文件为: `ef_env_legacy.c``ef_env_legacy_wl.c`
**legacy** 模式也具有磨损平衡及掉电保护功能,相比于 V 4.0 NG 模式,使用 legacy 模式,需要有额外的 RAM 空间来临时缓存每个 ENV ,最终调用 save 接口,统一擦除扇区再存储到 Flash 上。
#### 1.1.3 ENV 模式对比
| | V4.0 NG 模式 | V3.0 legacy 模式 |
| -------------------- | ------------------------------------------ | ------------------------ |
| RAM 资源占用 | 低 | 高 |
| 支持 Flash 全面性 | 个别 Flash 受限:例如 STM32L4 片内 | 比较全面 |
| 是否需要 GC 垃圾回收 | 需要 GC ,这会导致触发 GC 时,写入速度变慢 | 不需要 |
| value 类型限制 | 无限制 | 对字符串类型支持的比较好 |
| 掉电保护 | 支持 | 支持 |
| 磨损平衡 | 支持 | 支持 |
| 增量升级 | 支持 | 支持 |
### 1.2、资源占用 ### 1.2、资源占用
``` ```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 18 KiB

@ -85,7 +85,6 @@ EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size);
EfErrCode ef_log_write(const uint32_t *log, size_t size); EfErrCode ef_log_write(const uint32_t *log, size_t size);
EfErrCode ef_log_clean(void); EfErrCode ef_log_clean(void);
size_t ef_log_get_used_size(void); size_t ef_log_get_used_size(void);
size_t ef_log_get_total_size(void);
#endif #endif
/* ef_utils.c */ /* ef_utils.c */

@ -59,9 +59,6 @@
* only support 1(nor flash)/ 8(stm32f4)/ 32(stm32f1) */ * only support 1(nor flash)/ 8(stm32f4)/ 32(stm32f1) */
#define EF_WRITE_GRAN /* @note you must define it for a value */ #define EF_WRITE_GRAN /* @note you must define it for a value */
/* The size of read_env and continue_ff_addr function used*/
#define EF_READ_BUF_SIZE 32 /* @default 32, Larger numbers can improve first-time speed of alloc_env but require more stack space*/
/* /*
* *
* This all Backup Area Flash storage index. All used flash area configure is under here. * This all Backup Area Flash storage index. All used flash area configure is under here.

@ -142,11 +142,11 @@ ef_set_struct("
/* 获取结构体类型环境变量 */ /* 获取结构体类型环境变量 */
Student *student; Student *student;
student = ef_get_struct("张三学生", stu_get_cb); ef_get_struct("张三学生", student, stu_get_cb);
/* 打印获取到的结构体内容 */ /* 打印获取到的结构体内容 */
printf("姓名:%s 籍贯:%s \n", student->name, student->hometown.name); printf("姓名:%s 籍贯:%s \n", student->name, student->hometown.name);
/* 释放获取结构体类型环境变量过程中开辟的动态内存 */ /* 释放获取结构体类型环境变量过程中开辟的动态内存 */
s2jHook.free_fn(student); s2jHook.free_fn(student);
``` ```

@ -82,7 +82,7 @@ long ef_get_long(const char *key) {
return atol(value); return atol(value);
} else { } else {
EF_INFO("Couldn't find this ENV(%s)!\n", key); EF_INFO("Couldn't find this ENV(%s)!\n", key);
return 0; return NULL;
} }
} }
@ -96,7 +96,7 @@ double ef_get_double(const char *key) {
return atof(value); return atof(value);
} else { } else {
EF_INFO("Couldn't find this ENV(%s)!\n", key); EF_INFO("Couldn't find this ENV(%s)!\n", key);
return 0; return NULL;
} }
} }

@ -33,10 +33,6 @@
#include <stdbool.h> #include <stdbool.h>
#include "struct2json\inc\s2j.h" #include "struct2json\inc\s2j.h"
#ifdef __cplusplus
extern "C" {
#endif
/* EasyFlash types plugin's software version number */ /* EasyFlash types plugin's software version number */
#define EF_TYPES_SW_VERSION "0.11.03" #define EF_TYPES_SW_VERSION "0.11.03"
@ -77,8 +73,4 @@ EfErrCode ef_set_double_array(const char *key, double *value, size_t len);
EfErrCode ef_set_string_array(const char *key, char **value, size_t len); EfErrCode ef_set_string_array(const char *key, char **value, size_t len);
EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb); EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb);
#ifdef __cplusplus
}
#endif
#endif /* EF_TYPES_H_ */ #endif /* EF_TYPES_H_ */

@ -424,7 +424,7 @@ static bool get_env_from_cache(const char *name, size_t name_len, uint32_t *addr
*/ */
static uint32_t continue_ff_addr(uint32_t start, uint32_t end) static uint32_t continue_ff_addr(uint32_t start, uint32_t end)
{ {
uint8_t buf[EF_READ_BUF_SIZE], last_data = 0x00; uint8_t buf[32], last_data = 0x00;
size_t i, addr = start, read_size; size_t i, addr = start, read_size;
for (; start < end; start += sizeof(buf)) { for (; start < end; start += sizeof(buf)) {
@ -522,7 +522,7 @@ static uint32_t get_next_env_addr(sector_meta_data_t sector, env_node_obj_t pre_
static EfErrCode read_env(env_node_obj_t env) static EfErrCode read_env(env_node_obj_t env)
{ {
struct env_hdr_data env_hdr; struct env_hdr_data env_hdr;
uint8_t buf[EF_READ_BUF_SIZE]; uint8_t buf[32];
uint32_t calc_crc32 = 0, crc_data_len, env_name_addr; uint32_t calc_crc32 = 0, crc_data_len, env_name_addr;
EfErrCode result = EF_NO_ERR; EfErrCode result = EF_NO_ERR;
size_t len, size; size_t len, size;
@ -1194,11 +1194,16 @@ static uint32_t new_env(sector_meta_data_t sector, size_t env_size)
__retry: __retry:
if ((empty_env = alloc_env(sector, env_size)) == FAILED_ADDR && gc_request && !already_gc) { if ((empty_env = alloc_env(sector, env_size)) == FAILED_ADDR) {
EF_DEBUG("Warning: Alloc an ENV (size %d) failed when new ENV. Now will GC then retry.\n", env_size); if (gc_request && !already_gc) {
gc_collect(); EF_DEBUG("Warning: Alloc an ENV (size %d) failed when new ENV. Now will GC then retry.\n", env_size);
already_gc = true; gc_collect();
goto __retry; already_gc = true;
goto __retry;
} else {
EF_DEBUG("Error: Alloc an ENV (size %d) failed after GC. ENV full.\n", env_size);
gc_request = false;
}
} }
return empty_env; return empty_env;
@ -1286,13 +1291,9 @@ static EfErrCode align_write(uint32_t addr, const uint32_t *buf, size_t size)
#endif #endif
memset(align_data, 0xFF, align_data_size); memset(align_data, 0xFF, align_data_size);
align_remain = EF_WG_ALIGN_DOWN(size);//use align_remain temporary to save aligned size. result = ef_port_write(addr, buf, EF_WG_ALIGN_DOWN(size));
if(align_remain > 0){//it may be 0 in this function.
result = ef_port_write(addr, buf, align_remain);
}
align_remain = size - align_remain; align_remain = size - EF_WG_ALIGN_DOWN(size);
if (result == EF_NO_ERR && align_remain) { if (result == EF_NO_ERR && align_remain) {
memcpy(align_data, (uint8_t *)buf + EF_WG_ALIGN_DOWN(size), align_remain); memcpy(align_data, (uint8_t *)buf + EF_WG_ALIGN_DOWN(size), align_remain);
result = ef_port_write(addr + EF_WG_ALIGN_DOWN(size), (uint32_t *) align_data, align_data_size); result = ef_port_write(addr + EF_WG_ALIGN_DOWN(size), (uint32_t *) align_data, align_data_size);

@ -410,23 +410,6 @@ size_t ef_log_get_used_size(void) {
return physical_size - header_total_num * LOG_SECTOR_HEADER_SIZE; return physical_size - header_total_num * LOG_SECTOR_HEADER_SIZE;
} }
/**
* Get log flash total size.
*
* @return log flash total size. @note NOT contain sector headers
*/
size_t ef_log_get_total_size(void) {
size_t header_total_num = 0;
/* must be call this function after initialize OK */
if (!init_ok) {
return 0;
}
header_total_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
return LOG_AREA_SIZE - header_total_num * LOG_SECTOR_HEADER_SIZE;
}
/** /**
* Sequential reading log data. It will ignore sector headers. * Sequential reading log data. It will ignore sector headers.
* *
@ -474,15 +457,14 @@ static uint32_t log_index2addr(size_t index) {
size_t sector_num = index / (EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) + 1; size_t sector_num = index / (EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) + 1;
header_total_offset = sector_num * LOG_SECTOR_HEADER_SIZE; header_total_offset = sector_num * LOG_SECTOR_HEADER_SIZE;
uint32_t virtual_addr = log_start_addr + index + header_total_offset;
if (log_start_addr < log_end_addr) { if (log_start_addr < log_end_addr) {
return virtual_addr; return log_start_addr + index + header_total_offset;
} else { } else {
if (log_start_addr + index + header_total_offset < log_area_start_addr + LOG_AREA_SIZE) { if (log_start_addr + index + header_total_offset < log_area_start_addr + LOG_AREA_SIZE) {
return virtual_addr; return log_start_addr + index + header_total_offset;
} else { } else {
// the address will restart from the first sector address. return log_start_addr + index + header_total_offset - LOG_AREA_SIZE;
return virtual_addr - (log_area_start_addr + LOG_AREA_SIZE) + log_area_start_addr;
} }
} }
} }
@ -508,14 +490,8 @@ EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size) {
return result; return result;
} }
if (size % 4 != 0) { EF_ASSERT(size % 4 == 0);
EF_DEBUG("Error: size must be word aligned."); EF_ASSERT(index < cur_using_size);
return EF_READ_ERR;
}
if (index >= cur_using_size) {
EF_DEBUG("Error: index out of ranges, current using size is %d", cur_using_size);
return EF_READ_ERR;
}
if (index + size > cur_using_size) { if (index + size > cur_using_size) {
EF_DEBUG("Warning: Log read size out of bound. Cut read size.\n"); EF_DEBUG("Warning: Log read size out of bound. Cut read size.\n");

Loading…
Cancel
Save