From 2dea6969fea2dd35b92ae8a3d5ae54232158cc0f Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sat, 17 Dec 2022 02:20:10 +0400 Subject: [PATCH] [FL-3040] Audio support for SubGhz (#2131) * Furi_hal_speaker: multiple resource usage * Furi_hal_speaker: fix multiple resource usage * Furi_hal_speaker: fix music_player_worker * Furi_hal_speaker: fix mutex release queue handling * SubGhz: add furi_hal_subghz_set_debug_pin * SubGhz: add sound SubGhz Read, SubGhz Read RAW * furi_hal_speaker: add __attribute__((warn_unused_result)) for furi_hal_speaker_acquire() * Furi_hal_speaker: fix review comments * SubGhz: cleanup naming and locking timings * SubGhz,FuriHal: fix speaker deinit logic and subghz speaker release sequence * FuriHal: crash on speaker acquire/release from IRQ * Furi, FuriHal: FURI_WARN_UNUSED and documentation update * Bump api symbols version: fix broken speaker Co-authored-by: Aleksandr Kutuzov --- .../main/subghz/helpers/subghz_types.h | 7 + .../subghz/scenes/subghz_scene_read_raw.c | 3 + .../scenes/subghz_scene_receiver_config.c | 29 ++++ applications/main/subghz/subghz.c | 1 + applications/main/subghz/subghz_i.c | 42 ++++++ applications/main/subghz/subghz_i.h | 5 + .../music_player/music_player_worker.c | 76 ++++++----- .../services/notification/notification_app.c | 9 +- firmware/targets/f7/api_symbols.csv | 7 +- .../targets/f7/furi_hal/furi_hal_speaker.c | 51 ++++++- .../targets/f7/furi_hal/furi_hal_subghz.c | 125 +++++++++--------- .../furi_hal_include/furi_hal_speaker.h | 47 +++++++ .../furi_hal_include/furi_hal_subghz.h | 32 +++-- furi/core/common_defines.h | 4 + 14 files changed, 325 insertions(+), 113 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index bd6451f2..abf70539 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -28,6 +28,13 @@ typedef enum { SubGhzHopperStateRSSITimeOut, } SubGhzHopperState; +/** SubGhzSpeakerState state */ +typedef enum { + SubGhzSpeakerStateDisable, + SubGhzSpeakerStateShutdown, + SubGhzSpeakerStateEnable, +} SubGhzSpeakerState; + /** SubGhzRxKeyState state */ typedef enum { SubGhzRxKeyStateIDLE, diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 2e5ba096..b270dd48 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -259,6 +259,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { case SubGhzCustomEventViewReadRAWSendStop: subghz->state_notifications = SubGhzNotificationStateIDLE; if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_speaker_unmute(subghz); subghz_tx_stop(subghz); subghz_sleep(subghz); } @@ -376,10 +377,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); subghz_protocol_raw_save_to_file_pause( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true); + subghz_speaker_mute(subghz); } else { subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); subghz_protocol_raw_save_to_file_pause( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); + subghz_speaker_unmute(subghz); } } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index fd42829b..b49aac92 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -5,6 +5,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, + SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThesholdRSSI, }; @@ -48,6 +49,16 @@ const uint32_t hopping_value[HOPPING_COUNT] = { SubGhzHopperStateRunnig, }; +#define SPEAKER_COUNT 2 +const char* const speaker_text[SPEAKER_COUNT] = { + "OFF", + "ON", +}; +const uint32_t speaker_value[SPEAKER_COUNT] = { + SubGhzSpeakerStateShutdown, + SubGhzSpeakerStateEnable, +}; + uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { furi_assert(context); SubGhz* subghz = context; @@ -167,6 +178,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) subghz->txrx->hopper_state = hopping_value[index]; } +static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, speaker_text[index]); + subghz->txrx->speaker_state = speaker_value[index]; +} + static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -235,6 +254,16 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_text( item, subghz_setting_get_preset_name(subghz->setting, value_index)); + item = variable_item_list_add( + subghz->variable_item_list, + "Sound:", + SPEAKER_COUNT, + subghz_scene_receiver_config_set_speaker, + subghz); + value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerSet) { variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index df5a7652..b7564ab5 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -177,6 +177,7 @@ SubGhz* subghz_alloc() { subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->hopper_state = SubGhzHopperStateOFF; + subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; subghz->txrx->history = subghz_history_alloc(); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 736bcf36..0bcd7006 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -86,6 +86,7 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); + subghz_speaker_on(subghz); furi_hal_subghz_rx(); furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker); @@ -104,6 +105,7 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_write(&gpio_cc1101_g0, false); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + subghz_speaker_on(subghz); bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; return ret; @@ -119,11 +121,13 @@ void subghz_idle(SubGhz* subghz) { void subghz_rx_end(SubGhz* subghz) { furi_assert(subghz); furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx); + if(subghz_worker_is_running(subghz->txrx->worker)) { subghz_worker_stop(subghz->txrx->worker); furi_hal_subghz_stop_async_rx(); } furi_hal_subghz_idle(); + subghz_speaker_off(subghz); subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } @@ -212,6 +216,7 @@ void subghz_tx_stop(SubGhz* subghz) { subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path)); } subghz_idle(subghz); + subghz_speaker_off(subghz); notification_message(subghz->notifications, &sequence_reset_red); } @@ -585,3 +590,40 @@ void subghz_hopper_update(SubGhz* subghz) { subghz_rx(subghz, subghz->txrx->preset->frequency); } } + +void subghz_speaker_on(SubGhz* subghz) { + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_acquire(30)) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } else { + subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +void subghz_speaker_off(SubGhz* subghz) { + if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { + if(furi_hal_speaker_is_mine()) { + furi_hal_subghz_set_async_mirror_pin(NULL); + furi_hal_speaker_release(); + if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown) + subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +void subghz_speaker_mute(SubGhz* subghz) { + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } + } +} + +void subghz_speaker_unmute(SubGhz* subghz) { + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } + } +} diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 23436515..cd33da44 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -53,6 +53,7 @@ struct SubGhzTxRx { uint16_t idx_menu_chosen; SubGhzTxRxState txrx_state; SubGhzHopperState hopper_state; + SubGhzSpeakerState speaker_state; uint8_t hopper_timeout; uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; @@ -131,3 +132,7 @@ void subghz_file_name_clear(SubGhz* subghz); bool subghz_path_is_file(FuriString* path); uint32_t subghz_random_serial(void); void subghz_hopper_update(SubGhz* subghz); +void subghz_speaker_on(SubGhz* subghz); +void subghz_speaker_off(SubGhz* subghz); +void subghz_speaker_mute(SubGhz* subghz); +void subghz_speaker_unmute(SubGhz* subghz); diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/plugins/music_player/music_player_worker.c index 0d683f4a..60fd33a1 100644 --- a/applications/plugins/music_player/music_player_worker.c +++ b/applications/plugins/music_player/music_player_worker.c @@ -47,47 +47,51 @@ static int32_t music_player_worker_thread_callback(void* context) { NoteBlockArray_it_t it; NoteBlockArray_it(it, instance->notes); + if(furi_hal_speaker_acquire(1000)) { + while(instance->should_work) { + if(NoteBlockArray_end_p(it)) { + NoteBlockArray_it(it, instance->notes); + furi_delay_ms(10); + } else { + NoteBlock* note_block = NoteBlockArray_ref(it); - while(instance->should_work) { - if(NoteBlockArray_end_p(it)) { - NoteBlockArray_it(it, instance->notes); - furi_delay_ms(10); - } else { - NoteBlock* note_block = NoteBlockArray_ref(it); + float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; + float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); + float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / + note_block->duration; + uint32_t dots = note_block->dots; + while(dots > 0) { + duration += duration / 2; + dots--; + } + uint32_t next_tick = furi_get_tick() + duration; + float volume = instance->volume; - float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; - float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); - float duration = - 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / note_block->duration; - uint32_t dots = note_block->dots; - while(dots > 0) { - duration += duration / 2; - dots--; + if(instance->callback) { + instance->callback( + note_block->semitone, + note_block->dots, + note_block->duration, + 0.0, + instance->callback_context); + } + + furi_hal_speaker_stop(); + furi_hal_speaker_start(frequency, volume); + while(instance->should_work && furi_get_tick() < next_tick) { + volume *= 0.9945679; + furi_hal_speaker_set_volume(volume); + furi_delay_ms(2); + } + NoteBlockArray_next(it); } - uint32_t next_tick = furi_get_tick() + duration; - float volume = instance->volume; - - if(instance->callback) { - instance->callback( - note_block->semitone, - note_block->dots, - note_block->duration, - 0.0, - instance->callback_context); - } - - furi_hal_speaker_stop(); - furi_hal_speaker_start(frequency, volume); - while(instance->should_work && furi_get_tick() < next_tick) { - volume *= 0.9945679; - furi_hal_speaker_set_volume(volume); - furi_delay_ms(2); - } - NoteBlockArray_next(it); } - } - furi_hal_speaker_stop(); + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } else { + FURI_LOG_E(TAG, "Speaker system is busy with another process."); + } return 0; } diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 6091f0aa..4f6d42aa 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -150,11 +150,16 @@ void notification_vibro_off() { } void notification_sound_on(float freq, float volume) { - furi_hal_speaker_start(freq, volume); + if(furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(freq, volume); + } } void notification_sound_off() { - furi_hal_speaker_stop(); + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } } // display timer diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 80aaf7fa..b3afb842 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,10.1,, +Version,+,11.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1282,7 +1282,11 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,+,furi_hal_speaker_acquire,_Bool,uint32_t +Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, +Function,+,furi_hal_speaker_is_mine,_Bool, +Function,+,furi_hal_speaker_release,void, Function,+,furi_hal_speaker_set_volume,void,float Function,+,furi_hal_speaker_start,void,"float, float" Function,+,furi_hal_speaker_stop,void, @@ -1316,6 +1320,7 @@ Function,+,furi_hal_subghz_read_packet,void,"uint8_t*, uint8_t*" Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, Function,+,furi_hal_subghz_rx_pipe_not_empty,_Bool, +Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath diff --git a/firmware/targets/f7/furi_hal/furi_hal_speaker.c b/firmware/targets/f7/furi_hal/furi_hal_speaker.c index 03a7f094..c4a0bdd1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_speaker.c +++ b/firmware/targets/f7/furi_hal/furi_hal_speaker.c @@ -1,23 +1,66 @@ #include #include #include +#include #include +#include + +#define TAG "FuriHalSpeaker" #define FURI_HAL_SPEAKER_TIMER TIM16 #define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 #define FURI_HAL_SPEAKER_PRESCALER 500 #define FURI_HAL_SPEAKER_MAX_VOLUME 60 +static FuriMutex* furi_hal_speaker_mutex = NULL; + // #define FURI_HAL_SPEAKER_NEW_VOLUME void furi_hal_speaker_init() { + furi_assert(furi_hal_speaker_mutex == NULL); + furi_hal_speaker_mutex = furi_mutex_alloc(FuriMutexTypeNormal); FURI_CRITICAL_ENTER(); LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER); FURI_CRITICAL_EXIT(); + FURI_LOG_I(TAG, "Init OK"); +} - furi_hal_gpio_init_ex( - &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); +void furi_hal_speaker_deinit() { + furi_check(furi_hal_speaker_mutex != NULL); + LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER); + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_mutex_free(furi_hal_speaker_mutex); + furi_hal_speaker_mutex = NULL; +} + +bool furi_hal_speaker_acquire(uint32_t timeout) { + furi_check(!FURI_IS_IRQ_MODE()); + + if(furi_mutex_acquire(furi_hal_speaker_mutex, timeout) == FuriStatusOk) { + furi_hal_power_insomnia_enter(); + furi_hal_gpio_init_ex( + &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + return true; + } else { + return false; + } +} + +void furi_hal_speaker_release() { + furi_check(!FURI_IS_IRQ_MODE()); + furi_check(furi_hal_speaker_is_mine()); + + furi_hal_speaker_stop(); + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_power_insomnia_exit(); + + furi_check(furi_mutex_release(furi_hal_speaker_mutex) == FuriStatusOk); +} + +bool furi_hal_speaker_is_mine() { + return (FURI_IS_IRQ_MODE()) || + (furi_mutex_get_owner(furi_hal_speaker_mutex) == furi_thread_get_current_id()); } static inline uint32_t furi_hal_speaker_calculate_autoreload(float frequency) { @@ -54,6 +97,8 @@ static inline uint32_t furi_hal_speaker_calculate_compare(float volume) { } void furi_hal_speaker_start(float frequency, float volume) { + furi_check(furi_hal_speaker_is_mine()); + if(volume <= 0) { furi_hal_speaker_stop(); return; @@ -75,6 +120,7 @@ void furi_hal_speaker_start(float frequency, float volume) { } void furi_hal_speaker_set_volume(float volume) { + furi_check(furi_hal_speaker_is_mine()); if(volume <= 0) { furi_hal_speaker_stop(); return; @@ -88,6 +134,7 @@ void furi_hal_speaker_set_volume(float volume) { } void furi_hal_speaker_stop() { + furi_check(furi_hal_speaker_is_mine()); LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 726b2d7f..3441fd96 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -17,39 +16,26 @@ #define TAG "FuriHalSubGhz" -/* - * Uncomment define to enable duplication of - * IO GO0 CC1101 to an external comb. - * Debug pin can be assigned - * gpio_ext_pc0 - * gpio_ext_pc1 - * gpio_ext_pc3 - * gpio_ext_pb2 - * gpio_ext_pb3 - * gpio_ext_pa4 - * gpio_ext_pa6 - * gpio_ext_pa7 - * Attention this setting switches pin to output. - * Make sure it is not connected directly to power or ground - */ - -//#define SUBGHZ_DEBUG_CC1101_PIN gpio_ext_pa7 -#ifdef SUBGHZ_DEBUG_CC1101_PIN -uint32_t subghz_debug_gpio_buff[2]; -#endif +static uint32_t furi_hal_subghz_debug_gpio_buff[2]; typedef struct { volatile SubGhzState state; volatile SubGhzRegulation regulation; volatile FuriHalSubGhzPreset preset; + const GpioPin* async_mirror_pin; } FuriHalSubGhz; volatile FuriHalSubGhz furi_hal_subghz = { .state = SubGhzStateInit, .regulation = SubGhzRegulationTxRx, .preset = FuriHalSubGhzPresetIDLE, + .async_mirror_pin = NULL, }; +void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { + furi_hal_subghz.async_mirror_pin = pin; +} + void furi_hal_subghz_init() { furi_assert(furi_hal_subghz.state == SubGhzStateInit); furi_hal_subghz.state = SubGhzStateIdle; @@ -372,6 +358,29 @@ void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } +static bool furi_hal_subghz_start_debug() { + bool ret = false; + if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_init( + furi_hal_subghz.async_mirror_pin, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh); + ret = true; + } + return ret; +} + +static bool furi_hal_subghz_stop_debug() { + bool ret = false; + if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_init( + furi_hal_subghz.async_mirror_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + ret = true; + } + return ret; +} + volatile uint32_t furi_hal_subghz_capture_delta_duration = 0; volatile FuriHalSubGhzCaptureCallback furi_hal_subghz_capture_callback = NULL; volatile void* furi_hal_subghz_capture_callback_context = NULL; @@ -382,9 +391,9 @@ static void furi_hal_subghz_capture_ISR() { LL_TIM_ClearFlag_CC1(TIM2); furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); if(furi_hal_subghz_capture_callback) { -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_write(&SUBGHZ_DEBUG_CC1101_PIN, false); -#endif + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); + furi_hal_subghz_capture_callback( true, furi_hal_subghz_capture_delta_duration, @@ -395,9 +404,9 @@ static void furi_hal_subghz_capture_ISR() { if(LL_TIM_IsActiveFlag_CC2(TIM2)) { LL_TIM_ClearFlag_CC2(TIM2); if(furi_hal_subghz_capture_callback) { -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_write(&SUBGHZ_DEBUG_CC1101_PIN, true); -#endif + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + furi_hal_subghz_capture_callback( false, LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, @@ -459,10 +468,8 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_init( - &SUBGHZ_DEBUG_CC1101_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -#endif + // Start debug + furi_hal_subghz_start_debug(); // Switch to RX furi_hal_subghz_rx(); @@ -478,9 +485,8 @@ void furi_hal_subghz_stop_async_rx() { FURI_CRITICAL_ENTER(); LL_TIM_DeInit(TIM2); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_init(&SUBGHZ_DEBUG_CC1101_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -#endif + // Stop debug + furi_hal_subghz_stop_debug(); FURI_CRITICAL_EXIT(); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); @@ -673,30 +679,27 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_init( - &SUBGHZ_DEBUG_CC1101_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + // Start debug + if(furi_hal_subghz_start_debug()) { + const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; + furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; - const GpioPin* gpio = &SUBGHZ_DEBUG_CC1101_PIN; - subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; - subghz_debug_gpio_buff[1] = gpio->pin; - - dma_config.MemoryOrM2MDstAddress = (uint32_t)subghz_debug_gpio_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - -#endif + dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_debug_gpio_buff; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = 2; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + } return true; } @@ -730,10 +733,10 @@ void furi_hal_subghz_stop_async_tx() { // Deinitialize GPIO furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - furi_hal_gpio_init(&SUBGHZ_DEBUG_CC1101_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -#endif + // Stop debug + if(furi_hal_subghz_stop_debug()) { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + } FURI_CRITICAL_EXIT(); diff --git a/firmware/targets/furi_hal_include/furi_hal_speaker.h b/firmware/targets/furi_hal_include/furi_hal_speaker.h index 67de41d9..0b33d923 100644 --- a/firmware/targets/furi_hal_include/furi_hal_speaker.h +++ b/firmware/targets/furi_hal_include/furi_hal_speaker.h @@ -4,16 +4,63 @@ */ #pragma once +#include + #ifdef __cplusplus extern "C" { #endif +/** Init speaker */ void furi_hal_speaker_init(); +/** Deinit speaker */ +void furi_hal_speaker_deinit(); + +/** Acquire speaker ownership + * + * @warning You must acquire speaker ownership before use + * + * @param timeout Timeout during which speaker ownership must be acquired + * + * @return bool returns true on success + */ +FURI_WARN_UNUSED bool furi_hal_speaker_acquire(uint32_t timeout); + +/** Release speaker ownership + * + * @warning You must release speaker ownership after use + */ +void furi_hal_speaker_release(); + +/** Check current process speaker ownership + * + * @warning always returns true if called from ISR + * + * @return bool returns true if process owns speaker + */ +bool furi_hal_speaker_is_mine(); + +/** Play a note + * + * @warning no ownership check if called from ISR + * + * @param frequency The frequency + * @param volume The volume + */ void furi_hal_speaker_start(float frequency, float volume); +/** Set volume + * + * @warning no ownership check if called from ISR + * + * @param volume The volume + */ void furi_hal_speaker_set_volume(float volume); +/** Stop playback + * + * @warning no ownership check if called from ISR + */ void furi_hal_speaker_stop(); #ifdef __cplusplus diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h index 1f99386c..102981db 100644 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -34,9 +35,9 @@ typedef enum { /** Switchable Radio Paths */ typedef enum { FuriHalSubGhzPathIsolate, /**< Isolate Radio from antenna */ - FuriHalSubGhzPath433, /**< Center Frquency: 433MHz. Path 1: SW1RF1-SW2RF2, LCLCL */ - FuriHalSubGhzPath315, /**< Center Frquency: 315MHz. Path 2: SW1RF2-SW2RF1, LCLCLCL */ - FuriHalSubGhzPath868, /**< Center Frquency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ + FuriHalSubGhzPath433, /**< Center Frequency: 433MHz. Path 1: SW1RF1-SW2RF2, LCLCL */ + FuriHalSubGhzPath315, /**< Center Frequency: 315MHz. Path 2: SW1RF2-SW2RF1, LCLCLCL */ + FuriHalSubGhzPath868, /**< Center Frequency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ } FuriHalSubGhzPath; /** SubGhz state */ @@ -60,8 +61,17 @@ typedef enum { SubGhzRegulationTxRx, /**TxRx*/ } SubGhzRegulation; +/* Mirror RX/TX async modulation signal to specified pin + * + * @warning Configures pin to output mode. Make sure it is not connected + * directly to power or ground. + * + * @param[in] pin pointer to the gpio pin structure or NULL to disable + */ +void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin); + /** Initialize and switch to power save mode Used by internal API-HAL - * initalization routine Can be used to reinitialize device to safe state and + * initialization routine Can be used to reinitialize device to safe state and * send it to sleep */ void furi_hal_subghz_init(); @@ -105,13 +115,13 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]); */ void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size); -/** Check if recieve pipe is not empty +/** Check if receive pipe is not empty * * @return true if not empty */ bool furi_hal_subghz_rx_pipe_not_empty(); -/** Check if recieved data crc is valid +/** Check if received data crc is valid * * @return true if valid */ @@ -132,7 +142,7 @@ void furi_hal_subghz_flush_rx(); */ void furi_hal_subghz_flush_tx(); -/** Shutdown Issue spwd command +/** Shutdown Issue SPWD command * @warning registers content will be lost */ void furi_hal_subghz_shutdown(); @@ -146,7 +156,7 @@ void furi_hal_subghz_reset(); */ void furi_hal_subghz_idle(); -/** Switch to Recieve +/** Switch to Receive */ void furi_hal_subghz_rx(); @@ -172,7 +182,7 @@ uint8_t furi_hal_subghz_get_lqi(); * * @param value frequency in Hz * - * @return true if frequncy is valid, otherwise false + * @return true if frequency is valid, otherwise false */ bool furi_hal_subghz_is_frequency_valid(uint32_t value); @@ -181,7 +191,7 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); * * @param value frequency in Hz * - * @return real frequency in herz + * @return real frequency in Hz */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); @@ -189,7 +199,7 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); * * @param value frequency in Hz * - * @return real frequency in herz + * @return real frequency in Hz */ uint32_t furi_hal_subghz_set_frequency(uint32_t value); diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index 31be7fff..c7acf95b 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -11,6 +11,10 @@ extern "C" { #include +#ifndef FURI_WARN_UNUSED +#define FURI_WARN_UNUSED __attribute__((warn_unused_result)) +#endif + #ifndef FURI_IS_IRQ_MASKED #define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) #endif