diff --git a/applications/external/protoview/app.c b/applications/external/protoview/app.c index 3e22b3781..868ec8f16 100644 --- a/applications/external/protoview/app.c +++ b/applications/external/protoview/app.c @@ -118,6 +118,8 @@ static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) { /* Allocate the application state and initialize a number of stuff. * This is called in the entry point to create the application state. */ ProtoViewApp* protoview_app_alloc() { + furi_hal_power_suppress_charge_enter(); + ProtoViewApp* app = malloc(sizeof(ProtoViewApp)); // Init shared data structures @@ -167,16 +169,14 @@ ProtoViewApp* protoview_app_alloc() { app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */ - // Enable power for External CC1101 if it is connected - furi_hal_subghz_enable_ext_power(); - // Auto switch to internal radio if external radio is not available - furi_delay_ms(15); - if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_select_radio_type(SubGhzRadioInternal); - furi_hal_subghz_init_radio_type(SubGhzRadioInternal); - } + // Init & set radio_device + subghz_devices_init(); + app->radio_device = + radio_device_loader_set(app->radio_device, SubGhzRadioDeviceTypeExternalCC1101); + + subghz_devices_reset(app->radio_device); + subghz_devices_idle(app->radio_device); - furi_hal_power_suppress_charge_enter(); app->running = 1; return app; @@ -188,13 +188,10 @@ ProtoViewApp* protoview_app_alloc() { void protoview_app_free(ProtoViewApp* app) { furi_assert(app); - // Put CC1101 on sleep, this also restores charging. - radio_sleep(app); + subghz_devices_sleep(app->radio_device); + radio_device_loader_end(app->radio_device); - // Disable power for External CC1101 if it was enabled and module is connected - furi_hal_subghz_disable_ext_power(); - // Reinit SPI handles for internal radio / nfc - furi_hal_subghz_init_radio_type(SubGhzRadioInternal); + subghz_devices_deinit(); // View related. view_port_enabled_set(app->view_port, false); diff --git a/applications/external/protoview/app.h b/applications/external/protoview/app.h index 624c118e2..5fb0adf34 100644 --- a/applications/external/protoview/app.h +++ b/applications/external/protoview/app.h @@ -19,6 +19,7 @@ #include #include #include "raw_samples.h" +#include "helpers/radio_device_loader.h" #define TAG "ProtoView" #define PROTOVIEW_RAW_VIEW_DEFAULT_SCALE 100 // 100us is 1 pixel by default @@ -132,6 +133,8 @@ struct ProtoViewApp { ProtoViewTxRx* txrx; /* Radio state. */ SubGhzSetting* setting; /* A list of valid frequencies. */ + const SubGhzDevice* radio_device; + /* Generic app state. */ int running; /* Once false exists the app. */ uint32_t signal_bestlen; /* Longest coherent signal observed so far. */ diff --git a/applications/external/protoview/app_subghz.c b/applications/external/protoview/app_subghz.c index dcb6d492b..204dcba0d 100644 --- a/applications/external/protoview/app_subghz.c +++ b/applications/external/protoview/app_subghz.c @@ -37,22 +37,23 @@ ProtoViewModulation ProtoViewModulations[] = { * subghz system and put it into idle state. */ void radio_begin(ProtoViewApp* app) { furi_assert(app); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - - /* Power circuits are noisy. Suppressing the charge while we use - * ProtoView will improve the RF performances. */ - furi_hal_power_suppress_charge_enter(); + subghz_devices_reset(app->radio_device); + subghz_devices_idle(app->radio_device); /* The CC1101 preset can be either one of the standard presets, if * the modulation "custom" field is NULL, or a custom preset we * defined in custom_presets.h. */ if(ProtoViewModulations[app->modulation].custom == NULL) { - furi_hal_subghz_load_preset(ProtoViewModulations[app->modulation].preset); + subghz_devices_load_preset( + app->radio_device, ProtoViewModulations[app->modulation].preset, NULL); } else { - furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom); + subghz_devices_load_preset( + app->radio_device, + FuriHalSubGhzPresetCustom, + ProtoViewModulations[app->modulation].custom); } - furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), GpioModeInput, GpioPullNo, GpioSpeedLow); app->txrx->txrx_state = TxRxStateIDLE; } @@ -71,21 +72,28 @@ void protoview_rx_callback(bool level, uint32_t duration, void* context) { /* Setup the CC1101 to start receiving using a background worker. */ uint32_t radio_rx(ProtoViewApp* app) { furi_assert(app); - if(!furi_hal_subghz_is_frequency_valid(app->frequency)) { + + if(!subghz_devices_is_frequency_valid(app->radio_device, app->frequency)) { furi_crash(TAG " Incorrect RX frequency."); } if(app->txrx->txrx_state == TxRxStateRx) return app->frequency; - furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */ - uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); + subghz_devices_idle(app->radio_device); /* Put it into idle state in case it is sleeping. */ + uint32_t value = subghz_devices_set_frequency(app->radio_device, app->frequency); FURI_LOG_E(TAG, "Switched to frequency: %lu", value); - furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); + + subghz_devices_flush_rx(app->radio_device); + subghz_devices_set_rx(app->radio_device); + if(!app->txrx->debug_timer_sampling) { - furi_hal_subghz_start_async_rx(protoview_rx_callback, NULL); + subghz_devices_start_async_rx(app->radio_device, protoview_rx_callback, NULL); } else { + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), + GpioModeInput, + GpioPullNo, + GpioSpeedLow); raw_sampling_worker_start(app); } app->txrx->txrx_state = TxRxStateRx; @@ -98,12 +106,12 @@ void radio_rx_end(ProtoViewApp* app) { if(app->txrx->txrx_state == TxRxStateRx) { if(!app->txrx->debug_timer_sampling) { - furi_hal_subghz_stop_async_rx(); + subghz_devices_stop_async_rx(app->radio_device); } else { raw_sampling_worker_stop(app); } } - furi_hal_subghz_idle(); + subghz_devices_idle(app->radio_device); app->txrx->txrx_state = TxRxStateIDLE; } @@ -115,9 +123,8 @@ void radio_sleep(ProtoViewApp* app) { * chip into sleep. */ radio_rx_end(app); } - furi_hal_subghz_sleep(); + subghz_devices_sleep(app->radio_device); app->txrx->txrx_state = TxRxStateSleep; - furi_hal_power_suppress_charge_exit(); } /* =============================== Transmission ============================= */ @@ -131,17 +138,14 @@ void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder if(oldstate == TxRxStateRx) radio_rx_end(app); radio_begin(app); - furi_hal_subghz_idle(); - uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); + subghz_devices_idle(app->radio_device); + uint32_t value = subghz_devices_set_frequency(app->radio_device, app->frequency); FURI_LOG_E(TAG, "Switched to frequency: %lu", value); - furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); - furi_hal_gpio_init( - furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_subghz_start_async_tx(data_feeder, ctx); - while(!furi_hal_subghz_is_async_tx_complete()) furi_delay_ms(10); - furi_hal_subghz_stop_async_tx(); - furi_hal_subghz_idle(); + subghz_devices_start_async_tx(app->radio_device, data_feeder, ctx); + while(!subghz_devices_is_async_complete_tx(app->radio_device)) furi_delay_ms(10); + subghz_devices_stop_async_tx(app->radio_device); + subghz_devices_idle(app->radio_device); radio_begin(app); if(oldstate == TxRxStateRx) radio_rx(app); @@ -157,7 +161,7 @@ void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder void protoview_timer_isr(void* ctx) { ProtoViewApp* app = ctx; - bool level = furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin); + bool level = furi_hal_gpio_read(subghz_devices_get_data_gpio(app->radio_device)); if(app->txrx->last_g0_value != level) { uint32_t now = DWT->CYCCNT; uint32_t dur = now - app->txrx->last_g0_change_time; diff --git a/applications/external/protoview/helpers/radio_device_loader.c b/applications/external/protoview/helpers/radio_device_loader.c new file mode 100644 index 000000000..d2cffde58 --- /dev/null +++ b/applications/external/protoview/helpers/radio_device_loader.c @@ -0,0 +1,64 @@ +#include "radio_device_loader.h" + +#include +#include + +static void radio_device_loader_power_on() { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + //CC1101 power-up time + furi_delay_ms(10); + } +} + +static void radio_device_loader_power_off() { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + +bool radio_device_loader_is_connect_external(const char* name) { + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + radio_device_loader_power_on(); + } + + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } + + if(!is_otg_enabled) { + radio_device_loader_power_off(); + } + return is_connect; +} + +const SubGhzDevice* radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type) { + const SubGhzDevice* radio_device; + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + radio_device_loader_power_on(); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(radio_device); + } else if(current_radio_device == NULL) { + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } else { + radio_device_loader_end(current_radio_device); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } + + return radio_device; +} + +void radio_device_loader_end(const SubGhzDevice* radio_device) { + furi_assert(radio_device); + radio_device_loader_power_off(); + if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) { + subghz_devices_end(radio_device); + } +} \ No newline at end of file diff --git a/applications/external/protoview/helpers/radio_device_loader.h b/applications/external/protoview/helpers/radio_device_loader.h new file mode 100644 index 000000000..bee4e2c36 --- /dev/null +++ b/applications/external/protoview/helpers/radio_device_loader.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +/** SubGhzRadioDeviceType */ +typedef enum { + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +} SubGhzRadioDeviceType; + +const SubGhzDevice* radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type); + +void radio_device_loader_end(const SubGhzDevice* radio_device); \ No newline at end of file diff --git a/applications/external/protoview/view_direct_sampling.c b/applications/external/protoview/view_direct_sampling.c index 84486dc51..dc812901e 100644 --- a/applications/external/protoview/view_direct_sampling.c +++ b/applications/external/protoview/view_direct_sampling.c @@ -88,14 +88,18 @@ void view_enter_direct_sampling(ProtoViewApp* app) { privdata->show_usage_info = true; if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { - furi_hal_subghz_stop_async_rx(); + subghz_devices_stop_async_rx(app->radio_device); /* To read data asynchronously directly from the view, we need * to put the CC1101 back into reception mode (the previous call * to stop the async RX will put it into idle) and configure the * G0 pin for reading. */ - furi_hal_subghz_rx(); - furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_set_rx(app->radio_device); + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), + GpioModeInput, + GpioPullNo, + GpioSpeedLow); } else { raw_sampling_worker_stop(app); } @@ -114,8 +118,13 @@ void view_exit_direct_sampling(ProtoViewApp* app) { /* Restart normal data feeding. */ if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { - furi_hal_subghz_start_async_rx(protoview_rx_callback, NULL); + subghz_devices_start_async_rx(app->radio_device, protoview_rx_callback, NULL); } else { + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), + GpioModeInput, + GpioPullNo, + GpioSpeedLow); raw_sampling_worker_start(app); } } @@ -127,7 +136,7 @@ static void ds_timer_isr(void* ctx) { DirectSamplingViewPrivData* privdata = app->view_privdata; if(app->direct_sampling_enabled) { - bool level = furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin); + bool level = furi_hal_gpio_read(subghz_devices_get_data_gpio(app->radio_device)); bitmap_set(privdata->captured, CAPTURED_BITMAP_BYTES, privdata->captured_idx, level); privdata->captured_idx = (privdata->captured_idx + 1) % CAPTURED_BITMAP_BITS; }