[FL-3097] fbt, faploader: minimal app module implementation (#2420)

* fbt, faploader: minimal app module implementation
* faploader, libs: moved API hashtable core to flipper_application
* example: compound api
* lib: flipper_application: naming fixes, doxygen comments
* fbt: changed `requires` manifest field behavior for app extensions
* examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning
* loader: dropped support for debug apps & plugin menus
* moved applications/plugins -> applications/external
* Restored x bit on chiplist_convert.py
* git: fixed free-dap submodule path
* pvs: updated submodule paths
* examples: example_advanced_plugins.c: removed potential memory leak on errors
* examples: example_plugins: refined requires
* fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps
* apps: removed cdefines for external apps
* fbt: moved ext app path definition
* fbt: reworked fap_dist handling; f18: synced api_symbols.csv
* fbt: removed resources_paths for extapps
* scripts: reworked storage
* scripts: reworked runfap.py & selfupdate.py to use new api
* wip: fal runner
* fbt: moved file packaging into separate module
* scripts: storage: fixes
* scripts: storage: minor fixes for new api
* fbt: changed internal artifact storage details for external apps
* scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH()
* fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py
* fbt: extra check for plugins descriptors
* fbt: additional checks in emitter
* fbt: better info message on SDK rebuild
* scripts: removed requirements.txt
* loader: removed remnants of plugins & debug menus
* post-review fixes
This commit is contained in:
hedger
2023-03-14 18:29:28 +04:00
committed by GitHub
parent 4bd3dca16f
commit 53435579b3
376 changed files with 2041 additions and 1036 deletions

View File

@@ -0,0 +1,12 @@
App(
appid="signal_generator",
name="Signal Generator",
apptype=FlipperAppType.EXTERNAL,
entry_point="signal_gen_app",
requires=["gui"],
stack_size=1 * 1024,
order=50,
fap_icon="signal_gen_10px.png",
fap_category="GPIO",
fap_icon_assets="icons",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -0,0 +1,30 @@
#include "../signal_gen_app_i.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const signal_gen_scene_on_enter_handlers[])(void*) = {
#include "signal_gen_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const signal_gen_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "signal_gen_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const signal_gen_scene_on_exit_handlers[])(void* context) = {
#include "signal_gen_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers signal_gen_scene_handlers = {
.on_enter_handlers = signal_gen_scene_on_enter_handlers,
.on_event_handlers = signal_gen_scene_on_event_handlers,
.on_exit_handlers = signal_gen_scene_on_exit_handlers,
.scene_num = SignalGenSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) SignalGenScene##id,
typedef enum {
#include "signal_gen_scene_config.h"
SignalGenSceneNum,
} SignalGenScene;
#undef ADD_SCENE
extern const SceneManagerHandlers signal_gen_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "signal_gen_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "signal_gen_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "signal_gen_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,3 @@
ADD_SCENE(signal_gen, start, Start)
ADD_SCENE(signal_gen, pwm, Pwm)
ADD_SCENE(signal_gen, mco, Mco)

View File

@@ -0,0 +1,145 @@
#include "../signal_gen_app_i.h"
typedef enum {
LineIndexPin,
LineIndexSource,
LineIndexDivision,
} LineIndex;
static const char* const mco_pin_names[] = {
"13(Tx)",
};
static const char* const mco_source_names[] = {
"32768Hz",
"64MHz",
"~100K",
"~200K",
"~400K",
"~800K",
"~1MHz",
"~2MHz",
"~4MHz",
"~8MHz",
"~16MHz",
"~24MHz",
"~32MHz",
"~48MHz",
};
static const FuriHalClockMcoSourceId mco_sources[] = {
FuriHalClockMcoLse,
FuriHalClockMcoSysclk,
FuriHalClockMcoMsi100k,
FuriHalClockMcoMsi200k,
FuriHalClockMcoMsi400k,
FuriHalClockMcoMsi800k,
FuriHalClockMcoMsi1m,
FuriHalClockMcoMsi2m,
FuriHalClockMcoMsi4m,
FuriHalClockMcoMsi8m,
FuriHalClockMcoMsi16m,
FuriHalClockMcoMsi24m,
FuriHalClockMcoMsi32m,
FuriHalClockMcoMsi48m,
};
static const char* const mco_divisor_names[] = {
"1",
"2",
"4",
"8",
"16",
};
static const FuriHalClockMcoDivisorId mco_divisors[] = {
FuriHalClockMcoDiv1,
FuriHalClockMcoDiv2,
FuriHalClockMcoDiv4,
FuriHalClockMcoDiv8,
FuriHalClockMcoDiv16,
};
static void mco_source_list_change_callback(VariableItem* item) {
SignalGenApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, mco_source_names[index]);
app->mco_src = mco_sources[index];
view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenMcoEventUpdate);
}
static void mco_divisor_list_change_callback(VariableItem* item) {
SignalGenApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, mco_divisor_names[index]);
app->mco_div = mco_divisors[index];
view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenMcoEventUpdate);
}
void signal_gen_scene_mco_on_enter(void* context) {
SignalGenApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
VariableItem* item;
item = variable_item_list_add(var_item_list, "GPIO Pin", COUNT_OF(mco_pin_names), NULL, NULL);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, mco_pin_names[0]);
item = variable_item_list_add(
var_item_list,
"Frequency",
COUNT_OF(mco_source_names),
mco_source_list_change_callback,
app);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, mco_source_names[0]);
item = variable_item_list_add(
var_item_list,
"Freq. divider",
COUNT_OF(mco_divisor_names),
mco_divisor_list_change_callback,
app);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, mco_divisor_names[0]);
variable_item_list_set_selected_item(var_item_list, LineIndexSource);
view_dispatcher_switch_to_view(app->view_dispatcher, SignalGenViewVarItemList);
app->mco_src = FuriHalClockMcoLse;
app->mco_div = FuriHalClockMcoDiv1;
furi_hal_clock_mco_enable(app->mco_src, app->mco_div);
furi_hal_gpio_init_ex(
&gpio_usart_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedVeryHigh, GpioAltFn0MCO);
}
bool signal_gen_scene_mco_on_event(void* context, SceneManagerEvent event) {
SignalGenApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SignalGenMcoEventUpdate) {
consumed = true;
furi_hal_clock_mco_enable(app->mco_src, app->mco_div);
}
}
return consumed;
}
void signal_gen_scene_mco_on_exit(void* context) {
SignalGenApp* app = context;
variable_item_list_reset(app->var_item_list);
furi_hal_gpio_init_ex(
&gpio_usart_tx,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn7USART1);
furi_hal_clock_mco_disable();
}

View File

@@ -0,0 +1,60 @@
#include "../signal_gen_app_i.h"
static const FuriHalPwmOutputId pwm_ch_id[] = {
FuriHalPwmOutputIdTim1PA7,
FuriHalPwmOutputIdLptim2PA4,
};
#define DEFAULT_FREQ 1000
#define DEFAULT_DUTY 50
static void
signal_gen_pwm_callback(uint8_t channel_id, uint32_t freq, uint8_t duty, void* context) {
SignalGenApp* app = context;
app->pwm_freq = freq;
app->pwm_duty = duty;
if(app->pwm_ch != pwm_ch_id[channel_id]) { //-V1051
app->pwm_ch_prev = app->pwm_ch;
app->pwm_ch = pwm_ch_id[channel_id];
view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventChannelChange);
} else {
app->pwm_ch = pwm_ch_id[channel_id]; //-V1048
view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventUpdate);
}
}
void signal_gen_scene_pwm_on_enter(void* context) {
SignalGenApp* app = context;
view_dispatcher_switch_to_view(app->view_dispatcher, SignalGenViewPwm);
signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app);
signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY);
furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
}
bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
SignalGenApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SignalGenPwmEventUpdate) {
consumed = true;
furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty);
} else if(event.event == SignalGenPwmEventChannelChange) {
consumed = true;
furi_hal_pwm_stop(app->pwm_ch_prev);
furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
}
}
return consumed;
}
void signal_gen_scene_pwm_on_exit(void* context) {
SignalGenApp* app = context;
variable_item_list_reset(app->var_item_list);
furi_hal_pwm_stop(app->pwm_ch);
}

View File

