[FL-872] Furi, API-HAL, App-Loader cleanup and improvements (#334)
* Furi: replace obsolete furiac_exit with osThreadExit, drop obsolete apis and test. Rename systemd to flipper and move to separate file, cleanup. ApiHal: Rename timebase to os and move freertos hooks there, move insomnia api to power module. * Furi: new thread helper * Furi: cleanup thread documentation * Flipper, AppLoader: update to use FuriThread. Update tasks signatures to match FuriThreadCallback signature. * F4: rename API_HAL_TIMEBASE_DEBUG to API_HAL_OS_DEBUG * Applications: rename FuriApplication to FlipperApplication, use FuriThreadCallback signature for apps. * C++ app template sample, new exit method
This commit is contained in:
parent
7c5de59f53
commit
b835d7a451
@ -8,20 +8,19 @@
|
|||||||
#include <api-hal.h>
|
#include <api-hal.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
osThreadAttr_t app_thread_attr;
|
FuriThread* thread;
|
||||||
osThreadId_t app_thread_id;
|
|
||||||
ViewPort* view_port;
|
ViewPort* view_port;
|
||||||
const FuriApplication* current_app;
|
const FlipperApplication* current_app;
|
||||||
} AppLoaderState;
|
} AppLoaderState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
AppLoaderState* state;
|
AppLoaderState* state;
|
||||||
const FuriApplication* app;
|
const FlipperApplication* app;
|
||||||
} AppLoaderContext;
|
} AppLoaderContext;
|
||||||
|
|
||||||
// TODO add mutex for contex
|
// TODO add mutex for contex
|
||||||
|
|
||||||
static void render_callback(Canvas* canvas, void* _ctx) {
|
static void app_loader_render_callback(Canvas* canvas, void* _ctx) {
|
||||||
AppLoaderState* ctx = (AppLoaderState*)_ctx;
|
AppLoaderState* ctx = (AppLoaderState*)_ctx;
|
||||||
|
|
||||||
canvas_clear(canvas);
|
canvas_clear(canvas);
|
||||||
@ -33,77 +32,71 @@ static void render_callback(Canvas* canvas, void* _ctx) {
|
|||||||
canvas_draw_str(canvas, 2, 44, "press back to exit");
|
canvas_draw_str(canvas, 2, 44, "press back to exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void input_callback(InputEvent* input_event, void* _ctx) {
|
static void app_loader_input_callback(InputEvent* input_event, void* _ctx) {
|
||||||
AppLoaderState* ctx = (AppLoaderState*)_ctx;
|
AppLoaderState* ctx = (AppLoaderState*)_ctx;
|
||||||
|
|
||||||
if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) {
|
if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) {
|
||||||
osThreadTerminate(ctx->app_thread_id);
|
furi_thread_terminate(ctx->thread);
|
||||||
view_port_enabled_set(ctx->view_port, false);
|
|
||||||
api_hal_timebase_insomnia_exit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_menu(void* _ctx) {
|
static void app_loader_menu_callback(void* _ctx) {
|
||||||
AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
|
AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
|
||||||
|
|
||||||
if(ctx->app->app == NULL) return;
|
if(ctx->app->app == NULL) return;
|
||||||
|
|
||||||
view_port_enabled_set(ctx->state->view_port, true);
|
view_port_enabled_set(ctx->state->view_port, true);
|
||||||
|
|
||||||
// TODO how to call this?
|
api_hal_power_insomnia_enter();
|
||||||
// furiac_wait_libs(&FLIPPER_STARTUP[i].libs);
|
|
||||||
api_hal_timebase_insomnia_enter();
|
|
||||||
|
|
||||||
ctx->state->current_app = ctx->app;
|
ctx->state->current_app = ctx->app;
|
||||||
ctx->state->app_thread_attr.name = ctx->app->name;
|
|
||||||
ctx->state->app_thread_attr.attr_bits = osThreadDetached;
|
furi_thread_set_name(ctx->state->thread, ctx->app->name);
|
||||||
ctx->state->app_thread_attr.cb_mem = NULL;
|
furi_thread_set_stack_size(ctx->state->thread, ctx->app->stack_size);
|
||||||
ctx->state->app_thread_attr.cb_size = 0;
|
furi_thread_set_callback(ctx->state->thread, ctx->app->app);
|
||||||
ctx->state->app_thread_attr.stack_mem = NULL;
|
furi_thread_start(ctx->state->thread);
|
||||||
ctx->state->app_thread_attr.stack_size = ctx->app->stack_size;
|
|
||||||
ctx->state->app_thread_attr.priority = osPriorityNormal;
|
|
||||||
ctx->state->app_thread_attr.tz_module = 0;
|
|
||||||
ctx->state->app_thread_attr.reserved = 0;
|
|
||||||
ctx->state->app_thread_id = osThreadNew(ctx->app->app, NULL, &ctx->state->app_thread_attr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_cli(string_t args, void* _ctx) {
|
static void app_loader_cli_callback(string_t args, void* _ctx) {
|
||||||
AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
|
AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
|
||||||
|
|
||||||
if(ctx->app->app == NULL) return;
|
if(ctx->app->app == NULL) return;
|
||||||
|
|
||||||
printf("Starting furi application\r\n");
|
printf("Starting furi application\r\n");
|
||||||
|
|
||||||
ctx->state->current_app = ctx->app;
|
api_hal_power_insomnia_enter();
|
||||||
ctx->state->app_thread_attr.name = ctx->app->name;
|
|
||||||
ctx->state->app_thread_attr.attr_bits = osThreadDetached;
|
furi_thread_set_name(ctx->state->thread, ctx->app->name);
|
||||||
ctx->state->app_thread_attr.cb_mem = NULL;
|
furi_thread_set_stack_size(ctx->state->thread, ctx->app->stack_size);
|
||||||
ctx->state->app_thread_attr.cb_size = 0;
|
furi_thread_set_callback(ctx->state->thread, ctx->app->app);
|
||||||
ctx->state->app_thread_attr.stack_mem = NULL;
|
furi_thread_start(ctx->state->thread);
|
||||||
ctx->state->app_thread_attr.stack_size = ctx->app->stack_size;
|
|
||||||
ctx->state->app_thread_attr.priority = osPriorityNormal;
|
|
||||||
ctx->state->app_thread_attr.tz_module = 0;
|
|
||||||
ctx->state->app_thread_attr.reserved = 0;
|
|
||||||
ctx->state->app_thread_id = osThreadNew(ctx->app->app, NULL, &ctx->state->app_thread_attr);
|
|
||||||
|
|
||||||
printf("Press any key to kill application");
|
printf("Press any key to kill application");
|
||||||
|
|
||||||
char c;
|
char c;
|
||||||
cli_read(&c, 1);
|
cli_read(&c, 1);
|
||||||
osThreadTerminate(ctx->state->app_thread_id);
|
furi_thread_terminate(ctx->state->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_loader(void* p) {
|
void app_loader_thread_state_callback(FuriThreadState state, void* context) {
|
||||||
osThreadId_t self_id = osThreadGetId();
|
furi_assert(context);
|
||||||
furi_check(self_id);
|
AppLoaderState* app_loader_state = context;
|
||||||
|
if(state == FuriThreadStateStopped) {
|
||||||
|
view_port_enabled_set(app_loader_state->view_port, false);
|
||||||
|
api_hal_power_insomnia_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t app_loader(void* p) {
|
||||||
AppLoaderState state;
|
AppLoaderState state;
|
||||||
state.app_thread_id = NULL;
|
state.thread = furi_thread_alloc();
|
||||||
|
furi_thread_set_state_context(state.thread, &state);
|
||||||
|
furi_thread_set_state_callback(state.thread, app_loader_thread_state_callback);
|
||||||
|
|
||||||
state.view_port = view_port_alloc();
|
state.view_port = view_port_alloc();
|
||||||
view_port_enabled_set(state.view_port, false);
|
view_port_enabled_set(state.view_port, false);
|
||||||
view_port_draw_callback_set(state.view_port, render_callback, &state);
|
view_port_draw_callback_set(state.view_port, app_loader_render_callback, &state);
|
||||||
view_port_input_callback_set(state.view_port, input_callback, &state);
|
view_port_input_callback_set(state.view_port, app_loader_input_callback, &state);
|
||||||
|
|
||||||
ValueMutex* menu_mutex = furi_record_open("menu");
|
ValueMutex* menu_mutex = furi_record_open("menu");
|
||||||
Cli* cli = furi_record_open("cli");
|
Cli* cli = furi_record_open("cli");
|
||||||
@ -114,7 +107,7 @@ void app_loader(void* p) {
|
|||||||
// Main menu
|
// Main menu
|
||||||
with_value_mutex(
|
with_value_mutex(
|
||||||
menu_mutex, (Menu * menu) {
|
menu_mutex, (Menu * menu) {
|
||||||
for(size_t i = 0; i < FLIPPER_APPS_size(); i++) {
|
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
|
||||||
AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
|
AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
|
||||||
ctx->state = &state;
|
ctx->state = &state;
|
||||||
ctx->app = &FLIPPER_APPS[i];
|
ctx->app = &FLIPPER_APPS[i];
|
||||||
@ -124,14 +117,14 @@ void app_loader(void* p) {
|
|||||||
menu_item_alloc_function(
|
menu_item_alloc_function(
|
||||||
FLIPPER_APPS[i].name,
|
FLIPPER_APPS[i].name,
|
||||||
assets_icons_get(FLIPPER_APPS[i].icon),
|
assets_icons_get(FLIPPER_APPS[i].icon),
|
||||||
handle_menu,
|
app_loader_menu_callback,
|
||||||
ctx));
|
ctx));
|
||||||
|
|
||||||
// Add cli command
|
// Add cli command
|
||||||
string_t cli_name;
|
string_t cli_name;
|
||||||
string_init_set_str(cli_name, "app_");
|
string_init_set_str(cli_name, "app_");
|
||||||
string_cat_str(cli_name, FLIPPER_APPS[i].name);
|
string_cat_str(cli_name, FLIPPER_APPS[i].name);
|
||||||
cli_add_command(cli, string_get_cstr(cli_name), handle_cli, ctx);
|
cli_add_command(cli, string_get_cstr(cli_name), app_loader_cli_callback, ctx);
|
||||||
string_clear(cli_name);
|
string_clear(cli_name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -160,7 +153,7 @@ void app_loader(void* p) {
|
|||||||
MenuItem* menu_plugins =
|
MenuItem* menu_plugins =
|
||||||
menu_item_alloc_menu("Plugins", assets_icons_get(A_Plugins_14));
|
menu_item_alloc_menu("Plugins", assets_icons_get(A_Plugins_14));
|
||||||
|
|
||||||
for(size_t i = 0; i < FLIPPER_PLUGINS_size(); i++) {
|
for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
|
||||||
AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
|
AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
|
||||||
ctx->state = &state;
|
ctx->state = &state;
|
||||||
ctx->app = &FLIPPER_PLUGINS[i];
|
ctx->app = &FLIPPER_PLUGINS[i];
|
||||||
@ -170,14 +163,14 @@ void app_loader(void* p) {
|
|||||||
menu_item_alloc_function(
|
menu_item_alloc_function(
|
||||||
FLIPPER_PLUGINS[i].name,
|
FLIPPER_PLUGINS[i].name,
|
||||||
assets_icons_get(FLIPPER_PLUGINS[i].icon),
|
assets_icons_get(FLIPPER_PLUGINS[i].icon),
|
||||||
handle_menu,
|
app_loader_menu_callback,
|
||||||
ctx));
|
ctx));
|
||||||
|
|
||||||
// Add cli command
|
// Add cli command
|
||||||
string_t cli_name;
|
string_t cli_name;
|
||||||
string_init_set_str(cli_name, "app_");
|
string_init_set_str(cli_name, "app_");
|
||||||
string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
|
string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
|
||||||
cli_add_command(cli, string_get_cstr(cli_name), handle_cli, ctx);
|
cli_add_command(cli, string_get_cstr(cli_name), app_loader_cli_callback, ctx);
|
||||||
string_clear(cli_name);
|
string_clear(cli_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,5 +179,9 @@ void app_loader(void* p) {
|
|||||||
|
|
||||||
printf("[app loader] start\r\n");
|
printf("[app loader] start\r\n");
|
||||||
|
|
||||||
osThreadSuspend(self_id);
|
while(1) {
|
||||||
|
osThreadSuspend(osThreadGetId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,40 @@
|
|||||||
#include "applications.h"
|
#include "applications.h"
|
||||||
|
|
||||||
#ifdef APP_TEST
|
#ifdef APP_TEST
|
||||||
void flipper_test_app(void* p);
|
int32_t flipper_test_app(void* p);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void application_blink(void* p);
|
int32_t application_blink(void* p);
|
||||||
void application_uart_write(void* p);
|
int32_t application_uart_write(void* p);
|
||||||
void application_input_dump(void* p);
|
int32_t application_input_dump(void* p);
|
||||||
void u8g2_example(void* p);
|
int32_t u8g2_example(void* p);
|
||||||
void input_task(void* p);
|
int32_t input_task(void* p);
|
||||||
void menu_task(void* p);
|
int32_t menu_task(void* p);
|
||||||
void coreglitch_demo_0(void* p);
|
int32_t coreglitch_demo_0(void* p);
|
||||||
void u8g2_qrcode(void* p);
|
int32_t u8g2_qrcode(void* p);
|
||||||
void fatfs_list(void* p);
|
int32_t gui_task(void* p);
|
||||||
void gui_task(void* p);
|
int32_t backlight_control(void* p);
|
||||||
void backlight_control(void* p);
|
int32_t irda(void* p);
|
||||||
void irda(void* p);
|
int32_t app_loader(void* p);
|
||||||
void app_loader(void* p);
|
int32_t cc1101_workaround(void* p);
|
||||||
void cc1101_workaround(void* p);
|
int32_t lf_rfid_workaround(void* p);
|
||||||
void lf_rfid_workaround(void* p);
|
int32_t nfc_task(void* p);
|
||||||
void nfc_task(void* p);
|
int32_t dolphin_task(void* p);
|
||||||
void dolphin_task(void* p);
|
int32_t power_task(void* p);
|
||||||
void power_task(void* p);
|
int32_t bt_task(void* p);
|
||||||
void bt_task(void* p);
|
int32_t sd_card_test(void* p);
|
||||||
void sd_card_test(void* p);
|
int32_t application_vibro(void* p);
|
||||||
void application_vibro(void* p);
|
int32_t app_gpio_test(void* p);
|
||||||
void app_gpio_test(void* p);
|
int32_t app_ibutton(void* p);
|
||||||
void app_ibutton(void* p);
|
int32_t cli_task(void* p);
|
||||||
void cli_task(void* p);
|
int32_t music_player(void* p);
|
||||||
void music_player(void* p);
|
int32_t sdnfc(void* p);
|
||||||
void sdnfc(void* p);
|
int32_t floopper_bloopper(void* p);
|
||||||
void floopper_bloopper(void* p);
|
int32_t sd_filesystem(void* p);
|
||||||
void sd_filesystem(void* p);
|
|
||||||
|
|
||||||
void gui_test(void* p);
|
int32_t gui_test(void* p);
|
||||||
|
|
||||||
const FuriApplication FLIPPER_SERVICES[] = {
|
const FlipperApplication FLIPPER_SERVICES[] = {
|
||||||
#ifdef APP_CLI
|
#ifdef APP_CLI
|
||||||
{.app = cli_task, .name = "cli_task", .stack_size = 1024, .icon = A_Plugins_14},
|
{.app = cli_task, .name = "cli_task", .stack_size = 1024, .icon = A_Plugins_14},
|
||||||
#endif
|
#endif
|
||||||
@ -111,10 +110,6 @@ const FuriApplication FLIPPER_SERVICES[] = {
|
|||||||
{.app = u8g2_qrcode, .name = "u8g2_qrcode", .stack_size = 1024, .icon = A_Plugins_14},
|
{.app = u8g2_qrcode, .name = "u8g2_qrcode", .stack_size = 1024, .icon = A_Plugins_14},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_EXAMPLE_FATFS
|
|
||||||
{.app = fatfs_list, .name = "fatfs_list", .stack_size = 1024, .icon = A_Plugins_14},
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef APP_EXAMPLE_DISPLAY
|
#ifdef APP_EXAMPLE_DISPLAY
|
||||||
{.app = u8g2_example, .name = "u8g2_example", .stack_size = 1024, .icon = A_Plugins_14},
|
{.app = u8g2_example, .name = "u8g2_example", .stack_size = 1024, .icon = A_Plugins_14},
|
||||||
#endif
|
#endif
|
||||||
@ -155,12 +150,10 @@ const FuriApplication FLIPPER_SERVICES[] = {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t FLIPPER_SERVICES_size() {
|
const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
|
||||||
return sizeof(FLIPPER_SERVICES) / sizeof(FuriApplication);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main menu APP
|
// Main menu APP
|
||||||
const FuriApplication FLIPPER_APPS[] = {
|
const FlipperApplication FLIPPER_APPS[] = {
|
||||||
#ifdef BUILD_CC1101
|
#ifdef BUILD_CC1101
|
||||||
{.app = cc1101_workaround, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14},
|
{.app = cc1101_workaround, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14},
|
||||||
#endif
|
#endif
|
||||||
@ -182,12 +175,10 @@ const FuriApplication FLIPPER_APPS[] = {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t FLIPPER_APPS_size() {
|
const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplication);
|
||||||
return sizeof(FLIPPER_APPS) / sizeof(FuriApplication);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plugin menu
|
// Plugin menu
|
||||||
const FuriApplication FLIPPER_PLUGINS[] = {
|
const FlipperApplication FLIPPER_PLUGINS[] = {
|
||||||
#ifdef BUILD_EXAMPLE_BLINK
|
#ifdef BUILD_EXAMPLE_BLINK
|
||||||
{.app = application_blink, .name = "blink", .stack_size = 1024, .icon = A_Plugins_14},
|
{.app = application_blink, .name = "blink", .stack_size = 1024, .icon = A_Plugins_14},
|
||||||
#endif
|
#endif
|
||||||
@ -231,6 +222,4 @@ const FuriApplication FLIPPER_PLUGINS[] = {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t FLIPPER_PLUGINS_size() {
|
const size_t FLIPPER_PLUGINS_COUNT = sizeof(FLIPPER_PLUGINS) / sizeof(FlipperApplication);
|
||||||
return sizeof(FLIPPER_PLUGINS) / sizeof(FuriApplication);
|
|
||||||
}
|
|
||||||
|
@ -3,20 +3,27 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
|
|
||||||
typedef void (*FlipperApplication)(void*);
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const FlipperApplication app;
|
const FuriThreadCallback app;
|
||||||
const char* name;
|
const char* name;
|
||||||
const size_t stack_size;
|
const size_t stack_size;
|
||||||
const IconName icon;
|
const IconName icon;
|
||||||
} FuriApplication;
|
} FlipperApplication;
|
||||||
|
|
||||||
extern const FuriApplication FLIPPER_SERVICES[];
|
/* Services list
|
||||||
size_t FLIPPER_SERVICES_size();
|
* Spawned on startup
|
||||||
|
*/
|
||||||
|
extern const FlipperApplication FLIPPER_SERVICES[];
|
||||||
|
extern const size_t FLIPPER_SERVICES_COUNT;
|
||||||
|
|
||||||
extern const FuriApplication FLIPPER_APPS[];
|
/* Apps list
|
||||||
size_t FLIPPER_APPS_size();
|
* Spawned by app-loader
|
||||||
|
*/
|
||||||
|
extern const FlipperApplication FLIPPER_APPS[];
|
||||||
|
extern const size_t FLIPPER_APPS_COUNT;
|
||||||
|
|
||||||
extern const FuriApplication FLIPPER_PLUGINS[];
|
/* Plugins list
|
||||||
size_t FLIPPER_PLUGINS_size();
|
* Spawned by app-loader
|
||||||
|
*/
|
||||||
|
extern const FlipperApplication FLIPPER_PLUGINS[];
|
||||||
|
extern const size_t FLIPPER_PLUGINS_COUNT;
|
||||||
|
@ -78,15 +78,12 @@ endif
|
|||||||
APP_TEST ?= 0
|
APP_TEST ?= 0
|
||||||
ifeq ($(APP_TEST), 1)
|
ifeq ($(APP_TEST), 1)
|
||||||
CFLAGS += -DAPP_TEST
|
CFLAGS += -DAPP_TEST
|
||||||
C_SOURCES += $(APP_DIR)/tests/furiac_test.c
|
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_record_test.c
|
C_SOURCES += $(APP_DIR)/tests/furi_record_test.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/test_index.c
|
C_SOURCES += $(APP_DIR)/tests/test_index.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/minunit_test.c
|
C_SOURCES += $(APP_DIR)/tests/minunit_test.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_valuemutex_test.c
|
C_SOURCES += $(APP_DIR)/tests/furi_valuemutex_test.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_pubsub_test.c
|
C_SOURCES += $(APP_DIR)/tests/furi_pubsub_test.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_memmgr_test.c
|
C_SOURCES += $(APP_DIR)/tests/furi_memmgr_test.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_value_expanders_test.c
|
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_event_test.c
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
APP_EXAMPLE_BLINK ?= 0
|
APP_EXAMPLE_BLINK ?= 0
|
||||||
@ -147,18 +144,6 @@ C_SOURCES += $(APP_DIR)/examples/u8g2_qrcode.c
|
|||||||
C_SOURCES += $(LIB_DIR)/qrcode/qrcode.c
|
C_SOURCES += $(LIB_DIR)/qrcode/qrcode.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
APP_EXAMPLE_FATFS ?= 0
|
|
||||||
ifeq ($(APP_EXAMPLE_FATFS), 1)
|
|
||||||
CFLAGS += -DAPP_EXAMPLE_FATFS
|
|
||||||
BUILD_EXAMPLE_FATFS = 1
|
|
||||||
endif
|
|
||||||
BUILD_EXAMPLE_FATFS ?= 0
|
|
||||||
ifeq ($(BUILD_EXAMPLE_FATFS), 1)
|
|
||||||
CFLAGS += -DBUILD_EXAMPLE_FATFS
|
|
||||||
C_SOURCES += $(APP_DIR)/examples/fatfs_list.c
|
|
||||||
APP_INPUT = 1
|
|
||||||
endif
|
|
||||||
|
|
||||||
APP_CC1101 ?= 0
|
APP_CC1101 ?= 0
|
||||||
ifeq ($(APP_CC1101), 1)
|
ifeq ($(APP_CC1101), 1)
|
||||||
CFLAGS += -DAPP_CC1101
|
CFLAGS += -DAPP_CC1101
|
||||||
|
@ -7,7 +7,7 @@ static void event_cb(const void* value, void* ctx) {
|
|||||||
osThreadFlagsSet((osThreadId_t)ctx, BACKLIGHT_FLAG_ACTIVITY);
|
osThreadFlagsSet((osThreadId_t)ctx, BACKLIGHT_FLAG_ACTIVITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void backlight_control(void* p) {
|
int32_t backlight_control(void* p) {
|
||||||
// TODO open record
|
// TODO open record
|
||||||
const GpioPin* backlight_record = &backlight_gpio;
|
const GpioPin* backlight_record = &backlight_gpio;
|
||||||
|
|
||||||
@ -28,4 +28,6 @@ void backlight_control(void* p) {
|
|||||||
gpio_write(backlight_record, false);
|
gpio_write(backlight_record, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -37,7 +37,7 @@ void bt_cli_info(string_t args, void* context) {
|
|||||||
string_clear(buffer);
|
string_clear(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_task() {
|
int32_t bt_task() {
|
||||||
Bt* bt = bt_alloc();
|
Bt* bt = bt_alloc();
|
||||||
|
|
||||||
furi_record_create("bt", bt);
|
furi_record_create("bt", bt);
|
||||||
@ -48,4 +48,6 @@ void bt_task() {
|
|||||||
view_port_enabled_set(bt->statusbar_view_port, api_hal_bt_is_alive());
|
view_port_enabled_set(bt->statusbar_view_port, api_hal_bt_is_alive());
|
||||||
osDelay(1024);
|
osDelay(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -345,7 +345,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
|
|||||||
osMessageQueuePut(event_queue, &event, 0, 0);
|
osMessageQueuePut(event_queue, &event, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void cc1101_workaround(void* p) {
|
extern "C" int32_t cc1101_workaround(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
|
||||||
furi_check(event_queue);
|
furi_check(event_queue);
|
||||||
|
|
||||||
@ -363,7 +363,7 @@ extern "C" void cc1101_workaround(void* p) {
|
|||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
||||||
printf("[cc1101] cannot create mutex\r\n");
|
printf("[cc1101] cannot create mutex\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
ViewPort* view_port = view_port_alloc();
|
||||||
@ -375,7 +375,7 @@ extern "C" void cc1101_workaround(void* p) {
|
|||||||
Gui* gui = (Gui*)furi_record_open("gui");
|
Gui* gui = (Gui*)furi_record_open("gui");
|
||||||
if(gui == NULL) {
|
if(gui == NULL) {
|
||||||
printf("[cc1101] gui is not available\r\n");
|
printf("[cc1101] gui is not available\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
@ -397,7 +397,7 @@ extern "C" void cc1101_workaround(void* p) {
|
|||||||
printf("[cc1101] init done: %d\r\n", address);
|
printf("[cc1101] init done: %d\r\n", address);
|
||||||
} else {
|
} else {
|
||||||
printf("[cc1101] init fail\r\n");
|
printf("[cc1101] init fail\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
cc1101.SpiStrobe(CC1101_SIDLE);
|
cc1101.SpiStrobe(CC1101_SIDLE);
|
||||||
@ -439,7 +439,7 @@ extern "C" void cc1101_workaround(void* p) {
|
|||||||
|
|
||||||
// TODO remove all view_ports create by app
|
// TODO remove all view_ports create by app
|
||||||
view_port_enabled_set(view_port, false);
|
view_port_enabled_set(view_port, false);
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.value.input.type == InputTypeShort &&
|
if(event.value.input.type == InputTypeShort &&
|
||||||
@ -597,4 +597,6 @@ extern "C" void cc1101_workaround(void* p) {
|
|||||||
release_mutex(&state_mutex, state);
|
release_mutex(&state_mutex, state);
|
||||||
view_port_update(view_port);
|
view_port_update(view_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -172,7 +172,7 @@ void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* con
|
|||||||
string_clear(name_str);
|
string_clear(name_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_task(void* p) {
|
int32_t cli_task(void* p) {
|
||||||
Cli* cli = cli_alloc();
|
Cli* cli = cli_alloc();
|
||||||
|
|
||||||
// Init basic cli commands
|
// Init basic cli commands
|
||||||
@ -184,4 +184,6 @@ void cli_task(void* p) {
|
|||||||
while(1) {
|
while(1) {
|
||||||
cli_process_input(cli);
|
cli_process_input(cli);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
extern TIM_HandleTypeDef SPEAKER_TIM;
|
extern TIM_HandleTypeDef SPEAKER_TIM;
|
||||||
|
|
||||||
void coreglitch_demo_0(void* p) {
|
int32_t coreglitch_demo_0(void* p) {
|
||||||
printf("coreglitch demo!\r\n");
|
printf("coreglitch demo!\r\n");
|
||||||
|
|
||||||
float notes[] = {
|
float notes[] = {
|
||||||
@ -47,4 +47,6 @@ void coreglitch_demo_0(void* p) {
|
|||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) {
|
|||||||
furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK);
|
furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dolphin_task() {
|
int32_t dolphin_task() {
|
||||||
Dolphin* dolphin = dolphin_alloc();
|
Dolphin* dolphin = dolphin_alloc();
|
||||||
|
|
||||||
Gui* gui = furi_record_open("gui");
|
Gui* gui = furi_record_open("gui");
|
||||||
@ -163,4 +163,6 @@ void dolphin_task() {
|
|||||||
dolphin_state_save(dolphin->state);
|
dolphin_state_save(dolphin->state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ void rgb_set(
|
|||||||
gpio_write(led_b, !b);
|
gpio_write(led_b, !b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void application_blink(void* p) {
|
int32_t application_blink(void* p) {
|
||||||
// TODO open record
|
// TODO open record
|
||||||
const GpioPin* led_r_record = &led_gpio[0];
|
const GpioPin* led_r_record = &led_gpio[0];
|
||||||
const GpioPin* led_g_record = &led_gpio[1];
|
const GpioPin* led_g_record = &led_gpio[1];
|
||||||
@ -41,4 +41,6 @@ void application_blink(void* p) {
|
|||||||
rgb_set(0, 0, 0, led_r_record, led_g_record, led_b_record);
|
rgb_set(0, 0, 0, led_r_record, led_g_record, led_b_record);
|
||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ static void event_cb(const void* value, void* ctx) {
|
|||||||
printf("event: %02x %s\r\n", event->key, event->type ? "pressed" : "released");
|
printf("event: %02x %s\r\n", event->key, event->type ? "pressed" : "released");
|
||||||
}
|
}
|
||||||
|
|
||||||
void application_input_dump(void* p) {
|
int32_t application_input_dump(void* p) {
|
||||||
// open record
|
// open record
|
||||||
PubSub* event_record = furi_record_open("input_events");
|
PubSub* event_record = furi_record_open("input_events");
|
||||||
subscribe_pubsub(event_record, event_cb, NULL);
|
subscribe_pubsub(event_record, event_cb, NULL);
|
||||||
@ -23,4 +23,6 @@ void application_input_dump(void* p) {
|
|||||||
for(;;) {
|
for(;;) {
|
||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "u8g2/u8g2.h"
|
#include "u8g2/u8g2.h"
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
void u8g2_example(void* p) {
|
int32_t u8g2_example(void* p) {
|
||||||
// open record
|
// open record
|
||||||
u8g2_t* fb = furi_record_open("u8g2_fb");
|
u8g2_t* fb = furi_record_open("u8g2_fb");
|
||||||
u8g2_SetFont(fb, u8g2_font_6x10_mf);
|
u8g2_SetFont(fb, u8g2_font_6x10_mf);
|
||||||
@ -10,5 +10,5 @@ void u8g2_example(void* p) {
|
|||||||
u8g2_DrawStr(fb, 2, 12, "hello world!");
|
u8g2_DrawStr(fb, 2, 12, "hello world!");
|
||||||
furi_record_close("u8g2_fb");
|
furi_record_close("u8g2_fb");
|
||||||
|
|
||||||
furiac_exit(NULL);
|
return 0;
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ void u8g2_DrawPixelSize(u8g2_t* u8g2, uint8_t x, uint8_t y, uint8_t size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void u8g2_qrcode(void* p) {
|
int32_t u8g2_qrcode(void* p) {
|
||||||
// open record
|
// open record
|
||||||
FuriRecordSubscriber* fb_record =
|
FuriRecordSubscriber* fb_record =
|
||||||
furi_open_deprecated("u8g2_fb", false, false, NULL, NULL, NULL);
|
furi_open_deprecated("u8g2_fb", false, false, NULL, NULL, NULL);
|
||||||
@ -38,7 +38,7 @@ void u8g2_qrcode(void* p) {
|
|||||||
|
|
||||||
if(fb_record == NULL) {
|
if(fb_record == NULL) {
|
||||||
printf("[view_port] cannot create fb record\r\n");
|
printf("[view_port] cannot create fb record\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8g2_t* fb = furi_take(fb_record);
|
u8g2_t* fb = furi_take(fb_record);
|
||||||
@ -63,12 +63,14 @@ void u8g2_qrcode(void* p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_commit(fb_record);
|
furi_commit(fb_record);
|
||||||
|
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
@ -1,7 +1,7 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void application_uart_write(void* p) {
|
int32_t application_uart_write(void* p) {
|
||||||
// Red led for showing progress
|
// Red led for showing progress
|
||||||
GpioPin led = {.pin = GPIO_PIN_8, .port = GPIOA};
|
GpioPin led = {.pin = GPIO_PIN_8, .port = GPIOA};
|
||||||
// TODO open record
|
// TODO open record
|
||||||
@ -29,4 +29,6 @@ void application_uart_write(void* p) {
|
|||||||
// delay with overall perion of 1s
|
// delay with overall perion of 1s
|
||||||
delay(950);
|
delay(950);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -21,7 +21,7 @@ static void button_handler(const void* value, void* _ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void application_vibro(void* p) {
|
int32_t application_vibro(void* p) {
|
||||||
Ctx ctx = {.led = (GpioPin*)&led_gpio[1], .vibro = (GpioPin*)&vibro_gpio};
|
Ctx ctx = {.led = (GpioPin*)&led_gpio[1], .vibro = (GpioPin*)&vibro_gpio};
|
||||||
|
|
||||||
gpio_init(ctx.led, GpioModeOutputOpenDrain);
|
gpio_init(ctx.led, GpioModeOutputOpenDrain);
|
||||||
@ -37,4 +37,6 @@ void application_vibro(void* p) {
|
|||||||
while(1) {
|
while(1) {
|
||||||
osDelay(osWaitForever);
|
osDelay(osWaitForever);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
Subproject commit 598edd54197d58b35d8cc2513549ddda3938fa38
|
Subproject commit a68a7eb200712b24c16a62713a5d137152805cd1
|
@ -57,7 +57,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
|
|||||||
osMessageQueuePut(event_queue, &event, 0, 0);
|
osMessageQueuePut(event_queue, &event, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_gpio_test(void* p) {
|
int32_t app_gpio_test(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
|
||||||
furi_check(event_queue);
|
furi_check(event_queue);
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ void app_gpio_test(void* p) {
|
|||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
||||||
printf("[gpio-tester] cannot create mutex\r\n");
|
printf("[gpio-tester] cannot create mutex\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
ViewPort* view_port = view_port_alloc();
|
||||||
@ -98,7 +98,7 @@ void app_gpio_test(void* p) {
|
|||||||
printf("[gpio-tester] bye!\r\n");
|
printf("[gpio-tester] bye!\r\n");
|
||||||
// TODO remove all view_ports create by app
|
// TODO remove all view_ports create by app
|
||||||
view_port_enabled_set(view_port, false);
|
view_port_enabled_set(view_port, false);
|
||||||
furiac_exit(NULL);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.value.input.type == InputTypeShort &&
|
if(event.value.input.type == InputTypeShort &&
|
||||||
@ -130,4 +130,6 @@ void app_gpio_test(void* p) {
|
|||||||
release_mutex(&state_mutex, state);
|
release_mutex(&state_mutex, state);
|
||||||
view_port_update(view_port);
|
view_port_update(view_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ void text_input_callback(void* context, char* text) {
|
|||||||
next_view(context);
|
next_view(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_test(void* param) {
|
int32_t gui_test(void* param) {
|
||||||
(void)param;
|
(void)param;
|
||||||
GuiTester* gui_tester = gui_test_alloc();
|
GuiTester* gui_tester = gui_test_alloc();
|
||||||
|
|
||||||
@ -154,4 +154,6 @@ void gui_test(void* param) {
|
|||||||
while(1) {
|
while(1) {
|
||||||
osDelay(1000);
|
osDelay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -280,7 +280,7 @@ Gui* gui_alloc() {
|
|||||||
return gui;
|
return gui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_task(void* p) {
|
int32_t gui_task(void* p) {
|
||||||
Gui* gui = gui_alloc();
|
Gui* gui = gui_alloc();
|
||||||
|
|
||||||
furi_record_create("gui", gui);
|
furi_record_create("gui", gui);
|
||||||
@ -302,4 +302,6 @@ void gui_task(void* p) {
|
|||||||
gui_redraw(gui);
|
gui_redraw(gui);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ void AppiButton::run() {
|
|||||||
gpio_init(red_led_record, GpioModeOutputOpenDrain);
|
gpio_init(red_led_record, GpioModeOutputOpenDrain);
|
||||||
gpio_init(green_led_record, GpioModeOutputOpenDrain);
|
gpio_init(green_led_record, GpioModeOutputOpenDrain);
|
||||||
|
|
||||||
api_hal_timebase_insomnia_enter();
|
api_hal_power_insomnia_enter();
|
||||||
app_ready();
|
app_ready();
|
||||||
|
|
||||||
AppiButtonEvent event;
|
AppiButtonEvent event;
|
||||||
@ -35,9 +35,9 @@ void AppiButton::run() {
|
|||||||
event.value.input.key == InputKeyBack) {
|
event.value.input.key == InputKeyBack) {
|
||||||
view_port_enabled_set(view_port, false);
|
view_port_enabled_set(view_port, false);
|
||||||
gui_remove_view_port(gui, view_port);
|
gui_remove_view_port(gui, view_port);
|
||||||
api_hal_timebase_insomnia_exit();
|
api_hal_power_insomnia_exit();
|
||||||
|
|
||||||
osThreadExit();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.value.input.type == InputTypeShort &&
|
if(event.value.input.type == InputTypeShort &&
|
||||||
@ -174,7 +174,8 @@ void AppiButton::switch_to_mode(uint8_t mode_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// app enter function
|
// app enter function
|
||||||
extern "C" void app_ibutton(void* p) {
|
extern "C" int32_t app_ibutton(void* p) {
|
||||||
AppiButton* app = new AppiButton();
|
AppiButton* app = new AppiButton();
|
||||||
app->run();
|
app->run();
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -18,7 +18,7 @@ void input_isr(void* _pin, void* _ctx) {
|
|||||||
osThreadFlagsSet(input->thread, INPUT_THREAD_FLAG_ISR);
|
osThreadFlagsSet(input->thread, INPUT_THREAD_FLAG_ISR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_task() {
|
int32_t input_task() {
|
||||||
input = furi_alloc(sizeof(Input));
|
input = furi_alloc(sizeof(Input));
|
||||||
input->thread = osThreadGetId();
|
input->thread = osThreadGetId();
|
||||||
init_pubsub(&input->event_pubsub);
|
init_pubsub(&input->event_pubsub);
|
||||||
@ -69,4 +69,6 @@ void input_task() {
|
|||||||
osThreadFlagsWait(INPUT_THREAD_FLAG_ISR, osFlagsWaitAny, osWaitForever);
|
osThreadFlagsWait(INPUT_THREAD_FLAG_ISR, osFlagsWaitAny, osWaitForever);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ void init_packet(
|
|||||||
state->packets[index].command = command;
|
state->packets[index].command = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
void irda(void* p) {
|
int32_t irda(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL);
|
||||||
|
|
||||||
State _state;
|
State _state;
|
||||||
@ -262,7 +262,7 @@ void irda(void* p) {
|
|||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
||||||
printf("cannot create mutex\r\n");
|
printf("cannot create mutex\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
ViewPort* view_port = view_port_alloc();
|
||||||
@ -316,7 +316,7 @@ void irda(void* p) {
|
|||||||
osMessageQueueDelete(event_queue);
|
osMessageQueueDelete(event_queue);
|
||||||
|
|
||||||
// exit
|
// exit
|
||||||
furiac_exit(NULL);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.value.input.type == InputTypeShort &&
|
if(event.value.input.type == InputTypeShort &&
|
||||||
|
@ -160,7 +160,7 @@ static void extract_data(uint8_t* buf, uint8_t* customer, uint32_t* em_data) {
|
|||||||
*em_data = data;
|
*em_data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lf_rfid_workaround(void* p) {
|
int32_t lf_rfid_workaround(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
|
||||||
|
|
||||||
// create pin
|
// create pin
|
||||||
@ -195,7 +195,7 @@ void lf_rfid_workaround(void* p) {
|
|||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
||||||
printf("cannot create mutex\r\n");
|
printf("cannot create mutex\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
ViewPort* view_port = view_port_alloc();
|
||||||
@ -293,7 +293,7 @@ void lf_rfid_workaround(void* p) {
|
|||||||
|
|
||||||
// TODO remove all view_ports create by app
|
// TODO remove all view_ports create by app
|
||||||
view_port_enabled_set(view_port, false);
|
view_port_enabled_set(view_port, false);
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.value.input.type == InputTypePress &&
|
if(event.value.input.type == InputTypePress &&
|
||||||
@ -356,4 +356,6 @@ void lf_rfid_workaround(void* p) {
|
|||||||
release_mutex(&state_mutex, state);
|
release_mutex(&state_mutex, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ ValueMutex* menu_init() {
|
|||||||
ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
|
ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
|
||||||
if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
|
if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
|
||||||
printf("[menu_task] cannot create menu mutex\r\n");
|
printf("[menu_task] cannot create menu mutex\r\n");
|
||||||
furiac_exit(NULL);
|
furi_check(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenGui record
|
// OpenGui record
|
||||||
@ -223,7 +223,7 @@ void menu_exit(Menu* menu) {
|
|||||||
menu_update(menu);
|
menu_update(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_task(void* p) {
|
int32_t menu_task(void* p) {
|
||||||
ValueMutex* menu_mutex = menu_init();
|
ValueMutex* menu_mutex = menu_init();
|
||||||
|
|
||||||
MenuEvent* menu_event = NULL;
|
MenuEvent* menu_event = NULL;
|
||||||
@ -267,4 +267,6 @@ void menu_task(void* p) {
|
|||||||
|
|
||||||
release_mutex(menu_mutex, menu);
|
release_mutex(menu_mutex, menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -356,7 +356,7 @@ void music_player_thread(void* p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void music_player(void* p) {
|
int32_t music_player(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(MusicDemoEvent), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(MusicDemoEvent), NULL);
|
||||||
|
|
||||||
State _state;
|
State _state;
|
||||||
@ -370,7 +370,7 @@ void music_player(void* p) {
|
|||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
||||||
printf("cannot create mutex\r\n");
|
printf("cannot create mutex\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
ViewPort* view_port = view_port_alloc();
|
||||||
@ -394,7 +394,7 @@ void music_player(void* p) {
|
|||||||
|
|
||||||
if(player == NULL) {
|
if(player == NULL) {
|
||||||
printf("cannot create player thread\r\n");
|
printf("cannot create player thread\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
MusicDemoEvent event;
|
MusicDemoEvent event;
|
||||||
@ -449,4 +449,6 @@ void music_player(void* p) {
|
|||||||
view_port_update(view_port);
|
view_port_update(view_port);
|
||||||
release_mutex(&state_mutex, state);
|
release_mutex(&state_mutex, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_task(void* p) {
|
int32_t nfc_task(void* p) {
|
||||||
Nfc* nfc = nfc_alloc();
|
Nfc* nfc = nfc_alloc();
|
||||||
|
|
||||||
Gui* gui = furi_record_open("gui");
|
Gui* gui = furi_record_open("gui");
|
||||||
@ -142,4 +142,6 @@ void nfc_task(void* p) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -41,4 +41,4 @@ void nfc_menu_field_callback(void* context);
|
|||||||
|
|
||||||
void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state);
|
void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state);
|
||||||
|
|
||||||
void nfc_task(void* p);
|
int32_t nfc_task(void* p);
|
@ -55,7 +55,7 @@ void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
|
|||||||
void nfc_worker_task(void* context) {
|
void nfc_worker_task(void* context) {
|
||||||
NfcWorker* nfc_worker = context;
|
NfcWorker* nfc_worker = context;
|
||||||
|
|
||||||
api_hal_timebase_insomnia_enter();
|
api_hal_power_insomnia_enter();
|
||||||
|
|
||||||
rfalLowPowerModeStop();
|
rfalLowPowerModeStop();
|
||||||
if(nfc_worker->state == NfcWorkerStatePoll) {
|
if(nfc_worker->state == NfcWorkerStatePoll) {
|
||||||
@ -69,7 +69,7 @@ void nfc_worker_task(void* context) {
|
|||||||
|
|
||||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
||||||
|
|
||||||
api_hal_timebase_insomnia_exit();
|
api_hal_power_insomnia_exit();
|
||||||
osThreadExit();
|
osThreadExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ void power_cli_otg_off(string_t args, void* context) {
|
|||||||
api_hal_power_disable_otg();
|
api_hal_power_disable_otg();
|
||||||
}
|
}
|
||||||
|
|
||||||
void power_task(void* p) {
|
int32_t power_task(void* p) {
|
||||||
(void)p;
|
(void)p;
|
||||||
Power* power = power_alloc();
|
Power* power = power_alloc();
|
||||||
|
|
||||||
@ -223,4 +223,6 @@ void power_task(void* p) {
|
|||||||
view_port_enabled_set(power->usb_view_port, api_hal_power_is_charging());
|
view_port_enabled_set(power->usb_view_port, api_hal_power_is_charging());
|
||||||
osDelay(1024);
|
osDelay(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -904,7 +904,8 @@ void SdTest::render(Canvas* canvas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// app enter function
|
// app enter function
|
||||||
extern "C" void sd_card_test(void* p) {
|
extern "C" int32_t sd_card_test(void* p) {
|
||||||
SdTest* app = new SdTest();
|
SdTest* app = new SdTest();
|
||||||
app->run();
|
app->run();
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -101,9 +101,7 @@ SdApp* sd_app_alloc() {
|
|||||||
SdApp* sd_app = furi_alloc(sizeof(SdApp));
|
SdApp* sd_app = furi_alloc(sizeof(SdApp));
|
||||||
|
|
||||||
// init inner fs data
|
// init inner fs data
|
||||||
if(!_fs_init(&sd_app->info)) {
|
furi_check(_fs_init(&sd_app->info));
|
||||||
furiac_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
sd_app->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
|
sd_app->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
|
||||||
|
|
||||||
@ -476,7 +474,7 @@ static void cli_sd_info(string_t args, void* _ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sd_filesystem(void* p) {
|
int32_t sd_filesystem(void* p) {
|
||||||
SdApp* sd_app = sd_app_alloc();
|
SdApp* sd_app = sd_app_alloc();
|
||||||
FS_Api* fs_api = fs_api_alloc();
|
FS_Api* fs_api = fs_api_alloc();
|
||||||
|
|
||||||
@ -555,4 +553,6 @@ void sd_filesystem(void* p) {
|
|||||||
|
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -161,7 +161,8 @@ void AppSdNFC::blink_green() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// app enter function
|
// app enter function
|
||||||
extern "C" void sdnfc(void* p) {
|
extern "C" int32_t sdnfc(void* p) {
|
||||||
AppSdNFC* app = new AppSdNFC();
|
AppSdNFC* app = new AppSdNFC();
|
||||||
app->run();
|
app->run();
|
||||||
|
return 0;
|
||||||
}
|
}
|
@ -37,7 +37,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
|
|||||||
osMessageQueuePut(event_queue, &event, 0, 0);
|
osMessageQueuePut(event_queue, &event, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void template_app(void* p) {
|
int32_t template_app(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(Event), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(Event), NULL);
|
||||||
|
|
||||||
State _state;
|
State _state;
|
||||||
@ -45,7 +45,7 @@ void template_app(void* p) {
|
|||||||
ValueMutex state_mutex;
|
ValueMutex state_mutex;
|
||||||
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
|
||||||
printf("cannot create mutex\r\n");
|
printf("cannot create mutex\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
ViewPort* view_port = view_port_alloc();
|
||||||
@ -57,7 +57,7 @@ void template_app(void* p) {
|
|||||||
Gui* gui = furi_record_open("gui");
|
Gui* gui = furi_record_open("gui");
|
||||||
if(gui == NULL) {
|
if(gui == NULL) {
|
||||||
printf("gui is not available\r\n");
|
printf("gui is not available\r\n");
|
||||||
furiac_exit(NULL);
|
return 255;
|
||||||
}
|
}
|
||||||
gui_add_view_port(gui, view_port, /* specify UI layer */);
|
gui_add_view_port(gui, view_port, /* specify UI layer */);
|
||||||
|
|
||||||
@ -99,4 +99,6 @@ void template_app(void* p) {
|
|||||||
|
|
||||||
release_mutex(&state_mutex, state);
|
release_mutex(&state_mutex, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
#include <furi.h>
|
|
||||||
#include "minunit.h"
|
|
||||||
|
|
||||||
static void furi_concurent_app(void* p) {
|
|
||||||
Event* event = p;
|
|
||||||
|
|
||||||
signal_event(event);
|
|
||||||
|
|
||||||
furiac_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_furi_event() {
|
|
||||||
mu_assert(false, "please reimplement or delete test");
|
|
||||||
|
|
||||||
/*Event event;
|
|
||||||
|
|
||||||
mu_check(init_event(&event));
|
|
||||||
|
|
||||||
// The event should not be signalled right after creation
|
|
||||||
mu_check(!wait_event_with_timeout(&event, 100));
|
|
||||||
|
|
||||||
// Create second app
|
|
||||||
FuriApp* second_app __attribute__((unused)) =
|
|
||||||
furiac_start(furi_concurent_app, "furi concurent app", (void*)&event);
|
|
||||||
|
|
||||||
// The event should be signalled now
|
|
||||||
mu_check(wait_event_with_timeout(&event, 100));
|
|
||||||
|
|
||||||
// The event should not be signalled once it's processed
|
|
||||||
mu_check(!wait_event_with_timeout(&event, 100));
|
|
||||||
|
|
||||||
mu_check(delete_event(&event));*/
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
#include <furi.h>
|
|
||||||
#include "minunit.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t red;
|
|
||||||
uint8_t green;
|
|
||||||
uint8_t blue;
|
|
||||||
} Rgb;
|
|
||||||
|
|
||||||
static uint32_t rgb_final_state;
|
|
||||||
|
|
||||||
static void rgb_clear(void* ctx, void* state) {
|
|
||||||
Rgb* rgb = state;
|
|
||||||
rgb->red = 0;
|
|
||||||
rgb->green = 0;
|
|
||||||
rgb->blue = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rgb_commit(void* ctx, void* state) {
|
|
||||||
Rgb* rgb = state;
|
|
||||||
rgb_final_state = ((uint32_t)rgb->red) | (((uint32_t)rgb->green) << 8) |
|
|
||||||
(((uint32_t)rgb->blue) << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_red_composer(void* ctx, void* state) {
|
|
||||||
Rgb* rgb = state;
|
|
||||||
uint8_t* red = ctx;
|
|
||||||
|
|
||||||
rgb->red = *red;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_furi_value_composer() {
|
|
||||||
Rgb rgb = {0, 0, 0};
|
|
||||||
ValueComposer composer;
|
|
||||||
Rgb layer1_rgb = {0, 0, 0};
|
|
||||||
ValueMutex layer1_mutex;
|
|
||||||
uint8_t layer2_red = 0;
|
|
||||||
|
|
||||||
rgb_final_state = 0xdeadbeef;
|
|
||||||
|
|
||||||
mu_check(init_composer(&composer, &rgb));
|
|
||||||
|
|
||||||
mu_check(init_mutex(&layer1_mutex, &layer1_rgb, sizeof(layer1_rgb)));
|
|
||||||
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0xdeadbeef, rgb_final_state);
|
|
||||||
|
|
||||||
ValueComposerHandle* layer1_handle =
|
|
||||||
add_compose_layer(&composer, COPY_COMPOSE, &layer1_mutex, UiLayerNotify);
|
|
||||||
mu_assert_pointers_not_eq(layer1_handle, NULL);
|
|
||||||
|
|
||||||
// RGB state should be updated with the layer1 state
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x000000, rgb_final_state);
|
|
||||||
|
|
||||||
layer2_red = 0xcc;
|
|
||||||
ValueComposerHandle* layer2_handle =
|
|
||||||
add_compose_layer(&composer, set_red_composer, &layer2_red, UiLayerAboveNotify);
|
|
||||||
mu_assert_pointers_not_eq(layer2_handle, NULL);
|
|
||||||
|
|
||||||
// RGB state should be updated with the layer1 and layer2 state, in order
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x0000cc, rgb_final_state);
|
|
||||||
|
|
||||||
// Change layer1 state
|
|
||||||
Rgb* state = acquire_mutex(&layer1_mutex, 0);
|
|
||||||
mu_assert_pointers_not_eq(state, NULL);
|
|
||||||
state->red = 0x12;
|
|
||||||
state->green = 0x34;
|
|
||||||
state->blue = 0x56;
|
|
||||||
release_mutex(&layer1_mutex, state);
|
|
||||||
|
|
||||||
// Nothing should happen, we need to trigger composition request first
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x0000cc, rgb_final_state);
|
|
||||||
|
|
||||||
request_compose(layer1_handle);
|
|
||||||
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x5634cc, rgb_final_state);
|
|
||||||
|
|
||||||
// Change layer2 state
|
|
||||||
layer2_red = 0xff;
|
|
||||||
|
|
||||||
// Nothing should happen, we need to trigger composition request first
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x5634cc, rgb_final_state);
|
|
||||||
|
|
||||||
request_compose(layer2_handle);
|
|
||||||
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x5634ff, rgb_final_state);
|
|
||||||
|
|
||||||
// Remove layer1
|
|
||||||
mu_check(remove_compose_layer(layer1_handle));
|
|
||||||
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x0000ff, rgb_final_state);
|
|
||||||
|
|
||||||
// Remove layer2
|
|
||||||
mu_check(remove_compose_layer(layer2_handle));
|
|
||||||
|
|
||||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
|
||||||
mu_assert_int_eq(0x000000, rgb_final_state);
|
|
||||||
|
|
||||||
mu_check(delete_composer(&composer));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const uint32_t notify_value_0 = 0x12345678;
|
|
||||||
static const uint32_t notify_value_1 = 0x11223344;
|
|
||||||
|
|
||||||
static uint32_t pubsub_value = 0;
|
|
||||||
|
|
||||||
void test_value_manager_handler(const void* arg, void* ctx) {
|
|
||||||
pubsub_value = *(uint32_t*)arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_furi_value_manager() {
|
|
||||||
uint32_t value = 0;
|
|
||||||
ValueManager managed;
|
|
||||||
|
|
||||||
mu_check(init_managed(&managed, &value, sizeof(value)));
|
|
||||||
|
|
||||||
pubsub_value = 0;
|
|
||||||
|
|
||||||
PubSubItem* test_pubsub_item;
|
|
||||||
test_pubsub_item = subscribe_pubsub(&managed.pubsub, test_value_manager_handler, 0);
|
|
||||||
mu_assert_pointers_not_eq(test_pubsub_item, NULL);
|
|
||||||
|
|
||||||
mu_check(write_managed(&managed, (void*)¬ify_value_0, sizeof(notify_value_0), 100));
|
|
||||||
|
|
||||||
mu_assert_int_eq(pubsub_value, notify_value_0);
|
|
||||||
|
|
||||||
uint32_t* ptr = acquire_mutex(&managed.value, 100);
|
|
||||||
mu_assert_pointers_not_eq(ptr, NULL);
|
|
||||||
|
|
||||||
*ptr = notify_value_1;
|
|
||||||
|
|
||||||
mu_check(commit_managed(&managed, ptr));
|
|
||||||
|
|
||||||
mu_assert_int_eq(pubsub_value, notify_value_1);
|
|
||||||
|
|
||||||
mu_check(delete_managed(&managed));
|
|
||||||
}
|
|
@ -60,7 +60,7 @@ void furi_concurent_app(void* p) {
|
|||||||
ValueMutex* mutex = (ValueMutex*)p;
|
ValueMutex* mutex = (ValueMutex*)p;
|
||||||
if(mutex == NULL) {
|
if(mutex == NULL) {
|
||||||
printf("cannot open mutex\r\n");
|
printf("cannot open mutex\r\n");
|
||||||
furiac_exit(NULL);
|
osThreadExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < 10; i++) {
|
for(size_t i = 0; i < 10; i++) {
|
||||||
@ -69,7 +69,7 @@ void furi_concurent_app(void* p) {
|
|||||||
if(value == NULL) {
|
if(value == NULL) {
|
||||||
printf("cannot take record\r\n");
|
printf("cannot take record\r\n");
|
||||||
release_mutex(mutex, value);
|
release_mutex(mutex, value);
|
||||||
furiac_exit(NULL);
|
osThreadExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// emulate read-modify-write broken by context switching
|
// emulate read-modify-write broken by context switching
|
||||||
@ -83,7 +83,7 @@ void furi_concurent_app(void* p) {
|
|||||||
release_mutex(mutex, value);
|
release_mutex(mutex, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
furiac_exit(NULL);
|
osThreadExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_furi_concurrent_access() {
|
void test_furi_concurrent_access() {
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <furi.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
Test: creating and killing task
|
|
||||||
|
|
||||||
1. create task
|
|
||||||
2. delay 10 ms
|
|
||||||
3. kill task
|
|
||||||
4. check that value changes
|
|
||||||
5. delay 2 ms
|
|
||||||
6. check that value stay unchanged
|
|
||||||
*/
|
|
||||||
|
|
||||||
void create_kill_app(void* p) {
|
|
||||||
// this app simply increase counter
|
|
||||||
uint8_t* counter = (uint8_t*)p;
|
|
||||||
while(1) {
|
|
||||||
*counter = *counter + 1;
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test_furi_ac_create_kill() {
|
|
||||||
mu_assert(false, "please reimplement or delete test");
|
|
||||||
/*
|
|
||||||
uint8_t counter = 0;
|
|
||||||
|
|
||||||
uint8_t value_a = counter;
|
|
||||||
|
|
||||||
FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter);
|
|
||||||
if(widget == NULL) {
|
|
||||||
printf("create widget fail\r\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(10);
|
|
||||||
|
|
||||||
if(!furiac_kill(widget)) {
|
|
||||||
printf("kill widget fail\r\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(value_a == counter) {
|
|
||||||
printf("counter unchanged\r\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
value_a = counter;
|
|
||||||
|
|
||||||
delay(10);
|
|
||||||
|
|
||||||
if(value_a != counter) {
|
|
||||||
printf("counter changes after kill (counter = %d vs %d)\n", value_a, counter);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Test: switch between tasks
|
|
||||||
1. init s
|
|
||||||
2. create task A, add 'A" to sequence'
|
|
||||||
3. switch to task B, add 'B' to sequence
|
|
||||||
4. exit from task B -> switch to A and add 'A' to sequence
|
|
||||||
5. cleanup: exit from task A
|
|
||||||
6. check sequence
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define TEST_SWITCH_CONTEXT_SEQ_SIZE 8
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char sequence[TEST_SWITCH_CONTEXT_SEQ_SIZE];
|
|
||||||
size_t count;
|
|
||||||
} TestSwitchSequence;
|
|
||||||
|
|
||||||
void task_a(void*);
|
|
||||||
void task_b(void*);
|
|
||||||
|
|
||||||
void task_a(void* p) {
|
|
||||||
// simply starts, add 'A' letter to sequence and switch
|
|
||||||
// if sequence counter = 0, call task B, exit otherwise
|
|
||||||
|
|
||||||
TestSwitchSequence* seq = (TestSwitchSequence*)p;
|
|
||||||
|
|
||||||
seq->sequence[seq->count] = 'A';
|
|
||||||
seq->count++;
|
|
||||||
|
|
||||||
if(seq->count == 1) {
|
|
||||||
furiac_switch(task_b, "task B", p);
|
|
||||||
|
|
||||||
// if switch unsuccessfull, this code will executed
|
|
||||||
seq->sequence[seq->count] = 'x';
|
|
||||||
seq->count++;
|
|
||||||
} else {
|
|
||||||
// add '/' symbol on exit
|
|
||||||
seq->sequence[seq->count] = '/';
|
|
||||||
seq->count++;
|
|
||||||
furiac_exit(NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// application simply add 'B' end exit
|
|
||||||
void task_b(void* p) {
|
|
||||||
TestSwitchSequence* seq = (TestSwitchSequence*)p;
|
|
||||||
|
|
||||||
seq->sequence[seq->count] = 'B';
|
|
||||||
seq->count++;
|
|
||||||
|
|
||||||
furiac_exit(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test_furi_ac_switch_exit() {
|
|
||||||
// init sequence
|
|
||||||
TestSwitchSequence seq;
|
|
||||||
seq.count = 0;
|
|
||||||
|
|
||||||
furiac_start(task_a, "task A", (void*)&seq);
|
|
||||||
// TODO how to check that all child task ends?
|
|
||||||
|
|
||||||
delay(10); // wait while task do its work
|
|
||||||
|
|
||||||
seq.sequence[seq.count] = '\0';
|
|
||||||
|
|
||||||
if(strcmp(seq.sequence, "ABA/") != 0) {
|
|
||||||
printf("wrong sequence: %s\n", seq.sequence);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -3,17 +3,11 @@
|
|||||||
#include "minunit_vars.h"
|
#include "minunit_vars.h"
|
||||||
#include "minunit.h"
|
#include "minunit.h"
|
||||||
|
|
||||||
bool test_furi_ac_create_kill();
|
|
||||||
bool test_furi_ac_switch_exit();
|
|
||||||
|
|
||||||
// v2 tests
|
// v2 tests
|
||||||
void test_furi_create_open();
|
void test_furi_create_open();
|
||||||
void test_furi_valuemutex();
|
void test_furi_valuemutex();
|
||||||
void test_furi_concurrent_access();
|
void test_furi_concurrent_access();
|
||||||
void test_furi_pubsub();
|
void test_furi_pubsub();
|
||||||
void test_furi_value_composer();
|
|
||||||
void test_furi_value_manager();
|
|
||||||
void test_furi_event();
|
|
||||||
|
|
||||||
void test_furi_memmgr();
|
void test_furi_memmgr();
|
||||||
|
|
||||||
@ -31,14 +25,6 @@ MU_TEST(test_check) {
|
|||||||
mu_check(foo != 6);
|
mu_check(foo != 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(mu_test_furi_ac_create_kill) {
|
|
||||||
mu_assert_int_eq(test_furi_ac_create_kill(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
MU_TEST(mu_test_furi_ac_switch_exit) {
|
|
||||||
mu_assert_int_eq(test_furi_ac_switch_exit(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// v2 tests
|
// v2 tests
|
||||||
MU_TEST(mu_test_furi_create_open) {
|
MU_TEST(mu_test_furi_create_open) {
|
||||||
test_furi_create_open();
|
test_furi_create_open();
|
||||||
@ -62,30 +48,16 @@ MU_TEST(mu_test_furi_memmgr) {
|
|||||||
test_furi_memmgr();
|
test_furi_memmgr();
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(mu_test_furi_value_expanders) {
|
|
||||||
test_furi_value_composer();
|
|
||||||
test_furi_value_manager();
|
|
||||||
}
|
|
||||||
|
|
||||||
MU_TEST(mu_test_furi_event) {
|
|
||||||
test_furi_event();
|
|
||||||
}
|
|
||||||
|
|
||||||
MU_TEST_SUITE(test_suite) {
|
MU_TEST_SUITE(test_suite) {
|
||||||
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
||||||
|
|
||||||
MU_RUN_TEST(test_check);
|
MU_RUN_TEST(test_check);
|
||||||
MU_RUN_TEST(mu_test_furi_ac_create_kill);
|
|
||||||
MU_RUN_TEST(mu_test_furi_ac_switch_exit);
|
|
||||||
|
|
||||||
// v2 tests
|
// v2 tests
|
||||||
MU_RUN_TEST(mu_test_furi_create_open);
|
MU_RUN_TEST(mu_test_furi_create_open);
|
||||||
MU_RUN_TEST(mu_test_furi_valuemutex);
|
MU_RUN_TEST(mu_test_furi_valuemutex);
|
||||||
MU_RUN_TEST(mu_test_furi_concurrent_access);
|
MU_RUN_TEST(mu_test_furi_concurrent_access);
|
||||||
MU_RUN_TEST(mu_test_furi_pubsub);
|
MU_RUN_TEST(mu_test_furi_pubsub);
|
||||||
MU_RUN_TEST(mu_test_furi_value_expanders);
|
|
||||||
MU_RUN_TEST(mu_test_furi_event);
|
|
||||||
|
|
||||||
MU_RUN_TEST(mu_test_furi_memmgr);
|
MU_RUN_TEST(mu_test_furi_memmgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
int run_minunit();
|
int run_minunit();
|
||||||
|
|
||||||
void flipper_test_app(void* p) {
|
int32_t flipper_test_app(void* p) {
|
||||||
// create pins
|
// create pins
|
||||||
GpioPin red = {.pin = LED_RED_Pin, .port = LED_RED_GPIO_Port};
|
GpioPin red = {.pin = LED_RED_Pin, .port = LED_RED_GPIO_Port};
|
||||||
GpioPin green = {.pin = LED_GREEN_Pin, .port = LED_GREEN_GPIO_Port};
|
GpioPin green = {.pin = LED_GREEN_Pin, .port = LED_GREEN_GPIO_Port};
|
||||||
@ -38,7 +38,5 @@ void flipper_test_app(void* p) {
|
|||||||
gpio_write(blue_record, true);
|
gpio_write(blue_record, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_exitcode(exitcode);
|
return 0;
|
||||||
|
|
||||||
furiac_exit(NULL);
|
|
||||||
}
|
}
|
26
core/flipper.c
Normal file
26
core/flipper.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "flipper.h"
|
||||||
|
#include <applications.h>
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
void flipper_init() {
|
||||||
|
printf("[flipper] Build date:" BUILD_DATE ". "
|
||||||
|
"Git Commit:" GIT_COMMIT ". "
|
||||||
|
"Git Branch:" GIT_BRANCH ". "
|
||||||
|
"Commit Number:" GIT_BRANCH_NUM "\r\n");
|
||||||
|
|
||||||
|
printf("[flipper] starting services\r\n");
|
||||||
|
|
||||||
|
for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) {
|
||||||
|
printf("[flipper] starting service %s\r\n", FLIPPER_SERVICES[i].name);
|
||||||
|
|
||||||
|
FuriThread* thread = furi_thread_alloc();
|
||||||
|
|
||||||
|
furi_thread_set_name(thread, FLIPPER_SERVICES[i].name);
|
||||||
|
furi_thread_set_stack_size(thread, FLIPPER_SERVICES[i].stack_size);
|
||||||
|
furi_thread_set_callback(thread, FLIPPER_SERVICES[i].app);
|
||||||
|
|
||||||
|
furi_thread_start(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[flipper] services startup complete\r\n");
|
||||||
|
}
|
3
core/flipper.h
Normal file
3
core/flipper.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void flipper_init();
|
30
core/furi.c
30
core/furi.c
@ -1,12 +1,4 @@
|
|||||||
#include "furi.h"
|
#include "furi.h"
|
||||||
#include <applications.h>
|
|
||||||
|
|
||||||
// for testing purpose
|
|
||||||
uint32_t exitcode = 0;
|
|
||||||
|
|
||||||
void set_exitcode(uint32_t _exitcode) {
|
|
||||||
exitcode = _exitcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void furi_init() {
|
void furi_init() {
|
||||||
gpio_api_init();
|
gpio_api_init();
|
||||||
@ -14,25 +6,3 @@ void furi_init() {
|
|||||||
furi_record_init();
|
furi_record_init();
|
||||||
furi_stdglue_init();
|
furi_stdglue_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
int systemd() {
|
|
||||||
furi_init();
|
|
||||||
printf("[systemd] furi initialized\r\n");
|
|
||||||
// FURI startup
|
|
||||||
for(size_t i = 0; i < FLIPPER_SERVICES_size(); i++) {
|
|
||||||
printf("[systemd] starting service %s\r\n", FLIPPER_SERVICES[i].name);
|
|
||||||
osThreadAttr_t* attr = furi_alloc(sizeof(osThreadAttr_t));
|
|
||||||
attr->name = FLIPPER_SERVICES[i].name;
|
|
||||||
attr->stack_size = FLIPPER_SERVICES[i].stack_size;
|
|
||||||
osThreadNew(FLIPPER_SERVICES[i].app, NULL, attr);
|
|
||||||
}
|
|
||||||
printf("[systemd] all services started\r\n");
|
|
||||||
|
|
||||||
while(1) {
|
|
||||||
osThreadSuspend(osThreadGetId());
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("[systemd] === Bye from Flipper Zero! ===\r\n");
|
|
||||||
|
|
||||||
return (int)exitcode;
|
|
||||||
}
|
|
@ -3,12 +3,11 @@
|
|||||||
#include <cmsis_os2.h>
|
#include <cmsis_os2.h>
|
||||||
|
|
||||||
#include <furi/check.h>
|
#include <furi/check.h>
|
||||||
#include <furi/event.h>
|
|
||||||
#include <furi/memmgr.h>
|
#include <furi/memmgr.h>
|
||||||
#include <furi/pubsub.h>
|
#include <furi/pubsub.h>
|
||||||
#include <furi/record.h>
|
#include <furi/record.h>
|
||||||
#include <furi/stdglue.h>
|
#include <furi/stdglue.h>
|
||||||
#include <furi/value-expanders.h>
|
#include <furi/thread.h>
|
||||||
#include <furi/valuemutex.h>
|
#include <furi/valuemutex.h>
|
||||||
|
|
||||||
#include <api-hal/api-gpio.h>
|
#include <api-hal/api-gpio.h>
|
||||||
@ -21,7 +20,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define furiac_exit(ptr) osThreadExit()
|
void furi_init();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
#include "event.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
bool init_event(Event* event) {
|
|
||||||
event->semaphore_id = osSemaphoreNew(1, 0, NULL);
|
|
||||||
return event->semaphore_id != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool delete_event(Event* event) {
|
|
||||||
return osSemaphoreDelete(event->semaphore_id) == osOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void signal_event(Event* event) {
|
|
||||||
// Ignore the result, as we do not care about repeated event signalling.
|
|
||||||
osSemaphoreRelease(event->semaphore_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_event(Event* event) {
|
|
||||||
wait_event_with_timeout(event, osWaitForever);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wait_event_with_timeout(Event* event, uint32_t timeout_ms) {
|
|
||||||
return osSemaphoreAcquire(event->semaphore_id, timeout_ms) == osOK;
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <cmsis_os2.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
osSemaphoreId_t semaphore_id;
|
|
||||||
} Event;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates Event.
|
|
||||||
*/
|
|
||||||
bool init_event(Event* event);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Free resources allocated by `init_event`.
|
|
||||||
This function doesn't free the memory occupied by `Event` itself.
|
|
||||||
*/
|
|
||||||
bool delete_event(Event* event);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Signals the event.
|
|
||||||
If the event is already in "signalled" state, nothing happens.
|
|
||||||
*/
|
|
||||||
void signal_event(Event* event);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Waits until the event is signalled.
|
|
||||||
*/
|
|
||||||
void wait_event(Event* event);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Waits with a timeout until the event is signalled.
|
|
||||||
*/
|
|
||||||
bool wait_event_with_timeout(Event* event, uint32_t timeout_ms);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
122
core/furi/thread.c
Normal file
122
core/furi/thread.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include "thread.h"
|
||||||
|
#include "memmgr.h"
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
#include <m-string.h>
|
||||||
|
|
||||||
|
struct FuriThread {
|
||||||
|
FuriThreadState state;
|
||||||
|
int32_t ret;
|
||||||
|
|
||||||
|
FuriThreadCallback callback;
|
||||||
|
void* context;
|
||||||
|
|
||||||
|
FuriThreadStateCallback state_callback;
|
||||||
|
void* state_context;
|
||||||
|
|
||||||
|
osThreadAttr_t attr;
|
||||||
|
osThreadId_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
|
||||||
|
furi_assert(thread);
|
||||||
|
thread->state = state;
|
||||||
|
if(thread->state_callback) {
|
||||||
|
thread->state_callback(state, thread->state_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_body(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
FuriThread* thread = context;
|
||||||
|
|
||||||
|
furi_assert(thread->state == FuriThreadStateStarting);
|
||||||
|
furi_thread_set_state(thread, FuriThreadStateRunning);
|
||||||
|
|
||||||
|
thread->ret = thread->callback(thread->context);
|
||||||
|
|
||||||
|
furi_assert(thread->state == FuriThreadStateRunning);
|
||||||
|
furi_thread_set_state(thread, FuriThreadStateStopped);
|
||||||
|
|
||||||
|
osThreadExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
FuriThread* furi_thread_alloc() {
|
||||||
|
FuriThread* thread = furi_alloc(sizeof(FuriThread));
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_free(FuriThread* thread) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
|
||||||
|
if(thread->attr.name) free((void*)thread->attr.name);
|
||||||
|
free(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_set_name(FuriThread* thread, const char* name) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
if(thread->attr.name) free((void*)thread->attr.name);
|
||||||
|
thread->attr.name = strdup(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
thread->attr.stack_size = stack_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
thread->callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_set_context(FuriThread* thread, void* context) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
thread->context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
thread->state_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_thread_set_state_context(FuriThread* thread, void* context) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
thread->state_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool furi_thread_start(FuriThread* thread) {
|
||||||
|
furi_assert(thread);
|
||||||
|
furi_assert(thread->callback);
|
||||||
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
|
furi_thread_set_state(thread, FuriThreadStateStarting);
|
||||||
|
thread->id = osThreadNew(furi_thread_body, thread, &thread->attr);
|
||||||
|
if(thread->id) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
furi_assert(thread->state == FuriThreadStateStarting);
|
||||||
|
furi_thread_set_state(thread, FuriThreadStateStopped);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osStatus_t furi_thread_terminate(FuriThread* thread) {
|
||||||
|
furi_assert(thread);
|
||||||
|
osStatus_t ret = osThreadTerminate(thread->id);
|
||||||
|
if(ret == osOK) {
|
||||||
|
furi_thread_set_state(thread, FuriThreadStateStopped);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
osStatus_t furi_thread_join(FuriThread* thread) {
|
||||||
|
furi_assert(thread);
|
||||||
|
return osThreadJoin(thread->id);
|
||||||
|
}
|
102
core/furi/thread.h
Normal file
102
core/furi/thread.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <cmsis_os2.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* FuriThreadState */
|
||||||
|
typedef enum {
|
||||||
|
FuriThreadStateStopped,
|
||||||
|
FuriThreadStateStarting,
|
||||||
|
FuriThreadStateRunning,
|
||||||
|
} FuriThreadState;
|
||||||
|
|
||||||
|
/* FuriThread anonymous structure */
|
||||||
|
typedef struct FuriThread FuriThread;
|
||||||
|
|
||||||
|
/* FuriThreadCallback
|
||||||
|
* Your callback to run in new thread
|
||||||
|
* @warning don't use osThreadExit
|
||||||
|
*/
|
||||||
|
typedef int32_t (*FuriThreadCallback)(void* context);
|
||||||
|
|
||||||
|
/* FuriThread state change calback
|
||||||
|
* called upon thread state change
|
||||||
|
* @param state - new thread state
|
||||||
|
* @param context - callback context
|
||||||
|
*/
|
||||||
|
typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context);
|
||||||
|
|
||||||
|
/* Allocate FuriThread
|
||||||
|
* @return FuriThread instance
|
||||||
|
*/
|
||||||
|
FuriThread* furi_thread_alloc();
|
||||||
|
|
||||||
|
/* Release FuriThread
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
*/
|
||||||
|
void furi_thread_free(FuriThread* thread);
|
||||||
|
|
||||||
|
/* Set FuriThread name
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @param name - string
|
||||||
|
*/
|
||||||
|
void furi_thread_set_name(FuriThread* thread, const char* name);
|
||||||
|
|
||||||
|
/* Set FuriThread stack size
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @param stack_size - stack size in bytes
|
||||||
|
*/
|
||||||
|
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size);
|
||||||
|
|
||||||
|
/* Set FuriThread callback
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @param callback - FuriThreadCallback, called upon thread run
|
||||||
|
*/
|
||||||
|
void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback);
|
||||||
|
|
||||||
|
/* Set FuriThread context
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @param context - pointer to context for thread callback
|
||||||
|
*/
|
||||||
|
void furi_thread_set_context(FuriThread* thread, void* context);
|
||||||
|
|
||||||
|
/* Set FuriThread state change callback
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @param callack - state change callback
|
||||||
|
*/
|
||||||
|
void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback);
|
||||||
|
|
||||||
|
/* Set FuriThread state change context
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @param context - pointer to context
|
||||||
|
*/
|
||||||
|
void furi_thread_set_state_context(FuriThread* thread, void* context);
|
||||||
|
|
||||||
|
/* Start FuriThread
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool furi_thread_start(FuriThread* thread);
|
||||||
|
|
||||||
|
/* Treminate FuriThread
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @return osStatus_t
|
||||||
|
* @warning terminating statefull thread is dangerous
|
||||||
|
* use only if you know what you doing
|
||||||
|
*/
|
||||||
|
osStatus_t furi_thread_terminate(FuriThread* thread);
|
||||||
|
|
||||||
|
/* Join FuriThread
|
||||||
|
* @param thread - FuriThread instance
|
||||||
|
* @return osStatus_t
|
||||||
|
*/
|
||||||
|
osStatus_t furi_thread_join(FuriThread* thread);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -1,174 +0,0 @@
|
|||||||
#include "value-expanders.h"
|
|
||||||
|
|
||||||
bool init_composer(ValueComposer* composer, void* value) {
|
|
||||||
if(!init_mutex(&composer->value, value, 0)) return false;
|
|
||||||
|
|
||||||
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
|
|
||||||
list_composer_cb_init(composer->layers[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// mutex without name,
|
|
||||||
// no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
|
|
||||||
// with dynamic memory allocation
|
|
||||||
composer->mutex = osMutexNew(NULL);
|
|
||||||
if(composer->mutex == NULL) return false;
|
|
||||||
|
|
||||||
if(!init_event(&composer->request)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool delete_composer(ValueComposer* composer) {
|
|
||||||
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
|
||||||
bool result = true;
|
|
||||||
result &= delete_mutex(&composer->value);
|
|
||||||
|
|
||||||
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
|
|
||||||
list_composer_cb_clear(composer->layers[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
result &= osMutexDelete(composer->mutex) == osOK;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueComposerHandle*
|
|
||||||
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer) {
|
|
||||||
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
|
||||||
// put uninitialized item to the list
|
|
||||||
ValueComposerHandle* handle = list_composer_cb_push_raw(composer->layers[layer]);
|
|
||||||
|
|
||||||
handle->cb = cb;
|
|
||||||
handle->ctx = ctx;
|
|
||||||
handle->layer = layer;
|
|
||||||
handle->composer = composer;
|
|
||||||
|
|
||||||
// TODO unregister handle on app exit
|
|
||||||
//flapp_on_exit(remove_compose_layer, handle);
|
|
||||||
|
|
||||||
osMutexRelease(composer->mutex);
|
|
||||||
|
|
||||||
// Layers changed, request composition
|
|
||||||
signal_event(&composer->request);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remove_compose_layer(ValueComposerHandle* handle) {
|
|
||||||
ValueComposer* composer = handle->composer;
|
|
||||||
|
|
||||||
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
// iterate over items
|
|
||||||
list_composer_cb_it_t it;
|
|
||||||
for(list_composer_cb_it(it, composer->layers[handle->layer]); !list_composer_cb_end_p(it);
|
|
||||||
list_composer_cb_next(it)) {
|
|
||||||
const ValueComposerHandle* item = list_composer_cb_cref(it);
|
|
||||||
|
|
||||||
// if the iterator is equal to our element
|
|
||||||
if(item == handle) {
|
|
||||||
list_composer_cb_remove(composer->layers[handle->layer], it);
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
osMutexRelease(composer->mutex);
|
|
||||||
|
|
||||||
// Layers changed, request composition
|
|
||||||
signal_event(&composer->request);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_compose(ValueComposerHandle* handle) {
|
|
||||||
ValueComposer* composer = handle->composer;
|
|
||||||
signal_event(&composer->request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void perform_compose(
|
|
||||||
ValueComposer* composer,
|
|
||||||
ValueComposerCallback start_cb,
|
|
||||||
ValueComposerCallback end_cb,
|
|
||||||
void* ctx) {
|
|
||||||
if(!wait_event_with_timeout(&composer->request, 0)) return;
|
|
||||||
|
|
||||||
void* state = acquire_mutex(&composer->value, 0);
|
|
||||||
if(state == NULL) return;
|
|
||||||
|
|
||||||
if(start_cb != NULL) start_cb(ctx, state);
|
|
||||||
perform_compose_internal(composer, state);
|
|
||||||
if(end_cb != NULL) end_cb(ctx, state);
|
|
||||||
|
|
||||||
release_mutex(&composer->value, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void perform_compose_internal(ValueComposer* composer, void* state) {
|
|
||||||
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
|
||||||
// Compose all levels for now
|
|
||||||
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
|
|
||||||
// iterate over items
|
|
||||||
list_composer_cb_it_t it;
|
|
||||||
for(list_composer_cb_it(it, composer->layers[i]); !list_composer_cb_end_p(it);
|
|
||||||
list_composer_cb_next(it)) {
|
|
||||||
const ValueComposerHandle* h = list_composer_cb_cref(it);
|
|
||||||
h->cb(h->ctx, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
osMutexRelease(composer->mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void COPY_COMPOSE(void* ctx, void* state) {
|
|
||||||
read_mutex((ValueMutex*)ctx, state, 0, osWaitForever);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_managed(ValueManager* managed, void* value, size_t size) {
|
|
||||||
if(!init_pubsub(&managed->pubsub)) return false;
|
|
||||||
if(!init_mutex(&managed->value, value, size)) {
|
|
||||||
delete_pubsub(&managed->pubsub);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool delete_managed(ValueManager* managed) {
|
|
||||||
bool result = true;
|
|
||||||
result &= delete_mutex(&managed->value);
|
|
||||||
result &= delete_pubsub(&managed->pubsub);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout) {
|
|
||||||
void* value = acquire_mutex(&managed->value, timeout);
|
|
||||||
if(value == NULL) return false;
|
|
||||||
|
|
||||||
memcpy(value, data, len);
|
|
||||||
|
|
||||||
notify_pubsub(&managed->pubsub, value);
|
|
||||||
|
|
||||||
if(!release_mutex(&managed->value, value)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool commit_managed(ValueManager* managed, void* value) {
|
|
||||||
if(value != managed->value.value) return false;
|
|
||||||
|
|
||||||
notify_pubsub(&managed->pubsub, value);
|
|
||||||
|
|
||||||
if(!release_mutex(&managed->value, value)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "valuemutex.h"
|
|
||||||
#include "pubsub.h"
|
|
||||||
#include "event.h"
|
|
||||||
#include <m-list.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
== Value composer ==
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct ValueComposer ValueComposer;
|
|
||||||
|
|
||||||
typedef void (*ValueComposerCallback)(void* ctx, void* state);
|
|
||||||
|
|
||||||
typedef enum { UiLayerBelowNotify, UiLayerNotify, UiLayerAboveNotify } UiLayer;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ValueComposerCallback cb;
|
|
||||||
void* ctx;
|
|
||||||
UiLayer layer;
|
|
||||||
ValueComposer* composer;
|
|
||||||
} ValueComposerHandle;
|
|
||||||
|
|
||||||
LIST_DEF(list_composer_cb, ValueComposerHandle, M_POD_OPLIST);
|
|
||||||
|
|
||||||
struct ValueComposer {
|
|
||||||
ValueMutex value;
|
|
||||||
list_composer_cb_t layers[3];
|
|
||||||
osMutexId_t mutex;
|
|
||||||
Event request;
|
|
||||||
};
|
|
||||||
|
|
||||||
void COPY_COMPOSE(void* ctx, void* state);
|
|
||||||
|
|
||||||
bool init_composer(ValueComposer* composer, void* value);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Free resources allocated by `init_composer`.
|
|
||||||
This function doesn't free the memory occupied by `ValueComposer` itself.
|
|
||||||
*/
|
|
||||||
bool delete_composer(ValueComposer* composer);
|
|
||||||
|
|
||||||
ValueComposerHandle*
|
|
||||||
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer);
|
|
||||||
|
|
||||||
bool remove_compose_layer(ValueComposerHandle* handle);
|
|
||||||
|
|
||||||
void request_compose(ValueComposerHandle* handle);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Perform composition if requested.
|
|
||||||
|
|
||||||
`start_cb` and `end_cb` will be called before and after all layer callbacks, respectively.
|
|
||||||
Both `start_cb` and `end_cb` can be NULL. They can be used to set initial state (e.g. clear screen)
|
|
||||||
and commit the final state.
|
|
||||||
*/
|
|
||||||
void perform_compose(
|
|
||||||
ValueComposer* composer,
|
|
||||||
ValueComposerCallback start_cb,
|
|
||||||
ValueComposerCallback end_cb,
|
|
||||||
void* ctx);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Perform composition.
|
|
||||||
|
|
||||||
This function should be called with value mutex acquired.
|
|
||||||
This function is here for convenience, so that developers can write their own compose loops.
|
|
||||||
See `perform_compose` function body for an example.
|
|
||||||
*/
|
|
||||||
void perform_compose_internal(ValueComposer* composer, void* state);
|
|
||||||
|
|
||||||
// See [LED](LED-API) or [Display](Display-API) API for examples.
|
|
||||||
|
|
||||||
/*
|
|
||||||
== ValueManager ==
|
|
||||||
|
|
||||||
More complicated concept is ValueManager.
|
|
||||||
It is like ValueMutex, but user can subscribe to value updates.
|
|
||||||
|
|
||||||
First of all you can use value and pubsub part as showing above:
|
|
||||||
aquire/release mutex, read value, subscribe/unsubscribe pubsub.
|
|
||||||
There are two specific methods for ValueManager: write_managed, commit_managed
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ValueMutex value;
|
|
||||||
PubSub pubsub;
|
|
||||||
} ValueManager;
|
|
||||||
|
|
||||||
bool init_managed(ValueManager* managed, void* value, size_t size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Free resources allocated by `init_managed`.
|
|
||||||
This function doesn't free the memory occupied by `ValueManager` itself.
|
|
||||||
*/
|
|
||||||
bool delete_managed(ValueManager* managed);
|
|
||||||
|
|
||||||
/*
|
|
||||||
acquire value, changes it and send notify with current value.
|
|
||||||
*/
|
|
||||||
bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout);
|
|
||||||
|
|
||||||
/*
|
|
||||||
commit_managed works as `release_mutex` but send notify with current value.
|
|
||||||
*/
|
|
||||||
bool commit_managed(ValueManager* managed, void* value);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -16,6 +16,25 @@ typedef enum {
|
|||||||
/* Initialize drivers */
|
/* Initialize drivers */
|
||||||
void api_hal_power_init();
|
void api_hal_power_init();
|
||||||
|
|
||||||
|
/* Get current insomnia level
|
||||||
|
* @return insomnia level: 0 - no insomnia, >0 - insomnia, bearer count.
|
||||||
|
*/
|
||||||
|
uint16_t api_hal_power_insomnia_level();
|
||||||
|
|
||||||
|
/* Enter insomnia mode
|
||||||
|
* Prevents device from going to sleep
|
||||||
|
* @warning Internally increases insomnia level
|
||||||
|
* Must be paired with api_hal_power_insomnia_exit
|
||||||
|
*/
|
||||||
|
void api_hal_power_insomnia_enter();
|
||||||
|
|
||||||
|
/* Exit insomnia mode
|
||||||
|
* Allow device to go to sleep
|
||||||
|
* @warning Internally decreases insomnia level.
|
||||||
|
* Must be paired with api_hal_power_insomnia_enter
|
||||||
|
*/
|
||||||
|
void api_hal_power_insomnia_exit();
|
||||||
|
|
||||||
/* Check if deep sleep availble */
|
/* Check if deep sleep availble */
|
||||||
bool api_hal_power_deep_available();
|
bool api_hal_power_deep_available();
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
template <unsigned int N> struct STOP_EXTERNING_ME {};
|
template <unsigned int N> struct STOP_EXTERNING_ME {};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "api-hal-resources.h"
|
|
||||||
#include "api-hal-timebase.h"
|
|
||||||
#include "api-hal-boot.h"
|
#include "api-hal-boot.h"
|
||||||
|
#include "api-hal-os.h"
|
||||||
|
#include "api-hal-resources.h"
|
||||||
#include "api-hal-gpio.h"
|
#include "api-hal-gpio.h"
|
||||||
#include "api-hal-delay.h"
|
#include "api-hal-delay.h"
|
||||||
#include "api-hal-pwm.h"
|
#include "api-hal-pwm.h"
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
#include <cmsis_os2.h>
|
|
||||||
#include <FreeRTOS.h>
|
|
||||||
#include <task.h>
|
|
||||||
#include <main.h>
|
|
||||||
|
|
||||||
void systemd(void *argument);
|
|
||||||
|
|
||||||
osThreadId_t systemdHandle;
|
|
||||||
const osThreadAttr_t systemd_attributes = {
|
|
||||||
.name = "systemd",
|
|
||||||
.priority = (osPriority_t) osPriorityNormal,
|
|
||||||
.stack_size = 1024
|
|
||||||
};
|
|
||||||
|
|
||||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
|
|
||||||
/* Run time stack overflow checking is performed if
|
|
||||||
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
|
|
||||||
called if a stack overflow is detected. */
|
|
||||||
asm("bkpt 1");
|
|
||||||
while(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MX_FREERTOS_Init(void) {
|
|
||||||
systemdHandle = osThreadNew(systemd, NULL, &systemd_attributes);
|
|
||||||
}
|
|
@ -1,25 +1,6 @@
|
|||||||
/* USER CODE BEGIN Header */
|
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* @file : main.c
|
|
||||||
* @brief : Main program body
|
|
||||||
******************************************************************************
|
|
||||||
* @attention
|
|
||||||
*
|
|
||||||
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
|
|
||||||
* All rights reserved.</center></h2>
|
|
||||||
*
|
|
||||||
* This software component is licensed by ST under Ultimate Liberty license
|
|
||||||
* SLA0044, the "License"; You may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at:
|
|
||||||
* www.st.com/SLA0044
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
/* USER CODE END Header */
|
|
||||||
/* Includes ------------------------------------------------------------------*/
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "cmsis_os.h"
|
|
||||||
|
#include "cmsis_os2.h"
|
||||||
#include "adc.h"
|
#include "adc.h"
|
||||||
#include "aes.h"
|
#include "aes.h"
|
||||||
#include "comp.h"
|
#include "comp.h"
|
||||||
@ -34,234 +15,141 @@
|
|||||||
#include "usart.h"
|
#include "usart.h"
|
||||||
#include "usb_device.h"
|
#include "usb_device.h"
|
||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
|
|
||||||
/* Private includes ----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Includes */
|
|
||||||
#include "fatfs/fatfs.h"
|
#include "fatfs/fatfs.h"
|
||||||
#include "api-hal.h"
|
|
||||||
/* USER CODE END Includes */
|
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
#include <furi.h>
|
||||||
/* USER CODE BEGIN PTD */
|
#include <api-hal.h>
|
||||||
|
#include <flipper.h>
|
||||||
|
|
||||||
/* USER CODE END PTD */
|
|
||||||
|
|
||||||
/* Private define ------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PD */
|
|
||||||
/* USER CODE END PD */
|
|
||||||
|
|
||||||
/* Private macro -------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PM */
|
|
||||||
|
|
||||||
/* USER CODE END PM */
|
|
||||||
|
|
||||||
/* Private variables ---------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* USER CODE BEGIN PV */
|
|
||||||
|
|
||||||
/* USER CODE END PV */
|
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
|
||||||
void SystemClock_Config(void);
|
void SystemClock_Config(void);
|
||||||
void MX_FREERTOS_Init(void);
|
void MX_FREERTOS_Init(void);
|
||||||
/* USER CODE BEGIN PFP */
|
|
||||||
|
|
||||||
/* USER CODE END PFP */
|
|
||||||
|
|
||||||
/* Private user code ---------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN 0 */
|
|
||||||
|
|
||||||
/* USER CODE END 0 */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The application entry point.
|
|
||||||
* @retval int
|
|
||||||
*/
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN 1 */
|
HAL_Init();
|
||||||
|
SystemClock_Config();
|
||||||
|
|
||||||
/* USER CODE END 1 */
|
MX_GPIO_Init();
|
||||||
|
MX_ADC1_Init();
|
||||||
|
MX_I2C1_Init();
|
||||||
|
MX_RTC_Init();
|
||||||
|
MX_SPI1_Init();
|
||||||
|
MX_SPI2_Init();
|
||||||
|
MX_USART1_UART_Init();
|
||||||
|
MX_USB_Device_Init();
|
||||||
|
MX_TIM1_Init();
|
||||||
|
MX_TIM2_Init();
|
||||||
|
MX_TIM16_Init();
|
||||||
|
MX_COMP1_Init();
|
||||||
|
MX_RF_Init();
|
||||||
|
MX_PKA_Init();
|
||||||
|
MX_RNG_Init();
|
||||||
|
MX_AES1_Init();
|
||||||
|
MX_AES2_Init();
|
||||||
|
MX_CRC_Init();
|
||||||
|
|
||||||
/* MCU Configuration--------------------------------------------------------*/
|
api_hal_init();
|
||||||
|
MX_FATFS_Init();
|
||||||
|
delay_us_init_DWT();
|
||||||
|
|
||||||
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
|
furi_init();
|
||||||
HAL_Init();
|
// CMSIS initialization
|
||||||
|
osKernelInitialize();
|
||||||
|
// Init flipper
|
||||||
|
flipper_init();
|
||||||
|
// Start kernel
|
||||||
|
osKernelStart();
|
||||||
|
|
||||||
/* USER CODE BEGIN Init */
|
while (1) {}
|
||||||
/* USER CODE END Init */
|
|
||||||
|
|
||||||
/* Configure the system clock */
|
|
||||||
SystemClock_Config();
|
|
||||||
|
|
||||||
/* USER CODE BEGIN SysInit */
|
|
||||||
|
|
||||||
/* USER CODE END SysInit */
|
|
||||||
|
|
||||||
/* Initialize all configured peripherals */
|
|
||||||
MX_GPIO_Init();
|
|
||||||
MX_ADC1_Init();
|
|
||||||
MX_I2C1_Init();
|
|
||||||
MX_RTC_Init();
|
|
||||||
MX_SPI1_Init();
|
|
||||||
MX_SPI2_Init();
|
|
||||||
MX_USART1_UART_Init();
|
|
||||||
MX_USB_Device_Init();
|
|
||||||
MX_TIM1_Init();
|
|
||||||
MX_TIM2_Init();
|
|
||||||
MX_TIM16_Init();
|
|
||||||
MX_COMP1_Init();
|
|
||||||
MX_RF_Init();
|
|
||||||
MX_PKA_Init();
|
|
||||||
MX_RNG_Init();
|
|
||||||
MX_AES1_Init();
|
|
||||||
MX_AES2_Init();
|
|
||||||
MX_CRC_Init();
|
|
||||||
/* USER CODE BEGIN 2 */
|
|
||||||
api_hal_init();
|
|
||||||
MX_FATFS_Init();
|
|
||||||
delay_us_init_DWT();
|
|
||||||
// Errata 2.2.9, Flash OPTVERR flag is always set after system reset
|
|
||||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
|
||||||
/* USER CODE END 2 */
|
|
||||||
|
|
||||||
/* Init scheduler */
|
|
||||||
osKernelInitialize(); /* Call init function for freertos objects (in freertos.c) */
|
|
||||||
MX_FREERTOS_Init();
|
|
||||||
/* Start scheduler */
|
|
||||||
osKernelStart();
|
|
||||||
|
|
||||||
/* We should never get here as control is now taken by the scheduler */
|
|
||||||
/* Infinite loop */
|
|
||||||
/* USER CODE BEGIN WHILE */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE END WHILE */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN 3 */
|
|
||||||
}
|
|
||||||
/* USER CODE END 3 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief System Clock Configuration
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void SystemClock_Config(void)
|
void SystemClock_Config(void)
|
||||||
{
|
{
|
||||||
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
||||||
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
||||||
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
|
||||||
|
|
||||||
/** Configure LSE Drive Capability
|
HAL_PWR_EnableBkUpAccess();
|
||||||
*/
|
|
||||||
HAL_PWR_EnableBkUpAccess();
|
|
||||||
__HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMLOW);
|
|
||||||
/** Configure the main internal regulator output voltage
|
|
||||||
*/
|
|
||||||
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
|
|
||||||
/** Initializes the RCC Oscillators according to the specified parameters
|
|
||||||
* in the RCC_OscInitTypeDef structure.
|
|
||||||
*/
|
|
||||||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
|
|
||||||
|RCC_OSCILLATORTYPE_LSE;
|
|
||||||
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
|
||||||
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
|
|
||||||
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
|
|
||||||
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
|
|
||||||
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
|
|
||||||
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
|
||||||
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
|
|
||||||
RCC_OscInitStruct.PLL.PLLN = 8;
|
|
||||||
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
|
|
||||||
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
|
|
||||||
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
|
|
||||||
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
|
|
||||||
{
|
|
||||||
Error_Handler();
|
|
||||||
}
|
|
||||||
/** Configure the SYSCLKSource, HCLK, PCLK1 and PCLK2 clocks dividers
|
|
||||||
*/
|
|
||||||
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2
|
|
||||||
|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|
|
||||||
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
|
||||||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
|
||||||
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
|
|
||||||
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
|
|
||||||
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
|
|
||||||
RCC_ClkInitStruct.AHBCLK2Divider = RCC_SYSCLK_DIV2;
|
|
||||||
RCC_ClkInitStruct.AHBCLK4Divider = RCC_SYSCLK_DIV1;
|
|
||||||
|
|
||||||
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
|
__HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMLOW);
|
||||||
{
|
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
|
||||||
Error_Handler();
|
|
||||||
}
|
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
|
||||||
/** Initializes the peripherals clocks
|
|RCC_OSCILLATORTYPE_LSE;
|
||||||
*/
|
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
||||||
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SMPS|RCC_PERIPHCLK_RFWAKEUP
|
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
|
||||||
|RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
|
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
|
||||||
|RCC_PERIPHCLK_I2C1|RCC_PERIPHCLK_CLK48SEL
|
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
|
||||||
|RCC_PERIPHCLK_USB|RCC_PERIPHCLK_RNG
|
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
|
||||||
|RCC_PERIPHCLK_ADC;
|
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
||||||
PeriphClkInitStruct.PLLSAI1.PLLN = 6;
|
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
|
||||||
PeriphClkInitStruct.PLLSAI1.PLLP = RCC_PLLP_DIV2;
|
RCC_OscInitStruct.PLL.PLLN = 8;
|
||||||
PeriphClkInitStruct.PLLSAI1.PLLQ = RCC_PLLQ_DIV2;
|
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
|
||||||
PeriphClkInitStruct.PLLSAI1.PLLR = RCC_PLLR_DIV2;
|
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
|
||||||
PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_USBCLK|RCC_PLLSAI1_ADCCLK;
|
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
|
||||||
PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
|
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
|
||||||
PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
|
Error_Handler();
|
||||||
PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
|
}
|
||||||
PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_CLK48;
|
|
||||||
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
|
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2
|
||||||
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
|
|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|
||||||
PeriphClkInitStruct.RFWakeUpClockSelection = RCC_RFWKPCLKSOURCE_LSE;
|
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
||||||
PeriphClkInitStruct.SmpsClockSelection = RCC_SMPSCLKSOURCE_HSE;
|
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
||||||
PeriphClkInitStruct.SmpsDivSelection = RCC_SMPSCLKDIV_RANGE1;
|
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
|
||||||
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
|
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
|
||||||
{
|
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
|
||||||
Error_Handler();
|
RCC_ClkInitStruct.AHBCLK2Divider = RCC_SYSCLK_DIV2;
|
||||||
}
|
RCC_ClkInitStruct.AHBCLK4Divider = RCC_SYSCLK_DIV1;
|
||||||
/* USER CODE BEGIN Smps */
|
|
||||||
/* USER CODE END Smps */
|
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) {
|
||||||
/** Enables the Clock Security System
|
Error_Handler();
|
||||||
*/
|
}
|
||||||
HAL_RCC_EnableCSS();
|
|
||||||
/** Enables the Clock Security System
|
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SMPS|RCC_PERIPHCLK_RFWAKEUP
|
||||||
*/
|
|RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
|
||||||
HAL_RCCEx_EnableLSECSS();
|
|RCC_PERIPHCLK_I2C1|RCC_PERIPHCLK_CLK48SEL
|
||||||
|
|RCC_PERIPHCLK_USB|RCC_PERIPHCLK_RNG
|
||||||
|
|RCC_PERIPHCLK_ADC;
|
||||||
|
PeriphClkInitStruct.PLLSAI1.PLLN = 6;
|
||||||
|
PeriphClkInitStruct.PLLSAI1.PLLP = RCC_PLLP_DIV2;
|
||||||
|
PeriphClkInitStruct.PLLSAI1.PLLQ = RCC_PLLQ_DIV2;
|
||||||
|
PeriphClkInitStruct.PLLSAI1.PLLR = RCC_PLLR_DIV2;
|
||||||
|
PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_USBCLK|RCC_PLLSAI1_ADCCLK;
|
||||||
|
PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
|
||||||
|
PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
|
||||||
|
PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
|
||||||
|
PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_CLK48;
|
||||||
|
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
|
||||||
|
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
|
||||||
|
PeriphClkInitStruct.RFWakeUpClockSelection = RCC_RFWKPCLKSOURCE_LSE;
|
||||||
|
PeriphClkInitStruct.SmpsClockSelection = RCC_SMPSCLKSOURCE_HSE;
|
||||||
|
PeriphClkInitStruct.SmpsDivSelection = RCC_SMPSCLKDIV_RANGE1;
|
||||||
|
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
|
||||||
|
Error_Handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable CSS for both clocks
|
||||||
|
HAL_RCC_EnableCSS();
|
||||||
|
HAL_RCCEx_EnableLSECSS();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* USER CODE BEGIN 4 */
|
void Error_Handler(void) {
|
||||||
|
asm("bkpt 1");
|
||||||
/* USER CODE END 4 */
|
while(1) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function is executed in case of error occurrence.
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void Error_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN Error_Handler_Debug */
|
|
||||||
/* User can add his own implementation to report the HAL error return state */
|
|
||||||
asm("bkpt 1");
|
|
||||||
/* USER CODE END Error_Handler_Debug */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_FULL_ASSERT
|
#ifdef USE_FULL_ASSERT
|
||||||
/**
|
/**
|
||||||
* @brief Reports the name of the source file and the source line number
|
* @brief Reports the name of the source file and the source line number
|
||||||
* where the assert_param error has occurred.
|
* where the assert_param error has occurred.
|
||||||
* @param file: pointer to the source file name
|
* @param file: pointer to the source file name
|
||||||
* @param line: assert_param error line source number
|
* @param line: assert_param error line source number
|
||||||
* @retval None
|
* @retval None
|
||||||
*/
|
*/
|
||||||
void assert_failed(uint8_t *file, uint32_t line)
|
void assert_failed(uint8_t *file, uint32_t line) {
|
||||||
{
|
/* USER CODE BEGIN 6 */
|
||||||
/* USER CODE BEGIN 6 */
|
/* User can add his own implementation to report the file name and line number,
|
||||||
/* User can add his own implementation to report the file name and line number,
|
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
|
||||||
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
|
/* USER CODE END 6 */
|
||||||
/* USER CODE END 6 */
|
|
||||||
}
|
}
|
||||||
#endif /* USE_FULL_ASSERT */
|
#endif /* USE_FULL_ASSERT */
|
||||||
|
|
||||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
|
||||||
|
95
firmware/targets/f4/api-hal/api-hal-os-timer.h
Normal file
95
firmware/targets/f4/api-hal/api-hal-os-timer.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stm32wbxx_ll_lptim.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
static inline void assert(bool value) {
|
||||||
|
if (!value) asm("bkpt 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer used for system ticks
|
||||||
|
#define API_HAL_OS_TIMER_MAX 0xFFFF
|
||||||
|
#define API_HAL_OS_TIMER_REG_LOAD_DLY 0x1
|
||||||
|
#define API_HAL_OS_TIMER LPTIM2
|
||||||
|
#define API_HAL_OS_TIMER_IRQ LPTIM2_IRQn
|
||||||
|
#define API_HAL_OS_TIMER_CLOCK_INIT() \
|
||||||
|
{ \
|
||||||
|
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); \
|
||||||
|
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); \
|
||||||
|
} \
|
||||||
|
|
||||||
|
static inline void api_hal_os_timer_init() {
|
||||||
|
API_HAL_OS_TIMER_CLOCK_INIT();
|
||||||
|
|
||||||
|
LL_LPTIM_Enable(API_HAL_OS_TIMER);
|
||||||
|
while(!LL_LPTIM_IsEnabled(API_HAL_OS_TIMER)) {}
|
||||||
|
|
||||||
|
LL_LPTIM_SetClockSource(API_HAL_OS_TIMER, LL_LPTIM_CLK_SOURCE_INTERNAL);
|
||||||
|
LL_LPTIM_SetPrescaler(API_HAL_OS_TIMER, LL_LPTIM_PRESCALER_DIV1);
|
||||||
|
LL_LPTIM_SetPolarity(API_HAL_OS_TIMER, LL_LPTIM_OUTPUT_POLARITY_REGULAR);
|
||||||
|
LL_LPTIM_SetUpdateMode(API_HAL_OS_TIMER, LL_LPTIM_UPDATE_MODE_IMMEDIATE);
|
||||||
|
LL_LPTIM_SetCounterMode(API_HAL_OS_TIMER, LL_LPTIM_COUNTER_MODE_INTERNAL);
|
||||||
|
LL_LPTIM_TrigSw(API_HAL_OS_TIMER);
|
||||||
|
LL_LPTIM_SetInput1Src(API_HAL_OS_TIMER, LL_LPTIM_INPUT1_SRC_GPIO);
|
||||||
|
LL_LPTIM_SetInput2Src(API_HAL_OS_TIMER, LL_LPTIM_INPUT2_SRC_GPIO);
|
||||||
|
|
||||||
|
NVIC_SetPriority(API_HAL_OS_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
|
||||||
|
NVIC_EnableIRQ(API_HAL_OS_TIMER_IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t api_hal_os_timer_get_cnt() {
|
||||||
|
uint32_t counter = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
|
||||||
|
uint32_t counter_shadow = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
|
||||||
|
while(counter != counter_shadow) {
|
||||||
|
counter = counter_shadow;
|
||||||
|
counter_shadow = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
|
||||||
|
}
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool api_hal_os_timer_arr_is_ok() {
|
||||||
|
return LL_LPTIM_IsActiveFlag_ARROK(API_HAL_OS_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t api_hal_os_timer_get_arr() {
|
||||||
|
return LL_LPTIM_GetAutoReload(API_HAL_OS_TIMER);;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void api_hal_os_timer_set_arr(uint32_t value) {
|
||||||
|
value &= API_HAL_OS_TIMER_MAX;
|
||||||
|
if (value != api_hal_os_timer_get_arr()) {
|
||||||
|
assert(api_hal_os_timer_arr_is_ok());
|
||||||
|
LL_LPTIM_ClearFlag_ARROK(API_HAL_OS_TIMER);
|
||||||
|
LL_LPTIM_SetAutoReload(API_HAL_OS_TIMER, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool api_hal_os_timer_cmp_is_ok() {
|
||||||
|
return LL_LPTIM_IsActiveFlag_CMPOK(API_HAL_OS_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t api_hal_os_timer_get_cmp() {
|
||||||
|
return LL_LPTIM_GetCompare(API_HAL_OS_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void api_hal_os_timer_set_cmp(uint32_t value) {
|
||||||
|
value &= API_HAL_OS_TIMER_MAX;
|
||||||
|
if (value != api_hal_os_timer_get_cmp()) {
|
||||||
|
assert(api_hal_os_timer_cmp_is_ok());
|
||||||
|
LL_LPTIM_ClearFlag_CMPOK(API_HAL_OS_TIMER);
|
||||||
|
LL_LPTIM_SetCompare(API_HAL_OS_TIMER, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool api_hal_os_timer_is_safe() {
|
||||||
|
uint16_t cmp = api_hal_os_timer_get_cmp();
|
||||||
|
uint16_t cnt = api_hal_os_timer_get_cnt();
|
||||||
|
uint16_t margin = (cmp > cnt) ? cmp - cnt : cnt - cmp;
|
||||||
|
if (margin < 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!api_hal_os_timer_cmp_is_ok()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
169
firmware/targets/f4/api-hal/api-hal-os.c
Normal file
169
firmware/targets/f4/api-hal/api-hal-os.c
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include <api-hal-os.h>
|
||||||
|
#include <api-hal-os-timer.h>
|
||||||
|
#include <api-hal-power.h>
|
||||||
|
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <cmsis_os.h>
|
||||||
|
|
||||||
|
#define API_HAL_OS_CLK_FREQUENCY 32768
|
||||||
|
#define API_HAL_OS_TICK_PER_SECOND 1024
|
||||||
|
#define API_HAL_OS_CLK_PER_TICK (API_HAL_OS_CLK_FREQUENCY / API_HAL_OS_TICK_PER_SECOND)
|
||||||
|
#define API_HAL_OS_TICK_PER_EPOCH (API_HAL_OS_TIMER_MAX / API_HAL_OS_CLK_PER_TICK)
|
||||||
|
#define API_HAL_OS_MAX_SLEEP (API_HAL_OS_TICK_PER_EPOCH - 1)
|
||||||
|
|
||||||
|
#ifdef API_HAL_OS_DEBUG
|
||||||
|
#include <stm32wbxx_ll_gpio.h>
|
||||||
|
#define LED_GREEN_PORT GPIOA
|
||||||
|
#define LED_GREEN_PIN LL_GPIO_PIN_2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Tick counters
|
||||||
|
volatile uint32_t in_sleep;
|
||||||
|
volatile uint32_t in_awake;
|
||||||
|
// Error counters
|
||||||
|
volatile uint32_t sleep_error;
|
||||||
|
volatile uint32_t awake_error;
|
||||||
|
} ApiHalOs;
|
||||||
|
|
||||||
|
ApiHalOs api_hal_os = {
|
||||||
|
.in_sleep = 0,
|
||||||
|
.in_awake = 0,
|
||||||
|
.sleep_error = 0,
|
||||||
|
.awake_error = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
void api_hal_os_init() {
|
||||||
|
api_hal_os_timer_init();
|
||||||
|
LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
|
||||||
|
|
||||||
|
LL_LPTIM_EnableIT_CMPM(API_HAL_OS_TIMER);
|
||||||
|
LL_LPTIM_EnableIT_ARRM(API_HAL_OS_TIMER);
|
||||||
|
|
||||||
|
LL_LPTIM_SetAutoReload(API_HAL_OS_TIMER, API_HAL_OS_TIMER_MAX);
|
||||||
|
LL_LPTIM_SetCompare(API_HAL_OS_TIMER, API_HAL_OS_CLK_PER_TICK);
|
||||||
|
|
||||||
|
LL_LPTIM_StartCounter(API_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LPTIM2_IRQHandler(void) {
|
||||||
|
// Autoreload
|
||||||
|
const bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_OS_TIMER);
|
||||||
|
if(arrm_flag) {
|
||||||
|
LL_LPTIM_ClearFLAG_ARRM(API_HAL_OS_TIMER);
|
||||||
|
}
|
||||||
|
if(LL_LPTIM_IsActiveFlag_CMPM(API_HAL_OS_TIMER)) {
|
||||||
|
LL_LPTIM_ClearFLAG_CMPM(API_HAL_OS_TIMER);
|
||||||
|
|
||||||
|
// Store important value
|
||||||
|
uint16_t cnt = api_hal_os_timer_get_cnt();
|
||||||
|
uint16_t cmp = api_hal_os_timer_get_cmp();
|
||||||
|
uint16_t current_tick = cnt / API_HAL_OS_CLK_PER_TICK;
|
||||||
|
uint16_t compare_tick = cmp / API_HAL_OS_CLK_PER_TICK;
|
||||||
|
|
||||||
|
// Calculate error
|
||||||
|
// happens when HAL or other high priority IRQ takes our time
|
||||||
|
int32_t error = (int32_t)compare_tick - current_tick;
|
||||||
|
api_hal_os.awake_error += ((error>0) ? error : -error);
|
||||||
|
|
||||||
|
// Calculate and set next tick
|
||||||
|
uint16_t next_tick = current_tick + 1;
|
||||||
|
api_hal_os_timer_set_cmp(next_tick * API_HAL_OS_CLK_PER_TICK);
|
||||||
|
|
||||||
|
// Notify OS
|
||||||
|
api_hal_os.in_awake ++;
|
||||||
|
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
||||||
|
xPortSysTickHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t api_hal_os_sleep(TickType_t expected_idle_ticks) {
|
||||||
|
// Store important value before going to sleep
|
||||||
|
const uint16_t before_cnt = api_hal_os_timer_get_cnt();
|
||||||
|
const uint16_t before_tick = before_cnt / API_HAL_OS_CLK_PER_TICK;
|
||||||
|
|
||||||
|
// Calculate and set next wakeup compare value
|
||||||
|
const uint16_t expected_cnt = (before_tick + expected_idle_ticks - 2) * API_HAL_OS_CLK_PER_TICK;
|
||||||
|
api_hal_os_timer_set_cmp(expected_cnt);
|
||||||
|
|
||||||
|
HAL_SuspendTick();
|
||||||
|
// Go to stop2 mode
|
||||||
|
#ifdef API_HAL_OS_DEBUG
|
||||||
|
LL_GPIO_SetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||||
|
#endif
|
||||||
|
api_hal_power_deep_sleep();
|
||||||
|
#ifdef API_HAL_OS_DEBUG
|
||||||
|
LL_GPIO_ResetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
HAL_ResumeTick();
|
||||||
|
|
||||||
|
// Spin till we are in timer safe zone
|
||||||
|
while(!api_hal_os_timer_is_safe()) {}
|
||||||
|
|
||||||
|
// Store current counter value, calculate current tick
|
||||||
|
const uint16_t after_cnt = api_hal_os_timer_get_cnt();
|
||||||
|
const uint16_t after_tick = after_cnt / API_HAL_OS_CLK_PER_TICK;
|
||||||
|
|
||||||
|
// Store and clear interrupt flags
|
||||||
|
// we don't want handler to be called after renabling IRQ
|
||||||
|
bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_OS_TIMER);
|
||||||
|
|
||||||
|
// Calculate and set next wakeup compare value
|
||||||
|
const uint16_t next_cmp = (after_tick + 1) * API_HAL_OS_CLK_PER_TICK;
|
||||||
|
api_hal_os_timer_set_cmp(next_cmp);
|
||||||
|
|
||||||
|
// Calculate ticks count spent in sleep and perform sanity checks
|
||||||
|
int32_t completed_ticks = arrm_flag ? (int32_t)before_tick - after_tick : (int32_t)after_tick - before_tick;
|
||||||
|
|
||||||
|
return completed_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
|
||||||
|
if (!api_hal_power_deep_available()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit mount of ticks to maximum that timer can count
|
||||||
|
if (expected_idle_ticks > API_HAL_OS_MAX_SLEEP) {
|
||||||
|
expected_idle_ticks = API_HAL_OS_MAX_SLEEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop IRQ handling, no one should disturb us till we finish
|
||||||
|
__disable_irq();
|
||||||
|
|
||||||
|
// Confirm OS that sleep is still possible
|
||||||
|
// And check if timer is in safe zone
|
||||||
|
// (8 clocks till any IRQ event or ongoing synchronization)
|
||||||
|
if (eTaskConfirmSleepModeStatus() == eAbortSleep
|
||||||
|
|| !api_hal_os_timer_is_safe()) {
|
||||||
|
__enable_irq();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t completed_ticks = api_hal_os_sleep(expected_idle_ticks);
|
||||||
|
assert(completed_ticks >= 0);
|
||||||
|
|
||||||
|
// Reenable IRQ
|
||||||
|
__enable_irq();
|
||||||
|
|
||||||
|
// Notify system about time spent in sleep
|
||||||
|
if (completed_ticks > 0) {
|
||||||
|
api_hal_os.in_sleep += completed_ticks;
|
||||||
|
if (completed_ticks > expected_idle_ticks) {
|
||||||
|
// We are late, count error
|
||||||
|
api_hal_os.sleep_error += (completed_ticks - expected_idle_ticks);
|
||||||
|
// Freertos is not happy when we overleep
|
||||||
|
// But we are not going to tell her
|
||||||
|
vTaskStepTick(expected_idle_ticks);
|
||||||
|
} else {
|
||||||
|
vTaskStepTick(completed_ticks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
|
||||||
|
asm("bkpt 1");
|
||||||
|
while(1) {};
|
||||||
|
}
|
17
firmware/targets/f4/api-hal/api-hal-os.h
Normal file
17
firmware/targets/f4/api-hal/api-hal-os.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialize OS helpers
|
||||||
|
* Configure and start tick timer
|
||||||
|
*/
|
||||||
|
void api_hal_os_init();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -12,6 +12,8 @@
|
|||||||
#include <bq27220.h>
|
#include <bq27220.h>
|
||||||
#include <bq25896.h>
|
#include <bq25896.h>
|
||||||
|
|
||||||
|
volatile uint32_t api_hal_power_insomnia = 0;
|
||||||
|
|
||||||
void HAL_RCC_CSSCallback(void) {
|
void HAL_RCC_CSSCallback(void) {
|
||||||
LL_RCC_ForceBackupDomainReset();
|
LL_RCC_ForceBackupDomainReset();
|
||||||
LL_RCC_ReleaseBackupDomainReset();
|
LL_RCC_ReleaseBackupDomainReset();
|
||||||
@ -24,8 +26,20 @@ void api_hal_power_init() {
|
|||||||
bq25896_init();
|
bq25896_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t api_hal_power_insomnia_level() {
|
||||||
|
return api_hal_power_insomnia;
|
||||||
|
}
|
||||||
|
|
||||||
|
void api_hal_power_insomnia_enter() {
|
||||||
|
api_hal_power_insomnia++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void api_hal_power_insomnia_exit() {
|
||||||
|
api_hal_power_insomnia--;
|
||||||
|
}
|
||||||
|
|
||||||
bool api_hal_power_deep_available() {
|
bool api_hal_power_deep_available() {
|
||||||
return api_hal_bt_is_alive();
|
return api_hal_bt_is_alive() && api_hal_power_insomnia == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void api_hal_power_deep_sleep() {
|
void api_hal_power_deep_sleep() {
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stm32wbxx_ll_lptim.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
static inline void assert(bool value) {
|
|
||||||
if (!value) asm("bkpt 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timer used for system ticks
|
|
||||||
#define API_HAL_TIMEBASE_TIMER_MAX 0xFFFF
|
|
||||||
#define API_HAL_TIMEBASE_TIMER_REG_LOAD_DLY 0x1
|
|
||||||
#define API_HAL_TIMEBASE_TIMER LPTIM2
|
|
||||||
#define API_HAL_TIMEBASE_TIMER_IRQ LPTIM2_IRQn
|
|
||||||
#define API_HAL_TIMEBASE_TIMER_CLOCK_INIT() \
|
|
||||||
{ \
|
|
||||||
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); \
|
|
||||||
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); \
|
|
||||||
} \
|
|
||||||
|
|
||||||
static inline void api_hal_timebase_timer_init() {
|
|
||||||
API_HAL_TIMEBASE_TIMER_CLOCK_INIT();
|
|
||||||
|
|
||||||
LL_LPTIM_Enable(API_HAL_TIMEBASE_TIMER);
|
|
||||||
while(!LL_LPTIM_IsEnabled(API_HAL_TIMEBASE_TIMER)) {}
|
|
||||||
|
|
||||||
LL_LPTIM_SetClockSource(API_HAL_TIMEBASE_TIMER, LL_LPTIM_CLK_SOURCE_INTERNAL);
|
|
||||||
LL_LPTIM_SetPrescaler(API_HAL_TIMEBASE_TIMER, LL_LPTIM_PRESCALER_DIV1);
|
|
||||||
LL_LPTIM_SetPolarity(API_HAL_TIMEBASE_TIMER, LL_LPTIM_OUTPUT_POLARITY_REGULAR);
|
|
||||||
LL_LPTIM_SetUpdateMode(API_HAL_TIMEBASE_TIMER, LL_LPTIM_UPDATE_MODE_IMMEDIATE);
|
|
||||||
LL_LPTIM_SetCounterMode(API_HAL_TIMEBASE_TIMER, LL_LPTIM_COUNTER_MODE_INTERNAL);
|
|
||||||
LL_LPTIM_TrigSw(API_HAL_TIMEBASE_TIMER);
|
|
||||||
LL_LPTIM_SetInput1Src(API_HAL_TIMEBASE_TIMER, LL_LPTIM_INPUT1_SRC_GPIO);
|
|
||||||
LL_LPTIM_SetInput2Src(API_HAL_TIMEBASE_TIMER, LL_LPTIM_INPUT2_SRC_GPIO);
|
|
||||||
|
|
||||||
NVIC_SetPriority(API_HAL_TIMEBASE_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
|
|
||||||
NVIC_EnableIRQ(API_HAL_TIMEBASE_TIMER_IRQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t api_hal_timebase_timer_get_cnt() {
|
|
||||||
uint32_t counter = LL_LPTIM_GetCounter(API_HAL_TIMEBASE_TIMER);
|
|
||||||
uint32_t counter_shadow = LL_LPTIM_GetCounter(API_HAL_TIMEBASE_TIMER);
|
|
||||||
while(counter != counter_shadow) {
|
|
||||||
counter = counter_shadow;
|
|
||||||
counter_shadow = LL_LPTIM_GetCounter(API_HAL_TIMEBASE_TIMER);
|
|
||||||
}
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool api_hal_timebase_timer_arr_is_ok() {
|
|
||||||
return LL_LPTIM_IsActiveFlag_ARROK(API_HAL_TIMEBASE_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t api_hal_timebase_timer_get_arr() {
|
|
||||||
return LL_LPTIM_GetAutoReload(API_HAL_TIMEBASE_TIMER);;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void api_hal_timebase_timer_set_arr(uint32_t value) {
|
|
||||||
value &= API_HAL_TIMEBASE_TIMER_MAX;
|
|
||||||
if (value != api_hal_timebase_timer_get_arr()) {
|
|
||||||
assert(api_hal_timebase_timer_arr_is_ok());
|
|
||||||
LL_LPTIM_ClearFlag_ARROK(API_HAL_TIMEBASE_TIMER);
|
|
||||||
LL_LPTIM_SetAutoReload(API_HAL_TIMEBASE_TIMER, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool api_hal_timebase_timer_cmp_is_ok() {
|
|
||||||
return LL_LPTIM_IsActiveFlag_CMPOK(API_HAL_TIMEBASE_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t api_hal_timebase_timer_get_cmp() {
|
|
||||||
return LL_LPTIM_GetCompare(API_HAL_TIMEBASE_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void api_hal_timebase_timer_set_cmp(uint32_t value) {
|
|
||||||
value &= API_HAL_TIMEBASE_TIMER_MAX;
|
|
||||||
if (value != api_hal_timebase_timer_get_cmp()) {
|
|
||||||
assert(api_hal_timebase_timer_cmp_is_ok());
|
|
||||||
LL_LPTIM_ClearFlag_CMPOK(API_HAL_TIMEBASE_TIMER);
|
|
||||||
LL_LPTIM_SetCompare(API_HAL_TIMEBASE_TIMER, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool api_hal_timebase_timer_is_safe() {
|
|
||||||
uint16_t cmp = api_hal_timebase_timer_get_cmp();
|
|
||||||
uint16_t cnt = api_hal_timebase_timer_get_cnt();
|
|
||||||
uint16_t margin = (cmp > cnt) ? cmp - cnt : cnt - cmp;
|
|
||||||
if (margin < 8) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!api_hal_timebase_timer_cmp_is_ok()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,179 +0,0 @@
|
|||||||
#include <api-hal-timebase.h>
|
|
||||||
#include <api-hal-timebase-timer.h>
|
|
||||||
#include <api-hal-power.h>
|
|
||||||
|
|
||||||
#include <FreeRTOS.h>
|
|
||||||
#include <cmsis_os.h>
|
|
||||||
|
|
||||||
#define API_HAL_TIMEBASE_CLK_FREQUENCY 32768
|
|
||||||
#define API_HAL_TIMEBASE_TICK_PER_SECOND 1024
|
|
||||||
#define API_HAL_TIMEBASE_CLK_PER_TICK (API_HAL_TIMEBASE_CLK_FREQUENCY / API_HAL_TIMEBASE_TICK_PER_SECOND)
|
|
||||||
#define API_HAL_TIMEBASE_TICK_PER_EPOCH (API_HAL_TIMEBASE_TIMER_MAX / API_HAL_TIMEBASE_CLK_PER_TICK)
|
|
||||||
#define API_HAL_TIMEBASE_MAX_SLEEP (API_HAL_TIMEBASE_TICK_PER_EPOCH - 1)
|
|
||||||
|
|
||||||
#ifdef API_HAL_TIMEBASE_DEBUG
|
|
||||||
#include <stm32wbxx_ll_gpio.h>
|
|
||||||
#define LED_GREEN_PORT GPIOA
|
|
||||||
#define LED_GREEN_PIN LL_GPIO_PIN_2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Sleep control
|
|
||||||
volatile uint16_t insomnia;
|
|
||||||
// Tick counters
|
|
||||||
volatile uint32_t in_sleep;
|
|
||||||
volatile uint32_t in_awake;
|
|
||||||
// Error counters
|
|
||||||
volatile uint32_t sleep_error;
|
|
||||||
volatile uint32_t awake_error;
|
|
||||||
} ApiHalTimbase;
|
|
||||||
|
|
||||||
ApiHalTimbase api_hal_timebase = {
|
|
||||||
.insomnia = 0,
|
|
||||||
.in_sleep = 0,
|
|
||||||
.in_awake = 0,
|
|
||||||
.sleep_error = 0,
|
|
||||||
.awake_error = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
void api_hal_timebase_init() {
|
|
||||||
api_hal_timebase_timer_init();
|
|
||||||
LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
|
|
||||||
|
|
||||||
LL_LPTIM_EnableIT_CMPM(API_HAL_TIMEBASE_TIMER);
|
|
||||||
LL_LPTIM_EnableIT_ARRM(API_HAL_TIMEBASE_TIMER);
|
|
||||||
|
|
||||||
LL_LPTIM_SetAutoReload(API_HAL_TIMEBASE_TIMER, API_HAL_TIMEBASE_TIMER_MAX);
|
|
||||||
LL_LPTIM_SetCompare(API_HAL_TIMEBASE_TIMER, API_HAL_TIMEBASE_CLK_PER_TICK);
|
|
||||||
|
|
||||||
LL_LPTIM_StartCounter(API_HAL_TIMEBASE_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t api_hal_timebase_insomnia_level() {
|
|
||||||
return api_hal_timebase.insomnia;
|
|
||||||
}
|
|
||||||
|
|
||||||
void api_hal_timebase_insomnia_enter() {
|
|
||||||
api_hal_timebase.insomnia++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void api_hal_timebase_insomnia_exit() {
|
|
||||||
api_hal_timebase.insomnia--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LPTIM2_IRQHandler(void) {
|
|
||||||
// Autoreload
|
|
||||||
const bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_TIMEBASE_TIMER);
|
|
||||||
if(arrm_flag) {
|
|
||||||
LL_LPTIM_ClearFLAG_ARRM(API_HAL_TIMEBASE_TIMER);
|
|
||||||
}
|
|
||||||
if(LL_LPTIM_IsActiveFlag_CMPM(API_HAL_TIMEBASE_TIMER)) {
|
|
||||||
LL_LPTIM_ClearFLAG_CMPM(API_HAL_TIMEBASE_TIMER);
|
|
||||||
|
|
||||||
// Store important value
|
|
||||||
uint16_t cnt = api_hal_timebase_timer_get_cnt();
|
|
||||||
uint16_t cmp = api_hal_timebase_timer_get_cmp();
|
|
||||||
uint16_t current_tick = cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
|
|
||||||
uint16_t compare_tick = cmp / API_HAL_TIMEBASE_CLK_PER_TICK;
|
|
||||||
|
|
||||||
// Calculate error
|
|
||||||
// happens when HAL or other high priority IRQ takes our time
|
|
||||||
int32_t error = (int32_t)compare_tick - current_tick;
|
|
||||||
api_hal_timebase.awake_error += ((error>0) ? error : -error);
|
|
||||||
|
|
||||||
// Calculate and set next tick
|
|
||||||
uint16_t next_tick = current_tick + 1;
|
|
||||||
api_hal_timebase_timer_set_cmp(next_tick * API_HAL_TIMEBASE_CLK_PER_TICK);
|
|
||||||
|
|
||||||
// Notify OS
|
|
||||||
api_hal_timebase.in_awake ++;
|
|
||||||
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
|
||||||
xPortSysTickHandler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t api_hal_timebase_sleep(TickType_t expected_idle_ticks) {
|
|
||||||
// Store important value before going to sleep
|
|
||||||
const uint16_t before_cnt = api_hal_timebase_timer_get_cnt();
|
|
||||||
const uint16_t before_tick = before_cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
|
|
||||||
|
|
||||||
// Calculate and set next wakeup compare value
|
|
||||||
const uint16_t expected_cnt = (before_tick + expected_idle_ticks - 2) * API_HAL_TIMEBASE_CLK_PER_TICK;
|
|
||||||
api_hal_timebase_timer_set_cmp(expected_cnt);
|
|
||||||
|
|
||||||
HAL_SuspendTick();
|
|
||||||
// Go to stop2 mode
|
|
||||||
#ifdef API_HAL_TIMEBASE_DEBUG
|
|
||||||
LL_GPIO_SetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
|
|
||||||
#endif
|
|
||||||
api_hal_power_deep_sleep();
|
|
||||||
#ifdef API_HAL_TIMEBASE_DEBUG
|
|
||||||
LL_GPIO_ResetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
HAL_ResumeTick();
|
|
||||||
|
|
||||||
// Spin till we are in timer safe zone
|
|
||||||
while(!api_hal_timebase_timer_is_safe()) {}
|
|
||||||
|
|
||||||
// Store current counter value, calculate current tick
|
|
||||||
const uint16_t after_cnt = api_hal_timebase_timer_get_cnt();
|
|
||||||
const uint16_t after_tick = after_cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
|
|
||||||
|
|
||||||
// Store and clear interrupt flags
|
|
||||||
// we don't want handler to be called after renabling IRQ
|
|
||||||
bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_TIMEBASE_TIMER);
|
|
||||||
|
|
||||||
// Calculate and set next wakeup compare value
|
|
||||||
const uint16_t next_cmp = (after_tick + 1) * API_HAL_TIMEBASE_CLK_PER_TICK;
|
|
||||||
api_hal_timebase_timer_set_cmp(next_cmp);
|
|
||||||
|
|
||||||
// Calculate ticks count spent in sleep and perform sanity checks
|
|
||||||
int32_t completed_ticks = arrm_flag ? (int32_t)before_tick - after_tick : (int32_t)after_tick - before_tick;
|
|
||||||
|
|
||||||
return completed_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
|
|
||||||
if (!api_hal_power_deep_available() || api_hal_timebase.insomnia) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit mount of ticks to maximum that timer can count
|
|
||||||
if (expected_idle_ticks > API_HAL_TIMEBASE_MAX_SLEEP) {
|
|
||||||
expected_idle_ticks = API_HAL_TIMEBASE_MAX_SLEEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop IRQ handling, no one should disturb us till we finish
|
|
||||||
__disable_irq();
|
|
||||||
|
|
||||||
// Confirm OS that sleep is still possible
|
|
||||||
// And check if timer is in safe zone
|
|
||||||
// (8 clocks till any IRQ event or ongoing synchronization)
|
|
||||||
if (eTaskConfirmSleepModeStatus() == eAbortSleep
|
|
||||||
|| !api_hal_timebase_timer_is_safe()) {
|
|
||||||
__enable_irq();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t completed_ticks = api_hal_timebase_sleep(expected_idle_ticks);
|
|
||||||
assert(completed_ticks >= 0);
|
|
||||||
|
|
||||||
// Reenable IRQ
|
|
||||||
__enable_irq();
|
|
||||||
|
|
||||||
// Notify system about time spent in sleep
|
|
||||||
if (completed_ticks > 0) {
|
|
||||||
api_hal_timebase.in_sleep += completed_ticks;
|
|
||||||
if (completed_ticks > expected_idle_ticks) {
|
|
||||||
// We are late, count error
|
|
||||||
api_hal_timebase.sleep_error += (completed_ticks - expected_idle_ticks);
|
|
||||||
// Freertos is not happy when we overleep
|
|
||||||
// But we are not going to tell her
|
|
||||||
vTaskStepTick(expected_idle_ticks);
|
|
||||||
} else {
|
|
||||||
vTaskStepTick(completed_ticks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Initialize timebase
|
|
||||||
* Configure and start tick timer
|
|
||||||
*/
|
|
||||||
void api_hal_timebase_init();
|
|
||||||
|
|
||||||
/* Get current insomnia level
|
|
||||||
* @return insomnia level: 0 - no insomnia, >0 - insomnia, bearer count.
|
|
||||||
*/
|
|
||||||
uint16_t api_hal_timebase_insomnia_level();
|
|
||||||
|
|
||||||
/* Enter insomnia mode
|
|
||||||
* Prevents device from going to sleep
|
|
||||||
* @warning Internally increases insomnia level
|
|
||||||
* Must be paired with api_hal_timebase_insomnia_exit
|
|
||||||
*/
|
|
||||||
void api_hal_timebase_insomnia_enter();
|
|
||||||
|
|
||||||
/* Exit insomnia mode
|
|
||||||
* Allow device to go to sleep
|
|
||||||
* @warning Internally decreases insomnia level.
|
|
||||||
* Must be paired with api_hal_timebase_insomnia_enter
|
|
||||||
*/
|
|
||||||
void api_hal_timebase_insomnia_exit();
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,7 +1,7 @@
|
|||||||
#include <api-hal.h>
|
#include <api-hal.h>
|
||||||
|
|
||||||
void api_hal_init() {
|
void api_hal_init() {
|
||||||
api_hal_timebase_init();
|
api_hal_os_init();
|
||||||
api_hal_vcp_init();
|
api_hal_vcp_init();
|
||||||
api_hal_spi_init();
|
api_hal_spi_init();
|
||||||
}
|
}
|
@ -219,7 +219,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
|
|||||||
}
|
}
|
||||||
/* restart advertising */
|
/* restart advertising */
|
||||||
Adv_Request(APP_BLE_FAST_ADV);
|
Adv_Request(APP_BLE_FAST_ADV);
|
||||||
api_hal_timebase_insomnia_exit();
|
api_hal_power_insomnia_exit();
|
||||||
}
|
}
|
||||||
break; /* EVT_DISCONN_COMPLETE */
|
break; /* EVT_DISCONN_COMPLETE */
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
|
|||||||
break;
|
break;
|
||||||
case EVT_LE_CONN_COMPLETE:
|
case EVT_LE_CONN_COMPLETE:
|
||||||
{
|
{
|
||||||
api_hal_timebase_insomnia_enter();
|
api_hal_power_insomnia_enter();
|
||||||
hci_le_connection_complete_event_rp0 *connection_complete_event;
|
hci_le_connection_complete_event_rp0 *connection_complete_event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +52,7 @@ void APPE_Init() {
|
|||||||
HW_TS_Init(hw_ts_InitMode_Full, &hrtc); /**< Initialize the TimerServer */
|
HW_TS_Init(hw_ts_InitMode_Full, &hrtc); /**< Initialize the TimerServer */
|
||||||
|
|
||||||
// APPD_Init();
|
// APPD_Init();
|
||||||
api_hal_timebase_insomnia_enter();
|
api_hal_power_insomnia_enter();
|
||||||
|
|
||||||
appe_Tl_Init(); /* Initialize all transport layers */
|
appe_Tl_Init(); /* Initialize all transport layers */
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ static void APPE_SysUserEvtRx( void * pPayload ) {
|
|||||||
} else {
|
} else {
|
||||||
ble_glue_status = BleGlueStatusBroken;
|
ble_glue_status = BleGlueStatusBroken;
|
||||||
}
|
}
|
||||||
api_hal_timebase_insomnia_exit();
|
api_hal_power_insomnia_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************
|
/*************************************************************
|
||||||
|
@ -14,9 +14,9 @@ FLASH_ADDRESS = 0x08000000
|
|||||||
CFLAGS += -DNO_BOOTLOADER
|
CFLAGS += -DNO_BOOTLOADER
|
||||||
endif
|
endif
|
||||||
|
|
||||||
API_HAL_TIMEBASE_DEBUG ?= 0
|
API_HAL_OS_DEBUG ?= 0
|
||||||
ifeq ($(API_HAL_TIMEBASE_DEBUG), 1)
|
ifeq ($(API_HAL_OS_DEBUG), 1)
|
||||||
CFLAGS += -DAPI_HAL_TIMEBASE_DEBUG
|
CFLAGS += -DAPI_HAL_OS_DEBUG
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init"
|
OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init"
|
||||||
|
@ -44,12 +44,12 @@ public:
|
|||||||
// with template variables <state, events>
|
// with template variables <state, events>
|
||||||
class AppExample : public AppTemplate<AppExampleState, AppExampleEvent> {
|
class AppExample : public AppTemplate<AppExampleState, AppExampleEvent> {
|
||||||
public:
|
public:
|
||||||
void run();
|
uint8_t run();
|
||||||
void render(Canvas* canvas);
|
void render(Canvas* canvas);
|
||||||
};
|
};
|
||||||
|
|
||||||
// start app
|
// start app
|
||||||
void AppExample::run() {
|
uint8_t AppExample::run() {
|
||||||
// here we dont need to acquire or release state
|
// here we dont need to acquire or release state
|
||||||
// because before we call app_ready our application is "single threaded"
|
// because before we call app_ready our application is "single threaded"
|
||||||
state.example_data = 12;
|
state.example_data = 12;
|
||||||
@ -67,7 +67,7 @@ void AppExample::run() {
|
|||||||
// press events
|
// press events
|
||||||
if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) {
|
if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) {
|
||||||
printf("bye!\n");
|
printf("bye!\n");
|
||||||
exit();
|
return exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyUp) {
|
if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyUp) {
|
||||||
@ -95,7 +95,7 @@ void AppExample::render(Canvas* canvas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// app enter function
|
// app enter function
|
||||||
extern "C" void app_cpp_example(void* p) {
|
extern "C" uint8_t app_cpp_example(void* p) {
|
||||||
AppExample* app = new AppExample();
|
AppExample* app = new AppExample();
|
||||||
app->run();
|
return app->run();
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public:
|
|||||||
void release_state(void);
|
void release_state(void);
|
||||||
bool get_event(TEvent* event, uint32_t timeout);
|
bool get_event(TEvent* event, uint32_t timeout);
|
||||||
void app_ready(void);
|
void app_ready(void);
|
||||||
void exit(void);
|
uint8_t exit(void);
|
||||||
void update_gui(void);
|
void update_gui(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ template <class TState, class TEvent> AppTemplate<TState, TEvent>::AppTemplate()
|
|||||||
// TODO: use plain os mutex?
|
// TODO: use plain os mutex?
|
||||||
if(!init_mutex(&state_mutex, &state, sizeof(TState))) {
|
if(!init_mutex(&state_mutex, &state, sizeof(TState))) {
|
||||||
printf("cannot create mutex\n");
|
printf("cannot create mutex\n");
|
||||||
furiac_exit();
|
furi_check(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// open gui
|
// open gui
|
||||||
@ -100,10 +100,10 @@ template <class TState, class TEvent> void AppTemplate<TState, TEvent>::app_read
|
|||||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TState, class TEvent> void AppTemplate<TState, TEvent>::exit(void) {
|
template <class TState, class TEvent> uint8_t AppTemplate<TState, TEvent>::exit(void) {
|
||||||
// TODO remove all view_ports create by app
|
// TODO remove all view_ports create by app
|
||||||
view_port_enabled_set(view_port, false);
|
view_port_enabled_set(view_port, false);
|
||||||
osThreadExit();
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TState, class TEvent> void AppTemplate<TState, TEvent>::update_gui(void) {
|
template <class TState, class TEvent> void AppTemplate<TState, TEvent>::update_gui(void) {
|
||||||
|
Loading…
Reference in New Issue
Block a user