[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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく