[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 <alleteam@gmail.com>
This commit is contained in:
Skorpionm 2022-12-17 02:20:10 +04:00 committed by GitHub
parent 3681a5478c
commit 2dea6969fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 325 additions and 113 deletions

View File

@ -28,6 +28,13 @@ typedef enum {
SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState;
/** SubGhzSpeakerState state */
typedef enum {
SubGhzSpeakerStateDisable,
SubGhzSpeakerStateShutdown,
SubGhzSpeakerStateEnable,
} SubGhzSpeakerState;
/** SubGhzRxKeyState state */
typedef enum {
SubGhzRxKeyStateIDLE,

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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

1 entry status name type params
2 Version + 10.1 11.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1282 Function + furi_hal_rtc_set_pin_fails void uint32_t
1283 Function + furi_hal_rtc_set_register void FuriHalRtcRegister, uint32_t
1284 Function + furi_hal_rtc_validate_datetime _Bool FuriHalRtcDateTime*
1285 Function + furi_hal_speaker_acquire _Bool uint32_t
1286 Function - furi_hal_speaker_deinit void
1287 Function - furi_hal_speaker_init void
1288 Function + furi_hal_speaker_is_mine _Bool
1289 Function + furi_hal_speaker_release void
1290 Function + furi_hal_speaker_set_volume void float
1291 Function + furi_hal_speaker_start void float, float
1292 Function + furi_hal_speaker_stop void
1320 Function + furi_hal_subghz_reset void
1321 Function + furi_hal_subghz_rx void
1322 Function + furi_hal_subghz_rx_pipe_not_empty _Bool
1323 Function + furi_hal_subghz_set_async_mirror_pin void const GpioPin*
1324 Function + furi_hal_subghz_set_frequency uint32_t uint32_t
1325 Function + furi_hal_subghz_set_frequency_and_path uint32_t uint32_t
1326 Function + furi_hal_subghz_set_path void FuriHalSubGhzPath

View File

@ -1,23 +1,66 @@
#include <furi_hal_speaker.h>
#include <furi_hal_gpio.h>
#include <furi_hal_resources.h>
#include <furi_hal_power.h>
#include <stm32wbxx_ll_tim.h>
#include <furi_hal_cortex.h>
#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);
}

View File

@ -4,7 +4,6 @@
#include <furi_hal_region.h>
#include <furi_hal_version.h>
#include <furi_hal_rtc.h>
#include <furi_hal_gpio.h>
#include <furi_hal_spi.h>
#include <furi_hal_interrupt.h>
#include <furi_hal_resources.h>
@ -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();

View File

@ -4,16 +4,63 @@
*/
#pragma once
#include <furi.h>
#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

View File

@ -9,6 +9,7 @@
#include <stdint.h>
#include <stddef.h>
#include <toolbox/level_duration.h>
#include <furi_hal_gpio.h>
#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);

View File

@ -11,6 +11,10 @@ extern "C" {
#include <cmsis_compiler.h>
#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