Add core firmware modules: input, menu, NRF24L01, OLED, protocol, PWM, storage + UI assets

This commit is contained in:
root
2026-06-25 22:55:41 +08:00
parent 97ab93e93d
commit 5c0ff0ce63
22 changed files with 3671 additions and 17 deletions

182
Core/Src/storage.c Normal file
View File

@@ -0,0 +1,182 @@
/**
* 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];
}