[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:
parent
3681a5478c
commit
2dea6969fe
@ -28,6 +28,13 @@ typedef enum {
|
||||
SubGhzHopperStateRSSITimeOut,
|
||||
} SubGhzHopperState;
|
||||
|
||||
/** SubGhzSpeakerState state */
|
||||
typedef enum {
|
||||
SubGhzSpeakerStateDisable,
|
||||
SubGhzSpeakerStateShutdown,
|
||||
SubGhzSpeakerStateEnable,
|
||||
} SubGhzSpeakerState;
|
||||
|
||||
/** SubGhzRxKeyState state */
|
||||
typedef enum {
|
||||
SubGhzRxKeyStateIDLE,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user