/* * 配置相关 */ /* Includes ----------------------------------------------------------------- */ #include "module/config.h" #include #include "bsp/flash.h" #include #include "device/sx1281_driver/sx1281.h" /* Private typedef ---------------------------------------------------------- */ /* Private define ----------------------------------------------------------- */ /* Private macro ------------------------------------------------------------ */ /* Private variables -------------------------------------------------------- */ /* Exported variables ------------------------------------------------------- */ // 机器人参数配置 Config_t robot_config = { 0 }; /* Private function prototypes ---------------------------------------------- */ /* Exported functions ------------------------------------------------------- */ /** * @brief 获取机器人配置参数 * @return 机器人配置参数指针 */ Config_t* Config_Get(void) { return &robot_config; } /** * @brief Validate MR16 parameter values read from flash * @param p MR16_Param_t pointer to validate * @return true if values look valid, false otherwise */ /* Helper validators for sparse enums */ static bool is_valid_gfsk_ble_bitrate(uint8_t v) { switch (v) { case GFS_BLE_BR_2_000_BW_2_4: case GFS_BLE_BR_1_600_BW_2_4: case GFS_BLE_BR_1_000_BW_2_4: case GFS_BLE_BR_1_000_BW_1_2: case GFS_BLE_BR_0_800_BW_2_4: case GFS_BLE_BR_0_800_BW_1_2: case GFS_BLE_BR_0_500_BW_1_2: case GFS_BLE_BR_0_500_BW_0_6: case GFS_BLE_BR_0_400_BW_1_2: case GFS_BLE_BR_0_400_BW_0_6: case GFS_BLE_BR_0_250_BW_0_6: case GFS_BLE_BR_0_250_BW_0_3: case GFS_BLE_BR_0_125_BW_0_3: return true; default: return false; } } static bool is_valid_flrc_bitrate(uint8_t v) { switch (v) { case FLRC_BR_2_600_BW_2_4: case FLRC_BR_2_080_BW_2_4: case FLRC_BR_1_300_BW_1_2: case FLRC_BR_1_040_BW_1_2: case FLRC_BR_0_650_BW_0_6: case FLRC_BR_0_520_BW_0_6: case FLRC_BR_0_325_BW_0_3: case FLRC_BR_0_260_BW_0_3: return true; default: return false; } } static bool is_valid_lora_sf(uint8_t v) { switch (v) { case LORA_SF5: case LORA_SF6: case LORA_SF7: case LORA_SF8: case LORA_SF9: case LORA_SF10: case LORA_SF11: case LORA_SF12: return true; default: return false; } } static bool is_valid_lora_bw(uint8_t v) { switch (v) { case LORA_BW_0200: case LORA_BW_0400: case LORA_BW_0800: case LORA_BW_1600: return true; default: return false; } } static bool is_valid_mod_shaping(uint8_t v) { switch (v) { case RADIO_MOD_SHAPING_BT_OFF: case RADIO_MOD_SHAPING_BT_1_0: case RADIO_MOD_SHAPING_BT_0_5: return true; default: return false; } } static bool Config_ValidateMR16(const MR16_Param_t *p) { if (p == NULL) return false; /* Basic sanity checks */ if (p->TX_ID <= 0x0000 || p->TX_ID >= 0xFFFF) return false; /* RF frequency should be within 2.4GHz ISM band (2400..2500 MHz) */ if (p->radioParams.rfFrequency < 2400000000ULL || p->radioParams.rfFrequency > 2500000000ULL) return false; /* TX power reasonable check per SX1281 datasheet (-18dBm to +13dBm) */ if (p->radioParams.txOutputPower < -18 || p->radioParams.txOutputPower > 13) return false; /* Mode must be in enum range */ if (p->mode < MR16_MODE_NONE || p->mode >= MR16_MODE_NUM) return false; /* Validate ramp time enum (must be one of defined values) */ switch (p->radioParams.rampTime) { case RADIO_RAMP_02_US: case RADIO_RAMP_04_US: case RADIO_RAMP_06_US: case RADIO_RAMP_08_US: case RADIO_RAMP_10_US: case RADIO_RAMP_12_US: case RADIO_RAMP_16_US: case RADIO_RAMP_20_US: break; default: return false; } /* Validate baudrate enums */ if (p->radioParams.baudrate.ble >= RF_BAUDRATE_BLE_NUM) return false; if (p->radioParams.baudrate.lora >= RF_BAUDRATE_LORA_NUM) return false; if (p->radioParams.baudrate.gfks >= RF_BAUDRATE_GFSK_NUM) return false; if (p->radioParams.baudrate.flrc >= RF_BAUDRATE_FLRC_NUM) return false; /* PacketType consistency: modulation & packet params must agree */ // if (p->radioParams.modulationParams.PacketType != p->radioParams.packetParams.PacketType) return false; /* Validate modulation parameters based on packet type */ switch (p->radioParams.modulationParams.PacketType) { case PACKET_TYPE_BLE: if (!is_valid_gfsk_ble_bitrate((uint8_t)p->radioParams.modulationParams.Params.Ble.BitrateBandwidth)) return false; if (p->radioParams.modulationParams.Params.Ble.ModulationIndex > GFS_BLE_MOD_IND_4_00) return false; if (!is_valid_mod_shaping((uint8_t)p->radioParams.modulationParams.Params.Ble.ModulationShaping)) return false; break; case PACKET_TYPE_GFSK: if (!is_valid_gfsk_ble_bitrate((uint8_t)p->radioParams.modulationParams.Params.Gfsk.BitrateBandwidth)) return false; if (p->radioParams.modulationParams.Params.Gfsk.ModulationIndex > GFS_BLE_MOD_IND_4_00) return false; if (!is_valid_mod_shaping((uint8_t)p->radioParams.modulationParams.Params.Gfsk.ModulationShaping)) return false; break; case PACKET_TYPE_LORA: if (!is_valid_lora_sf((uint8_t)p->radioParams.modulationParams.Params.LoRa.SpreadingFactor)) return false; if (!is_valid_lora_bw((uint8_t)p->radioParams.modulationParams.Params.LoRa.Bandwidth)) return false; if (p->radioParams.modulationParams.Params.LoRa.CodingRate < LORA_CR_4_5 || p->radioParams.modulationParams.Params.LoRa.CodingRate > LORA_CR_LI_4_7) return false; break; case PACKET_TYPE_FLRC: if (!is_valid_flrc_bitrate((uint8_t)p->radioParams.modulationParams.Params.Flrc.BitrateBandwidth)) return false; if (p->radioParams.modulationParams.Params.Flrc.CodingRate > FLRC_CR_1_0) return false; if (!is_valid_mod_shaping((uint8_t)p->radioParams.modulationParams.Params.Flrc.ModulationShaping)) return false; break; default: return false; } /* Validate packet parameters based on packet type */ switch (p->radioParams.packetParams.PacketType) { case PACKET_TYPE_BLE: if (p->radioParams.packetParams.Params.Ble.BlePacketType > BLE_RXTX_TEST_MODE) return false; if (p->radioParams.packetParams.Params.Ble.ConnectionState > BLE_RXTX_TEST_MODE) return false; if (p->radioParams.packetParams.Params.Ble.CrcField != BLE_CRC_3B && p->radioParams.packetParams.Params.Ble.CrcField != BLE_CRC_OFF) return false; if (p->radioParams.packetParams.Params.Ble.Whitening != RADIO_WHITENING_ON && p->radioParams.packetParams.Params.Ble.Whitening != RADIO_WHITENING_OFF) return false; break; case PACKET_TYPE_GFSK: switch (p->radioParams.packetParams.Params.Gfsk.PreambleLength) { case PREAMBLE_LENGTH_04_BITS: case PREAMBLE_LENGTH_08_BITS: case PREAMBLE_LENGTH_12_BITS: case PREAMBLE_LENGTH_16_BITS: case PREAMBLE_LENGTH_20_BITS: case PREAMBLE_LENGTH_24_BITS: case PREAMBLE_LENGTH_28_BITS: case PREAMBLE_LENGTH_32_BITS: break; default: return false; } switch (p->radioParams.packetParams.Params.Gfsk.SyncWordLength) { case GFS_SYNCWORD_LENGTH_1_BYTE: case GFS_SYNCWORD_LENGTH_2_BYTE: case GFS_SYNCWORD_LENGTH_3_BYTE: case GFS_SYNCWORD_LENGTH_4_BYTE: case GFS_SYNCWORD_LENGTH_5_BYTE: break; default: return false; } switch (p->radioParams.packetParams.Params.Gfsk.SyncWordMatch) { case RADIO_RX_MATCH_SYNCWORD_OFF: case RADIO_RX_MATCH_SYNCWORD_1: case RADIO_RX_MATCH_SYNCWORD_2: case RADIO_RX_MATCH_SYNCWORD_1_2: case RADIO_RX_MATCH_SYNCWORD_3: case RADIO_RX_MATCH_SYNCWORD_1_3: case RADIO_RX_MATCH_SYNCWORD_2_3: case RADIO_RX_MATCH_SYNCWORD_1_2_3: break; default: return false; } if (p->radioParams.packetParams.Params.Gfsk.HeaderType != RADIO_PACKET_VARIABLE_LENGTH && p->radioParams.packetParams.Params.Gfsk.HeaderType != RADIO_PACKET_FIXED_LENGTH) return false; if (p->radioParams.packetParams.Params.Gfsk.PayloadLength == 0 || p->radioParams.packetParams.Params.Gfsk.PayloadLength > 255) return false; switch (p->radioParams.packetParams.Params.Gfsk.CrcLength) { case RADIO_CRC_OFF: case RADIO_CRC_1_BYTES: case RADIO_CRC_2_BYTES: case RADIO_CRC_3_BYTES: break; default: return false; } if (p->radioParams.packetParams.Params.Gfsk.Whitening != RADIO_WHITENING_ON && p->radioParams.packetParams.Params.Gfsk.Whitening != RADIO_WHITENING_OFF) return false; break; case PACKET_TYPE_LORA: if (p->radioParams.packetParams.Params.LoRa.PreambleLength == 0 || p->radioParams.packetParams.Params.LoRa.PreambleLength > 65535) return false; if (p->radioParams.packetParams.Params.LoRa.HeaderType != LORA_PACKET_VARIABLE_LENGTH && p->radioParams.packetParams.Params.LoRa.HeaderType != LORA_PACKET_FIXED_LENGTH) return false; if (p->radioParams.packetParams.Params.LoRa.PayloadLength == 0 || p->radioParams.packetParams.Params.LoRa.PayloadLength > 255) return false; if (p->radioParams.packetParams.Params.LoRa.CrcMode != LORA_CRC_ON && p->radioParams.packetParams.Params.LoRa.CrcMode != LORA_CRC_OFF) return false; if (p->radioParams.packetParams.Params.LoRa.InvertIQ != LORA_IQ_NORMAL && p->radioParams.packetParams.Params.LoRa.InvertIQ != LORA_IQ_INVERTED) return false; break; case PACKET_TYPE_FLRC: switch (p->radioParams.packetParams.Params.Flrc.PreambleLength) { case PREAMBLE_LENGTH_04_BITS: case PREAMBLE_LENGTH_08_BITS: case PREAMBLE_LENGTH_12_BITS: case PREAMBLE_LENGTH_16_BITS: case PREAMBLE_LENGTH_20_BITS: case PREAMBLE_LENGTH_24_BITS: case PREAMBLE_LENGTH_28_BITS: case PREAMBLE_LENGTH_32_BITS: break; default: return false; } if (p->radioParams.packetParams.Params.Flrc.SyncWordLength != FLRC_NO_SYNCWORD && p->radioParams.packetParams.Params.Flrc.SyncWordLength != FLRC_SYNCWORD_LENGTH_4_BYTE) return false; switch (p->radioParams.packetParams.Params.Flrc.SyncWordMatch) { case RADIO_RX_MATCH_SYNCWORD_OFF: case RADIO_RX_MATCH_SYNCWORD_1: case RADIO_RX_MATCH_SYNCWORD_2: case RADIO_RX_MATCH_SYNCWORD_1_2: case RADIO_RX_MATCH_SYNCWORD_3: case RADIO_RX_MATCH_SYNCWORD_1_3: case RADIO_RX_MATCH_SYNCWORD_2_3: case RADIO_RX_MATCH_SYNCWORD_1_2_3: break; default: return false; } if (p->radioParams.packetParams.Params.Flrc.HeaderType != RADIO_PACKET_VARIABLE_LENGTH && p->radioParams.packetParams.Params.Flrc.HeaderType != RADIO_PACKET_FIXED_LENGTH) return false; if (p->radioParams.packetParams.Params.Flrc.PayloadLength == 0 || p->radioParams.packetParams.Params.Flrc.PayloadLength > 255) return false; switch (p->radioParams.packetParams.Params.Flrc.CrcLength) { case RADIO_CRC_OFF: case RADIO_CRC_1_BYTES: case RADIO_CRC_2_BYTES: case RADIO_CRC_3_BYTES: break; default: return false; } /* FLRC whitening handled in packetParams, allowed values validated above */ break; default: return false; } /* RX_ID array: each ID must be within valid 16-bit range; 0xFFFF reserved invalid, 0x0000 allowed (broadcast) */ for (int i = 0; i < 3; ++i) { if (p->RX_ID[i] == 0xFFFF) return false; } return true; } /** * @brief Try to load MR16 parameters from Flash and validate them. * If valid, copy into runtime config and return pointer; otherwise return NULL. * @return pointer to `robot_config.mr16` on success, NULL on invalid/missing flash data */ MR16_Param_t* Config_LoadMR16FromFlash(void) { MR16_Param_t tmp; /* Read from flash (STMFLASH_Read expects half-words count) */ STMFLASH_Read(FLASH_SAVE_ADDR, (uint16_t*)&tmp, sizeof(MR16_Param_t)/2); if (Config_ValidateMR16(&tmp)) { memcpy(&robot_config.mr16, &tmp, sizeof(MR16_Param_t)); return &robot_config.mr16; } return NULL; } /** * @brief Read MR16 params from flash and validate them. * If valid, copy into runtime config and return true; otherwise return false. * @return true if flash contained valid MR16 params and they were loaded into config */ bool Config_LoadMR16FromFlashBool(void) { MR16_Param_t tmp; STMFLASH_Read(FLASH_SAVE_ADDR, (uint16_t*)&tmp, sizeof(MR16_Param_t)/2); if (Config_ValidateMR16(&tmp)) { memcpy(&robot_config.mr16, &tmp, sizeof(MR16_Param_t)); return true; } return false; } /** * @brief Check if flash contains valid MR16 parameters without loading them * @return true if flash data is valid, false otherwise */ bool Config_IsFlashValid(void) { MR16_Param_t tmp; STMFLASH_Read(FLASH_SAVE_ADDR, (uint16_t*)&tmp, sizeof(MR16_Param_t)/2); return Config_ValidateMR16(&tmp); } /** * @brief Load MR16 parameters from flash (assumes flash is valid) */ void Config_LoadFromFlash(void) { STMFLASH_Read(FLASH_SAVE_ADDR, (uint16_t*)&robot_config.mr16, sizeof(MR16_Param_t)/2); } /** * @brief Set default values for MR16 parameters */ void Config_SetDefaults(void) { /* MR16 defaults */ robot_config.mr16.RX_ID[0] = 0x0000; robot_config.mr16.RX_ID[1] = 0x0000; robot_config.mr16.RX_ID[2] = 0x0000; robot_config.mr16.mode = MR16_MODE_NONE; /* Radio defaults */ robot_config.mr16.radioParams.rfFrequency = 2426000000; robot_config.mr16.radioParams.txOutputPower = 13; robot_config.mr16.radioParams.rampTime = RADIO_RAMP_02_US; /* Baudrate defaults - high-level user configurations */ robot_config.mr16.radioParams.baudrate.ble = RF_BAUDRATE_BLE_1M; robot_config.mr16.radioParams.baudrate.lora = RF_BAUDRATE_LORA_005K; robot_config.mr16.radioParams.baudrate.gfks = RF_BAUDRATE_GFSK_125K; robot_config.mr16.radioParams.baudrate.flrc = RF_BAUDRATE_FLRC_130K; /* Set detailed modulation parameters to default values based on baudrate */ /* BLE parameters */ robot_config.mr16.radioParams.modulationParams.Params.Ble.BitrateBandwidth = GFS_BLE_BR_1_000_BW_2_4; // Default for RF_BAUDRATE_BLE_1M robot_config.mr16.radioParams.modulationParams.Params.Ble.ModulationIndex = GFS_BLE_MOD_IND_0_50; robot_config.mr16.radioParams.modulationParams.Params.Ble.ModulationShaping = RADIO_MOD_SHAPING_BT_0_5; robot_config.mr16.radioParams.packetParams.Params.Ble.BlePacketType = BLE_EYELONG_1_0; robot_config.mr16.radioParams.packetParams.Params.Ble.ConnectionState = BLE_ADVERTISER; robot_config.mr16.radioParams.packetParams.Params.Ble.CrcField = BLE_CRC_3B; robot_config.mr16.radioParams.packetParams.Params.Ble.Whitening = RADIO_WHITENING_ON; /* LoRa parameters */ robot_config.mr16.radioParams.modulationParams.Params.LoRa.SpreadingFactor = LORA_SF12; robot_config.mr16.radioParams.modulationParams.Params.LoRa.Bandwidth = LORA_BW_0200; robot_config.mr16.radioParams.modulationParams.Params.LoRa.CodingRate = LORA_CR_LI_4_7; robot_config.mr16.radioParams.packetParams.Params.LoRa.PreambleLength = 12; robot_config.mr16.radioParams.packetParams.Params.LoRa.HeaderType = LORA_PACKET_VARIABLE_LENGTH; robot_config.mr16.radioParams.packetParams.Params.LoRa.PayloadLength = BUFFER_SIZE; robot_config.mr16.radioParams.packetParams.Params.LoRa.CrcMode = LORA_CRC_ON; robot_config.mr16.radioParams.packetParams.Params.LoRa.InvertIQ = LORA_IQ_NORMAL; /* GFSK parameters */ robot_config.mr16.radioParams.modulationParams.Params.Gfsk.BitrateBandwidth = GFS_BLE_BR_0_125_BW_0_3; robot_config.mr16.radioParams.modulationParams.Params.Gfsk.ModulationIndex = GFS_BLE_MOD_IND_0_50; robot_config.mr16.radioParams.modulationParams.Params.Gfsk.ModulationShaping = RADIO_MOD_SHAPING_BT_0_5; robot_config.mr16.radioParams.packetParams.Params.Gfsk.PreambleLength = PREAMBLE_LENGTH_32_BITS; robot_config.mr16.radioParams.packetParams.Params.Gfsk.SyncWordLength = GFS_SYNCWORD_LENGTH_5_BYTE; robot_config.mr16.radioParams.packetParams.Params.Gfsk.SyncWordMatch = RADIO_RX_MATCH_SYNCWORD_1; robot_config.mr16.radioParams.packetParams.Params.Gfsk.HeaderType = RADIO_PACKET_VARIABLE_LENGTH; robot_config.mr16.radioParams.packetParams.Params.Gfsk.PayloadLength = BUFFER_SIZE; robot_config.mr16.radioParams.packetParams.Params.Gfsk.CrcLength = RADIO_CRC_3_BYTES; robot_config.mr16.radioParams.packetParams.Params.Gfsk.Whitening = RADIO_WHITENING_ON; /* FLRC parameters */ robot_config.mr16.radioParams.modulationParams.Params.Flrc.BitrateBandwidth = FLRC_BR_0_260_BW_0_3; robot_config.mr16.radioParams.modulationParams.Params.Flrc.CodingRate = FLRC_CR_1_2; robot_config.mr16.radioParams.modulationParams.Params.Flrc.ModulationShaping = RADIO_MOD_SHAPING_BT_1_0; robot_config.mr16.radioParams.packetParams.Params.Flrc.PreambleLength = PREAMBLE_LENGTH_32_BITS; robot_config.mr16.radioParams.packetParams.Params.Flrc.SyncWordLength = FLRC_SYNCWORD_LENGTH_4_BYTE; robot_config.mr16.radioParams.packetParams.Params.Flrc.SyncWordMatch = RADIO_RX_MATCH_SYNCWORD_1; robot_config.mr16.radioParams.packetParams.Params.Flrc.HeaderType = RADIO_PACKET_VARIABLE_LENGTH; robot_config.mr16.radioParams.packetParams.Params.Flrc.PayloadLength = BUFFER_SIZE; robot_config.mr16.radioParams.packetParams.Params.Flrc.CrcLength = RADIO_CRC_3_BYTES; robot_config.mr16.radioParams.packetParams.Params.Flrc.Whitening = RADIO_WHITENING_OFF; }