@@ -0,0 +1,55 @@
#include "../signal_gen_app_i.h"
typedef enum {
SubmenuIndexPwm,
SubmenuIndexClockOutput,
} SubmenuIndex;
void signal_gen_scene_start_submenu_callback(void* context, uint32_t index) {
SignalGenApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void signal_gen_scene_start_on_enter(void* context) {
SignalGenApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu, "PWM Generator", SubmenuIndexPwm, signal_gen_scene_start_submenu_callback, app);
submenu_add_item(
submenu,
"Clock Generator",
SubmenuIndexClockOutput,
signal_gen_scene_start_submenu_callback,
app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, SignalGenSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, SignalGenViewSubmenu);
}
bool signal_gen_scene_start_on_event(void* context, SceneManagerEvent event) {
SignalGenApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexPwm) {
scene_manager_next_scene(app->scene_manager, SignalGenScenePwm);
consumed = true;
} else if(event.event == SubmenuIndexClockOutput) {
scene_manager_next_scene(app->scene_manager, SignalGenSceneMco);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, SignalGenSceneStart, event.event);
}
return consumed;
}
void signal_gen_scene_start_on_exit(void* context) {
SignalGenApp* app = context;
submenu_reset(app->submenu);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1,93 @@
#include "signal_gen_app_i.h"
#include <furi.h>
#include <furi_hal.h>
static bool signal_gen_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
SignalGenApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool signal_gen_app_back_event_callback(void* context) {
furi_assert(context);
SignalGenApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void signal_gen_app_tick_event_callback(void* context) {
furi_assert(context);
SignalGenApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
SignalGenApp* signal_gen_app_alloc() {
SignalGenApp* app = malloc(sizeof(SignalGenApp));
app->gui = furi_record_open(RECORD_GUI);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&signal_gen_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, signal_gen_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, signal_gen_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, signal_gen_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SignalGenViewVarItemList,
variable_item_list_get_view(app->var_item_list));
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SignalGenViewSubmenu, submenu_get_view(app->submenu));
app->pwm_view = signal_gen_pwm_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SignalGenViewPwm, signal_gen_pwm_get_view(app->pwm_view));
scene_manager_next_scene(app->scene_manager, SignalGenSceneStart);
return app;
}
void signal_gen_app_free(SignalGenApp* app) {
furi_assert(app);
// Views
view_dispatcher_remove_view(app->view_dispatcher, SignalGenViewVarItemList);
view_dispatcher_remove_view(app->view_dispatcher, SignalGenViewSubmenu);
view_dispatcher_remove_view(app->view_dispatcher, SignalGenViewPwm);
submenu_free(app->submenu);
variable_item_list_free(app->var_item_list);
signal_gen_pwm_free(app->pwm_view);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Close records
furi_record_close(RECORD_GUI);
free(app);
}
int32_t signal_gen_app(void* p) {
UNUSED(p);
SignalGenApp* signal_gen_app = signal_gen_app_alloc();
view_dispatcher_run(signal_gen_app->view_dispatcher);
signal_gen_app_free(signal_gen_app);
return 0;
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "scenes/signal_gen_scene.h"
#include <furi_hal_clock.h>
#include <furi_hal_pwm.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/submenu.h>
#include "views/signal_gen_pwm.h"
typedef struct SignalGenApp SignalGenApp;
struct SignalGenApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
VariableItemList* var_item_list;
Submenu* submenu;
SignalGenPwm* pwm_view;
FuriHalClockMcoSourceId mco_src;
FuriHalClockMcoDivisorId mco_div;
FuriHalPwmOutputId pwm_ch_prev;
FuriHalPwmOutputId pwm_ch;
uint32_t pwm_freq;
uint8_t pwm_duty;
};
typedef enum {
SignalGenViewVarItemList,
SignalGenViewSubmenu,
SignalGenViewPwm,
} SignalGenAppView;
typedef enum {
SignalGenMcoEventUpdate,
SignalGenPwmEventUpdate,
SignalGenPwmEventChannelChange,
} SignalGenCustomEvent;

View File

@@ -0,0 +1,310 @@
#include "../signal_gen_app_i.h"
#include <furi_hal.h>
#include <gui/elements.h>
#include <signal_generator_icons.h>
typedef enum {
LineIndexChannel,
LineIndexFrequency,
LineIndexDuty,
LineIndexTotalCount
} LineIndex;
static const char* const pwm_ch_names[] = {"2(A7)", "4(A4)"};
struct SignalGenPwm {
View* view;
SignalGenPwmViewCallback callback;
void* context;
};
typedef struct {
LineIndex line_sel;
bool edit_mode;
uint8_t edit_digit;
uint8_t channel_id;
uint32_t freq;
uint8_t duty;
} SignalGenPwmViewModel;
#define ITEM_H 64 / 3
#define ITEM_W 128
#define VALUE_X 100
#define VALUE_W 45
#define FREQ_VALUE_X 62
#define FREQ_MAX 1000000UL
#define FREQ_DIGITS_NB 7
static void pwm_set_config(SignalGenPwm* pwm) {
FuriHalPwmOutputId channel;
uint32_t freq;
uint8_t duty;
with_view_model(
pwm->view,
SignalGenPwmViewModel * model,
{
channel = model->channel_id;
freq = model->freq;
duty = model->duty;
},
false);
furi_assert(pwm->callback);
pwm->callback(channel, freq, duty, pwm->context);
}
static void pwm_channel_change(SignalGenPwmViewModel* model, InputEvent* event) {
if(event->key == InputKeyLeft) {
if(model->channel_id > 0) {
model->channel_id--;
}
} else if(event->key == InputKeyRight) {
if(model->channel_id < (COUNT_OF(pwm_ch_names) - 1)) {
model->channel_id++;
}
}
}
static void pwm_duty_change(SignalGenPwmViewModel* model, InputEvent* event) {
if(event->key == InputKeyLeft) {
if(model->duty > 0) {
model->duty--;
}
} else if(event->key == InputKeyRight) {
if(model->duty < 100) {
model->duty++;
}
}
}
static bool pwm_freq_edit(SignalGenPwmViewModel* model, InputEvent* event) {
bool consumed = false;
if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
if(event->key == InputKeyRight) {
if(model->edit_digit > 0) {
model->edit_digit--;
}
consumed = true;
} else if(event->key == InputKeyLeft) {
if(model->edit_digit < (FREQ_DIGITS_NB - 1)) {
model->edit_digit++;
}
consumed = true;
} else if(event->key == InputKeyUp) {
uint32_t step = 1;
for(uint8_t i = 0; i < model->edit_digit; i++) {
step *= 10;
}
if((model->freq + step) < FREQ_MAX) {
model->freq += step;
} else {
model->freq = FREQ_MAX;
}
consumed = true;
} else if(event->key == InputKeyDown) {
uint32_t step = 1;
for(uint8_t i = 0; i < model->edit_digit; i++) {
step *= 10;
}
if(model->freq > (step + 1)) {
model->freq -= step;
} else {
model->freq = 1;
}
consumed = true;
}
}
return consumed;
}
static void signal_gen_pwm_draw_callback(Canvas* canvas, void* _model) {
SignalGenPwmViewModel* model = _model;
char* line_label = NULL;
char val_text[16];
for(size_t line = 0; line < LineIndexTotalCount; line++) {
if(line == LineIndexChannel) {
line_label = "GPIO Pin";
} else if(line == LineIndexFrequency) {
line_label = "Frequency";
} else if(line == LineIndexDuty) { //-V547
line_label = "Pulse width";
}
canvas_set_color(canvas, ColorBlack);
if(line == model->line_sel) {
elements_slightly_rounded_box(canvas, 0, ITEM_H * line + 1, ITEM_W, ITEM_H - 1);
canvas_set_color(canvas, ColorWhite);
}
uint8_t text_y = ITEM_H * line + ITEM_H / 2 + 2;
canvas_draw_str_aligned(canvas, 6, text_y, AlignLeft, AlignCenter, line_label);
if(line == LineIndexChannel) {
snprintf(val_text, sizeof(val_text), "%s", pwm_ch_names[model->channel_id]);
canvas_draw_str_aligned(canvas, VALUE_X, text_y, AlignCenter, AlignCenter, val_text);
if(model->channel_id != 0) {
canvas_draw_str_aligned(
canvas, VALUE_X - VALUE_W / 2, text_y, AlignCenter, AlignCenter, "<");
}
if(model->channel_id != (COUNT_OF(pwm_ch_names) - 1)) {
canvas_draw_str_aligned(
canvas, VALUE_X + VALUE_W / 2, text_y, AlignCenter, AlignCenter, ">");
}
} else if(line == LineIndexFrequency) {
snprintf(val_text, sizeof(val_text), "%7lu Hz", model->freq);
canvas_set_font(canvas, FontKeyboard);
canvas_draw_str_aligned(
canvas, FREQ_VALUE_X, text_y, AlignLeft, AlignCenter, val_text);
canvas_set_font(canvas, FontSecondary);
if(model->edit_mode) {
uint8_t icon_x = (FREQ_VALUE_X) + (FREQ_DIGITS_NB - model->edit_digit - 1) * 6;
canvas_draw_icon(canvas, icon_x, text_y - 9, &I_SmallArrowUp_3x5);
canvas_draw_icon(canvas, icon_x, text_y + 5, &I_SmallArrowDown_3x5);
}
} else if(line == LineIndexDuty) { //-V547
snprintf(val_text, sizeof(val_text), "%d%%", model->duty);
canvas_draw_str_aligned(canvas, VALUE_X, text_y, AlignCenter, AlignCenter, val_text);
if(model->duty != 0) {
canvas_draw_str_aligned(
canvas, VALUE_X - VALUE_W / 2, text_y, AlignCenter, AlignCenter, "<");
}
if(model->duty != 100) {
canvas_draw_str_aligned(
canvas, VALUE_X + VALUE_W / 2, text_y, AlignCenter, AlignCenter, ">");
}
}
}
}
static bool signal_gen_pwm_input_callback(InputEvent* event, void* context) {
furi_assert(context);
SignalGenPwm* pwm = context;
bool consumed = false;
bool need_update = false;
with_view_model(
pwm->view,
SignalGenPwmViewModel * model,
{
if(model->edit_mode == false) {
if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
if(event->key == InputKeyUp) {
if(model->line_sel == 0) {
model->line_sel = LineIndexTotalCount - 1;
} else {
model->line_sel =
CLAMP(model->line_sel - 1, LineIndexTotalCount - 1, 0);
}
consumed = true;
} else if(event->key == InputKeyDown) {
if(model->line_sel == LineIndexTotalCount - 1) {
model->line_sel = 0;
} else {
model->line_sel =
CLAMP(model->line_sel + 1, LineIndexTotalCount - 1, 0);
}
consumed = true;
} else if((event->key == InputKeyLeft) || (event->key == InputKeyRight)) {
if(model->line_sel == LineIndexChannel) {
pwm_channel_change(model, event);
need_update = true;
} else if(model->line_sel == LineIndexDuty) {
pwm_duty_change(model, event);
need_update = true;
} else if(model->line_sel == LineIndexFrequency) {
model->edit_mode = true;
}
consumed = true;
} else if(event->key == InputKeyOk) {
if(model->line_sel == LineIndexFrequency) {
model->edit_mode = true;
}
consumed = true;
}
}
} else {
if((event->key == InputKeyOk) || (event->key == InputKeyBack)) {
if(event->type == InputTypeShort) {
model->edit_mode = false;
consumed = true;
}
} else {
if(model->line_sel == LineIndexFrequency) {
consumed = pwm_freq_edit(model, event);
need_update = consumed;
}
}
}
},
true);
if(need_update) {
pwm_set_config(pwm);
}
return consumed;
}
SignalGenPwm* signal_gen_pwm_alloc() {
SignalGenPwm* pwm = malloc(sizeof(SignalGenPwm));
pwm->view = view_alloc();
view_allocate_model(pwm->view, ViewModelTypeLocking, sizeof(SignalGenPwmViewModel));
view_set_context(pwm->view, pwm);
view_set_draw_callback(pwm->view, signal_gen_pwm_draw_callback);
view_set_input_callback(pwm->view, signal_gen_pwm_input_callback);
return pwm;
}
void signal_gen_pwm_free(SignalGenPwm* pwm) {
furi_assert(pwm);
view_free(pwm->view);
free(pwm);
}
View* signal_gen_pwm_get_view(SignalGenPwm* pwm) {
furi_assert(pwm);
return pwm->view;
}
void signal_gen_pwm_set_callback(
SignalGenPwm* pwm,
SignalGenPwmViewCallback callback,
void* context) {
furi_assert(pwm);
furi_assert(callback);
with_view_model(
pwm->view,
SignalGenPwmViewModel * model,
{
UNUSED(model);
pwm->callback = callback;
pwm->context = context;
},
false);
}
void signal_gen_pwm_set_params(SignalGenPwm* pwm, uint8_t channel_id, uint32_t freq, uint8_t duty) {
with_view_model(
pwm->view,
SignalGenPwmViewModel * model,
{
model->channel_id = channel_id;
model->freq = freq;
model->duty = duty;
},
true);
furi_assert(pwm->callback);
pwm->callback(channel_id, freq, duty, pwm->context);
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <gui/view.h>
#include "../signal_gen_app_i.h"
typedef struct SignalGenPwm SignalGenPwm;
typedef void (
*SignalGenPwmViewCallback)(uint8_t channel_id, uint32_t freq, uint8_t duty, void* context);
SignalGenPwm* signal_gen_pwm_alloc();
void signal_gen_pwm_free(SignalGenPwm* pwm);
View* signal_gen_pwm_get_view(SignalGenPwm* pwm);
void signal_gen_pwm_set_callback(
SignalGenPwm* pwm,
SignalGenPwmViewCallback callback,
void* context);
void signal_gen_pwm_set_params(SignalGenPwm* pwm, uint8_t channel_id, uint32_t freq, uint8_t duty);