mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-26 14:30:25 +00:00
[FL-3762] Configurable Infrared TX output (#3484)
* Implement Gui for the feature * Implement furi_hal side * Implement IR module detection * Fix PVS warnings * Add comments Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
6de2934394
commit
adafe96924
10 changed files with 326 additions and 43 deletions
|
@ -1,7 +1,10 @@
|
||||||
#include "infrared_app_i.h"
|
#include "infrared_app_i.h"
|
||||||
|
|
||||||
|
#include <furi_hal_power.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <toolbox/path.h>
|
#include <toolbox/path.h>
|
||||||
|
#include <toolbox/saved_struct.h>
|
||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
#define TAG "InfraredApp"
|
#define TAG "InfraredApp"
|
||||||
|
@ -9,6 +12,14 @@
|
||||||
#define INFRARED_TX_MIN_INTERVAL_MS (50U)
|
#define INFRARED_TX_MIN_INTERVAL_MS (50U)
|
||||||
#define INFRARED_TASK_STACK_SIZE (2048UL)
|
#define INFRARED_TASK_STACK_SIZE (2048UL)
|
||||||
|
|
||||||
|
#define INFRARED_SETTINGS_PATH INT_PATH(".infrared.settings")
|
||||||
|
#define INFRARED_SETTINGS_VERSION (0)
|
||||||
|
#define INFRARED_SETTINGS_MAGIC (0x1F)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t tx_pin;
|
||||||
|
} InfraredSettings;
|
||||||
|
|
||||||
static const NotificationSequence*
|
static const NotificationSequence*
|
||||||
infrared_notification_sequences[InfraredNotificationMessageCount] = {
|
infrared_notification_sequences[InfraredNotificationMessageCount] = {
|
||||||
&sequence_success,
|
&sequence_success,
|
||||||
|
@ -181,6 +192,12 @@ static InfraredApp* infrared_alloc(void) {
|
||||||
infrared->popup = popup_alloc();
|
infrared->popup = popup_alloc();
|
||||||
view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup));
|
view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup));
|
||||||
|
|
||||||
|
infrared->var_item_list = variable_item_list_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
view_dispatcher,
|
||||||
|
InfraredViewVariableList,
|
||||||
|
variable_item_list_get_view(infrared->var_item_list));
|
||||||
|
|
||||||
infrared->view_stack = view_stack_alloc();
|
infrared->view_stack = view_stack_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack));
|
view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack));
|
||||||
|
@ -237,6 +254,9 @@ static void infrared_free(InfraredApp* infrared) {
|
||||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup);
|
view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup);
|
||||||
popup_free(infrared->popup);
|
popup_free(infrared->popup);
|
||||||
|
|
||||||
|
view_dispatcher_remove_view(view_dispatcher, InfraredViewVariableList);
|
||||||
|
variable_item_list_free(infrared->var_item_list);
|
||||||
|
|
||||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewStack);
|
view_dispatcher_remove_view(view_dispatcher, InfraredViewStack);
|
||||||
view_stack_free(infrared->view_stack);
|
view_stack_free(infrared->view_stack);
|
||||||
|
|
||||||
|
@ -431,6 +451,60 @@ void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, .
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin) {
|
||||||
|
if(tx_pin < FuriHalInfraredTxPinMax) {
|
||||||
|
furi_hal_infrared_set_tx_output(tx_pin);
|
||||||
|
} else {
|
||||||
|
FuriHalInfraredTxPin tx_pin_detected = furi_hal_infrared_detect_tx_output();
|
||||||
|
furi_hal_infrared_set_tx_output(tx_pin_detected);
|
||||||
|
if(tx_pin_detected != FuriHalInfraredTxPinInternal) {
|
||||||
|
infrared_enable_otg(infrared, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infrared->app_state.tx_pin = tx_pin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void infrared_enable_otg(InfraredApp* infrared, bool enable) {
|
||||||
|
if(enable) {
|
||||||
|
furi_hal_power_enable_otg();
|
||||||
|
} else {
|
||||||
|
furi_hal_power_disable_otg();
|
||||||
|
}
|
||||||
|
infrared->app_state.is_otg_enabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void infrared_load_settings(InfraredApp* infrared) {
|
||||||
|
InfraredSettings settings = {0};
|
||||||
|
|
||||||
|
if(!saved_struct_load(
|
||||||
|
INFRARED_SETTINGS_PATH,
|
||||||
|
&settings,
|
||||||
|
sizeof(InfraredSettings),
|
||||||
|
INFRARED_SETTINGS_MAGIC,
|
||||||
|
INFRARED_SETTINGS_VERSION)) {
|
||||||
|
FURI_LOG_D(TAG, "Failed to load settings, using defaults");
|
||||||
|
infrared_save_settings(infrared);
|
||||||
|
}
|
||||||
|
|
||||||
|
infrared_set_tx_pin(infrared, settings.tx_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void infrared_save_settings(InfraredApp* infrared) {
|
||||||
|
InfraredSettings settings = {
|
||||||
|
.tx_pin = infrared->app_state.tx_pin,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!saved_struct_save(
|
||||||
|
INFRARED_SETTINGS_PATH,
|
||||||
|
&settings,
|
||||||
|
sizeof(InfraredSettings),
|
||||||
|
INFRARED_SETTINGS_MAGIC,
|
||||||
|
INFRARED_SETTINGS_VERSION)) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to save settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
InfraredApp* infrared = context;
|
InfraredApp* infrared = context;
|
||||||
|
@ -471,6 +545,7 @@ void infrared_popup_closed_callback(void* context) {
|
||||||
int32_t infrared_app(void* p) {
|
int32_t infrared_app(void* p) {
|
||||||
InfraredApp* infrared = infrared_alloc();
|
InfraredApp* infrared = infrared_alloc();
|
||||||
|
|
||||||
|
infrared_load_settings(infrared);
|
||||||
infrared_make_app_folder(infrared);
|
infrared_make_app_folder(infrared);
|
||||||
|
|
||||||
bool is_remote_loaded = false;
|
bool is_remote_loaded = false;
|
||||||
|
@ -513,6 +588,9 @@ int32_t infrared_app(void* p) {
|
||||||
|
|
||||||
view_dispatcher_run(infrared->view_dispatcher);
|
view_dispatcher_run(infrared->view_dispatcher);
|
||||||
|
|
||||||
|
infrared_set_tx_pin(infrared, FuriHalInfraredTxPinInternal);
|
||||||
|
infrared_enable_otg(infrared, false);
|
||||||
infrared_free(infrared);
|
infrared_free(infrared);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi_hal_infrared.h>
|
||||||
|
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/view.h>
|
#include <gui/view.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
|
@ -18,13 +20,13 @@
|
||||||
#include <gui/modules/text_input.h>
|
#include <gui/modules/text_input.h>
|
||||||
#include <gui/modules/button_menu.h>
|
#include <gui/modules/button_menu.h>
|
||||||
#include <gui/modules/button_panel.h>
|
#include <gui/modules/button_panel.h>
|
||||||
|
#include <gui/modules/variable_item_list.h>
|
||||||
|
|
||||||
#include <rpc/rpc_app.h>
|
#include <rpc/rpc_app.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
|
|
||||||
#include <notification/notification_messages.h>
|
#include <notification/notification_messages.h>
|
||||||
|
|
||||||
#include <infrared/worker/infrared_worker.h>
|
#include <infrared/worker/infrared_worker.h>
|
||||||
|
|
||||||
#include "infrared_app.h"
|
#include "infrared_app.h"
|
||||||
|
@ -82,11 +84,13 @@ typedef struct {
|
||||||
bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */
|
bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */
|
||||||
bool is_debug_enabled; /**< Whether to enable or disable debugging features. */
|
bool is_debug_enabled; /**< Whether to enable or disable debugging features. */
|
||||||
bool is_transmitting; /**< Whether a signal is currently being transmitted. */
|
bool is_transmitting; /**< Whether a signal is currently being transmitted. */
|
||||||
|
bool is_otg_enabled; /**< Whether OTG power (external 5V) is enabled. */
|
||||||
InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */
|
InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */
|
||||||
InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */
|
InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */
|
||||||
int32_t current_button_index; /**< Selected button index (move destination). */
|
int32_t current_button_index; /**< Selected button index (move destination). */
|
||||||
int32_t prev_button_index; /**< Previous button index (move source). */
|
int32_t prev_button_index; /**< Previous button index (move source). */
|
||||||
uint32_t last_transmit_time; /**< Lat time a signal was transmitted. */
|
uint32_t last_transmit_time; /**< Lat time a signal was transmitted. */
|
||||||
|
FuriHalInfraredTxPin tx_pin;
|
||||||
} InfraredAppState;
|
} InfraredAppState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,6 +114,7 @@ struct InfraredApp {
|
||||||
DialogEx* dialog_ex; /**< Standard view for displaying dialogs. */
|
DialogEx* dialog_ex; /**< Standard view for displaying dialogs. */
|
||||||
ButtonMenu* button_menu; /**< Custom view for interacting with IR remotes. */
|
ButtonMenu* button_menu; /**< Custom view for interacting with IR remotes. */
|
||||||
Popup* popup; /**< Standard view for displaying messages. */
|
Popup* popup; /**< Standard view for displaying messages. */
|
||||||
|
VariableItemList* var_item_list; /**< Standard view for displaying menus of choice items. */
|
||||||
|
|
||||||
ViewStack* view_stack; /**< Standard view for displaying stacked interfaces. */
|
ViewStack* view_stack; /**< Standard view for displaying stacked interfaces. */
|
||||||
InfraredDebugView* debug_view; /**< Custom view for displaying debug information. */
|
InfraredDebugView* debug_view; /**< Custom view for displaying debug information. */
|
||||||
|
@ -138,6 +143,7 @@ typedef enum {
|
||||||
InfraredViewDialogEx,
|
InfraredViewDialogEx,
|
||||||
InfraredViewButtonMenu,
|
InfraredViewButtonMenu,
|
||||||
InfraredViewPopup,
|
InfraredViewPopup,
|
||||||
|
InfraredViewVariableList,
|
||||||
InfraredViewStack,
|
InfraredViewStack,
|
||||||
InfraredViewDebugView,
|
InfraredViewDebugView,
|
||||||
InfraredViewMove,
|
InfraredViewMove,
|
||||||
|
@ -273,6 +279,32 @@ void infrared_play_notification_message(
|
||||||
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...)
|
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...)
|
||||||
_ATTRIBUTE((__format__(__printf__, 2, 3)));
|
_ATTRIBUTE((__format__(__printf__, 2, 3)));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set which pin will be used to transmit infrared signals.
|
||||||
|
*
|
||||||
|
* Setting tx_pin to InfraredTxPinInternal will enable transmission via
|
||||||
|
* the built-in infrared LEDs.
|
||||||
|
*
|
||||||
|
* @param[in] infrared pointer to the application instance.
|
||||||
|
* @param[in] tx_pin pin to be used for signal transmission.
|
||||||
|
*/
|
||||||
|
void infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable or disable 5V at the GPIO pin 1.
|
||||||
|
*
|
||||||
|
* @param[in] infrared pointer to the application instance.
|
||||||
|
* @param[in] enable boolean value corresponding to OTG state (true = enable, false = disable)
|
||||||
|
*/
|
||||||
|
void infrared_enable_otg(InfraredApp* infrared, bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save current settings to a file.
|
||||||
|
*
|
||||||
|
* @param[in] infrared pointer to the application instance.
|
||||||
|
*/
|
||||||
|
void infrared_save_settings(InfraredApp* infrared);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Common received signal callback.
|
* @brief Common received signal callback.
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,6 +22,9 @@ enum InfraredCustomEventType {
|
||||||
InfraredCustomEventTypeRpcButtonPressIndex,
|
InfraredCustomEventTypeRpcButtonPressIndex,
|
||||||
InfraredCustomEventTypeRpcButtonRelease,
|
InfraredCustomEventTypeRpcButtonRelease,
|
||||||
InfraredCustomEventTypeRpcSessionClose,
|
InfraredCustomEventTypeRpcSessionClose,
|
||||||
|
|
||||||
|
InfraredCustomEventTypeGpioTxPinChanged,
|
||||||
|
InfraredCustomEventTypeGpioOtgChanged,
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|
|
@ -19,6 +19,7 @@ ADD_SCENE(infrared, universal_tv, UniversalTV)
|
||||||
ADD_SCENE(infrared, universal_ac, UniversalAC)
|
ADD_SCENE(infrared, universal_ac, UniversalAC)
|
||||||
ADD_SCENE(infrared, universal_audio, UniversalAudio)
|
ADD_SCENE(infrared, universal_audio, UniversalAudio)
|
||||||
ADD_SCENE(infrared, universal_projector, UniversalProjector)
|
ADD_SCENE(infrared, universal_projector, UniversalProjector)
|
||||||
|
ADD_SCENE(infrared, gpio_settings, GpioSettings)
|
||||||
ADD_SCENE(infrared, debug, Debug)
|
ADD_SCENE(infrared, debug, Debug)
|
||||||
ADD_SCENE(infrared, error_databases, ErrorDatabases)
|
ADD_SCENE(infrared, error_databases, ErrorDatabases)
|
||||||
ADD_SCENE(infrared, rpc, Rpc)
|
ADD_SCENE(infrared, rpc, Rpc)
|
||||||
|
|
102
applications/main/infrared/scenes/infrared_scene_gpio_settings.c
Normal file
102
applications/main/infrared/scenes/infrared_scene_gpio_settings.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#include "../infrared_app_i.h"
|
||||||
|
|
||||||
|
static const char* infrared_scene_gpio_settings_pin_text[] = {
|
||||||
|
"Flipper",
|
||||||
|
"2 (A7)",
|
||||||
|
"Detect",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* infrared_scene_gpio_settings_otg_text[] = {
|
||||||
|
"OFF",
|
||||||
|
"ON",
|
||||||
|
};
|
||||||
|
|
||||||
|
static void infrared_scene_gpio_settings_pin_change_callback(VariableItem* item) {
|
||||||
|
InfraredApp* infrared = variable_item_get_context(item);
|
||||||
|
const uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
variable_item_set_current_value_text(item, infrared_scene_gpio_settings_pin_text[index]);
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
infrared->view_dispatcher,
|
||||||
|
infrared_custom_event_pack(InfraredCustomEventTypeGpioTxPinChanged, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void infrared_scene_gpio_settings_otg_change_callback(VariableItem* item) {
|
||||||
|
InfraredApp* infrared = variable_item_get_context(item);
|
||||||
|
const uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
variable_item_set_current_value_text(item, infrared_scene_gpio_settings_otg_text[index]);
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
infrared->view_dispatcher,
|
||||||
|
infrared_custom_event_pack(InfraredCustomEventTypeGpioOtgChanged, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void infrared_scene_gpio_settings_init(InfraredApp* infrared) {
|
||||||
|
VariableItemList* var_item_list = infrared->var_item_list;
|
||||||
|
VariableItem* item;
|
||||||
|
uint8_t value_index;
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
var_item_list,
|
||||||
|
"Signal Output",
|
||||||
|
COUNT_OF(infrared_scene_gpio_settings_pin_text),
|
||||||
|
infrared_scene_gpio_settings_pin_change_callback,
|
||||||
|
infrared);
|
||||||
|
|
||||||
|
value_index = infrared->app_state.tx_pin;
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
variable_item_set_current_value_text(item, infrared_scene_gpio_settings_pin_text[value_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
var_item_list,
|
||||||
|
"5V on GPIO",
|
||||||
|
COUNT_OF(infrared_scene_gpio_settings_otg_text),
|
||||||
|
infrared_scene_gpio_settings_otg_change_callback,
|
||||||
|
infrared);
|
||||||
|
|
||||||
|
if(infrared->app_state.tx_pin < FuriHalInfraredTxPinMax) {
|
||||||
|
value_index = infrared->app_state.is_otg_enabled;
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, infrared_scene_gpio_settings_otg_text[value_index]);
|
||||||
|
} else {
|
||||||
|
variable_item_set_values_count(item, 1);
|
||||||
|
variable_item_set_current_value_index(item, 0);
|
||||||
|
variable_item_set_current_value_text(item, "Auto");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void infrared_scene_gpio_settings_on_enter(void* context) {
|
||||||
|
InfraredApp* infrared = context;
|
||||||
|
infrared_scene_gpio_settings_init(infrared);
|
||||||
|
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableList);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool infrared_scene_gpio_settings_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
InfraredApp* infrared = context;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
const uint16_t custom_event_type = infrared_custom_event_get_type(event.event);
|
||||||
|
const uint16_t custom_event_value = infrared_custom_event_get_value(event.event);
|
||||||
|
|
||||||
|
if(custom_event_type == InfraredCustomEventTypeGpioTxPinChanged) {
|
||||||
|
infrared_set_tx_pin(infrared, custom_event_value);
|
||||||
|
variable_item_list_reset(infrared->var_item_list);
|
||||||
|
infrared_scene_gpio_settings_init(infrared);
|
||||||
|
} else if(custom_event_type == InfraredCustomEventTypeGpioOtgChanged) {
|
||||||
|
infrared_enable_otg(infrared, custom_event_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void infrared_scene_gpio_settings_on_exit(void* context) {
|
||||||
|
InfraredApp* infrared = context;
|
||||||
|
variable_item_list_reset(infrared->var_item_list);
|
||||||
|
infrared_save_settings(infrared);
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ enum SubmenuIndex {
|
||||||
SubmenuIndexUniversalRemotes,
|
SubmenuIndexUniversalRemotes,
|
||||||
SubmenuIndexLearnNewRemote,
|
SubmenuIndexLearnNewRemote,
|
||||||
SubmenuIndexSavedRemotes,
|
SubmenuIndexSavedRemotes,
|
||||||
|
SubmenuIndexGpioSettings,
|
||||||
SubmenuIndexDebug
|
SubmenuIndexDebug
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,6 +36,12 @@ void infrared_scene_start_on_enter(void* context) {
|
||||||
SubmenuIndexSavedRemotes,
|
SubmenuIndexSavedRemotes,
|
||||||
infrared_scene_start_submenu_callback,
|
infrared_scene_start_submenu_callback,
|
||||||
infrared);
|
infrared);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"GPIO Settings",
|
||||||
|
SubmenuIndexGpioSettings,
|
||||||
|
infrared_scene_start_submenu_callback,
|
||||||
|
infrared);
|
||||||
|
|
||||||
if(infrared->app_state.is_debug_enabled) {
|
if(infrared->app_state.is_debug_enabled) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
|
@ -60,19 +67,19 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
scene_manager_set_scene_state(scene_manager, InfraredSceneStart, submenu_index);
|
scene_manager_set_scene_state(scene_manager, InfraredSceneStart, submenu_index);
|
||||||
if(submenu_index == SubmenuIndexUniversalRemotes) {
|
if(submenu_index == SubmenuIndexUniversalRemotes) {
|
||||||
scene_manager_next_scene(scene_manager, InfraredSceneUniversal);
|
scene_manager_next_scene(scene_manager, InfraredSceneUniversal);
|
||||||
consumed = true;
|
|
||||||
} else if(submenu_index == SubmenuIndexLearnNewRemote) {
|
} else if(submenu_index == SubmenuIndexLearnNewRemote) {
|
||||||
infrared->app_state.is_learning_new_remote = true;
|
infrared->app_state.is_learning_new_remote = true;
|
||||||
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
|
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
|
||||||
consumed = true;
|
|
||||||
} else if(submenu_index == SubmenuIndexSavedRemotes) {
|
} else if(submenu_index == SubmenuIndexSavedRemotes) {
|
||||||
furi_string_set(infrared->file_path, INFRARED_APP_FOLDER);
|
furi_string_set(infrared->file_path, INFRARED_APP_FOLDER);
|
||||||
scene_manager_next_scene(scene_manager, InfraredSceneRemoteList);
|
scene_manager_next_scene(scene_manager, InfraredSceneRemoteList);
|
||||||
consumed = true;
|
} else if(submenu_index == SubmenuIndexGpioSettings) {
|
||||||
|
scene_manager_next_scene(scene_manager, InfraredSceneGpioSettings);
|
||||||
} else if(submenu_index == SubmenuIndexDebug) {
|
} else if(submenu_index == SubmenuIndexDebug) {
|
||||||
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
|
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
|
||||||
consumed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,60.2,,
|
Version,+,60.3,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
|
|
|
|
@ -1,5 +1,5 @@
|
||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,60.2,,
|
Version,+,60.3,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
|
@ -1324,7 +1324,9 @@ Function,+,furi_hal_infrared_async_tx_set_signal_sent_isr_callback,void,"FuriHal
|
||||||
Function,+,furi_hal_infrared_async_tx_start,void,"uint32_t, float"
|
Function,+,furi_hal_infrared_async_tx_start,void,"uint32_t, float"
|
||||||
Function,+,furi_hal_infrared_async_tx_stop,void,
|
Function,+,furi_hal_infrared_async_tx_stop,void,
|
||||||
Function,+,furi_hal_infrared_async_tx_wait_termination,void,
|
Function,+,furi_hal_infrared_async_tx_wait_termination,void,
|
||||||
|
Function,+,furi_hal_infrared_detect_tx_output,FuriHalInfraredTxPin,
|
||||||
Function,+,furi_hal_infrared_is_busy,_Bool,
|
Function,+,furi_hal_infrared_is_busy,_Bool,
|
||||||
|
Function,+,furi_hal_infrared_set_tx_output,void,FuriHalInfraredTxPin
|
||||||
Function,-,furi_hal_init,void,
|
Function,-,furi_hal_init,void,
|
||||||
Function,-,furi_hal_init_early,void,
|
Function,-,furi_hal_init_early,void,
|
||||||
Function,-,furi_hal_interrupt_init,void,
|
Function,-,furi_hal_interrupt_init,void,
|
||||||
|
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <furi_hal_infrared.h>
|
#include <furi_hal_infrared.h>
|
||||||
#include <furi_hal_interrupt.h>
|
#include <furi_hal_interrupt.h>
|
||||||
#include <furi_hal_resources.h>
|
#include <furi_hal_resources.h>
|
||||||
|
#include <furi_hal_cortex.h>
|
||||||
#include <furi_hal_bus.h>
|
#include <furi_hal_bus.h>
|
||||||
|
|
||||||
#include <stm32wbxx_ll_tim.h>
|
#include <stm32wbxx_ll_tim.h>
|
||||||
|
@ -9,12 +10,6 @@
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
// #define INFRARED_TX_DEBUG
|
|
||||||
|
|
||||||
#if defined INFRARED_TX_DEBUG
|
|
||||||
#define gpio_infrared_tx gpio_ext_pa7
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define INFRARED_TIM_TX_DMA_BUFFER_SIZE 200
|
#define INFRARED_TIM_TX_DMA_BUFFER_SIZE 200
|
||||||
#define INFRARED_POLARITY_SHIFT 1
|
#define INFRARED_POLARITY_SHIFT 1
|
||||||
|
|
||||||
|
@ -81,10 +76,16 @@ typedef enum {
|
||||||
InfraredStateMAX,
|
InfraredStateMAX,
|
||||||
} InfraredState;
|
} InfraredState;
|
||||||
|
|
||||||
|
static FuriHalInfraredTxPin infrared_tx_output = FuriHalInfraredTxPinInternal;
|
||||||
static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle;
|
static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle;
|
||||||
static InfraredTimTx infrared_tim_tx;
|
static InfraredTimTx infrared_tim_tx;
|
||||||
static InfraredTimRx infrared_tim_rx;
|
static InfraredTimRx infrared_tim_rx;
|
||||||
|
|
||||||
|
static const GpioPin* infrared_tx_pins[FuriHalInfraredTxPinMax] = {
|
||||||
|
[FuriHalInfraredTxPinInternal] = &gpio_infrared_tx,
|
||||||
|
[FuriHalInfraredTxPinExtPA7] = &gpio_ext_pa7,
|
||||||
|
};
|
||||||
|
|
||||||
static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift);
|
static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift);
|
||||||
static void furi_hal_infrared_async_tx_free_resources(void);
|
static void furi_hal_infrared_async_tx_free_resources(void);
|
||||||
static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift);
|
static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift);
|
||||||
|
@ -352,27 +353,31 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc
|
||||||
LL_TIM_SetAutoReload(
|
LL_TIM_SetAutoReload(
|
||||||
INFRARED_DMA_TIMER,
|
INFRARED_DMA_TIMER,
|
||||||
__LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(INFRARED_DMA_TIMER), freq));
|
__LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(INFRARED_DMA_TIMER), freq));
|
||||||
#if defined INFRARED_TX_DEBUG
|
|
||||||
LL_TIM_OC_SetCompareCH1(
|
if(infrared_tx_output == FuriHalInfraredTxPinInternal) {
|
||||||
INFRARED_DMA_TIMER, ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle)));
|
LL_TIM_OC_SetCompareCH3(
|
||||||
LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
|
INFRARED_DMA_TIMER,
|
||||||
/* LL_TIM_OCMODE_PWM2 set by DMA */
|
((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle)));
|
||||||
LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE);
|
LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3);
|
||||||
LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
|
/* LL_TIM_OCMODE_PWM2 set by DMA */
|
||||||
LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
|
LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE);
|
||||||
LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N);
|
LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH);
|
||||||
LL_TIM_DisableIT_CC1(INFRARED_DMA_TIMER);
|
LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3);
|
||||||
#else
|
LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N);
|
||||||
LL_TIM_OC_SetCompareCH3(
|
LL_TIM_DisableIT_CC3(INFRARED_DMA_TIMER);
|
||||||
INFRARED_DMA_TIMER, ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle)));
|
} else if(infrared_tx_output == FuriHalInfraredTxPinExtPA7) {
|
||||||
LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3);
|
LL_TIM_OC_SetCompareCH1(
|
||||||
/* LL_TIM_OCMODE_PWM2 set by DMA */
|
INFRARED_DMA_TIMER,
|
||||||
LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE);
|
((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle)));
|
||||||
LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH);
|
LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
|
||||||
LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3);
|
/* LL_TIM_OCMODE_PWM2 set by DMA */
|
||||||
LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N);
|
LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE);
|
||||||
LL_TIM_DisableIT_CC3(INFRARED_DMA_TIMER);
|
LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
|
||||||
#endif
|
LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
|
||||||
|
LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N);
|
||||||
|
LL_TIM_DisableIT_CC1(INFRARED_DMA_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
LL_TIM_DisableMasterSlaveMode(INFRARED_DMA_TIMER);
|
LL_TIM_DisableMasterSlaveMode(INFRARED_DMA_TIMER);
|
||||||
LL_TIM_EnableAllOutputs(INFRARED_DMA_TIMER);
|
LL_TIM_EnableAllOutputs(INFRARED_DMA_TIMER);
|
||||||
LL_TIM_DisableIT_UPDATE(INFRARED_DMA_TIMER);
|
LL_TIM_DisableIT_UPDATE(INFRARED_DMA_TIMER);
|
||||||
|
@ -381,11 +386,13 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc
|
||||||
|
|
||||||
static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) {
|
static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) {
|
||||||
LL_DMA_InitTypeDef dma_config = {0};
|
LL_DMA_InitTypeDef dma_config = {0};
|
||||||
#if defined INFRARED_TX_DEBUG
|
|
||||||
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR1);
|
if(infrared_tx_output == FuriHalInfraredTxPinInternal) {
|
||||||
#else
|
dma_config.PeriphOrM2MSrcAddress = (uint32_t)(&(INFRARED_DMA_TIMER->CCMR2));
|
||||||
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR2);
|
} else if(infrared_tx_output == FuriHalInfraredTxPinExtPA7) {
|
||||||
#endif
|
dma_config.PeriphOrM2MSrcAddress = (uint32_t)(&(INFRARED_DMA_TIMER->CCMR1));
|
||||||
|
}
|
||||||
|
|
||||||
dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL;
|
dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL;
|
||||||
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||||
dma_config.Mode = LL_DMA_MODE_NORMAL;
|
dma_config.Mode = LL_DMA_MODE_NORMAL;
|
||||||
|
@ -582,7 +589,8 @@ static void furi_hal_infrared_async_tx_free_resources(void) {
|
||||||
(furi_hal_infrared_state == InfraredStateIdle) ||
|
(furi_hal_infrared_state == InfraredStateIdle) ||
|
||||||
(furi_hal_infrared_state == InfraredStateAsyncTxStopped));
|
(furi_hal_infrared_state == InfraredStateAsyncTxStopped));
|
||||||
|
|
||||||
furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
|
furi_hal_gpio_init(
|
||||||
|
infrared_tx_pins[infrared_tx_output], GpioModeAnalog, GpioPullDown, GpioSpeedLow);
|
||||||
furi_hal_interrupt_set_isr(INFRARED_DMA_CH1_IRQ, NULL, NULL);
|
furi_hal_interrupt_set_isr(INFRARED_DMA_CH1_IRQ, NULL, NULL);
|
||||||
furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, NULL, NULL);
|
furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, NULL, NULL);
|
||||||
|
|
||||||
|
@ -643,10 +651,11 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) {
|
||||||
furi_delay_us(5);
|
furi_delay_us(5);
|
||||||
LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* DMA -> TIMx_RCR */
|
LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* DMA -> TIMx_RCR */
|
||||||
furi_delay_us(5);
|
furi_delay_us(5);
|
||||||
LL_GPIO_ResetOutputPin(
|
|
||||||
gpio_infrared_tx.port, gpio_infrared_tx.pin); /* when disable it prevents false pulse */
|
const GpioPin* tx_gpio = infrared_tx_pins[infrared_tx_output];
|
||||||
|
LL_GPIO_ResetOutputPin(tx_gpio->port, tx_gpio->pin); /* when disable it prevents false pulse */
|
||||||
furi_hal_gpio_init_ex(
|
furi_hal_gpio_init_ex(
|
||||||
&gpio_infrared_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1);
|
tx_gpio, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1);
|
||||||
|
|
||||||
FURI_CRITICAL_ENTER();
|
FURI_CRITICAL_ENTER();
|
||||||
LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* TIMx_RCR -> Repetition counter */
|
LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* TIMx_RCR -> Repetition counter */
|
||||||
|
@ -691,3 +700,23 @@ void furi_hal_infrared_async_tx_set_signal_sent_isr_callback(
|
||||||
infrared_tim_tx.signal_sent_callback = callback;
|
infrared_tim_tx.signal_sent_callback = callback;
|
||||||
infrared_tim_tx.signal_sent_context = context;
|
infrared_tim_tx.signal_sent_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FuriHalInfraredTxPin furi_hal_infrared_detect_tx_output(void) {
|
||||||
|
for(FuriHalInfraredTxPin pin = FuriHalInfraredTxPinInternal + 1; //-V1008
|
||||||
|
pin < FuriHalInfraredTxPinMax;
|
||||||
|
++pin) {
|
||||||
|
const GpioPin* gpio = infrared_tx_pins[pin];
|
||||||
|
furi_hal_gpio_init(gpio, GpioModeInput, GpioPullUp, GpioSpeedLow);
|
||||||
|
furi_hal_cortex_delay_us(1000U);
|
||||||
|
const bool level = furi_hal_gpio_read(gpio);
|
||||||
|
furi_hal_gpio_init(gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||||
|
if(!level) return pin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FuriHalInfraredTxPinInternal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_hal_infrared_set_tx_output(FuriHalInfraredTxPin tx_pin) {
|
||||||
|
furi_check(tx_pin < FuriHalInfraredTxPinMax);
|
||||||
|
infrared_tx_output = tx_pin;
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,12 @@ extern "C" {
|
||||||
#define INFRARED_MAX_FREQUENCY 56000
|
#define INFRARED_MAX_FREQUENCY 56000
|
||||||
#define INFRARED_MIN_FREQUENCY 10000
|
#define INFRARED_MIN_FREQUENCY 10000
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FuriHalInfraredTxPinInternal,
|
||||||
|
FuriHalInfraredTxPinExtPA7,
|
||||||
|
FuriHalInfraredTxPinMax,
|
||||||
|
} FuriHalInfraredTxPin;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FuriHalInfraredTxGetDataStateOk, /**< New data obtained */
|
FuriHalInfraredTxGetDataStateOk, /**< New data obtained */
|
||||||
FuriHalInfraredTxGetDataStateDone, /**< New data obtained, and this is end of package */
|
FuriHalInfraredTxGetDataStateDone, /**< New data obtained, and this is end of package */
|
||||||
|
@ -143,6 +149,29 @@ void furi_hal_infrared_async_tx_set_signal_sent_isr_callback(
|
||||||
FuriHalInfraredTxSignalSentISRCallback callback,
|
FuriHalInfraredTxSignalSentISRCallback callback,
|
||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
|
/** Detect which pin has an external IR module connected.
|
||||||
|
*
|
||||||
|
* External IR modules are detected by enabling a weak pull-up
|
||||||
|
* on supported pins and testing whether the input is still low.
|
||||||
|
*
|
||||||
|
* This method works best on modules that employ a FET with a
|
||||||
|
* strong pull-down or a BJT for driving IR LEDs.
|
||||||
|
*
|
||||||
|
* The module MUST pull the input voltage down to at least 0.9V
|
||||||
|
* or lower in order for it to be detected.
|
||||||
|
*
|
||||||
|
* If no module has been detected, FuriHalInfraredTxPinInternal is returned.
|
||||||
|
*
|
||||||
|
* @return numeric identifier of the first pin with a module detected.
|
||||||
|
*/
|
||||||
|
FuriHalInfraredTxPin furi_hal_infrared_detect_tx_output(void);
|
||||||
|
|
||||||
|
/** Set which pin will be used to transmit infrared signals.
|
||||||
|
*
|
||||||
|
* @param[in] tx_pin pin to be used for signal transmission.
|
||||||
|
*/
|
||||||
|
void furi_hal_infrared_set_tx_output(FuriHalInfraredTxPin tx_pin);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue