FlashTLV-STM32F0内部Flash适配版
原项目链接:https://www.cnblogs.com/yanye0xcc/p/16240531.html
STM32F0内部Flash的限制:
1:只能2字节对齐编程,最小写入单元是2字节。
2:待写入地址的内容必须是0xFFFF,擦除后只能写一次,不支持覆盖写入。

因此tlv_block数据块结构需要调整,分别使用sts_write变量作为写入完成标记,使用sts_delete作为擦除标记,且擦除标记的优先级是高于写入完成标记。
写入完成时sts_write=TLV_STATE_WRITE,sts_delete=TLV_STATE_NONE(0xFFFF)
标记擦除后sts_write=TLV_STATE_WRITE,sts_delete=TLV_STATE_DELETE
typedef struct _tlv_block {
// 0x55 0xaa
uint16_t header;
uint16_t sts_write; // TLV_STATE_NONE or TLV_STATE_WRITE
uint16_t sts_delete; // TLV_STATE_NONE or TLV_STATE_DELETE
uint16_t tag;
uint16_t length;
uint16_t crc16;
// no store
uint32_t entity;
} tlv_block_t;
对于数据写入,需要对用户传入的数据长度做2字节的向上取整,那么tlv_block_t::length就不是用户数据实际字节数,而是占用的存储空间字节数。
嵌入式应用场合,大部分是存储结构体变量,存储者和读取者都知道内容的实际长度;当存储变长字符串,tlv_block_t::length总是大于或等于实际字符串长度,读出后使用库函数strlen()计算下真实长度即可。
因此不在tlv_block_t中对真实用户数据长度做存储,也不在存储逻辑中做实际长度和存储长度的转换,简化程序逻辑和降低存储块元数据大小。
bool flash_tlv_append(tlv_sector_t *sector, uint16_t tag, const uint8_t *data, uint16_t length) {
// 省略其它代码
length = length + (length & 0x1);
block.tag = tag;
block.length = length;
status = search_tlv(sector, &block, TLV_BLOCK_APPEND);
if(status == TLV_RESULT_OK) {
goto LAB_WRITE_TLV;
}
// 省略其它代码
代码下载:
https://files.cnblogs.com/files/blogs/575121/flash_tlv_stm32f0.zip
ESP32 NVS存储接口兼容层,进一步简化上层应用操作逻辑。
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_NVS_H
#define ESP_NVS_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NVS_ERR_OK 0
#define NVS_ERR_INVALID_ARG 100
#define NVS_ERR_INVALID_HANDLE 101
#define NVS_ERR_NOT_ENOUGH_SPACE 102
#define NVS_ERR_REMOVE_FAILED 103
#define NVS_ERR_VALUE_TOO_LONG 104
#define NVS_ERR_INVALID_LENGTH 105
#define NVS_ERR_NOT_FOUND 106
#define NVS_ERR_VERIFY_FAIL 107
#define NVS_ERR_READ_INCPLT 108
typedef uint32_t nvs_err_t;
/**
* Opaque pointer type representing non-volatile storage handle
*/
typedef void * nvs_handle_t;
/**
* @brief Open non-volatile storage with a given namespace from the default NVS partition
*
* Multiple internal ESP-IDF and third party application modules can store
* their key-value pairs in the NVS module. In order to reduce possible
* conflicts on key names, each module can use its own namespace.
* The default NVS partition is the one that is labelled "nvs" in the partition
* table.
*
* @param[in] name Namespace name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty.
* @param[out] out_handle If successful (return code is zero), handle will be
* returned in this argument.
*
* @return
* - If successful, handle will be returned in this argument.
*/
nvs_handle_t nvs_open(const char* name);
/**
* @brief set variable length binary value for given key
*
*
* @param[in] handle Handle obtained from nvs_open function.
* @param[in] key Key name, from 0 to 65535.
* @param[in] value The value to set.
* @param[in] length length of binary value to set, in bytes,
* must be 2bytes aligned, 2 ~ 65534
* @return
* - NVS_ERR_OK if value was set successfully
* - NVS_ERR_INVALID_HANDLE if handle has been closed or is NULL
* - NVS_ERR_NOT_ENOUGH_SPACE if there is not enough space in the
* underlying storage to save the value
* - NVS_ERR_REMOVE_FAILED if the value wasn't updated because flash
* write operation has failed. The value was written however, and
* update will be finished after re-initialization of nvs, provided that
* flash operation doesn't fail again.
* - NVS_ERR_VALUE_TOO_LONG if the value is too long
*/
nvs_err_t nvs_set_blob(nvs_handle_t handle, uint16_t key, const uint8_t* value, uint32_t length);
/**
* @brief get blob value for given key
*
* These functions retrieve the data of an entry, given its key. If key does not
* exist, an error is returned.
*
* In case of any error, out_value is not modified.
*
* All functions expect out_value to be a pointer to an already allocated variable
* of the given type.
*
* nvs_get_blob functions support WinAPI-style length queries.
* To get the size necessary to store the value, call nvs_get_blob
* with zero out_value and non-zero pointer to length. Variable pointed to
* by length argument will be set to the required length. For nvs_get_str,
* this length includes the zero terminator. When calling nvs_get_str and
* nvs_get_blob with non-zero out_value, length has to be non-zero and has to
* point to the length available in out_value.
* It is suggested that nvs_get/set_str is used for zero-terminated C strings, and
* nvs_get/set_blob used for arbitrary data structures.
*
*
* @param[in] handle Handle obtained from nvs_open function.
* @param[in] key Key name, from 0 to 65535.
* @param[out] out_value Pointer to the output value.
* May be NULL for nvs_get_str and nvs_get_blob, in this
* case required length will be returned in length argument.
* @param[inout] length A non-zero pointer to the variable holding the length of out_value.
* In case out_value a zero, will be set to the length
* required to hold the value. In case out_value is not
* zero, will be set to the actual length of the value
* written. For nvs_get_str this includes zero terminator.
*
* @return
* - NVS_ERR_OK if the value was retrieved successfully
* - NVS_ERR_NOT_FOUND if the requested key doesn't exist
* - NVS_ERR_INVALID_HANDLE if handle has been closed or is NULL
* - NVS_ERR_VERIFY_FAIL
* - NVS_ERR_READ_INCPLT
* - NVS_ERR_INVALID_LENGTH if \c length is not sufficient to store data
*/
nvs_err_t nvs_get_blob(nvs_handle_t handle, uint16_t key, uint8_t* out_value, uint32_t* length);
/**
* @brief Erase key-value pair with given key name.
* @param[in] handle Storage handle obtained with nvs_open.
* @param[in] key Key name, from 0 to 65535.
* @return
* - NVS_ERR_OK if erase operation was successful
* - NVS_ERR_INVALID_HANDLE if handle has been closed or is NULL
* - NVS_ERR_NOT_FOUND if the requested key doesn't exist
*/
nvs_err_t nvs_erase_key(nvs_handle_t handle, uint16_t key);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //ESP_NVS_H
#include "nvs.h"
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include "flash_tlv.h"
#include "flash_map.h"
// 2+2KB
#define NVS_TLV1_MAJOR_SEC 0x0803D000u
#define NVS_TLV1_MINOR_SEC 0x0803D800u
#define NVS_TLV1_PART_SIZE 2048u
// 4+4KB
#define NVS_TLV2_MAJOR_SEC 0x0803E000u
#define NVS_TLV2_MINOR_SEC 0x0803F000u
#define NVS_TLV2_PART_SIZE 4096u
static tlv_sector_t nvs1_sec = {0u, 0u, 0u, 0u, 0u, 0u};
static tlv_sector_t nvs2_sec = {0u, 0u, 0u, 0u, 0u, 0u};
// @param[in] name support "nvs1" and "nvs2"
nvs_handle_t nvs_open(const char* name) {
int len = strlen(name);
if(len != 4) {
return NULL;
}
if(strncmp(name, "nvs", 3) != 0) {
return NULL;
}
if(name[3] == '1') {
if(nvs1_sec.major_sector == 0) {
flash_tlv_init(&nvs1_sec, NVS_TLV1_MAJOR_SEC, NVS_TLV1_MINOR_SEC, NVS_TLV1_PART_SIZE);
}
return (void *)&nvs1_sec;
}else if(name[3] == '2') {
if(nvs2_sec.major_sector == 0) {
flash_tlv_init(&nvs2_sec, NVS_TLV2_MAJOR_SEC, NVS_TLV2_MINOR_SEC, NVS_TLV2_PART_SIZE);
}
return (void *)&nvs2_sec;
}
return NULL;
}
nvs_err_t nvs_set_blob(nvs_handle_t handle, uint16_t key, const uint8_t* value, uint32_t length) {
if(handle == NULL) {
return NVS_ERR_INVALID_HANDLE;
}
if((length > 65534) || (length & 0x1) || (length == 0)) {
return NVS_ERR_INVALID_LENGTH;
}
bool success = flash_tlv_append((tlv_sector_t *)handle, key, value, (uint16_t)length);
return success ? NVS_ERR_OK : NVS_ERR_NOT_ENOUGH_SPACE;
}
nvs_err_t nvs_get_blob(nvs_handle_t handle, uint16_t key, uint8_t* out_value, uint32_t* length) {
bool success;
tlv_block_t blk;
uint32_t readin;
uint16_t tlvlen;
if(handle == NULL) {
return NVS_ERR_INVALID_HANDLE;
}
success = flash_tlv_query((tlv_sector_t *)handle, key, &blk);
if(!success) {
return NVS_ERR_NOT_FOUND;
}
// Win32API-style length queries
if(out_value == NULL) {
if(length != NULL) {
*length = blk.length;
}
return NVS_ERR_OK;
}
if(length == NULL || (*length < blk.length)) {
return NVS_ERR_INVALID_LENGTH;
}
success = flash_tlv_verify(&blk);
if(!success) {
return NVS_ERR_VERIFY_FAIL;
}
tlvlen = (uint16_t)(*length & 0xFFFF);
readin = flash_tlv_read(&blk, out_value, 0, tlvlen);
*length = readin;
return (readin == tlvlen) ? NVS_ERR_OK : NVS_ERR_READ_INCPLT;
}
nvs_err_t nvs_erase_key(nvs_handle_t handle, uint16_t key) {
if(handle == NULL) {
return NVS_ERR_INVALID_HANDLE;
}
bool success = flash_tlv_delete((tlv_sector_t *)handle, key);
return success ? NVS_ERR_OK : NVS_ERR_NOT_FOUND;
}

浙公网安备 33010602011771号