diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index 17b18360..d09b8670 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -1,9 +1,11 @@ #include "subghz_txrx_i.h" // IWYU pragma: keep #include +#include #include #include #include +#include "../../../../lib/subghz/devices/subghz_preset_delta.h" #include #define TAG "SubGhzTxRx" @@ -498,6 +500,36 @@ void subghz_txrx_hopper_pause(SubGhzTxRx* instance) { } } +// Identify the hop index (0=AM650, 1=FM476, 2=FM95) from the name. +// Must match the order defined in subghz_preset_delta.h +static int subghz_hop_index_from_name(const char* name) { + if(strcmp(name, "AM650") == 0) return 0; + if(strcmp(name, "FM476") == 0) return 1; + if(strcmp(name, "FM95") == 0) return 2; + return -1; // is not part of the fast hopping set +} + +// Applies the target preset using delta-patch (without SRES) when possible, +// or falls back to the original full reload in any other case. +static void subghz_txrx_apply_preset_fast( + SubGhzTxRx* instance, + const char* old_preset_name, + const char* preset_name) { + int from_idx = subghz_hop_index_from_name(old_preset_name); + int to_idx = subghz_hop_index_from_name(preset_name); + + if(instance->radio_device_type == SubGhzRadioDeviceTypeInternal && from_idx >= 0 && + to_idx >= 0 && from_idx != to_idx) { + // Fast path: delta-patch without SRES or full reload (only internal CC1101) + const PresetDeltaEntry* e = &preset_delta_table[from_idx][to_idx]; + furi_hal_subghz_apply_preset_delta(e->delta, e->delta_len, e->needs_scal, e->pa_table); + } else { + // Fallback: original behavior (full reload) + subghz_devices_load_preset( + instance->radio_device, FuriHalSubGhzPresetCustom, instance->preset->data); + } +} + void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold) { furi_assert(instance); @@ -550,22 +582,7 @@ void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold subghz_txrx_set_preset_internal( instance, instance->preset->frequency, actual_preset_idx, 0); - bool old_is_am = (strstr(old_preset_name, "AM") != NULL); - bool new_is_am = (strstr(preset_name, "AM") != NULL); - bool modulation_changed = (old_is_am != new_is_am); - - if(modulation_changed) { - subghz_devices_reset(instance->radio_device); - subghz_devices_load_preset( - instance->radio_device, - FuriHalSubGhzPresetCustom, - instance->preset->data); - } else { - subghz_devices_load_preset( - instance->radio_device, - FuriHalSubGhzPresetCustom, - instance->preset->data); - } + subghz_txrx_apply_preset_fast(instance, old_preset_name, preset_name); subghz_txrx_rx(instance, instance->preset->frequency); } @@ -588,22 +605,7 @@ void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold subghz_txrx_set_preset_internal( instance, instance->preset->frequency, instance->preset_hopper_idx, 0); - bool old_is_am = (strstr(old_preset_name, "AM") != NULL); - bool new_is_am = (strstr(preset_name, "AM") != NULL); - bool modulation_changed = (old_is_am != new_is_am); - - if(modulation_changed) { - subghz_devices_reset(instance->radio_device); - subghz_devices_load_preset( - instance->radio_device, - FuriHalSubGhzPresetCustom, - instance->preset->data); - } else { - subghz_devices_load_preset( - instance->radio_device, - FuriHalSubGhzPresetCustom, - instance->preset->data); - } + subghz_txrx_apply_preset_fast(instance, old_preset_name, preset_name); subghz_txrx_rx(instance, instance->preset->frequency); } diff --git a/lib/subghz/devices/subghz_preset_delta.c b/lib/subghz/devices/subghz_preset_delta.c new file mode 100644 index 00000000..c67577f6 --- /dev/null +++ b/lib/subghz/devices/subghz_preset_delta.c @@ -0,0 +1,79 @@ +#include +#include +#include "subghz_preset_delta.h" + +static const uint8_t preset_delta_AM650_to_FM476[] = { + 0x10, 0x67, + 0x11, 0x83, + 0x12, 0x04, + 0x13, 0x02, + 0x19, 0x16, + 0x21, 0x56, + 0x22, 0x10, +}; +static const uint8_t preset_delta_AM650_to_FM476_pa[8] = { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uint8_t preset_delta_AM650_to_FM95[] = { + 0x10, 0x67, + 0x11, 0x83, + 0x12, 0x04, + 0x13, 0x02, + 0x14, 0xF8, + 0x15, 0x24, + 0x19, 0x16, + 0x21, 0x56, + 0x22, 0x10, +}; +static const uint8_t preset_delta_AM650_to_FM95_pa[8] = { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uint8_t preset_delta_FM476_to_AM650[] = { + 0x10, 0x17, + 0x11, 0x32, + 0x12, 0x30, + 0x13, 0x00, + 0x19, 0x18, + 0x21, 0xB6, + 0x22, 0x11, +}; +static const uint8_t preset_delta_FM476_to_AM650_pa[8] = { 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uint8_t preset_delta_FM476_to_FM95[] = { + 0x14, 0xF8, + 0x15, 0x24, +}; + +static const uint8_t preset_delta_FM95_to_AM650[] = { + 0x10, 0x17, + 0x11, 0x32, + 0x12, 0x30, + 0x13, 0x00, + 0x14, 0x00, + 0x15, 0x47, + 0x19, 0x18, + 0x21, 0xB6, + 0x22, 0x11, +}; +static const uint8_t preset_delta_FM95_to_AM650_pa[8] = { 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uint8_t preset_delta_FM95_to_FM476[] = { + 0x14, 0x00, + 0x15, 0x47, +}; + +const PresetDeltaEntry preset_delta_table[HOP_PRESET_COUNT][HOP_PRESET_COUNT] = { + { + {0}, + { .delta = preset_delta_AM650_to_FM476, .delta_len = 14, .needs_scal = false, .pa_table = preset_delta_AM650_to_FM476_pa }, + { .delta = preset_delta_AM650_to_FM95, .delta_len = 18, .needs_scal = false, .pa_table = preset_delta_AM650_to_FM95_pa }, + }, + { + { .delta = preset_delta_FM476_to_AM650, .delta_len = 14, .needs_scal = false, .pa_table = preset_delta_FM476_to_AM650_pa }, + {0}, + { .delta = preset_delta_FM476_to_FM95, .delta_len = 4, .needs_scal = false, .pa_table = NULL }, + }, + { + { .delta = preset_delta_FM95_to_AM650, .delta_len = 18, .needs_scal = false, .pa_table = preset_delta_FM95_to_AM650_pa }, + { .delta = preset_delta_FM95_to_FM476, .delta_len = 4, .needs_scal = false, .pa_table = NULL }, + {0}, + }, +}; diff --git a/lib/subghz/devices/subghz_preset_delta.h b/lib/subghz/devices/subghz_preset_delta.h new file mode 100644 index 00000000..04b1a3b9 --- /dev/null +++ b/lib/subghz/devices/subghz_preset_delta.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +typedef struct { + const uint8_t* delta; + size_t delta_len; + bool needs_scal; + const uint8_t* pa_table; +} PresetDeltaEntry; + +typedef enum { + HOP_AM650 = 0, + HOP_FM476 = 1, + HOP_FM95 = 2, + HOP_PRESET_COUNT, +} SubghzHopPreset; + +extern const PresetDeltaEntry preset_delta_table[HOP_PRESET_COUNT][HOP_PRESET_COUNT]; diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 6bb47110..c4b96df8 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -56,6 +56,7 @@ class FlipperApplication: name: Optional[str] = "" entry_point: Optional[str] = None flags: List[str] = field(default_factory=lambda: ["Default"]) + cflags: List[str] = field(default_factory=list) cdefines: List[str] = field(default_factory=list) requires: List[str] = field(default_factory=list) conflicts: List[str] = field(default_factory=list) diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index e00edabe..8c48f07a 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -61,6 +61,7 @@ class AppBuilder: ("FAP_VERSION", f'\\"{".".join(map(str, self.app.fap_version))}\\"'), *self.app.cdefines, ], + CCFLAGS=self.app.cflags, ) self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False) diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 62ebc1bd..ccfab63c 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,89.1,, +Version,+,89.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/applications.h,, Header,+,applications/services/bt/bt_service/bt.h,, @@ -1783,6 +1783,7 @@ Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,const FuriHalSpiBusHandle* +Function,+,furi_hal_subghz_apply_preset_delta,void,"const uint8_t*, size_t, _Bool, const uint8_t*" Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, @@ -3462,8 +3463,8 @@ Function,+,subghz_block_generic_global_reset,void,void* Function,+,subghz_block_generic_serialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_custom_btn_get,uint8_t, Function,-,subghz_custom_btn_get_long,_Bool, -Function,+,subghz_custom_btn_get_original,uint8_t, Function,+,subghz_custom_btn_get_max_pages,uint8_t, +Function,+,subghz_custom_btn_get_original,uint8_t, Function,+,subghz_custom_btn_get_page,uint8_t, Function,+,subghz_custom_btn_has_pages,_Bool, Function,+,subghz_custom_btn_is_allowed,_Bool, diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index b23b2ea7..312d93f5 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -218,6 +218,59 @@ void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { } } +/** +* @brief Applies only to the registers that change between the current preset +* and the destination preset, without SRES or a full reload. +* +* Requires the chip state to be RX/TX or IDLE before calling; +* the function itself forces IDLE (without SRES) before writing. +* +* @param delta array of {reg, val} pairs (exact length given by delta_len) +* @param delta_len number of bytes in delta (multiple of 2) +* @param needs_scal if true, forces SCAL after writing to the delta +* @param pa_table 8 bytes of new PA table, or NULL if not changing +*/ +void furi_hal_subghz_apply_preset_delta( + const uint8_t* delta, + size_t delta_len, + bool needs_scal, + const uint8_t* pa_table) { + + furi_check(delta); + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + + // Ensure IDLE (not SRES!) before touching records + cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); + + // Write only the records that change + for(size_t i = 0; i + 1 < delta_len; i += 2) { + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, delta[i], delta[i + 1]); + } + + if(needs_scal) { + cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + furi_check( + cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); + } + + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + + if(pa_table) { + furi_hal_subghz_load_patable(pa_table); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + FURI_LOG_D( + TAG, + "Applied preset delta: %u regs, scal=%d, pa=%d", + (unsigned)(delta_len / 2), + needs_scal, + pa_table != NULL); + } +} + void furi_hal_subghz_load_registers(const uint8_t* data) { furi_check(data); diff --git a/targets/f7/furi_hal/furi_hal_subghz.h b/targets/f7/furi_hal/furi_hal_subghz.h index b08d41ba..f33cbbe9 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.h +++ b/targets/f7/furi_hal/furi_hal_subghz.h @@ -66,6 +66,12 @@ void furi_hal_subghz_dump_state(void); */ void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data); +void furi_hal_subghz_apply_preset_delta( + const uint8_t* delta, + size_t delta_len, + bool needs_scal, + const uint8_t* pa_table); + /** Load registers * * @param data Registers data