/** * @file card_flash_impl.c * @brief 卡号白名单:EEPROM `S_CARD_MNG` + SPI Flash 定长槽(48B/槽,与 fault 相同的帧头+CRC 低字节) * * 单槽布局:RECORD_HEADER + CARD_FLASH_BODY_T(46) + crc8 * 写任意槽需对该槽所在扇区做读-合并-擦除-整扇区写回(NOR Flash 不可原地改已写位)。 */ #include "publicdata/public_define.h" #include "publicdata/publicdata.h" #include "app_fatfs/fatfs_card.h" #include "app_fatfs/fatfs_init.h" #include "card_flash_impl.h" #include "eeprom/fm24cl16.h" #include "externalflash/flash_external_data.h" #include /* 动态内存抽象:默认使用 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 #if FATFS_EN #if 1 #define CARD_FLASH_MAX_CNT CARD_MAX_COUNT #define CARD_BODY_SIZE 46u #define CARD_SLOT_SIZE 48u #define CARD_SLOTS_PER_SECTOR ((U32_T)SPI_SECTOR_SIZE / (U32_T)CARD_SLOT_SIZE) #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L _Static_assert(sizeof(CARD_FLASH_BODY_T) == 46u, "CARD_FLASH_BODY_T must be 46 bytes"); #endif static bool g_card_init; static S_CARD_MNG s_cardMng; /** * @brief z_strnlen。 */ static size_t z_strnlen(const char *s, size_t max) { size_t i; for (i = 0; i < max && s[i] != '\0'; i++) { } return i; } /** * @brief v_pack_card_num。 */ static void v_pack_card_num(const char *src, U8_T dst[32]) { memset(dst, 0, 32u); if (src == NULL) { return; } size_t n = z_strnlen(src, (size_t)CARD_NUM_MAX_LEN); memcpy(dst, src, n); } /** * @brief b_card_num_eq。 */ static bool b_card_num_eq(const char *src, const U8_T dst[32]) { U8_T a[32]; v_pack_card_num(src, a); return memcmp(a, dst, 32u) == 0; } /** * @brief v_read_eeprom_card_mng。 */ static void v_read_eeprom_card_mng(S_CARD_MNG *m) { int i; S_CARD_MNG tmp; for (i = 0; i < 3; i++) { if (eeprom_read(EEPROM_ADDR_CARD_MNG, (U8_T *)&tmp, (S32_T)sizeof(S_CARD_MNG)) == HAL_OK) { m->u16_head = tmp.u16_head; m->u16_cardCnt = tmp.u16_cardCnt; m->u16_reserved = tmp.u16_reserved; return; } } m->u16_head = 0; m->u16_cardCnt = 0; m->u16_reserved = 0; } /** * @brief v_write_eeprom_card_mng。 */ static void v_write_eeprom_card_mng(const S_CARD_MNG *m) { S_CARD_MNG out; out.u16_head = CARD_MNG_HEAD; out.u16_cardCnt = m->u16_cardCnt; out.u16_reserved = m->u16_reserved; eeprom_write(EEPROM_ADDR_CARD_MNG, (U8_T *)&out, (S32_T)sizeof(S_CARD_MNG)); } /** * @brief u32_slot_addr。 */ static U32_T u32_slot_addr(U16_T slot) { U32_T sec = (U32_T)slot / CARD_SLOTS_PER_SECTOR; U32_T j = (U32_T)slot % CARD_SLOTS_PER_SECTOR; return (U32_T)DATAFLASH_CARD_ADDR + sec * (U32_T)SPI_SECTOR_SIZE + j * (U32_T)CARD_SLOT_SIZE; } /** * @brief v_read_slot_raw。 */ static void v_read_slot_raw(U16_T slot, U8_T *out) { (void)s32_flash_dataflash_read(u32_slot_addr(slot), out, CARD_SLOT_SIZE); } /** * @brief b_slot_buf_valid。 */ static bool b_slot_buf_valid(const U8_T *buf) { U8_T crc; if (buf[0] != RECORD_HEADER) { return false; } crc = (U8_T)u16_crc_checksum((U8_T *)(buf + 1), (U16_T)CARD_BODY_SIZE); return buf[CARD_SLOT_SIZE - 1u] == crc; } /** * @brief v_body_to_card_info。 */ static void v_body_to_card_info(const CARD_FLASH_BODY_T *b, CardInfo *ci) { uint32_t ct; uint32_t lt; memset(ci, 0, sizeof(*ci)); memcpy(ci->card_num, b->card_num, sizeof(b->card_num)); ci->card_num[CARD_NUM_MAX_LEN - 1u] = '\0'; ci->balance = b->amount_milli / 10u; memcpy(&ct, &b->rsv[0], 4u); memcpy(<, &b->rsv[4], 4u); ci->create_time = ct; ci->last_use_time = lt; ci->status = b->status; memcpy(ci->reserved, b->rsv, sizeof(b->rsv)); } /** * @brief v_card_info_to_body。 */ static void v_card_info_to_body(const CardInfo *ci, CARD_FLASH_BODY_T *b) { memset(b, 0, sizeof(*b)); v_pack_card_num(ci->card_num, b->card_num); b->amount_milli = ci->balance * 10u; b->status = ci->status; memcpy(&b->rsv[0], &ci->create_time, 4u); memcpy(&b->rsv[4], &ci->last_use_time, 4u); } /** * @brief u16_count_valid_slots。 */ static U16_T u16_count_valid_slots(void) { U16_T n = 0; U16_T i; U8_T buf[CARD_SLOT_SIZE]; for (i = 0; i < CARD_FLASH_MAX_CNT; i++) { v_read_slot_raw(i, buf); if (b_slot_buf_valid(buf)) { n++; } } return n; } /** * @brief s_find_slot_by_num。 */ static S16_T s_find_slot_by_num(const char *num) { U16_T i; U8_T buf[CARD_SLOT_SIZE]; if (num == NULL) { return -1; } for (i = 0; i < CARD_FLASH_MAX_CNT; i++) { v_read_slot_raw(i, buf); if (!b_slot_buf_valid(buf)) { continue; } if (b_card_num_eq(num, buf + 1)) { return (S16_T)i; } } return -1; } /** * @brief s_find_free_slot。 */ static S16_T s_find_free_slot(void) { U16_T i; U8_T buf[CARD_SLOT_SIZE]; for (i = 0; i < CARD_FLASH_MAX_CNT; i++) { v_read_slot_raw(i, buf); if (!b_slot_buf_valid(buf)) { return (S16_T)i; } } return -1; } /** * @brief v_erase_all_card_sectors。 */ static void v_erase_all_card_sectors(void) { U8_T s; for (s = 0; s < (U8_T)DATAFLASH_CARD_SECTOR_CNT; s++) { (void)s32_flash_dataflash_erase_sector((U32_T)DATAFLASH_CARD_ADDR + (U32_T)s * (U32_T)SPI_SECTOR_SIZE); } } /** * @brief 写单槽:读扇区内其它有效槽,合并后擦除扇区再写回整扇区 * @param body_or_null 非 NULL 写入/更新;NULL 表示删除该槽(置为擦除态) */ static CardStatus e_flush_slot(U16_T slot, const CARD_FLASH_BODY_T *body_or_null) { U32_T sec = (U32_T)slot / CARD_SLOTS_PER_SECTOR; U32_T j = (U32_T)slot % CARD_SLOTS_PER_SECTOR; U32_T sector_base = (U32_T)DATAFLASH_CARD_ADDR + sec * (U32_T)SPI_SECTOR_SIZE; U32_T k; U8_T tmp[CARD_SLOT_SIZE]; U8_T *sector_rw = (U8_T *)FLASH_MGR_MALLOC((size_t)SPI_SECTOR_SIZE); if (sector_rw == NULL) { return CARD_STATUS_WRITE_FAILED; } memset(sector_rw, 0xFF, SPI_SECTOR_SIZE); for (k = 0; k < CARD_SLOTS_PER_SECTOR; k++) { U16_T g = (U16_T)(sec * CARD_SLOTS_PER_SECTOR + k); if (g >= CARD_FLASH_MAX_CNT) { break; } if (g == slot) { continue; } v_read_slot_raw(g, tmp); if (b_slot_buf_valid(tmp)) { memcpy(§or_rw[k * CARD_SLOT_SIZE], tmp, CARD_SLOT_SIZE); } } if (body_or_null != NULL) { U8_T *dst = §or_rw[j * CARD_SLOT_SIZE]; dst[0] = RECORD_HEADER; memcpy(dst + 1, body_or_null, sizeof(CARD_FLASH_BODY_T)); dst[CARD_SLOT_SIZE - 1u] = (U8_T)u16_crc_checksum(dst + 1, (U16_T)CARD_BODY_SIZE); } (void)s32_flash_dataflash_erase_sector(sector_base); if (s32_flash_dataflash_write(sector_base, sector_rw, (U32_T)SPI_SECTOR_SIZE) != 0u) { FLASH_MGR_FREE(sector_rw); return CARD_STATUS_WRITE_FAILED; } FLASH_MGR_FREE(sector_rw); return CARD_STATUS_OK; } /** * @brief card_init。 */ CardStatus card_init(void) { U16_T actual; if (g_card_init) { return CARD_STATUS_ALREADY_INIT; } v_read_eeprom_card_mng(&s_cardMng); actual = u16_count_valid_slots(); if (s_cardMng.u16_head != CARD_MNG_HEAD) { s_cardMng.u16_head = CARD_MNG_HEAD; s_cardMng.u16_cardCnt = actual; s_cardMng.u16_reserved = 0; v_write_eeprom_card_mng(&s_cardMng); } else if (s_cardMng.u16_cardCnt != actual) { s_cardMng.u16_cardCnt = actual; v_write_eeprom_card_mng(&s_cardMng); } g_card_init = true; FATFS_PRINT("card flash: init ok, cnt=%u\r\n", (unsigned)s_cardMng.u16_cardCnt); return CARD_STATUS_OK; } /** * @brief card_deinit。 */ void card_deinit(void) { g_card_init = false; } /** * @brief card_add。 */ CardStatus card_add(const CardInfo *card_info) { CARD_FLASH_BODY_T body; S16_T free_slot; if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } if (card_info == NULL) { return CARD_STATUS_PARAM_ERROR; } if (s_find_slot_by_num(card_info->card_num) >= 0) { return CARD_STATUS_CARD_EXIST; } free_slot = s_find_free_slot(); if (free_slot < 0) { return CARD_STATUS_NO_SPACE; } v_card_info_to_body(card_info, &body); if (e_flush_slot((U16_T)free_slot, &body) != CARD_STATUS_OK) { return CARD_STATUS_WRITE_FAILED; } s_cardMng.u16_cardCnt++; v_write_eeprom_card_mng(&s_cardMng); return CARD_STATUS_OK; } /** * @brief card_update。 */ CardStatus card_update(const CardInfo *card_info) { CARD_FLASH_BODY_T body; S16_T idx; if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } if (card_info == NULL) { return CARD_STATUS_PARAM_ERROR; } idx = s_find_slot_by_num(card_info->card_num); if (idx < 0) { return CARD_STATUS_CARD_NOT_FOUND; } v_card_info_to_body(card_info, &body); if (e_flush_slot((U16_T)idx, &body) != CARD_STATUS_OK) { return CARD_STATUS_WRITE_FAILED; } return CARD_STATUS_OK; } /** * @brief card_delete。 */ CardStatus card_delete(const char *card_num) { S16_T idx; if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } if (card_num == NULL) { return CARD_STATUS_PARAM_ERROR; } idx = s_find_slot_by_num(card_num); if (idx < 0) { return CARD_STATUS_CARD_NOT_FOUND; } if (e_flush_slot((U16_T)idx, NULL) != CARD_STATUS_OK) { return CARD_STATUS_WRITE_FAILED; } if (s_cardMng.u16_cardCnt > 0) { s_cardMng.u16_cardCnt--; } v_write_eeprom_card_mng(&s_cardMng); return CARD_STATUS_OK; } /** * @brief card_find。 */ CardStatus card_find(const char *card_num, CardInfo *card_info) { S16_T idx; U8_T buf[CARD_SLOT_SIZE]; CARD_FLASH_BODY_T body; if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } if (card_num == NULL || card_info == NULL) { return CARD_STATUS_PARAM_ERROR; } idx = s_find_slot_by_num(card_num); if (idx < 0) { return CARD_STATUS_CARD_NOT_FOUND; } v_read_slot_raw((U16_T)idx, buf); if (!b_slot_buf_valid(buf)) { return CARD_STATUS_READ_FAILED; } memcpy(&body, buf + 1, sizeof(CARD_FLASH_BODY_T)); v_body_to_card_info(&body, card_info); return CARD_STATUS_OK; } /** * @brief card_get_count。 */ CardStatus card_get_count(uint32_t *count) { if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } if (count == NULL) { return CARD_STATUS_PARAM_ERROR; } *count = (uint32_t)s_cardMng.u16_cardCnt; return CARD_STATUS_OK; } /** * @brief card_get_all。 */ CardStatus card_get_all(CardInfo *card_list, uint32_t *count, uint32_t max_count) { U16_T i; U32_T n; U8_T buf[CARD_SLOT_SIZE]; CARD_FLASH_BODY_T body; if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } if (count == NULL) { return CARD_STATUS_PARAM_ERROR; } n = 0; for (i = 0; i < CARD_FLASH_MAX_CNT && n < max_count; i++) { v_read_slot_raw(i, buf); if (!b_slot_buf_valid(buf)) { continue; } if (card_list != NULL) { memcpy(&body, buf + 1, sizeof(CARD_FLASH_BODY_T)); v_body_to_card_info(&body, &card_list[n]); } n++; } *count = n; return CARD_STATUS_OK; } /** * @brief card_check_exist。 */ CardStatus card_check_exist(const char *card_num, bool *exist) { if (card_num == NULL || exist == NULL) { return CARD_STATUS_PARAM_ERROR; } if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } *exist = (s_find_slot_by_num(card_num) >= 0); return CARD_STATUS_OK; } /** * @brief card_backup。 */ CardStatus card_backup(void) { /* 无独立备份区;保留接口兼容旧 CSV 备份语义 */ return CARD_STATUS_OK; } /** * @brief card_restore。 */ CardStatus card_restore(void) { return CARD_STATUS_OK; } /** * @brief card_clear_all。 */ CardStatus card_clear_all(void) { if (!g_card_init) { return CARD_STATUS_INIT_FAILED; } v_erase_all_card_sectors(); s_cardMng.u16_head = CARD_MNG_HEAD; s_cardMng.u16_cardCnt = 0; s_cardMng.u16_reserved = 0; v_write_eeprom_card_mng(&s_cardMng); FATFS_PRINT("card flash: cleared\r\n"); return CARD_STATUS_OK; } #else /* !FATFS_ENABLE_CARD */ CardStatus card_init(void) { return CARD_STATUS_INIT_FAILED; } void card_deinit(void) {} /** * @brief card_add。 */ CardStatus card_add(const CardInfo *card_info) { (void)card_info; return CARD_STATUS_INIT_FAILED; } /** * @brief card_update。 */ CardStatus card_update(const CardInfo *card_info) { (void)card_info; return CARD_STATUS_INIT_FAILED; } /** * @brief card_delete。 */ CardStatus card_delete(const char *card_num) { (void)card_num; return CARD_STATUS_INIT_FAILED; } /** * @brief card_find。 */ CardStatus card_find(const char *card_num, CardInfo *card_info) { (void)card_num; (void)card_info; return CARD_STATUS_INIT_FAILED; } /** * @brief card_get_count。 */ CardStatus card_get_count(uint32_t *count) { if (count) { *count = 0; } return CARD_STATUS_INIT_FAILED; } /** * @brief card_get_all。 */ CardStatus card_get_all(CardInfo *card_list, uint32_t *count, uint32_t max_count) { (void)card_list; (void)max_count; if (count) { *count = 0; } return CARD_STATUS_INIT_FAILED; } /** * @brief card_check_exist。 */ CardStatus card_check_exist(const char *card_num, bool *exist) { (void)card_num; if (exist) { *exist = false; } return CARD_STATUS_INIT_FAILED; } /** * @brief card_backup。 */ CardStatus card_backup(void) { return CARD_STATUS_INIT_FAILED; } /** * @brief card_restore。 */ CardStatus card_restore(void) { return CARD_STATUS_INIT_FAILED; } /** * @brief card_clear_all。 */ CardStatus card_clear_all(void) { return CARD_STATUS_INIT_FAILED; } #endif /* FATFS_ENABLE_CARD */ #else /* !FATFS_EN */ CardStatus card_init(void) { return CARD_STATUS_INIT_FAILED; } void card_deinit(void) {} /** * @brief card_add。 */ CardStatus card_add(const CardInfo *card_info) { (void)card_info; return CARD_STATUS_INIT_FAILED; } /** * @brief card_update。 */ CardStatus card_update(const CardInfo *card_info) { (void)card_info; return CARD_STATUS_INIT_FAILED; } /** * @brief card_delete。 */ CardStatus card_delete(const char *card_num) { (void)card_num; return CARD_STATUS_INIT_FAILED; } /** * @brief card_find。 */ CardStatus card_find(const char *card_num, CardInfo *card_info) { (void)card_num; (void)card_info; return CARD_STATUS_INIT_FAILED; } /** * @brief card_get_count。 */ CardStatus card_get_count(uint32_t *count) { if (count) { *count = 0; } return CARD_STATUS_INIT_FAILED; } /** * @brief card_get_all。 */ CardStatus card_get_all(CardInfo *card_list, uint32_t *count, uint32_t max_count) { (void)card_list; (void)max_count; if (count) { *count = 0; } return CARD_STATUS_INIT_FAILED; } /** * @brief card_check_exist。 */ CardStatus card_check_exist(const char *card_num, bool *exist) { (void)card_num; if (exist) { *exist = false; } return CARD_STATUS_INIT_FAILED; } /** * @brief card_backup。 */ CardStatus card_backup(void) { return CARD_STATUS_INIT_FAILED; } /** * @brief card_restore。 */ CardStatus card_restore(void) { return CARD_STATUS_INIT_FAILED; } /** * @brief card_clear_all。 */ CardStatus card_clear_all(void) { return CARD_STATUS_INIT_FAILED; } #endif /* FATFS_EN */