/** * @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 #include #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); // 写入EEPROM,eeprom_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; // 无效记录 }