Merge branch 'fz-dev' into dev

This commit is contained in:
MX 2022-09-29 05:57:02 +03:00
commit f202d27206
No known key found for this signature in database
GPG key ID: 6C4C311DFD4B4AB5
33 changed files with 1364 additions and 56 deletions

View file

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

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,132 @@
#include "../signal_gen_app_i.h"
typedef enum {
LineIndexSource,
LineIndexDivision,
} LineIndex;
static const char* const mco_source_names[] = {
"32768",
"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, "Source", 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,
"Division",
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]) {
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];
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", SubmenuIndexPwm, signal_gen_scene_start_submenu_callback, app);
submenu_add_item(
submenu,
"Clock Output",
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,301 @@
#include "../signal_gen_app_i.h"
#include "furi_hal.h"
#include <gui/elements.h>
typedef enum {
LineIndexChannel,
LineIndexFrequency,
LineIndexDuty,
LineIndexTotalCount
} LineIndex;
static const char* const pwm_ch_names[] = {"TIM1(2)", "LPTIM2(4)"};
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 95
#define VALUE_W 55
#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;
return 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(uint8_t line = 0; line < LineIndexTotalCount; line++) {
if(line == LineIndexChannel) {
line_label = "PWM Channel";
} else if(line == LineIndexFrequency) {
line_label = "Frequency";
} else if(line == LineIndexDuty) {
line_label = "Duty Cycle";
}
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 - 1) + (FREQ_DIGITS_NB - model->edit_digit - 1) * 6;
canvas_draw_icon(canvas, icon_x, text_y - 9, &I_SmallArrowUp_4x7);
canvas_draw_icon(canvas, icon_x, text_y + 4, &I_SmallArrowDown_4x7);
}
} else if(line == LineIndexDuty) {
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;
}
}
}
return 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;
return 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;
return 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);

View file

@ -62,7 +62,7 @@ typedef struct {
uint8_t descender;
} CanvasFontParameters;
/** Canvas anonymouse structure */
/** Canvas anonymous structure */
typedef struct Canvas Canvas;
/** Get Canvas width
@ -298,7 +298,7 @@ void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r);
* @param y y coordinate of base and height intersection
* @param base length of triangle side
* @param height length of triangle height
* @param dir CanvasDirection triangle orientaion
* @param dir CanvasDirection triangle orientation
*/
void canvas_draw_triangle(
Canvas* canvas,
@ -324,7 +324,7 @@ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch);
*/
void canvas_set_bitmap_mode(Canvas* canvas, bool alpha);
/** Draw rounded-corner frame of width, height at x,y, with round value raduis
/** Draw rounded-corner frame of width, height at x,y, with round value radius
*
* @param canvas Canvas instance
* @param x x coordinate

View file

@ -59,7 +59,7 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re
* @param button_panel ButtonPanel instance
* @param index value to pass to callback
* @param matrix_place_x coordinates by x-axis on virtual grid, it
* is only used for naviagation
* is only used for navigation
* @param matrix_place_y coordinates by y-axis on virtual grid, it
* is only used for naviagation
* @param x x-coordinate to draw icon on

View file

@ -232,7 +232,8 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) {
canvas_set_color(canvas, ColorBlack);
}
if(text_length == 0 && char_is_lowercase(keys[column].text)) {
if(model->clear_default_text ||
(text_length == 0 && char_is_lowercase(keys[column].text))) {
canvas_draw_glyph(
canvas,
keyboard_origin_x + keys[column].x,

View file

@ -10,7 +10,7 @@ extern "C" {
#include <stdbool.h>
#include <m-string.h>
#define UPDATE_DELAY_OPERATION_OK 300
#define UPDATE_DELAY_OPERATION_OK 10
#define UPDATE_DELAY_OPERATION_ERROR INT_MAX
typedef enum {

View file

@ -9,6 +9,7 @@
#include <update_util/dfu_file.h>
#include <update_util/lfs_backup.h>
#include <update_util/update_operation.h>
#include <update_util/resources/manifest.h>
#include <toolbox/tar/tar_archive.h>
#include <toolbox/crc32_calc.h>
@ -50,10 +51,46 @@ static bool update_task_resource_unpack_cb(const char* name, bool is_directory,
update_task_set_progress(
unpack_progress->update_task,
UpdateTaskStageProgress,
unpack_progress->processed_files * 100 / (unpack_progress->total_files + 1));
/* For this stage, last 70% of progress = extraction */
30 + (unpack_progress->processed_files * 70) / (unpack_progress->total_files + 1));
return true;
}
static void
update_task_cleanup_resources(UpdateTask* update_task, uint32_t n_approx_file_entries) {
ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(update_task->storage);
do {
FURI_LOG_I(TAG, "Cleaning up old manifest");
if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("Manifest"))) {
FURI_LOG_W(TAG, "No existing manifest");
break;
}
/* We got # of entries in TAR file. Approx 1/4th is dir entries, we skip them */
n_approx_file_entries = n_approx_file_entries * 3 / 4 + 1;
uint32_t n_processed_files = 0;
ResourceManifestEntry* entry_ptr = NULL;
while((entry_ptr = resource_manifest_reader_next(manifest_reader))) {
if(entry_ptr->type == ResourceManifestEntryTypeFile) {
update_task_set_progress(
update_task,
UpdateTaskStageProgress,
/* For this stage, first 30% of progress = cleanup */
(n_processed_files++ * 30) / (n_approx_file_entries + 1));
string_t file_path;
string_init(file_path);
path_concat(STORAGE_EXT_PATH_PREFIX, string_get_cstr(entry_ptr->name), file_path);
FURI_LOG_D(TAG, "Removing %s", string_get_cstr(file_path));
storage_simply_remove(update_task->storage, string_get_cstr(file_path));
string_clear(file_path);
}
}
} while(false);
resource_manifest_reader_free(manifest_reader);
}
static bool update_task_post_update(UpdateTask* update_task) {
bool success = false;
@ -88,6 +125,8 @@ static bool update_task_post_update(UpdateTask* update_task) {
progress.total_files = tar_archive_get_entries_count(archive);
if(progress.total_files > 0) {
update_task_cleanup_resources(update_task, progress.total_files);
CHECK_RESULT(tar_archive_unpack_to(archive, STORAGE_EXT_PATH_PREFIX, NULL));
}
}

View file

@ -308,7 +308,7 @@ bool update_task_validate_optionbytes(UpdateTask* update_task) {
}
}
} else {
FURI_LOG_I(
FURI_LOG_D(
TAG,
"OB MATCH: #%d: real %08X == %08X (exp.)",
idx,

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

@ -48,7 +48,7 @@ The following parameters are used only for [FAPs](./AppsOnSDCard.md):
* **fap_icon**: name of a .png file, 1-bit color depth, 10x10px, to be embedded within .fap file.
* **fap_libs**: list of extra libraries to link application against. Provides access to extra functions that are not exported as a part of main firmware at expense of increased .fap file size and RAM consumption.
* **fap_category**: string, may be empty. App subcategory, also works as path of FAP within apps folder in the file system.
* **fap_description**: string, may be empty. Short application descriotion.
* **fap_description**: string, may be empty. Short application description.
* **fap_author**: string, may be empty. Application's author.
* **fap_weburl**: string, may be empty. Application's homepage.

View file

@ -43,6 +43,7 @@ Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_types.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_idle_timer.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_resources.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_config.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,,
@ -974,6 +975,8 @@ Function,+,furi_hal_cdc_set_callbacks,void,"uint8_t, CdcCallbacks*, void*"
Function,+,furi_hal_clock_deinit_early,void,
Function,-,furi_hal_clock_init,void,
Function,-,furi_hal_clock_init_early,void,
Function,+,furi_hal_clock_mco_disable,void,
Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId"
Function,-,furi_hal_clock_resume_tick,void,
Function,-,furi_hal_clock_suspend_tick,void,
Function,-,furi_hal_clock_switch_to_hsi,void,
@ -1170,6 +1173,9 @@ Function,+,furi_hal_power_sleep,void,
Function,+,furi_hal_power_sleep_available,_Bool,
Function,+,furi_hal_power_suppress_charge_enter,void,
Function,+,furi_hal_power_suppress_charge_exit,void,
Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t"
Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t"
Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId
Function,+,furi_hal_random_fill_buf,void,"uint8_t*, uint32_t"
Function,+,furi_hal_random_get,uint32_t,
Function,+,furi_hal_region_get_name,const char*,
@ -2735,6 +2741,7 @@ Function,+,tar_archive_get_entries_count,int32_t,TarArchive*
Function,+,tar_archive_open,_Bool,"TarArchive*, const char*, TarOpenMode"
Function,+,tar_archive_set_file_callback,void,"TarArchive*, tar_unpack_file_cb, void*"
Function,+,tar_archive_store_data,_Bool,"TarArchive*, const char*, const uint8_t*, const int32_t"
Function,+,tar_archive_unpack_file,_Bool,"TarArchive*, const char*, const char*"
Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, Storage_name_converter"
Function,-,tempnam,char*,"const char*, const char*"
Function,+,text_box_alloc,TextBox*,
@ -4208,6 +4215,8 @@ Variable,+,I_SDQuestion_35x43,const Icon,
Variable,+,I_SDcardFail_11x8,const Icon,
Variable,+,I_SDcardMounted_11x8,const Icon,
Variable,+,I_Scanning_123x52,const Icon,
Variable,+,I_SmallArrowDown_4x7,const Icon,
Variable,+,I_SmallArrowUp_4x7,const Icon,
Variable,+,I_Smile_18x18,const Icon,
Variable,+,I_Space_65x18,const Icon,
Variable,+,I_Swing_25x27,const Icon,

1 entry status name type params
43 Header + firmware/targets/f7/furi_hal/furi_hal_idle_timer.h
44 Header + firmware/targets/f7/furi_hal/furi_hal_interrupt.h
45 Header + firmware/targets/f7/furi_hal/furi_hal_os.h
46 Header + firmware/targets/f7/furi_hal/furi_hal_pwm.h
47 Header + firmware/targets/f7/furi_hal/furi_hal_resources.h
48 Header + firmware/targets/f7/furi_hal/furi_hal_spi_config.h
49 Header + firmware/targets/f7/furi_hal/furi_hal_spi_types.h
975 Function + furi_hal_clock_deinit_early void
976 Function - furi_hal_clock_init void
977 Function - furi_hal_clock_init_early void
978 Function + furi_hal_clock_mco_disable void
979 Function + furi_hal_clock_mco_enable void FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId
980 Function - furi_hal_clock_resume_tick void
981 Function - furi_hal_clock_suspend_tick void
982 Function - furi_hal_clock_switch_to_hsi void
1173 Function + furi_hal_power_sleep_available _Bool
1174 Function + furi_hal_power_suppress_charge_enter void
1175 Function + furi_hal_power_suppress_charge_exit void
1176 Function + furi_hal_pwm_set_params void FuriHalPwmOutputId, uint32_t, uint8_t
1177 Function + furi_hal_pwm_start void FuriHalPwmOutputId, uint32_t, uint8_t
1178 Function + furi_hal_pwm_stop void FuriHalPwmOutputId
1179 Function + furi_hal_random_fill_buf void uint8_t*, uint32_t
1180 Function + furi_hal_random_get uint32_t
1181 Function + furi_hal_region_get_name const char*
2741 Function + tar_archive_open _Bool TarArchive*, const char*, TarOpenMode
2742 Function + tar_archive_set_file_callback void TarArchive*, tar_unpack_file_cb, void*
2743 Function + tar_archive_store_data _Bool TarArchive*, const char*, const uint8_t*, const int32_t
2744 Function + tar_archive_unpack_file _Bool TarArchive*, const char*, const char*
2745 Function + tar_archive_unpack_to _Bool TarArchive*, const char*, Storage_name_converter
2746 Function - tempnam char* const char*, const char*
2747 Function + text_box_alloc TextBox*
4215 Variable + I_SDcardFail_11x8 const Icon
4216 Variable + I_SDcardMounted_11x8 const Icon
4217 Variable + I_Scanning_123x52 const Icon
4218 Variable + I_SmallArrowDown_4x7 const Icon
4219 Variable + I_SmallArrowUp_4x7 const Icon
4220 Variable + I_Smile_18x18 const Icon
4221 Variable + I_Space_65x18 const Icon
4222 Variable + I_Swing_25x27 const Icon

View file

@ -1,4 +1,5 @@
#include <furi_hal_clock.h>
#include <furi_hal_resources.h>
#include <furi.h>
#include <stm32wbxx_ll_pwr.h>
@ -236,3 +237,63 @@ void furi_hal_clock_suspend_tick() {
void furi_hal_clock_resume_tick() {
SET_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk);
}
void furi_hal_clock_mco_enable(FuriHalClockMcoSourceId source, FuriHalClockMcoDivisorId div) {
if(source == FuriHalClockMcoLse) {
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_LSE, div);
} else if(source == FuriHalClockMcoSysclk) {
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_SYSCLK, div);
} else {
LL_RCC_MSI_Enable();
while(LL_RCC_MSI_IsReady() != 1)
;
switch(source) {
case FuriHalClockMcoMsi100k:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_0);
break;
case FuriHalClockMcoMsi200k:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_1);
break;
case FuriHalClockMcoMsi400k:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_2);
break;
case FuriHalClockMcoMsi800k:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_3);
break;
case FuriHalClockMcoMsi1m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_4);
break;
case FuriHalClockMcoMsi2m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_5);
break;
case FuriHalClockMcoMsi4m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6);
break;
case FuriHalClockMcoMsi8m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_7);
break;
case FuriHalClockMcoMsi16m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_8);
break;
case FuriHalClockMcoMsi24m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_9);
break;
case FuriHalClockMcoMsi32m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_10);
break;
case FuriHalClockMcoMsi48m:
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_11);
break;
default:
break;
}
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_MSI, div);
}
}
void furi_hal_clock_mco_disable() {
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_NOCLOCK, FuriHalClockMcoDiv1);
LL_RCC_MSI_Disable();
while(LL_RCC_MSI_IsReady() != 0)
;
}

View file

@ -4,6 +4,33 @@
extern "C" {
#endif
#include <stm32wbxx_ll_rcc.h>
typedef enum {
FuriHalClockMcoLse,
FuriHalClockMcoSysclk,
FuriHalClockMcoMsi100k,
FuriHalClockMcoMsi200k,
FuriHalClockMcoMsi400k,
FuriHalClockMcoMsi800k,
FuriHalClockMcoMsi1m,
FuriHalClockMcoMsi2m,
FuriHalClockMcoMsi4m,
FuriHalClockMcoMsi8m,
FuriHalClockMcoMsi16m,
FuriHalClockMcoMsi24m,
FuriHalClockMcoMsi32m,
FuriHalClockMcoMsi48m,
} FuriHalClockMcoSourceId;
typedef enum {
FuriHalClockMcoDiv1 = LL_RCC_MCO1_DIV_1,
FuriHalClockMcoDiv2 = LL_RCC_MCO1_DIV_2,
FuriHalClockMcoDiv4 = LL_RCC_MCO1_DIV_4,
FuriHalClockMcoDiv8 = LL_RCC_MCO1_DIV_8,
FuriHalClockMcoDiv16 = LL_RCC_MCO1_DIV_16,
} FuriHalClockMcoDivisorId;
/** Early initialization */
void furi_hal_clock_init_early();
@ -25,6 +52,16 @@ void furi_hal_clock_suspend_tick();
/** Continue SysTick counter operation */
void furi_hal_clock_resume_tick();
/** Enable clock output on MCO pin
*
* @param source MCO clock source
* @param div MCO clock division
*/
void furi_hal_clock_mco_enable(FuriHalClockMcoSourceId source, FuriHalClockMcoDivisorId div);
/** Disable clock output on MCO pin */
void furi_hal_clock_mco_disable();
#ifdef __cplusplus
}
#endif

View file

@ -6,6 +6,9 @@ void furi_hal_cortex_init_early() {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0U;
/* Enable instruction prefetch */
SET_BIT(FLASH->ACR, FLASH_ACR_PRFTEN);
}
void furi_hal_cortex_delay_us(uint32_t microseconds) {

View file

@ -0,0 +1,138 @@
#include "furi_hal_pwm.h"
#include <core/check.h>
#include <furi_hal_resources.h>
#include <stdint.h>
#include <stm32wbxx_ll_tim.h>
#include <stm32wbxx_ll_lptim.h>
#include <stm32wbxx_ll_rcc.h>
#include <furi.h>
const uint32_t lptim_psc_table[] = {
LL_LPTIM_PRESCALER_DIV1,
LL_LPTIM_PRESCALER_DIV2,
LL_LPTIM_PRESCALER_DIV4,
LL_LPTIM_PRESCALER_DIV8,
LL_LPTIM_PRESCALER_DIV16,
LL_LPTIM_PRESCALER_DIV32,
LL_LPTIM_PRESCALER_DIV64,
LL_LPTIM_PRESCALER_DIV128,
};
void furi_hal_pwm_start(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) {
if(channel == FuriHalPwmOutputIdTim1PA7) {
furi_hal_gpio_init_ex(
&gpio_ext_pa7,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn1TIM1);
FURI_CRITICAL_ENTER();
LL_TIM_DeInit(TIM1);
FURI_CRITICAL_EXIT();
LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetRepetitionCounter(TIM1, 0);
LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_EnableARRPreload(TIM1);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);
LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N);
LL_TIM_EnableAllOutputs(TIM1);
furi_hal_pwm_set_params(channel, freq, duty);
LL_TIM_EnableCounter(TIM1);
} else if(channel == FuriHalPwmOutputIdLptim2PA4) {
furi_hal_gpio_init_ex(
&gpio_ext_pa4,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn14LPTIM2);
FURI_CRITICAL_ENTER();
LL_LPTIM_DeInit(LPTIM2);
FURI_CRITICAL_EXIT();
LL_LPTIM_SetUpdateMode(LPTIM2, LL_LPTIM_UPDATE_MODE_ENDOFPERIOD);
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_PCLK1);
LL_LPTIM_SetClockSource(LPTIM2, LL_LPTIM_CLK_SOURCE_INTERNAL);
LL_LPTIM_ConfigOutput(
LPTIM2, LL_LPTIM_OUTPUT_WAVEFORM_PWM, LL_LPTIM_OUTPUT_POLARITY_INVERSE);
LL_LPTIM_SetCounterMode(LPTIM2, LL_LPTIM_COUNTER_MODE_INTERNAL);
LL_LPTIM_Enable(LPTIM2);
furi_hal_pwm_set_params(channel, freq, duty);
LL_LPTIM_StartCounter(LPTIM2, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
}
}
void furi_hal_pwm_stop(FuriHalPwmOutputId channel) {
if(channel == FuriHalPwmOutputIdTim1PA7) {
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
FURI_CRITICAL_ENTER();
LL_TIM_DeInit(TIM1);
FURI_CRITICAL_EXIT();
} else if(channel == FuriHalPwmOutputIdLptim2PA4) {
furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog);
FURI_CRITICAL_ENTER();
LL_LPTIM_DeInit(LPTIM2);
FURI_CRITICAL_EXIT();
}
}
void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) {
furi_assert(freq > 0);
uint32_t freq_div = 64000000LU / freq;
if(channel == FuriHalPwmOutputIdTim1PA7) {
uint32_t prescaler = freq_div / 0x10000LU;
uint32_t period = freq_div / (prescaler + 1);
uint32_t compare = period * duty / 100;
LL_TIM_SetPrescaler(TIM1, prescaler);
LL_TIM_SetAutoReload(TIM1, period - 1);
LL_TIM_OC_SetCompareCH1(TIM1, compare);
} else if(channel == FuriHalPwmOutputIdLptim2PA4) {
uint32_t prescaler = 0;
uint32_t period = 0;
bool clock_lse = false;
do {
period = freq_div / (1 << prescaler);
if(period <= 0xFFFF) {
break;
}
prescaler++;
if(prescaler > 7) {
prescaler = 0;
clock_lse = true;
period = 32768LU / freq;
break;
}
} while(1);
uint32_t compare = period * duty / 100;
LL_LPTIM_SetPrescaler(LPTIM2, lptim_psc_table[prescaler]);
LL_LPTIM_SetAutoReload(LPTIM2, period);
LL_LPTIM_SetCompare(LPTIM2, compare);
if(clock_lse) {
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE);
} else {
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_PCLK1);
}
}
}

View file

@ -0,0 +1,42 @@
/**
* @file furi_hal_pwm.h
* PWM contol HAL
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef enum {
FuriHalPwmOutputIdTim1PA7,
FuriHalPwmOutputIdLptim2PA4,
} FuriHalPwmOutputId;
/** Enable PWM channel and set parameters
*
* @param[in] channel PWM channel (FuriHalPwmOutputId)
* @param[in] freq Frequency in Hz
* @param[in] duty Duty cycle value in %
*/
void furi_hal_pwm_start(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty);
/** Disable PWM channel
*
* @param[in] channel PWM channel (FuriHalPwmOutputId)
*/
void furi_hal_pwm_stop(FuriHalPwmOutputId channel);
/** Set PWM channel parameters
*
* @param[in] channel PWM channel (FuriHalPwmOutputId)
* @param[in] freq Frequency in Hz
* @param[in] duty Duty cycle value in %
*/
void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty);
#ifdef __cplusplus
}
#endif

View file

@ -85,7 +85,6 @@ static void furi_thread_body(void* context) {
}
furi_assert(thread->state == FuriThreadStateRunning);
furi_thread_set_state(thread, FuriThreadStateStopped);
if(thread->is_service) {
FURI_LOG_E(
@ -99,7 +98,10 @@ static void furi_thread_body(void* context) {
furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL);
vTaskSetThreadLocalStoragePointer(NULL, 0, NULL);
vTaskDelete(thread->task_handle);
// from here we can't use thread pointer
furi_thread_set_state(thread, FuriThreadStateStopped);
vTaskDelete(NULL);
furi_thread_catch();
}
@ -205,7 +207,9 @@ void furi_thread_start(FuriThread* thread) {
bool furi_thread_join(FuriThread* thread) {
furi_assert(thread);
while(thread->state != FuriThreadStateStopped) {
furi_check(furi_thread_get_current() != thread);
while(eTaskGetState(thread->task_handle) != eDeleted) {
furi_delay_ms(10);
}

View file

@ -168,7 +168,44 @@ typedef struct {
Storage_name_converter converter;
} TarArchiveDirectoryOpParams;
static bool archive_extract_current_file(TarArchive* archive, const char* dst_path) {
mtar_t* tar = &archive->tar;
File* out_file = storage_file_alloc(archive->storage);
uint8_t* readbuf = malloc(FILE_BLOCK_SIZE);
bool success = true;
uint8_t n_tries = FILE_OPEN_NTRIES;
do {
while(n_tries-- > 0) {
if(storage_file_open(out_file, dst_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
break;
}
FURI_LOG_W(TAG, "Failed to open '%s', reties: %d", dst_path, n_tries);
storage_file_close(out_file);
furi_delay_ms(FILE_OPEN_RETRY_DELAY);
}
if(!storage_file_is_open(out_file)) {
success = false;
break;
}
while(!mtar_eof_data(tar)) {
int32_t readcnt = mtar_read_data(tar, readbuf, FILE_BLOCK_SIZE);
if(!readcnt || !storage_file_write(out_file, readbuf, readcnt)) {
success = false;
break;
}
}
} while(false);
storage_file_free(out_file);
free(readbuf);
return success;
}
static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, void* param) {
UNUSED(tar);
TarArchiveDirectoryOpParams* op_params = param;
TarArchive* archive = op_params->archive;
@ -199,58 +236,22 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header,
return 0;
}
string_init(full_extracted_fname);
FURI_LOG_D(TAG, "Extracting %d bytes to '%s'", header->size, header->name);
string_t converted_fname;
string_init_set(converted_fname, header->name);
if(op_params->converter) {
op_params->converter(converted_fname);
}
string_init(full_extracted_fname);
path_concat(op_params->work_dir, string_get_cstr(converted_fname), full_extracted_fname);
bool success = archive_extract_current_file(archive, string_get_cstr(full_extracted_fname));
string_clear(converted_fname);
FURI_LOG_D(TAG, "Extracting %d bytes to '%s'", header->size, header->name);
File* out_file = storage_file_alloc(archive->storage);
uint8_t* readbuf = malloc(FILE_BLOCK_SIZE);
bool failed = false;
uint8_t n_tries = FILE_OPEN_NTRIES;
do {
while(n_tries-- > 0) {
if(storage_file_open(
out_file,
string_get_cstr(full_extracted_fname),
FSAM_WRITE,
FSOM_CREATE_ALWAYS)) {
break;
}
FURI_LOG_W(
TAG,
"Failed to open '%s', reties: %d",
string_get_cstr(full_extracted_fname),
n_tries);
storage_file_close(out_file);
furi_delay_ms(FILE_OPEN_RETRY_DELAY);
}
if(!storage_file_is_open(out_file)) {
failed = true;
break;
}
while(!mtar_eof_data(tar)) {
int32_t readcnt = mtar_read_data(tar, readbuf, FILE_BLOCK_SIZE);
if(!readcnt || !storage_file_write(out_file, readbuf, readcnt)) {
failed = true;
break;
}
}
} while(false);
storage_file_free(out_file);
free(readbuf);
string_clear(full_extracted_fname);
return failed ? -1 : 0;
return success ? 0 : -1;
}
bool tar_archive_unpack_to(
@ -369,3 +370,16 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch
storage_file_free(directory);
return success;
}
bool tar_archive_unpack_file(
TarArchive* archive,
const char* archive_fname,
const char* destination) {
furi_assert(archive);
furi_assert(archive_fname);
furi_assert(destination);
if(mtar_find(&archive->tar, archive_fname) != MTAR_ESUCCESS) {
return false;
}
return archive_extract_current_file(archive, destination);
}

View file

@ -41,6 +41,11 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch
int32_t tar_archive_get_entries_count(TarArchive* archive);
bool tar_archive_unpack_file(
TarArchive* archive,
const char* archive_fname,
const char* destination);
/* Optional per-entry callback on unpacking - return false to skip entry */
typedef bool (*tar_unpack_file_cb)(const char* name, bool is_directory, void* context);

View file

@ -0,0 +1,115 @@
#include "manifest.h"
#include <toolbox/stream/buffered_file_stream.h>
#include <toolbox/hex.h>
struct ResourceManifestReader {
Storage* storage;
Stream* stream;
string_t linebuf;
ResourceManifestEntry entry;
};
ResourceManifestReader* resource_manifest_reader_alloc(Storage* storage) {
ResourceManifestReader* resource_manifest =
(ResourceManifestReader*)malloc(sizeof(ResourceManifestReader));
resource_manifest->storage = storage;
resource_manifest->stream = buffered_file_stream_alloc(resource_manifest->storage);
memset(&resource_manifest->entry, 0, sizeof(ResourceManifestEntry));
string_init(resource_manifest->entry.name);
string_init(resource_manifest->linebuf);
return resource_manifest;
}
void resource_manifest_reader_free(ResourceManifestReader* resource_manifest) {
furi_assert(resource_manifest);
string_clear(resource_manifest->linebuf);
string_clear(resource_manifest->entry.name);
buffered_file_stream_close(resource_manifest->stream);
stream_free(resource_manifest->stream);
free(resource_manifest);
}
bool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, const char* filename) {
furi_assert(resource_manifest);
return buffered_file_stream_open(
resource_manifest->stream, filename, FSAM_READ, FSOM_OPEN_EXISTING);
}
/* Read entries in format of
* F:<hash>:<size>:<name>
* D:<name>
*/
ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest) {
furi_assert(resource_manifest);
string_reset(resource_manifest->entry.name);
resource_manifest->entry.type = ResourceManifestEntryTypeUnknown;
resource_manifest->entry.size = 0;
memset(resource_manifest->entry.hash, 0, sizeof(resource_manifest->entry.hash));
do {
if(!stream_read_line(resource_manifest->stream, resource_manifest->linebuf)) {
return NULL;
}
/* Trim end of line */
string_strim(resource_manifest->linebuf);
char type_code = string_get_char(resource_manifest->linebuf, 0);
switch(type_code) {
case 'F':
resource_manifest->entry.type = ResourceManifestEntryTypeFile;
break;
case 'D':
resource_manifest->entry.type = ResourceManifestEntryTypeDirectory;
break;
default: /* Skip other entries - version, timestamp, etc */
continue;
};
if(resource_manifest->entry.type == ResourceManifestEntryTypeFile) {
/* Parse file entry
F:<hash>:<size>:<name> */
/* Remove entry type code */
string_right(resource_manifest->linebuf, 2);
if(string_search_char(resource_manifest->linebuf, ':') !=
sizeof(resource_manifest->entry.hash) * 2) {
/* Invalid hash */
continue;
}
/* Read hash */
hex_chars_to_uint8(
string_get_cstr(resource_manifest->linebuf), resource_manifest->entry.hash);
/* Remove hash */
string_right(
resource_manifest->linebuf, sizeof(resource_manifest->entry.hash) * 2 + 1);
resource_manifest->entry.size = atoi(string_get_cstr(resource_manifest->linebuf));
/* Remove size */
size_t offs = string_search_char(resource_manifest->linebuf, ':');
string_right(resource_manifest->linebuf, offs + 1);
string_set(resource_manifest->entry.name, resource_manifest->linebuf);
} else if(resource_manifest->entry.type == ResourceManifestEntryTypeDirectory) {
/* Parse directory entry
D:<name> */
/* Remove entry type code */
string_right(resource_manifest->linebuf, 2);
string_set(resource_manifest->entry.name, resource_manifest->linebuf);
}
return &resource_manifest->entry;
} while(true);
return NULL;
}

View file

@ -0,0 +1,58 @@
#pragma once
#include <storage/storage.h>
#include <m-string.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
ResourceManifestEntryTypeUnknown = 0,
ResourceManifestEntryTypeDirectory,
ResourceManifestEntryTypeFile,
} ResourceManifestEntryType;
typedef struct {
ResourceManifestEntryType type;
string_t name;
uint32_t size;
uint8_t hash[16];
} ResourceManifestEntry;
typedef struct ResourceManifestReader ResourceManifestReader;
/**
* @brief Initialize resource manifest reader
* @param storage Storage API pointer
* @return allocated object
*/
ResourceManifestReader* resource_manifest_reader_alloc(Storage* storage);
/**
* @brief Release resource manifest reader
* @param resource_manifest allocated object
*/
void resource_manifest_reader_free(ResourceManifestReader* resource_manifest);
/**
* @brief Initialize resource manifest reader iteration
* @param resource_manifest allocated object
* @param filename manifest file name
* @return true if file opened
*/
bool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, const char* filename);
/**
* @brief Read next file/dir entry from manifest
* @param resource_manifest allocated object
* @return entry or NULL if end of file
*/
ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -111,7 +111,7 @@ class Main(App):
if not filenames:
continue
if "frame_rate" in filenames:
self.logger.debug(f"Folder contatins animation")
self.logger.debug(f"Folder contains animation")
icon_name = "A_" + os.path.split(dirpath)[1].replace("-", "_")
width = height = None
frame_count = 0