Files
CCU621M/BSP/flash_file_mgr/meter_calculate_flash_impl.c
T

527 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file meter_calculate_flash_impl.c
* @brief 充电订单与临时订单的 EEPROM + 外部 SPI Flash 持久化实现
*
* 功能说明:
* - 订单管理块 `S_CHG_ORDER_MNG` 存于 EEPROM`EEPROM_ADDR_CHGRCD_MNG`):头标志、已存条数、
* 环形槽当前位置、单调递增的 `u32_lastIndex`(对外唯一订单号)。
* - 订单正文 `S_LOG_DATA` 存于 Flash 区 `DATAFLASH_HIS_RECORD_ADDR` 起;单条布局为
* 1 字节 `RECORD_HEADER` + 数据 + 1 字节 CRC`u16_crc_checksum` 低 8 位),按扇区擦除后顺序写入。
* - `u32_chg_order_save_record`:分配新 `u32_lastIndex`,按 `(lastIndex-1)%CHG_ORDER_MAX_CNT` 映射槽位并写 Flash。
* - `v_read_chg_order_record(index)`:用 `(index-1)%CHG_ORDER_MAX_CNT` 定位槽,并校验记录内 `u32_index` 与请求一致,防止覆盖残留。
* - `v_read_chg_order_by_position`:在未满/已满环形缓冲下,按“第几条历史”语义换算槽位。
* - 双枪临时订单:标志字节 + `S_LOG_DATA` 存于 EEPROM`EEPROM_ADDR_A_LOG_DATA` / `B`),用于断电续传;清除时仅写无效标志以省擦写。
*
* 注意:`v_chg_order_clear_all` 仅复位管理元数据,不整片擦除 Flash(由后续写入覆盖)。
*/
#include "meter_calculate_flash_impl.h"
#include <string.h>
#include <stdlib.h>
#include "mylog/mylog.h"
/* 动态内存抽象:默认使用 FreeRTOS 堆接口,可按需覆盖 */
#ifndef FLASH_MGR_MALLOC
#define FLASH_MGR_MALLOC(sz) pvPortMalloc((sz))
#endif
#ifndef FLASH_MGR_FREE
#define FLASH_MGR_FREE(ptr) vPortFree((ptr))
#endif
/* 模块内部全局变量 */
S_CHG_ORDER_MNG s_chgOrderMng;
/* 内部函数声明 */
static void v_write_chg_order_record(S_LOG_DATA *order_data, U16_T position);
static U8_T v_read_chg_order_record_internal(S_LOG_DATA *order_data, U16_T position);
/***************************************************************
* EEPROM管理函数
***************************************************************/
/**
* @brief 写入充电订单管理信息到EEPROM
*/
void v_write_eeprom_chgOrderMng(S_CHG_ORDER_MNG *chgOrderMng)
{
S_CHG_ORDER_MNG tempMng;
// 设置头标志
tempMng.u16_head = 0x5AA5;
tempMng.u16_chgOrderCnt = chgOrderMng->u16_chgOrderCnt;
tempMng.u16_currIndex = chgOrderMng->u16_currIndex;
tempMng.u32_lastIndex = chgOrderMng->u32_lastIndex;
// 写入EEPROM
eeprom_write(EEPROM_ADDR_CHGRCD_MNG, (U8_T *)&tempMng, sizeof(S_CHG_ORDER_MNG));
}
/**
* @brief 从EEPROM读取充电订单管理信息
*/
void v_read_eeprom_chgOrderMng(S_CHG_ORDER_MNG *chgOrderMng)
{
S_CHG_ORDER_MNG tempMng;
int i;
for (i = 0; i < 3; i++) {
// eeprom_read返回HAL_OK(0)表示成功,非0表示失败
if (eeprom_read(EEPROM_ADDR_CHGRCD_MNG, (U8_T *)&tempMng, sizeof(S_CHG_ORDER_MNG)) == HAL_OK) {
// 验证头标志
if (tempMng.u16_head == 0x5AA5) {
chgOrderMng->u16_head = 0x5AA5;
chgOrderMng->u16_chgOrderCnt = tempMng.u16_chgOrderCnt;
chgOrderMng->u16_currIndex = tempMng.u16_currIndex;
chgOrderMng->u32_lastIndex = tempMng.u32_lastIndex;
return;
}
}
}
// 读取失败或数据无效,初始化默认值
chgOrderMng->u16_head = 0x5AA5;
chgOrderMng->u16_chgOrderCnt = 0;
chgOrderMng->u16_currIndex = RECORD_ZERO_SAVE;
chgOrderMng->u32_lastIndex = 0;
}
/***************************************************************
* Flash记录读写函数(内部)
***************************************************************/
/**
* @brief 向Flash写充电订单记录(内部函数)
*
* @param order_data 充电订单数据指针
* @param position 循环缓冲区中的位置(1-N范围)
*/
static void v_write_chg_order_record(S_LOG_DATA *order_data, U16_T position)
{
U16_T u16_oneRcdSize = CHG_ORDER_RECORD_SIZE; // 一条记录大小(可能大于255
U32_T u32_addr; // 存储地址
U16_T SectorIndex = 0; // 扇区偏移索引
U16_T SectorRcdIndex = 0; // 扇区内记录索引号
U16_T OneSectorRcdCnt = CHG_ORDER_PER_SECTOR_CNT; // 一个扇区内最多存储记录数
U32_T SectorAddr = 0; // 扇区地址
U16_T CurSectorRcdCnt = 0; // 当前扇区内应存放最大数目
// position参数范围是1-N,转换为0-N范围用于内部计算
U16_T internal_position = position - 1;
U8_T *tmpbuf = (U8_T *)FLASH_MGR_MALLOC(CHG_ORDER_FULL_RECORD_SIZE);
if (tmpbuf == NULL) {
return;
}
// 填充待存储数据缓存
tmpbuf[0] = RECORD_HEADER;
memcpy(tmpbuf + 1, order_data, u16_oneRcdSize); // 充电订单记录
// 计算并存储CRC
U16_T calculated_crc = u16_crc_checksum((U8_T *)order_data, u16_oneRcdSize);
tmpbuf[u16_oneRcdSize + 1] = (U8_T)calculated_crc; // 存储CRC低字节
#ifdef DEBUG_FLASH_WRITE
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] Write to addr 0x%08X (position %u, internal %u):", u32_addr, position, internal_position);
MYLOG_MSG(TASK_ID_Meterfee, " Header: 0x%02X", tmpbuf[0]);
MYLOG_MSG(TASK_ID_Meterfee, " Calculated CRC: 0x%04X -> stored low byte: 0x%02X", calculated_crc, tmpbuf[u16_oneRcdSize + 1]);
#endif
// 确定存储位置(使用internal_position,范围0-N
SectorIndex = internal_position / OneSectorRcdCnt;
SectorRcdIndex = internal_position % OneSectorRcdCnt;
SectorAddr = DATAFLASH_HIS_RECORD_ADDR + SectorIndex * SPI_SECTOR_SIZE;
// 如果是扇区内第一条记录,需要擦除整个扇区
if (SectorRcdIndex == 0) {
s32_flash_dataflash_erase_sector(SectorAddr);
// 如果记录数超过最大限制,需要调整计数
if (s_chgOrderMng.u16_chgOrderCnt > CHG_ORDER_MAX_CNT) {
// 如果待存储扇区为最后一个扇区
U16_T totalSectors = (CHG_ORDER_MAX_CNT + OneSectorRcdCnt - 1) / OneSectorRcdCnt;
if ((SectorIndex + 1) == totalSectors) {
CurSectorRcdCnt = CHG_ORDER_MAX_CNT % OneSectorRcdCnt;
if (CurSectorRcdCnt == 0) {
CurSectorRcdCnt = OneSectorRcdCnt;
}
} else {
CurSectorRcdCnt = OneSectorRcdCnt;
}
s_chgOrderMng.u16_chgOrderCnt = CHG_ORDER_MAX_CNT - CurSectorRcdCnt + 1;
}
}
// 计算具体地址并写入
u32_addr = SectorAddr + SectorRcdIndex * CHG_ORDER_FULL_RECORD_SIZE;
s32_flash_dataflash_write(u32_addr, tmpbuf, CHG_ORDER_FULL_RECORD_SIZE);
FLASH_MGR_FREE(tmpbuf);
}
/**
* @brief 从Flash读充电订单记录(内部函数)
*
* @param order_data 充电订单数据指针(用于存储读取到的数据)
* @param position 循环缓冲区中的位置(1-N范围)
* @return U8_T 读取结果:0-成功,1-失败
*/
static U8_T v_read_chg_order_record_internal(S_LOG_DATA *order_data, U16_T position)
{
U16_T u16_oneRcdSize = CHG_ORDER_RECORD_SIZE; // 一条记录大小(可能大于255
U32_T u32_addr; // 读取地址
U16_T SectorIndex = 0; // 扇区偏移索引
U16_T SectorRcdIndex = 0; // 扇区内记录索引号
U16_T OneSectorRcdCnt = CHG_ORDER_PER_SECTOR_CNT; // 一个扇区内最多存储记录数
// position参数范围是1-N,转换为0-N范围用于内部计算
U16_T internal_position = position - 1;
U8_T *tmpbuf = (U8_T *)FLASH_MGR_MALLOC(CHG_ORDER_FULL_RECORD_SIZE);
if (tmpbuf == NULL) {
return 1;
}
// 确定读取位置(使用internal_position,范围0-N
SectorIndex = internal_position / OneSectorRcdCnt;
SectorRcdIndex = internal_position % OneSectorRcdCnt;
u32_addr = DATAFLASH_HIS_RECORD_ADDR + SectorIndex * SPI_SECTOR_SIZE +
SectorRcdIndex * CHG_ORDER_FULL_RECORD_SIZE;
// 读取数据
s32_flash_dataflash_read(u32_addr, tmpbuf, CHG_ORDER_FULL_RECORD_SIZE);
// 调试信息:打印读取到的数据
#ifdef DEBUG_FLASH_READ
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] Read from addr 0x%08X (position %u, internal %u):", u32_addr, position, internal_position);
MYLOG_MSG(TASK_ID_Meterfee, " Header: 0x%02X (expected: 0x%02X)", tmpbuf[0], RECORD_HEADER);
// 计算CRC
U16_T calculated_crc = u16_crc_checksum(tmpbuf + 1, u16_oneRcdSize);
U8_T stored_crc = tmpbuf[u16_oneRcdSize + 1];
MYLOG_MSG(TASK_ID_Meterfee, " Stored CRC: 0x%02X", stored_crc);
MYLOG_MSG(TASK_ID_Meterfee, " Calculated CRC: 0x%04X -> low byte: 0x%02X", calculated_crc, (U8_T)calculated_crc);
#endif
// 验证数据完整性
if ((tmpbuf[0] == RECORD_HEADER) &&
(tmpbuf[u16_oneRcdSize + 1] == (U8_T)u16_crc_checksum(tmpbuf + 1, u16_oneRcdSize))) {
#ifdef DEBUG_FLASH_READ
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] v_read_chg_order_record_internal: data ok, memcpy dst=0x%08X, size=%u",
(U32_T)order_data, u16_oneRcdSize);
#endif
memcpy(order_data, tmpbuf + 1, u16_oneRcdSize);
FLASH_MGR_FREE(tmpbuf);
return 0;
}
#ifdef DEBUG_FLASH_READ
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] CRC check failed!");
MYLOG_MSG(TASK_ID_Meterfee, " Header match: %s", (tmpbuf[0] == RECORD_HEADER) ? "YES" : "NO");
MYLOG_MSG(TASK_ID_Meterfee, " CRC match: %s", (tmpbuf[u16_oneRcdSize + 1] == (U8_T)u16_crc_checksum(tmpbuf + 1, u16_oneRcdSize)) ? "YES" : "NO");
#endif
FLASH_MGR_FREE(tmpbuf);
return 1;
}
/***************************************************************
* 公共接口函数实现
***************************************************************/
/**
* @brief 初始化充电订单Flash存储模块
*/
void v_chg_order_flash_init(void)
{
// 从EEPROM读取管理信息
v_read_eeprom_chgOrderMng(&s_chgOrderMng);
// 如果头标志无效,初始化默认值
if (s_chgOrderMng.u16_head != 0x5AA5)
{
s_chgOrderMng.u16_head = 0x5AA5;
s_chgOrderMng.u16_chgOrderCnt = 0;
s_chgOrderMng.u16_currIndex = RECORD_ZERO_SAVE;
s_chgOrderMng.u32_lastIndex = 0;
// 写入EEPROM
v_write_eeprom_chgOrderMng(&s_chgOrderMng);
}
// MYLOG_MSG(TASK_ID_Meterfee, "OrderMng: init ok, cnt=%u",
// s_chgOrderMng.u16_chgOrderCnt);
}
/**
* @brief 清除所有充电订单记录
*/
void v_chg_order_clear_all(void)
{
// 重置管理信息
s_chgOrderMng.u16_head = 0x5AA5;
s_chgOrderMng.u16_chgOrderCnt = 0;
s_chgOrderMng.u16_currIndex = RECORD_ZERO_SAVE;
s_chgOrderMng.u32_lastIndex = 0;
// 写入EEPROM
v_write_eeprom_chgOrderMng(&s_chgOrderMng);
// 注意:这里不清除Flash中的实际数据,因为Flash擦除成本高
// 实际数据会在后续写入时被覆盖
}
/**
* @brief 保存充电订单记录到Flash
*
* 新设计:索引号直接对应Flash物理位置
* 索引号 = u32_lastIndex + 1
* 存储位置 = (u32_lastIndex % CHG_ORDER_MAX_CNT)
* 注意:此函数会修改order_data中的u32_index字段
*/
U32_T u32_chg_order_save_record(S_LOG_DATA *order_data)
{
U32_T assignedIndex;
// 分配唯一递增索引
s_chgOrderMng.u32_lastIndex++;
assignedIndex = s_chgOrderMng.u32_lastIndex;
// 计算存储位置(基于索引号的循环缓冲区)
U16_T storagePosition = (s_chgOrderMng.u32_lastIndex - 1) % CHG_ORDER_MAX_CNT;
// 更新当前索引位置(存储0-N范围)
s_chgOrderMng.u16_currIndex = storagePosition;
// 记录条数递增(不超过最大限制)
if (s_chgOrderMng.u16_chgOrderCnt < CHG_ORDER_MAX_CNT) {
s_chgOrderMng.u16_chgOrderCnt++;
}
// 直接修改传入的订单数据,设置索引
order_data->u32_index = assignedIndex;
// 写入Flash记录,传递1-N范围的position
v_write_chg_order_record(order_data, storagePosition + 1);
// 更新管理信息到EEPROM
v_write_eeprom_chgOrderMng(&s_chgOrderMng);
return assignedIndex;
}
/**
* @brief 从Flash读取指定索引的充电订单记录
*
* 新设计:根据索引号直接计算存储位置,无需遍历查找
* 存储位置 = (index - 1) % CHG_ORDER_MAX_CNT
*/
U8_T v_read_chg_order_record(S_LOG_DATA *order_data, U32_T index)
{
// 检查索引号有效性
if (index == 0 || index > s_chgOrderMng.u32_lastIndex) {
return 1; // 索引号无效
}
// 如果没有记录,直接返回失败
if (s_chgOrderMng.u16_chgOrderCnt == 0) {
return 1;
}
// 计算存储位置(基于索引号的循环缓冲区)
U16_T storagePosition = (index - 1) % CHG_ORDER_MAX_CNT;
// 直接读取到传入的缓冲区,传递1-N范围的position
if (v_read_chg_order_record_internal(order_data, storagePosition + 1) == 0) {
// 检查索引是否匹配(防止数据被覆盖)
if (order_data->u32_index == index) {
return 0; // 成功
}
}
return 1; // 读取失败或索引不匹配
}
/**
* @brief 读取指定位置的充电订单记录(基于循环缓冲区位置)
*/
U8_T v_read_chg_order_by_position(S_LOG_DATA *order_data, U16_T position)
{
// 检查位置是否有效
if (position >= s_chgOrderMng.u16_chgOrderCnt) {
return 1;
}
// 计算实际Flash位置(考虑循环缓冲区)
U16_T actual_position;
if (s_chgOrderMng.u16_chgOrderCnt < CHG_ORDER_MAX_CNT) {
// 缓冲区未满,直接按顺序读取
actual_position = position;
} else {
// 缓冲区已满,需要计算循环位置
actual_position = (s_chgOrderMng.u16_currIndex + 1 + position) % CHG_ORDER_MAX_CNT;
}
// 传递1-N范围的position给内部函数
return v_read_chg_order_record_internal(order_data, actual_position + 1);
}
/**
* @brief 获取充电订单记录总数
*/
U16_T v_get_chg_order_count(void)
{
return s_chgOrderMng.u16_chgOrderCnt;
}
/**
* @brief 获取当前索引位置
*/
U16_T v_get_chg_order_curr_index(void)
{
return s_chgOrderMng.u16_currIndex;
}
/**
* @brief 获取最后分配的索引值
*/
U32_T v_get_chg_order_last_index(void)
{
return s_chgOrderMng.u32_lastIndex;
}
/***************************************************************
* EEPROM临时充电记录存储函数
***************************************************************/
/**
* @brief 获取指定枪号的EEPROM地址
*/
static U32_T v_get_eeprom_addr_for_gun(U8_T gunNo)
{
if (gunNo == 0) {
return EEPROM_ADDR_A_LOG_DATA;
} else {
return EEPROM_ADDR_B_LOG_DATA;
}
}
/**
* @brief 保存临时充电记录到EEPROM(用于断电续传)
*/
U8_T v_save_temp_chg_record_to_eeprom(U8_T gunNo, const S_LOG_DATA *order_data, E_TEMP_CHG_FLAG flag)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T *buffer = NULL;
U8_T ret = 1;
// 检查参数有效性
if (order_data == NULL || flag >= TEMP_CHG_FLAG_INVALID) {
return 1;
}
// 动态分配内存
buffer = (U8_T *)FLASH_MGR_MALLOC(TEMP_CHG_RECORD_SIZE + 1);
if (buffer == NULL) {
return 1; // 内存分配失败
}
// 构建缓冲区:标志位 + 订单数据
buffer[0] = (U8_T)flag;
memcpy(buffer + 1, order_data, TEMP_CHG_RECORD_SIZE);
// 写入EEPROMeeprom_write返回HAL_OK(0)表示成功
if (eeprom_write(eeprom_addr, buffer, TEMP_CHG_RECORD_SIZE + 1) == HAL_OK) {
ret = 0; // 成功
}
// 释放内存
FLASH_MGR_FREE(buffer);
return ret;
}
/**
* @brief 从EEPROM读取临时充电记录(用于断电续传恢复)
*/
U8_T v_read_temp_chg_record_from_eeprom(U8_T gunNo, S_LOG_DATA *order_data, E_TEMP_CHG_FLAG *flag)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T *buffer = NULL;
// 检查参数有效性
if (order_data == NULL) {
return 1;
}
// 动态分配内存
buffer = (U8_T *)FLASH_MGR_MALLOC(TEMP_CHG_RECORD_SIZE + 1);
if (buffer == NULL) {
return 1; // 内存分配失败
}
// 从EEPROM读取,eeprom_read返回HAL_OK(0)表示成功
if (eeprom_read(eeprom_addr, buffer, TEMP_CHG_RECORD_SIZE + 1) != HAL_OK) {
FLASH_MGR_FREE(buffer);
return 1; // 读取失败
}
// 检查标志位有效性
U8_T read_flag = buffer[0];
if (read_flag >= TEMP_CHG_FLAG_INVALID) {
FLASH_MGR_FREE(buffer);
return 1; // 无效标志位
}
// 返回标志位(如果提供了指针)
if (flag != NULL) {
*flag = (E_TEMP_CHG_FLAG)read_flag;
}
// 复制订单数据
memcpy(order_data, buffer + 1, TEMP_CHG_RECORD_SIZE);
// 释放内存
FLASH_MGR_FREE(buffer);
return 0; // 成功
}
/**
* @brief 清除EEPROM中的临时充电记录
*/
U8_T v_clear_temp_chg_record_in_eeprom(U8_T gunNo)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T invalid_flag = TEMP_CHG_FLAG_INVALID;
// 只写入无效标志位,不清除整个区域(节省EEPROM写寿命)
// eeprom_write返回HAL_OK(0)表示成功
if (eeprom_write(eeprom_addr, &invalid_flag, 1) == HAL_OK) {
return 0; // 成功
}
return 1; // 失败
}
/**
* @brief 检查EEPROM中是否有有效的临时充电记录
*/
U8_T v_check_temp_chg_record_valid(U8_T gunNo)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T flag_byte;
// 读取标志位,eeprom_read返回HAL_OK(0)表示成功
if (eeprom_read(eeprom_addr, &flag_byte, 1) != HAL_OK) {
return 0; // 读取失败,视为无效
}
// 检查标志位是否有效
if (flag_byte == TEMP_CHG_FLAG_UNSETTLED || flag_byte == TEMP_CHG_FLAG_SETTLED) {
return 1; // 有有效记录
}
return 0; // 无效记录
}