/** * Flash 存储模块实现 * 使用 STM32F1 HAL Flash 驱动 */ #include "storage.h" /* 全局配置 */ Config_t g_config; /* CRC32 计算 */ static uint32_t Storage_CRC32(const uint8_t *data, uint32_t len) { uint32_t crc = 0xFFFFFFFF; for (uint32_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 1) crc = (crc >> 1) ^ 0xEDB88320; else crc >>= 1; } } return ~crc; } void Storage_Init(void) { /* 解锁 Flash */ HAL_FLASH_Unlock(); } void Storage_Load(void) { Config_t *flash_cfg = (Config_t *)FLASH_STORAGE_ADDR; /* 检查魔数 */ if (flash_cfg->magic[0] == 'L' && flash_cfg->magic[1] == 'O' && flash_cfg->magic[2] == 'L' && flash_cfg->magic[3] == 'I') { /* 校验 CRC */ uint32_t calc_crc = Storage_CRC32((uint8_t*)flash_cfg, sizeof(Config_t) - sizeof(uint32_t)); if (calc_crc == flash_cfg->crc32) { /* 有效配置, 加载 */ memcpy(&g_config, flash_cfg, sizeof(Config_t)); return; } } /* 无效或无配置, 恢复出厂 */ Storage_Reset(); } void Storage_Save(void) { /* 计算 CRC */ g_config.crc32 = Storage_CRC32((uint8_t*)&g_config, sizeof(Config_t) - sizeof(uint32_t)); /* 擦除页 */ FLASH_EraseInitTypeDef erase = {0}; uint32_t page_error; erase.TypeErase = FLASH_TYPEERASE_PAGES; erase.PageAddress = FLASH_STORAGE_ADDR; erase.NbPages = 1; HAL_FLASH_Unlock(); HAL_FLASHEx_Erase(&erase, &page_error); /* 写入 (按 32-bit 写入) */ uint32_t *src = (uint32_t*)&g_config; uint32_t *dst = (uint32_t*)FLASH_STORAGE_ADDR; uint32_t count = (sizeof(Config_t) + 3) / 4; for (uint32_t i = 0; i < count; i++) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t)(dst + i), src[i]); } HAL_FLASH_Lock(); } void Storage_Reset(void) { memset(&g_config, 0, sizeof(Config_t)); /* 魔数 */ g_config.magic[0] = 'L'; g_config.magic[1] = 'O'; g_config.magic[2] = 'L'; g_config.magic[3] = 'I'; /* 默认模型 */ g_config.active_model = 0; strcpy(g_config.models[0].name, "Model1"); g_config.models[0].type = MODEL_TYPE_AIRPLANE; /* 默认通道映射: CH1->ADC_IN0, CH2->ADC_IN1, CH3->ADC_IN2, CH4->ADC_IN3 */ for (uint8_t i = 0; i < 4; i++) { g_config.models[0].channel_map[i] = i; g_config.models[0].input_type[i] = INPUT_TYPE_ADC; g_config.models[0].mixer_type[i] = MIXER_TYPE_SIMPLE; g_config.models[0].mixer_scale[i] = 100; /* 1:1 */ g_config.models[0].mixer_offset[i] = 0; } for (uint8_t i = 4; i < CHANNEL_COUNT; i++) { g_config.models[0].channel_map[i] = i; g_config.models[0].input_type[i] = INPUT_TYPE_NONE; g_config.models[0].mixer_type[i] = MIXER_TYPE_NONE; } /* 默认高频头: NRF24 */ g_config.rf_type = 0; g_config.nrf_channel = 40; g_config.nrf_rate = NRF_RATE_1M; g_config.nrf_power = NRF_PA_MAX; memcpy(g_config.nrf_addr, "\xE7\xE7\xE7\xE7\xE7", 5); strcpy(g_config.nrf_phrase, "LOVE"); /* CRFS 默认 */ g_config.crfs_packet_rate = 500; g_config.crfs_telemetry_ratio = 4; g_config.crfs_power = 100; /* PWM 默认 */ g_config.pwm1_mode = PWM_MODE_DISABLED; g_config.pwm2_mode = PWM_MODE_DISABLED; g_config.ws2812_effect = WS2812_MODE_RAINBOW_BREATH; g_config.led_brightness = 50; g_config.vibrate_level = 5; /* 首页默认: 回传 */ g_config.home_page = 0; /* 保存 */ Storage_Save(); } /* ---- 模型操作 ---- */ uint8_t Storage_ModelAdd(const char *name, uint8_t type) { for (uint8_t i = 0; i < MODEL_MAX_COUNT; i++) { if (g_config.models[i].name[0] == '\0') { strncpy(g_config.models[i].name, name, MODEL_NAME_LEN - 1); g_config.models[i].name[MODEL_NAME_LEN - 1] = '\0'; g_config.models[i].type = type; /* 默认通道映射 */ for (uint8_t j = 0; j < 4; j++) { g_config.models[i].channel_map[j] = j; g_config.models[i].input_type[j] = INPUT_TYPE_ADC; g_config.models[i].mixer_type[j] = MIXER_TYPE_SIMPLE; g_config.models[i].mixer_scale[j] = 100; g_config.models[i].mixer_offset[j] = 0; } for (uint8_t j = 4; j < CHANNEL_COUNT; j++) { g_config.models[i].channel_map[j] = j; g_config.models[i].input_type[j] = INPUT_TYPE_NONE; g_config.models[i].mixer_type[j] = MIXER_TYPE_NONE; } return i; } } return 0xFF; /* 已满 */ } void Storage_ModelDelete(uint8_t idx) { if (idx >= MODEL_MAX_COUNT) return; memset(&g_config.models[idx], 0, sizeof(Model_t)); if (g_config.active_model == idx) { g_config.active_model = 0; } } void Storage_ModelSelect(uint8_t idx) { if (idx >= MODEL_MAX_COUNT) return; if (g_config.models[idx].name[0] == '\0') return; /* 空模型 */ g_config.active_model = idx; } Model_t* Storage_GetActiveModel(void) { return &g_config.models[g_config.active_model]; } Model_t* Storage_GetModel(uint8_t idx) { if (idx >= MODEL_MAX_COUNT) return NULL; return &g_config.models[idx]; }