Merge branch 'master' into ibutton-app
This commit is contained in:
		
						commit
						a8fdbc6376
					
				
							
								
								
									
										
											BIN
										
									
								
								.github/assets/Born2bSportyV2.ttf
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								.github/assets/Born2bSportyV2.ttf
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								.github/assets/latest-firmware-template.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								.github/assets/latest-firmware-template.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 19 KiB | 
							
								
								
									
										30
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @ -13,7 +13,8 @@ jobs: | ||||
|       - name: Checkout code | ||||
|         uses: actions/checkout@v2 | ||||
|         with: | ||||
|             submodules: true | ||||
|           fetch-depth: 0 | ||||
|           submodules: true | ||||
|       - uses: satackey/action-docker-layer-caching@v0.0.8 | ||||
|         continue-on-error: true | ||||
|         with: | ||||
| @ -110,12 +111,12 @@ jobs: | ||||
|       - name: Get bootloader | ||||
|         uses: actions/download-artifact@v2 | ||||
|         with: | ||||
|           name: bootloader_f2 | ||||
|           name: bootloader_f3 | ||||
|           path: bootloader | ||||
|       - name: Get firmware | ||||
|         uses: actions/download-artifact@v2 | ||||
|         with: | ||||
|           name: firmware_f2 | ||||
|           name: firmware_f3 | ||||
|           path: firmware | ||||
|       - name: Upload bootloader | ||||
|         uses: burnett01/rsync-deployments@4.1 | ||||
| @ -145,18 +146,13 @@ jobs: | ||||
|       - name: Get bootloader | ||||
|         uses: actions/download-artifact@v2 | ||||
|         with: | ||||
|           name: bootloader_f2 | ||||
|           name: bootloader_f3 | ||||
|           path: bootloader | ||||
|       - name: Get firmware | ||||
|         uses: actions/download-artifact@v2 | ||||
|         with: | ||||
|           name: firmware_f2 | ||||
|           path: firmware | ||||
|       - name: Get firmware | ||||
|         uses: actions/download-artifact@v2 | ||||
|         with: | ||||
|           name: firmware_f2 | ||||
|           path: firmware      | ||||
|           name: firmware_f3 | ||||
|           path: firmware  | ||||
|       - name: cp  | ||||
|         run: cp ./bootloader/bootloader.bin full_firmware_latest.bin | ||||
|       - name: truncate  | ||||
| @ -185,14 +181,8 @@ jobs: | ||||
|         uses: actions/checkout@v2 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|           ref: master | ||||
|           ref: master  | ||||
|           submodules: true       | ||||
|       - name: Echo debug | ||||
|         run: echo ${{ github.ref }} | ||||
|       - name: Get template and font | ||||
|         run: | | ||||
|           wget https://zhovner.com/tmp/latest-firmware-template.png | ||||
|           wget https://zhovner.com/tmp/Born2bSportyV2.ttf | ||||
|       - name: Set test env | ||||
|         run: echo "NUMBER_OF_COMMITS=$(git rev-list --count HEAD)" >> $GITHUB_ENV | ||||
|       - name: Test output NUMBER_OF_COMMITS | ||||
| @ -205,12 +195,12 @@ jobs: | ||||
|       - name: Test output PREP_DATE | ||||
|         run: echo $PREP_DATE           | ||||
|       - name: Gen pic | ||||
|         run: convert latest-firmware-template.png -font ./Born2bSportyV2.ttf -weight 700 -pointsize 140 -annotate +900+330 "$NUMBER_OF_COMMITS $PREP_DATE" out.png | ||||
|         run: convert ./.github/assets/latest-firmware-template.png -font ./.github/assets/Born2bSportyV2.ttf -weight 700 -pointsize 140 -annotate +900+330 "$NUMBER_OF_COMMITS $PREP_DATE" latest-firmware-banner.png | ||||
|       - name: Upload pic | ||||
|         uses: burnett01/rsync-deployments@4.1 | ||||
|         with: | ||||
|           switches: -avzp --delete | ||||
|           path: out.png | ||||
|           path: latest-firmware-banner.png | ||||
|           remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}/" | ||||
|           remote_host: ${{ secrets.RSYNC_DEPLOY_HOST }} | ||||
|           remote_user: ${{ secrets.RSYNC_DEPLOY_USER }} | ||||
|  | ||||
| @ -8,7 +8,7 @@ Welcome to [Flipper Zero](https://flipperzero.one/)'s Firmware repo! Our goal is | ||||
| 
 | ||||
| # Update firmware | ||||
| 
 | ||||
| <a href="https://update.flipperzero.one/full_firmware_latest.bin"><img width="300" src="https://update.flipperzero.one/latest-firmware-banner.gif" /></a> | ||||
| <a href="https://update.flipperzero.one/full_firmware_latest.bin"><img width="300" src="https://update.flipperzero.one/latest-firmware-banner.png" /></a> | ||||
| 
 | ||||
| 
 | ||||
| Flipper Zero's firmware consists of two components: Bootloader and main firmware. Bootloader controls firmware update process over USB. You need working bootloader installed before update firmware over USB. | ||||
|  | ||||
| @ -36,12 +36,18 @@ void sd_card_test(void* p); | ||||
| void application_vibro(void* p); | ||||
| void app_gpio_test(void* p); | ||||
| void app_ibutton(void* p); | ||||
| void cli_task(void* p); | ||||
| void music_player(void* p); | ||||
| 
 | ||||
| const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
| #ifdef APP_DISPLAY | ||||
|     {.app = display_u8g2, .name = "display_u8g2", .libs = {0}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_CLI | ||||
|     {.app = cli_task, .name = "cli_task", .libs = {0}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_EXAMPLE_BLINK | ||||
|     {.app = application_blink, .name = "blink", .libs = {1, FURI_LIB{"input_task"}}}, | ||||
| #endif | ||||
| @ -69,7 +75,7 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_POWER | ||||
|     {.app = power_task, .name = "power_task", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
|     {.app = power_task, .name = "power_task", .libs = {2, FURI_LIB{"cli_task", "gui_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_CC1101 | ||||
| @ -117,6 +123,10 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
|     {.app = sd_card_test, .name = "sd_card_test", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_MUSIC_PLAYER | ||||
|     {.app = music_player, .name = "music player", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_GPIO_DEMO | ||||
|     { | ||||
|         .app = app_gpio_test, | ||||
| @ -173,4 +183,8 @@ const FlipperStartupApp FLIPPER_APPS[] = { | ||||
| #ifdef BUILD_IBUTTON | ||||
|     {.app = app_ibutton, .name = "ibutton", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef BUILD_MUSIC_PLAYER | ||||
|     {.app = music_player, .name = "music player", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
| #endif | ||||
| }; | ||||
| @ -11,6 +11,7 @@ ifeq ($(APP_RELEASE), 1) | ||||
| APP_MENU = 1 | ||||
| APP_NFC  = 1 | ||||
| APP_POWER = 1 | ||||
| APP_CLI = 1 | ||||
| BUILD_IRDA  = 1 | ||||
| APP_IRUKAGOTCHI = 1 | ||||
| BUILD_EXAMPLE_BLINK = 1 | ||||
| @ -22,6 +23,7 @@ BUILD_SPEAKER_DEMO = 1 | ||||
| BUILD_VIBRO_DEMO = 1 | ||||
| BUILD_SD_TEST = 1 | ||||
| BUILD_GPIO_DEMO = 1 | ||||
| BUILD_MUSIC_PLAYER = 1 | ||||
| endif | ||||
| 
 | ||||
| APP_NFC ?= 0 | ||||
| @ -45,6 +47,13 @@ CFLAGS		+= -DAPP_POWER | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/power/*.c) | ||||
| endif | ||||
| 
 | ||||
| APP_CLI ?= 0 | ||||
| ifeq ($(APP_CLI), 1) | ||||
| APP_GUI		= 1 | ||||
| CFLAGS		+= -DAPP_CLI | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/cli/*.c) | ||||
| endif | ||||
| 
 | ||||
| APP_MENU ?= 0 | ||||
| ifeq ($(APP_MENU), 1) | ||||
| CFLAGS += -DAPP_MENU | ||||
| @ -238,6 +247,17 @@ CFLAGS		+= -DBUILD_GPIO_DEMO | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/gpio-tester/*.c) | ||||
| endif | ||||
| 
 | ||||
| APP_MUSIC_PLAYER ?= 0 | ||||
| ifeq ($(APP_MUSIC_PLAYER), 1) | ||||
| CFLAGS		+= -DAPP_MUSIC_PLAYER | ||||
| BUILD_MUSIC_PLAYER = 1 | ||||
| endif | ||||
| BUILD_MUSIC_PLAYER ?= 0 | ||||
| ifeq ($(BUILD_MUSIC_PLAYER), 1) | ||||
| CFLAGS		+= -DBUILD_MUSIC_PLAYER | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/music-player/*.c) | ||||
| endif | ||||
| 
 | ||||
| # device drivers
 | ||||
| 
 | ||||
| APP_GUI	?= 0 | ||||
|  | ||||
							
								
								
									
										173
									
								
								applications/cli/cli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								applications/cli/cli.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,173 @@ | ||||
| #include "cli_i.h" | ||||
| #include "cli_commands.h" | ||||
| 
 | ||||
| #include <api-hal-vcp.h> | ||||
| 
 | ||||
| Cli* cli_alloc() { | ||||
|     Cli* cli = furi_alloc(sizeof(Cli)); | ||||
|     CliCommandDict_init(cli->commands); | ||||
| 
 | ||||
|     cli->mutex = osMutexNew(NULL); | ||||
|     furi_check(cli->mutex); | ||||
| 
 | ||||
|     cli_reset_state(cli); | ||||
| 
 | ||||
|     return cli; | ||||
| } | ||||
| 
 | ||||
| void cli_free(Cli* cli) { | ||||
|     free(cli); | ||||
| } | ||||
| 
 | ||||
| void cli_reset_state(Cli* cli) { | ||||
|     string_clear(cli->line); | ||||
|     string_init(cli->line); | ||||
| } | ||||
| 
 | ||||
| void cli_putc(char c) { | ||||
|     api_hal_vcp_tx((uint8_t*)&c, 1); | ||||
| } | ||||
| 
 | ||||
| void cli_print(const char* str) { | ||||
|     api_hal_vcp_tx((uint8_t*)str, strlen(str)); | ||||
| } | ||||
| 
 | ||||
| void cli_print_version() { | ||||
|     cli_print("Build date:" BUILD_DATE ". " | ||||
|               "Git Commit:" GIT_COMMIT ". " | ||||
|               "Git Branch:" GIT_BRANCH ". " | ||||
|               "Commit Number:" GIT_BRANCH_NUM "."); | ||||
| } | ||||
| 
 | ||||
| void cli_motd() { | ||||
|     cli_print("Flipper cli.\r\n"); | ||||
|     cli_print_version(); | ||||
| } | ||||
| 
 | ||||
| void cli_nl() { | ||||
|     cli_print("\r\n"); | ||||
| } | ||||
| 
 | ||||
| void cli_prompt() { | ||||
|     cli_print("\r\n>: "); | ||||
| } | ||||
| 
 | ||||
| void cli_backspace(Cli* cli) { | ||||
|     size_t s = string_size(cli->line); | ||||
|     if(s > 0) { | ||||
|         s--; | ||||
|         string_left(cli->line, s); | ||||
|         cli_putc(CliSymbolAsciiBackspace); | ||||
|         cli_putc(CliSymbolAsciiSpace); | ||||
|         cli_putc(CliSymbolAsciiBackspace); | ||||
|     } else { | ||||
|         cli_putc(CliSymbolAsciiBell); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cli_enter(Cli* cli) { | ||||
|     // Normalize input
 | ||||
|     string_strim(cli->line); | ||||
|     if(string_size(cli->line) == 0) { | ||||
|         cli_prompt(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Get first word as command name
 | ||||
|     string_t command; | ||||
|     string_init(command); | ||||
|     size_t ws = string_search_char(cli->line, ' '); | ||||
|     if(ws == STRING_FAILURE) { | ||||
|         string_set(command, cli->line); | ||||
|         string_clear(cli->line); | ||||
|         string_init(cli->line); | ||||
|     } else { | ||||
|         string_set_n(command, cli->line, 0, ws); | ||||
|         string_right(cli->line, ws); | ||||
|         string_strim(cli->line); | ||||
|     } | ||||
| 
 | ||||
|     // Search for command
 | ||||
|     furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK); | ||||
|     CliCommand* cli_command = CliCommandDict_get(cli->commands, command); | ||||
|     furi_check(osMutexRelease(cli->mutex) == osOK); | ||||
|     if(cli_command) { | ||||
|         cli_nl(); | ||||
|         cli_command->callback(cli->line, cli_command->context); | ||||
|         cli_prompt(); | ||||
|     } else { | ||||
|         cli_nl(); | ||||
|         cli_print("Command not found: "); | ||||
|         cli_print(string_get_cstr(command)); | ||||
|         cli_prompt(); | ||||
|         cli_putc(CliSymbolAsciiBell); | ||||
|     } | ||||
| 
 | ||||
|     // Always finish with clean state
 | ||||
|     cli_reset_state(cli); | ||||
| } | ||||
| 
 | ||||
| void cli_process_input(Cli* cli) { | ||||
|     char c; | ||||
|     size_t r; | ||||
| 
 | ||||
|     r = api_hal_vcp_rx((uint8_t*)&c, 1); | ||||
|     if(r == 0) { | ||||
|         cli_reset_state(cli); | ||||
|     } | ||||
| 
 | ||||
|     if(c == CliSymbolAsciiTab) { | ||||
|         cli_putc(CliSymbolAsciiBell); | ||||
|     } else if(c == CliSymbolAsciiSOH) { | ||||
|         cli_motd(); | ||||
|         cli_prompt(); | ||||
|     } else if(c == CliSymbolAsciiEOT) { | ||||
|         cli_reset_state(cli); | ||||
|     } else if(c == CliSymbolAsciiEsc) { | ||||
|         r = api_hal_vcp_rx((uint8_t*)&c, 1); | ||||
|         if(r && c == '[') { | ||||
|             api_hal_vcp_rx((uint8_t*)&c, 1); | ||||
|         } else { | ||||
|             cli_putc(CliSymbolAsciiBell); | ||||
|         } | ||||
|     } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { | ||||
|         cli_backspace(cli); | ||||
|     } else if(c == CliSymbolAsciiCR) { | ||||
|         cli_enter(cli); | ||||
|     } else if(c >= 0x20 && c < 0x7F) { | ||||
|         string_push_back(cli->line, c); | ||||
|         cli_putc(c); | ||||
|     } else { | ||||
|         cli_putc(CliSymbolAsciiBell); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* context) { | ||||
|     string_t name_str; | ||||
|     string_init_set_str(name_str, name); | ||||
|     CliCommand c; | ||||
|     c.callback = callback; | ||||
|     c.context = context; | ||||
| 
 | ||||
|     furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK); | ||||
|     CliCommandDict_set_at(cli->commands, name_str, c); | ||||
|     furi_check(osMutexRelease(cli->mutex) == osOK); | ||||
| } | ||||
| 
 | ||||
| void cli_task(void* p) { | ||||
|     Cli* cli = cli_alloc(); | ||||
| 
 | ||||
|     // Init basic cli commands
 | ||||
|     cli_commands_init(cli); | ||||
| 
 | ||||
|     if(!furi_create("cli", cli)) { | ||||
|         printf("[cli_task] cannot create the cli record\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
|     furiac_ready(); | ||||
| 
 | ||||
|     while(1) { | ||||
|         cli_process_input(cli); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								applications/cli/cli.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								applications/cli/cli.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| /* Cli type
 | ||||
|  * Anonymous structure. Use cli_i.h if you need to go deeper. | ||||
|  */ | ||||
| typedef struct Cli Cli; | ||||
| 
 | ||||
| /* Cli callback function pointer.
 | ||||
|  * Implement this interface and use add_cli_command | ||||
|  * @param args - string with what was passed after command | ||||
|  * @param context - pointer to whatever you gave us on cli_add_command | ||||
|  */ | ||||
| typedef void (*CliCallback)(string_t args, void* context); | ||||
| 
 | ||||
| /* Add cli command
 | ||||
|  * Registers you command callback | ||||
|  * @param cli - pointer to cli instance | ||||
|  * @param name - command name | ||||
|  * @param callback - callback function | ||||
|  * @param context - pointer to whatever we need to pass to callback | ||||
|  */ | ||||
| void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* context); | ||||
| 
 | ||||
| /* Read terminal input.
 | ||||
|  * Do it only from inside of callback. | ||||
|  * @param buffer - buffer pointer to char buffer | ||||
|  * @param size - size of buffer in bytes | ||||
|  */ | ||||
| void cli_read(char* buffer, size_t size); | ||||
| 
 | ||||
| /* Print to terminal
 | ||||
|  * Do it only from inside of callback. | ||||
|  * @param buffer - pointer to null terminated string to print. | ||||
|  */ | ||||
| void cli_print(const char* buffer); | ||||
| 
 | ||||
| /* New line 
 | ||||
|  * Send new ine sequence | ||||
|  */ | ||||
| void cli_nl(); | ||||
							
								
								
									
										54
									
								
								applications/cli/cli_commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								applications/cli/cli_commands.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| #include "cli_commands.h" | ||||
| #include <api-hal.h> | ||||
| 
 | ||||
| void cli_command_help(string_t args, void* context) { | ||||
|     (void)args; | ||||
|     Cli* cli = context; | ||||
|     cli_print("Commands we have:"); | ||||
| 
 | ||||
|     furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK); | ||||
|     CliCommandDict_it_t it; | ||||
|     for(CliCommandDict_it(it, cli->commands); !CliCommandDict_end_p(it); CliCommandDict_next(it)) { | ||||
|         CliCommandDict_itref_t* ref = CliCommandDict_ref(it); | ||||
|         cli_print(" "); | ||||
|         cli_print(string_get_cstr(ref->key)); | ||||
|     }; | ||||
|     furi_check(osMutexRelease(cli->mutex) == osOK); | ||||
| 
 | ||||
|     if(string_size(args) > 0) { | ||||
|         cli_nl(); | ||||
|         cli_print("Also I have no clue what '"); | ||||
|         cli_print(string_get_cstr(args)); | ||||
|         cli_print("' is."); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cli_command_version(string_t args, void* context) { | ||||
|     (void)args; | ||||
|     (void)context; | ||||
|     cli_print_version(); | ||||
| } | ||||
| 
 | ||||
| void cli_command_uuid(string_t args, void* context) { | ||||
|     (void)args; | ||||
|     (void)context; | ||||
|     size_t uid_size = api_hal_uid_size(); | ||||
|     const uint8_t* uid = api_hal_uid(); | ||||
| 
 | ||||
|     string_t byte_str; | ||||
|     string_init(byte_str); | ||||
|     string_cat_printf(byte_str, "UID:"); | ||||
|     for(size_t i = 0; i < uid_size; i++) { | ||||
|         uint8_t uid_byte = uid[i]; | ||||
|         string_cat_printf(byte_str, "%02X", uid_byte); | ||||
|     } | ||||
|     cli_print(string_get_cstr(byte_str)); | ||||
| } | ||||
| 
 | ||||
| void cli_commands_init(Cli* cli) { | ||||
|     cli_add_command(cli, "help", cli_command_help, cli); | ||||
|     cli_add_command(cli, "?", cli_command_help, cli); | ||||
|     cli_add_command(cli, "version", cli_command_version, cli); | ||||
|     cli_add_command(cli, "!", cli_command_version, cli); | ||||
|     cli_add_command(cli, "uid", cli_command_uuid, cli); | ||||
| } | ||||
							
								
								
									
										5
									
								
								applications/cli/cli_commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								applications/cli/cli_commands.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "cli_i.h" | ||||
| 
 | ||||
| void cli_commands_init(Cli* cli); | ||||
							
								
								
									
										42
									
								
								applications/cli/cli_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								applications/cli/cli_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "cli.h" | ||||
| 
 | ||||
| #include <flipper.h> | ||||
| #include <flipper_v2.h> | ||||
| 
 | ||||
| #include <m-dict.h> | ||||
| 
 | ||||
| #define CLI_LINE_SIZE_MAX | ||||
| 
 | ||||
| typedef struct { | ||||
|     CliCallback callback; | ||||
|     void* context; | ||||
| } CliCommand; | ||||
| 
 | ||||
| DICT_DEF2(CliCommandDict, string_t, STRING_OPLIST, CliCommand, M_POD_OPLIST) | ||||
| 
 | ||||
| typedef enum { | ||||
|     CliSymbolAsciiSOH = 0x01, | ||||
|     CliSymbolAsciiEOT = 0x04, | ||||
|     CliSymbolAsciiBell = 0x07, | ||||
|     CliSymbolAsciiBackspace = 0x08, | ||||
|     CliSymbolAsciiTab = 0x09, | ||||
|     CliSymbolAsciiCR = 0x0D, | ||||
|     CliSymbolAsciiEsc = 0x1B, | ||||
|     CliSymbolAsciiUS = 0x1F, | ||||
|     CliSymbolAsciiSpace = 0x20, | ||||
|     CliSymbolAsciiDel = 0x7F, | ||||
| } CliSymbols; | ||||
| 
 | ||||
| struct Cli { | ||||
|     CliCommandDict_t commands; | ||||
|     osMutexId_t mutex; | ||||
|     string_t line; | ||||
| }; | ||||
| 
 | ||||
| Cli* cli_alloc(); | ||||
| void cli_free(Cli* cli); | ||||
| void cli_reset_state(Cli* cli); | ||||
| void cli_print_version(); | ||||
| void cli_putc(char c); | ||||
							
								
								
									
										444
									
								
								applications/music-player/music-player.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								applications/music-player/music-player.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,444 @@ | ||||
| #include "flipper_v2.h" | ||||
| 
 | ||||
| // TODO float note freq
 | ||||
| typedef enum { | ||||
|     // Delay
 | ||||
|     N = 0, | ||||
|     // Octave 4
 | ||||
|     B4 = 494, | ||||
|     // Octave 5
 | ||||
|     C5 = 523, | ||||
|     D5 = 587, | ||||
|     E5 = 659, | ||||
|     F_5 = 740, | ||||
|     G5 = 784, | ||||
|     A5 = 880, | ||||
|     B5 = 988, | ||||
|     // Octave 6
 | ||||
|     C6 = 1046, | ||||
|     D6 = 1175, | ||||
|     E6 = 1319, | ||||
| } MelodyEventNote; | ||||
| 
 | ||||
| typedef enum { | ||||
|     L1 = 1, | ||||
|     L2 = 2, | ||||
|     L4 = 4, | ||||
|     L8 = 8, | ||||
|     L16 = 16, | ||||
|     L32 = 32, | ||||
|     L64 = 64, | ||||
|     L128 = 128, | ||||
| } MelodyEventLength; | ||||
| 
 | ||||
| typedef struct { | ||||
|     MelodyEventNote note; | ||||
|     MelodyEventLength length; | ||||
| } MelodyEventRecord; | ||||
| 
 | ||||
| typedef struct { | ||||
|     const MelodyEventRecord* record; | ||||
|     int8_t loop_count; | ||||
| } SongPattern; | ||||
| 
 | ||||
| const MelodyEventRecord melody_start[] = { | ||||
|     {E6, L8}, {N, L8},   {E5, L8}, {B5, L8},  {N, L4},  {E5, L8},  {A5, L8},  {G5, L8}, {A5, L8}, | ||||
|     {E5, L8}, {B5, L8},  {N, L8},  {G5, L8},  {A5, L8}, {D6, L8},  {N, L4},   {D5, L8}, {B5, L8}, | ||||
|     {N, L4},  {D5, L8},  {A5, L8}, {G5, L8},  {A5, L8}, {D5, L8},  {F_5, L8}, {N, L8},  {G5, L8}, | ||||
|     {A5, L8}, {D6, L8},  {N, L4},  {F_5, L8}, {B5, L8}, {N, L4},   {F_5, L8}, {D6, L8}, {C6, L8}, | ||||
|     {B5, L8}, {F_5, L8}, {A5, L8}, {N, L8},   {G5, L8}, {F_5, L8}, {E5, L8},  {N, L8},  {C5, L8}, | ||||
|     {E5, L8}, {B5, L8},  {B4, L8}, {C5, L8},  {D5, L8}, {D6, L8},  {C6, L8},  {B5, L8}, {F_5, L8}, | ||||
|     {A5, L8}, {N, L8},   {G5, L8}, {A5, L8},  {E6, L8}}; | ||||
| 
 | ||||
| const MelodyEventRecord melody_loop[] = { | ||||
|     {N, L4},   {E5, L8}, {B5, L8},  {N, L4},  {E5, L8},  {A5, L8},  {G5, L8}, {A5, L8},  {E5, L8}, | ||||
|     {B5, L8},  {N, L8},  {G5, L8},  {A5, L8}, {D6, L8},  {N, L4},   {D5, L8}, {B5, L8},  {N, L4}, | ||||
|     {D5, L8},  {A5, L8}, {G5, L8},  {A5, L8}, {D5, L8},  {F_5, L8}, {N, L8},  {G5, L8},  {A5, L8}, | ||||
|     {D6, L8},  {N, L4},  {F_5, L8}, {B5, L8}, {N, L4},   {F_5, L8}, {D6, L8}, {C6, L8},  {B5, L8}, | ||||
|     {F_5, L8}, {A5, L8}, {N, L8},   {G5, L8}, {F_5, L8}, {E5, L8},  {N, L8},  {C5, L8},  {E5, L8}, | ||||
|     {B5, L8},  {B4, L8}, {C5, L8},  {D5, L8}, {D6, L8},  {C6, L8},  {B5, L8}, {F_5, L8}, {A5, L8}, | ||||
|     {N, L8},   {G5, L8}, {A5, L8},  {E6, L8}}; | ||||
| 
 | ||||
| const MelodyEventRecord melody_chords_1bar[] = { | ||||
|     {E6, L8},   {N, L8},    {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, | ||||
|     {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, | ||||
|     {B4, L128}, {E5, L128}, {B5, L8},   {N, L4},    {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, | ||||
|     {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, | ||||
|     {B4, L128}, {E5, L128}, {B4, L128}, {E5, L128}, {A5, L8}}; | ||||
| 
 | ||||
| const SongPattern song[] = {{melody_start, 1}, {melody_loop, -1}}; | ||||
| 
 | ||||
| typedef enum { | ||||
|     EventTypeTick, | ||||
|     EventTypeKey, | ||||
|     EventTypeNote, | ||||
|     // add your events type
 | ||||
| } MusicDemoEventType; | ||||
| 
 | ||||
| typedef struct { | ||||
|     union { | ||||
|         InputEvent input; | ||||
|         const MelodyEventRecord* note_record; | ||||
|     } value; | ||||
|     MusicDemoEventType type; | ||||
| } MusicDemoEvent; | ||||
| 
 | ||||
| typedef struct { | ||||
|     ValueMutex* state_mutex; | ||||
|     osMessageQueueId_t event_queue; | ||||
| 
 | ||||
| } MusicDemoContext; | ||||
| 
 | ||||
| #define note_stack_size 4 | ||||
| typedef struct { | ||||
|     // describe state here
 | ||||
|     const MelodyEventRecord* note_record; | ||||
|     const MelodyEventRecord* note_stack[note_stack_size]; | ||||
|     uint8_t volume_id; | ||||
|     uint8_t volume_id_max; | ||||
| } State; | ||||
| 
 | ||||
| float volumes[] = {0, 0.02, 0.05, 0.1, 0.5}; | ||||
| 
 | ||||
| bool is_white_note(const MelodyEventRecord* note_record, uint8_t id) { | ||||
|     if(note_record == NULL) return false; | ||||
| 
 | ||||
|     switch(note_record->note) { | ||||
|     case C5: | ||||
|     case C6: | ||||
|         if(id == 0) return true; | ||||
|         break; | ||||
|     case D5: | ||||
|     case D6: | ||||
|         if(id == 1) return true; | ||||
|         break; | ||||
|     case E5: | ||||
|     case E6: | ||||
|         if(id == 2) return true; | ||||
|         break; | ||||
|     case G5: | ||||
|         if(id == 4) return true; | ||||
|         break; | ||||
|     case A5: | ||||
|         if(id == 5) return true; | ||||
|         break; | ||||
|     case B4: | ||||
|     case B5: | ||||
|         if(id == 6) return true; | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool is_black_note(const MelodyEventRecord* note_record, uint8_t id) { | ||||
|     if(note_record == NULL) return false; | ||||
| 
 | ||||
|     switch(note_record->note) { | ||||
|     case F_5: | ||||
|         if(id == 3) return true; | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| const char* get_note_name(const MelodyEventRecord* note_record) { | ||||
|     if(note_record == NULL) return ""; | ||||
| 
 | ||||
|     switch(note_record->note) { | ||||
|     case N: | ||||
|         return "---"; | ||||
|         break; | ||||
|     case B4: | ||||
|         return "B4-"; | ||||
|         break; | ||||
|     case C5: | ||||
|         return "C5-"; | ||||
|         break; | ||||
|     case D5: | ||||
|         return "D5-"; | ||||
|         break; | ||||
|     case E5: | ||||
|         return "E5-"; | ||||
|         break; | ||||
|     case F_5: | ||||
|         return "F#5"; | ||||
|         break; | ||||
|     case G5: | ||||
|         return "G5-"; | ||||
|         break; | ||||
|     case A5: | ||||
|         return "A5-"; | ||||
|         break; | ||||
|     case B5: | ||||
|         return "B5-"; | ||||
|         break; | ||||
|     case C6: | ||||
|         return "C6-"; | ||||
|         break; | ||||
|     case D6: | ||||
|         return "D6-"; | ||||
|         break; | ||||
|     case E6: | ||||
|         return "E6-"; | ||||
|         break; | ||||
|     default: | ||||
|         return "UNK"; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| const char* get_note_len_name(const MelodyEventRecord* note_record) { | ||||
|     if(note_record == NULL) return ""; | ||||
| 
 | ||||
|     switch(note_record->length) { | ||||
|     case L1: | ||||
|         return "1-"; | ||||
|         break; | ||||
|     case L2: | ||||
|         return "2-"; | ||||
|         break; | ||||
|     case L4: | ||||
|         return "4-"; | ||||
|         break; | ||||
|     case L8: | ||||
|         return "8-"; | ||||
|         break; | ||||
|     case L16: | ||||
|         return "16"; | ||||
|         break; | ||||
|     case L32: | ||||
|         return "32"; | ||||
|         break; | ||||
|     case L64: | ||||
|         return "64"; | ||||
|         break; | ||||
|     case L128: | ||||
|         return "1+"; | ||||
|         break; | ||||
|     default: | ||||
|         return "--"; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void render_callback(CanvasApi* canvas, void* ctx) { | ||||
|     State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); | ||||
| 
 | ||||
|     canvas->clear(canvas); | ||||
|     canvas->set_color(canvas, ColorBlack); | ||||
|     canvas->set_font(canvas, FontPrimary); | ||||
|     canvas->draw_str(canvas, 0, 12, "MusicPlayer"); | ||||
| 
 | ||||
|     uint8_t x_pos = 0; | ||||
|     uint8_t y_pos = 24; | ||||
|     const uint8_t white_w = 10; | ||||
|     const uint8_t white_h = 40; | ||||
| 
 | ||||
|     const int8_t black_x = 6; | ||||
|     const int8_t black_y = -5; | ||||
|     const uint8_t black_w = 8; | ||||
|     const uint8_t black_h = 32; | ||||
| 
 | ||||
|     // white keys
 | ||||
|     for(size_t i = 0; i < 7; i++) { | ||||
|         if(is_white_note(state->note_record, i)) { | ||||
|             canvas->draw_box(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h); | ||||
|         } else { | ||||
|             canvas->draw_frame(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // black keys
 | ||||
|     for(size_t i = 0; i < 7; i++) { | ||||
|         if(i != 2 && i != 6) { | ||||
|             canvas->set_color(canvas, ColorWhite); | ||||
|             canvas->draw_box( | ||||
|                 canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); | ||||
|             canvas->set_color(canvas, ColorBlack); | ||||
|             if(is_black_note(state->note_record, i)) { | ||||
|                 canvas->draw_box( | ||||
|                     canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); | ||||
|             } else { | ||||
|                 canvas->draw_frame( | ||||
|                     canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // volume widget
 | ||||
|     x_pos = 124; | ||||
|     y_pos = 0; | ||||
|     const uint8_t volume_h = (64 / (state->volume_id_max - 1)) * state->volume_id; | ||||
|     canvas->draw_frame(canvas, x_pos, y_pos, 4, 64); | ||||
|     canvas->draw_box(canvas, x_pos, y_pos + (64 - volume_h), 4, volume_h); | ||||
| 
 | ||||
|     // note stack widget
 | ||||
|     x_pos = 73; | ||||
|     y_pos = 0; | ||||
|     canvas->set_color(canvas, ColorBlack); | ||||
|     canvas->set_font(canvas, FontPrimary); | ||||
|     canvas->draw_frame(canvas, x_pos, y_pos, 49, 64); | ||||
|     canvas->draw_line(canvas, x_pos + 28, 0, x_pos + 28, 64); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < note_stack_size; i++) { | ||||
|         if(i == 0) { | ||||
|             canvas->draw_box(canvas, x_pos, y_pos + 48, 49, 16); | ||||
|             canvas->set_color(canvas, ColorWhite); | ||||
|         } else { | ||||
|             canvas->set_color(canvas, ColorBlack); | ||||
|         } | ||||
|         canvas->draw_str(canvas, x_pos + 4, 64 - 16 * i - 3, get_note_name(state->note_stack[i])); | ||||
|         canvas->draw_str( | ||||
|             canvas, x_pos + 31, 64 - 16 * i - 3, get_note_len_name(state->note_stack[i])); | ||||
|         canvas->draw_line(canvas, x_pos, 64 - 16 * i, x_pos + 48, 64 - 16 * i); | ||||
|     } | ||||
| 
 | ||||
|     release_mutex((ValueMutex*)ctx, state); | ||||
| } | ||||
| 
 | ||||
| static void input_callback(InputEvent* input_event, void* ctx) { | ||||
|     osMessageQueueId_t event_queue = (QueueHandle_t)ctx; | ||||
| 
 | ||||
|     MusicDemoEvent event; | ||||
|     event.type = EventTypeKey; | ||||
|     event.value.input = *input_event; | ||||
|     osMessageQueuePut(event_queue, &event, 0, 0); | ||||
| } | ||||
| 
 | ||||
| void process_note( | ||||
|     const MelodyEventRecord* note_record, | ||||
|     float bar_length_ms, | ||||
|     MusicDemoContext* context) { | ||||
|     MusicDemoEvent event; | ||||
|     // send note event
 | ||||
|     event.type = EventTypeNote; | ||||
|     event.value.note_record = note_record; | ||||
|     osMessageQueuePut(context->event_queue, &event, 0, 0); | ||||
| 
 | ||||
|     // read volume
 | ||||
|     State* state = (State*)acquire_mutex(context->state_mutex, 25); | ||||
|     float volume = volumes[state->volume_id]; | ||||
|     release_mutex(context->state_mutex, state); | ||||
| 
 | ||||
|     // play note
 | ||||
|     float note_delay = bar_length_ms / (float)note_record->length; | ||||
|     if(note_record->note != N) { | ||||
|         hal_pwm_set(volume, note_record->note, &SPEAKER_TIM, SPEAKER_CH); | ||||
|     } | ||||
|     delay(note_delay); | ||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); | ||||
| } | ||||
| 
 | ||||
| void music_player_thread(void* p) { | ||||
|     MusicDemoContext* context = (MusicDemoContext*)p; | ||||
| 
 | ||||
|     const float bpm = 130.0f; | ||||
|     // 4/4
 | ||||
|     const float bar_length_ms = (60.0f * 1000.0f / bpm) * 4; | ||||
|     const uint16_t melody_start_events_count = sizeof(melody_start) / sizeof(melody_start[0]); | ||||
|     const uint16_t melody_loop_events_count = sizeof(melody_loop) / sizeof(melody_loop[0]); | ||||
| 
 | ||||
|     for(size_t i = 0; i < melody_start_events_count; i++) { | ||||
|         process_note(&melody_start[i], bar_length_ms, context); | ||||
|     } | ||||
| 
 | ||||
|     while(1) { | ||||
|         for(size_t i = 0; i < melody_loop_events_count; i++) { | ||||
|             process_note(&melody_loop[i], bar_length_ms, context); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void music_player(void* p) { | ||||
|     osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(MusicDemoEvent), NULL); | ||||
| 
 | ||||
|     State _state; | ||||
|     _state.note_record = NULL; | ||||
|     for(size_t i = 0; i < note_stack_size; i++) { | ||||
|         _state.note_stack[i] = NULL; | ||||
|     } | ||||
|     _state.volume_id = 1; | ||||
|     _state.volume_id_max = sizeof(volumes) / sizeof(volumes[0]); | ||||
| 
 | ||||
|     ValueMutex state_mutex; | ||||
|     if(!init_mutex(&state_mutex, &_state, sizeof(State))) { | ||||
|         printf("cannot create mutex\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
|     Widget* widget = widget_alloc(); | ||||
|     widget_draw_callback_set(widget, render_callback, &state_mutex); | ||||
|     widget_input_callback_set(widget, input_callback, event_queue); | ||||
| 
 | ||||
|     // Open GUI and register widget
 | ||||
|     GuiApi* gui = (GuiApi*)furi_open("gui"); | ||||
|     if(gui == NULL) { | ||||
|         printf("gui is not available\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
|     gui->add_widget(gui, widget, GuiLayerFullscreen); | ||||
| 
 | ||||
|     // open input record
 | ||||
|     PubSub* input_events_record = furi_open("input_events"); | ||||
|     // prepare "do nothing" event
 | ||||
|     InputEvent input_event = {InputRight, true}; | ||||
| 
 | ||||
|     // start player thread
 | ||||
|     // TODO change to fuirac_start
 | ||||
|     osThreadAttr_t player_attr = {.name = "music_player_thread", .stack_size = 512}; | ||||
|     MusicDemoContext context = {.state_mutex = &state_mutex, .event_queue = event_queue}; | ||||
|     osThreadId_t player = osThreadNew(music_player_thread, &context, &player_attr); | ||||
| 
 | ||||
|     MusicDemoEvent event; | ||||
|     while(1) { | ||||
|         osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100); | ||||
| 
 | ||||
|         State* state = (State*)acquire_mutex_block(&state_mutex); | ||||
| 
 | ||||
|         if(event_status == osOK) { | ||||
|             if(event.type == EventTypeKey) { | ||||
|                 // press events
 | ||||
|                 if(event.value.input.state && event.value.input.input == InputBack) { | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.state && event.value.input.input == InputUp) { | ||||
|                     if(state->volume_id < state->volume_id_max - 1) state->volume_id++; | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.state && event.value.input.input == InputDown) { | ||||
|                     if(state->volume_id > 0) state->volume_id--; | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.state && event.value.input.input == InputLeft) { | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.state && event.value.input.input == InputRight) { | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.input == InputOk) { | ||||
|                 } | ||||
| 
 | ||||
|             } else if(event.type == EventTypeNote) { | ||||
|                 // send "do nothing" event to prevent display backlight off
 | ||||
|                 notify_pubsub(input_events_record, &input_event); | ||||
| 
 | ||||
|                 state->note_record = event.value.note_record; | ||||
| 
 | ||||
|                 for(size_t i = note_stack_size - 1; i > 0; i--) { | ||||
|                     state->note_stack[i] = state->note_stack[i - 1]; | ||||
|                 } | ||||
|                 state->note_stack[0] = state->note_record; | ||||
|             } | ||||
|         } else { | ||||
|             // event timeout
 | ||||
|         } | ||||
| 
 | ||||
|         widget_update(widget); | ||||
|         release_mutex(&state_mutex, state); | ||||
|     } | ||||
| } | ||||
| @ -68,6 +68,12 @@ void nfc_test_callback(void* context) { | ||||
|     nfc->screen = 0; | ||||
|     widget_enabled_set(nfc->widget, true); | ||||
| 
 | ||||
|     // TODO only for workaround
 | ||||
|     if(nfc->ret != ERR_NONE) { | ||||
|         nfc->ret = rfalNfcInitialize(); | ||||
|         rfalLowPowerModeStart(); | ||||
|     } | ||||
| 
 | ||||
|     if(nfc->ret == ERR_NONE && !nfc->worker) { | ||||
|         // TODO change to fuirac_start
 | ||||
|         nfc->worker = osThreadNew(nfc_worker_task, nfc, &nfc->worker_attr); | ||||
| @ -77,11 +83,29 @@ void nfc_test_callback(void* context) { | ||||
| } | ||||
| 
 | ||||
| void nfc_field_on_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // TODO only for workaround
 | ||||
|     if(nfc->ret != ERR_NONE) { | ||||
|         nfc->ret = rfalNfcInitialize(); | ||||
|         rfalLowPowerModeStart(); | ||||
|     } | ||||
| 
 | ||||
|     st25r3916OscOn(); | ||||
|     st25r3916TxRxOn(); | ||||
| } | ||||
| 
 | ||||
| void nfc_field_off_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // TODO only for workaround
 | ||||
|     if(nfc->ret != ERR_NONE) { | ||||
|         nfc->ret = rfalNfcInitialize(); | ||||
|         rfalLowPowerModeStart(); | ||||
|     } | ||||
| 
 | ||||
|     st25r3916TxRxOff(); | ||||
| } | ||||
| 
 | ||||
| @ -158,8 +182,8 @@ void nfc_task(void* p) { | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
|     nfc->ret = rfalNfcInitialize(); | ||||
|     rfalLowPowerModeStart(); | ||||
|     // TODO only for workaround
 | ||||
|     nfc->ret = ERR_WRONG_STATE; | ||||
| 
 | ||||
|     furiac_ready(); | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| #include <gui/widget.h> | ||||
| #include <assets_icons.h> | ||||
| #include <api-hal-power.h> | ||||
| #include <cli/cli.h> | ||||
| 
 | ||||
| struct Power { | ||||
|     Icon* usb_icon; | ||||
| @ -17,6 +18,7 @@ struct Power { | ||||
|     Widget* battery_widget; | ||||
| 
 | ||||
|     ValueMutex* menu_vm; | ||||
|     Cli* cli; | ||||
|     MenuItem* menu; | ||||
| 
 | ||||
|     uint8_t charge; | ||||
| @ -54,6 +56,8 @@ Power* power_alloc() { | ||||
|     power->menu_vm = furi_open("menu"); | ||||
|     furi_check(power->menu_vm); | ||||
| 
 | ||||
|     power->cli = furi_open("cli"); | ||||
| 
 | ||||
|     power->menu = menu_item_alloc_menu("Power", NULL); | ||||
|     menu_item_subitem_add( | ||||
|         power->menu, menu_item_alloc_function("Poweroff", NULL, power_off_callback, power)); | ||||
| @ -82,10 +86,20 @@ void power_free(Power* power) { | ||||
|     free(power); | ||||
| } | ||||
| 
 | ||||
| void power_cli_poweroff(string_t args, void* context) { | ||||
|     cli_print("Poweroff in 5 seconds"); | ||||
|     osDelay(5000); | ||||
|     api_hal_power_off(); | ||||
| } | ||||
| 
 | ||||
| void power_task(void* p) { | ||||
|     (void)p; | ||||
|     Power* power = power_alloc(); | ||||
| 
 | ||||
|     if(power->cli) { | ||||
|         cli_add_command(power->cli, "poweroff", power_cli_poweroff, power); | ||||
|     } | ||||
| 
 | ||||
|     FuriRecordSubscriber* gui_record = furi_open_deprecated("gui", false, false, NULL, NULL, NULL); | ||||
|     assert(gui_record); | ||||
|     GuiApi* gui = furi_take(gui_record); | ||||
|  | ||||
| @ -1,5 +1,15 @@ | ||||
| #include "flipper_v2.h" | ||||
| 
 | ||||
| bool init_flipper_api(void) { | ||||
|     return gpio_api_init(); | ||||
|     bool no_errors = true; | ||||
| 
 | ||||
|     if(!furi_init()) { | ||||
|         no_errors = false; | ||||
|     } | ||||
| 
 | ||||
|     if(!gpio_api_init()) { | ||||
|         no_errors = false; | ||||
|     } | ||||
| 
 | ||||
|     return no_errors; | ||||
| } | ||||
| @ -11,6 +11,13 @@ | ||||
| 
 | ||||
| static FuriRecord records[MAX_RECORD_COUNT]; | ||||
| static size_t current_buffer_idx = 0; | ||||
| osMutexId_t furi_core_mutex; | ||||
| 
 | ||||
| bool furi_init(void) { | ||||
|     furi_core_mutex = osMutexNew(NULL); | ||||
|     if(furi_core_mutex == NULL) return false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| // find record pointer by name
 | ||||
| static FuriRecord* find_record(const char* name) { | ||||
| @ -32,6 +39,11 @@ bool furi_create_deprecated(const char* name, void* value, size_t size) { | ||||
|     printf("[FURI] creating %s record\n", name); | ||||
| #endif | ||||
| 
 | ||||
|     // acquire mutex to prevent simultaneous write to record with same index
 | ||||
|     if(osMutexAcquire(furi_core_mutex, osWaitForever) != osOK) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     FuriRecord* record = find_record(name); | ||||
| 
 | ||||
|     if(record != NULL) { | ||||
| @ -69,6 +81,8 @@ bool furi_create_deprecated(const char* name, void* value, size_t size) { | ||||
| 
 | ||||
|     current_buffer_idx++; | ||||
| 
 | ||||
|     osMutexRelease(furi_core_mutex); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -87,6 +87,9 @@ typedef struct { | ||||
|     FlipperAppLibrary libs; | ||||
| } FlipperStartupApp; | ||||
| 
 | ||||
| // Init core
 | ||||
| bool furi_init(void); | ||||
| 
 | ||||
| /*!
 | ||||
| Simply starts application. | ||||
| It call app entrypoint with param passed as argument. | ||||
|  | ||||
| @ -31,12 +31,12 @@ void furiac_wait_libs(const FlipperAppLibrary* libs) { | ||||
| 
 | ||||
|         if(app_id == INVALID_TASK_ID) { | ||||
| #ifdef FURI_DEBUG | ||||
|             printf("[FURIAC] Invalid library name %s\n", lib_name); | ||||
|             printf("[FURIAC] Invalid library name %s\n", libs->name[i]); | ||||
| #endif | ||||
|         } else { | ||||
|             while(!task_buffer[app_id].ready) { | ||||
| #ifdef FURI_DEBUG | ||||
|                 printf("[FURIAC] waiting for library \"%s\"\n", lib_name); | ||||
|                 printf("[FURIAC] waiting for library \"%s\"\n", libs->name[i]); | ||||
| #endif | ||||
|                 osDelay(50); | ||||
|             } | ||||
|  | ||||
							
								
								
									
										138
									
								
								core/ring.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								core/ring.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | ||||
| #include "ring.h" | ||||
| #include <flipper_v2.h> | ||||
| 
 | ||||
| struct Ring { | ||||
|     uint8_t* data; | ||||
|     size_t size; | ||||
|     volatile size_t read_ptr; | ||||
|     volatile size_t write_ptr; | ||||
| }; | ||||
| 
 | ||||
| Ring* ring_alloc(size_t size) { | ||||
|     Ring* ring = furi_alloc(sizeof(Ring)); | ||||
|     ring->size = size + 1; | ||||
|     ring->data = furi_alloc(ring->size); | ||||
|     ring_clear(ring); | ||||
|     return ring; | ||||
| } | ||||
| 
 | ||||
| void ring_free(Ring* ring) { | ||||
|     furi_assert(ring); | ||||
|     free(ring->data); | ||||
|     free(ring); | ||||
| } | ||||
| 
 | ||||
| size_t ring_size(Ring* ring) { | ||||
|     furi_assert(ring); | ||||
|     return ring->size - 1; | ||||
| } | ||||
| 
 | ||||
| inline static size_t ring_read_calculate(Ring* ring, size_t r, size_t w) { | ||||
|     if(w >= r) { | ||||
|         return w - r; | ||||
|     } else { | ||||
|         return ring->size - (r - w); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| size_t ring_read_space(Ring* ring) { | ||||
|     furi_assert(ring); | ||||
| 
 | ||||
|     const size_t r = ring->read_ptr; | ||||
|     const size_t w = ring->write_ptr; | ||||
| 
 | ||||
|     return ring_read_calculate(ring, r, w); | ||||
| } | ||||
| 
 | ||||
| inline static size_t ring_write_calculate(Ring* ring, size_t r, size_t w) { | ||||
|     if(r > w) { | ||||
|         return r - w - 1; | ||||
|     } else { | ||||
|         return ring->size - (r - w); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| size_t ring_write_space(Ring* ring) { | ||||
|     furi_assert(ring); | ||||
| 
 | ||||
|     const size_t r = ring->read_ptr; | ||||
|     const size_t w = ring->write_ptr; | ||||
| 
 | ||||
|     return ring_write_calculate(ring, r, w); | ||||
| } | ||||
| 
 | ||||
| size_t ring_push(Ring* ring, const uint8_t* data, size_t size) { | ||||
|     furi_assert(ring); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     const size_t r = ring->read_ptr; | ||||
|     size_t w = ring->write_ptr; | ||||
|     const size_t write_space = ring_write_calculate(ring, r, w); | ||||
| 
 | ||||
|     if(write_space == 0) return 0; | ||||
| 
 | ||||
|     const size_t to_write = size > write_space ? write_space : size; | ||||
|     size_t end, first, second; | ||||
| 
 | ||||
|     end = w + to_write; | ||||
|     if(end > ring->size) { | ||||
|         first = ring->size - w; | ||||
|         second = end % ring->size; | ||||
|     } else { | ||||
|         first = to_write; | ||||
|         second = 0; | ||||
|     } | ||||
| 
 | ||||
|     memcpy(ring->data + w, data, first); | ||||
|     w = (w + first) % ring->size; | ||||
| 
 | ||||
|     if(second) { | ||||
|         memcpy(ring->data + w, data + first, second); | ||||
|         w = (w + second) % ring->size; | ||||
|     } | ||||
| 
 | ||||
|     ring->write_ptr = w; | ||||
| 
 | ||||
|     return to_write; | ||||
| } | ||||
| 
 | ||||
| size_t ring_pull(Ring* ring, uint8_t* data, size_t size) { | ||||
|     furi_assert(ring); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     size_t r = ring->read_ptr; | ||||
|     const size_t w = ring->write_ptr; | ||||
|     const size_t read_space = ring_read_calculate(ring, r, w); | ||||
| 
 | ||||
|     if(read_space == 0) return 0; | ||||
| 
 | ||||
|     size_t to_read = size > read_space ? read_space : size; | ||||
|     size_t end, first, second; | ||||
| 
 | ||||
|     end = r + to_read; | ||||
|     if(end > ring->size) { | ||||
|         first = ring->size - r; | ||||
|         second = end % ring->size; | ||||
|     } else { | ||||
|         first = to_read; | ||||
|         second = 0; | ||||
|     } | ||||
| 
 | ||||
|     memcpy(data, ring->data + r, first); | ||||
|     r = (r + first) % ring->size; | ||||
| 
 | ||||
|     if(second) { | ||||
|         memcpy(data + first, ring->data + r, second); | ||||
|         r = (r + second) % ring->size; | ||||
|     } | ||||
| 
 | ||||
|     ring->read_ptr = r; | ||||
| 
 | ||||
|     return to_read; | ||||
| } | ||||
| 
 | ||||
| void ring_clear(Ring* ring) { | ||||
|     furi_assert(ring); | ||||
|     ring->read_ptr = 0; | ||||
|     ring->write_ptr = 0; | ||||
| } | ||||
							
								
								
									
										22
									
								
								core/ring.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								core/ring.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| typedef struct Ring Ring; | ||||
| 
 | ||||
| Ring* ring_alloc(size_t size); | ||||
| 
 | ||||
| void ring_free(Ring* ring); | ||||
| 
 | ||||
| size_t ring_size(Ring* ring); | ||||
| 
 | ||||
| size_t ring_read_space(Ring* ring); | ||||
| 
 | ||||
| size_t ring_write_space(Ring* ring); | ||||
| 
 | ||||
| size_t ring_push(Ring* ring, const uint8_t* data, size_t size); | ||||
| 
 | ||||
| size_t ring_pull(Ring* ring, uint8_t* data, size_t size); | ||||
| 
 | ||||
| void ring_clear(Ring* ring); | ||||
| @ -1,105 +0,0 @@ | ||||
| # script for stm32wbx family | ||||
| 
 | ||||
| gdb_port 4242 | ||||
| 
 | ||||
| # | ||||
| # stm32wb devices support both JTAG and SWD transports. | ||||
| # | ||||
| source [find target/swj-dp.tcl] | ||||
| source [find mem_helper.tcl] | ||||
| 
 | ||||
| if { [info exists CHIPNAME] } { | ||||
|    set _CHIPNAME $CHIPNAME | ||||
| } else { | ||||
|    set _CHIPNAME stm32wbx | ||||
| } | ||||
| 
 | ||||
| set _ENDIAN little | ||||
| 
 | ||||
| # Work-area is a space in RAM used for flash programming | ||||
| # By default use 64kB | ||||
| if { [info exists WORKAREASIZE] } { | ||||
|    set _WORKAREASIZE $WORKAREASIZE | ||||
| } else { | ||||
|    set _WORKAREASIZE 0x10000 | ||||
| } | ||||
| 
 | ||||
| #jtag scan chain | ||||
| if { [info exists CPUTAPID] } { | ||||
|    set _CPUTAPID $CPUTAPID | ||||
| } else { | ||||
|    if { [using_jtag] } { | ||||
|       set _CPUTAPID 0x6ba00477 | ||||
|    } else { | ||||
|       # SWD IDCODE (single drop, arm) | ||||
|       set _CPUTAPID 0x6ba02477 | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID | ||||
| dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu | ||||
| 
 | ||||
| if {[using_jtag]} { | ||||
|    jtag newtap $_CHIPNAME bs -irlen 5 | ||||
| } | ||||
| 
 | ||||
| set _TARGETNAME $_CHIPNAME.cpu | ||||
| target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap | ||||
| 
 | ||||
| $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 | ||||
| 
 | ||||
| set _FLASHNAME $_CHIPNAME.flash | ||||
| flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME | ||||
| 
 | ||||
| # Common knowledges tells JTAG speed should be <= F_CPU/6. | ||||
| # F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on | ||||
| # the safe side. | ||||
| # | ||||
| # Note that there is a pretty wide band where things are | ||||
| # more or less stable, see http://openocd.zylin.com/#/c/3366/ | ||||
| adapter speed 500 | ||||
| 
 | ||||
| adapter srst delay 100 | ||||
| if {[using_jtag]} { | ||||
|  jtag_ntrst_delay 100 | ||||
| } | ||||
| 
 | ||||
| reset_config srst_nogate | ||||
| 
 | ||||
| if {![using_hla]} { | ||||
|    # if srst is not fitted use SYSRESETREQ to | ||||
|    # perform a soft reset | ||||
|    cortex_m reset_config sysresetreq | ||||
| } | ||||
| 
 | ||||
| $_TARGETNAME configure -event reset-init { | ||||
|     # CPU comes out of reset with MSI_ON | MSI_RDY | MSI Range 4 MHz. | ||||
|     # Configure system to use MSI 24 MHz clock, compliant with VOS default Range1. | ||||
|     # 2 WS compliant with VOS=Range1 and 24 MHz. | ||||
|     mmw 0x58004000 0x00000102 0  ;# FLASH_ACR |= PRFTBE | 2(Latency) | ||||
|     mmw 0x58000000 0x00000091 0  ;# RCC_CR = MSI_ON | MSI Range 24 MHz | ||||
|     # Boost JTAG frequency | ||||
|     adapter speed 4000 | ||||
| } | ||||
| 
 | ||||
| $_TARGETNAME configure -event reset-start { | ||||
|     # Reset clock is MSI (4 MHz) | ||||
|     adapter speed 500 | ||||
| } | ||||
| 
 | ||||
| $_TARGETNAME configure -event examine-end { | ||||
|     # Enable debug during low power modes (uses more power) | ||||
|     # DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP | ||||
|     mmw 0xE0042004 0x00000007 0 | ||||
| 
 | ||||
|     # Stop watchdog counters during halt | ||||
|     # DBGMCU_APB1_FZR1 |= DBG_IWDG_STOP | DBG_WWDG_STOP | ||||
|     mmw 0xE004203C 0x00001800 0 | ||||
| } | ||||
| 
 | ||||
| $_TARGETNAME configure -event trace-config { | ||||
|     # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync | ||||
|     # change this value accordingly to configure trace pins | ||||
|     # assignment | ||||
|     mmw 0xE0042004 0x00000020 0 | ||||
| } | ||||
							
								
								
									
										10
									
								
								firmware/targets/Inc/api-hal-uid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								firmware/targets/Inc/api-hal-uid.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| 
 | ||||
| /* Get platform UID size in bytes */ | ||||
| size_t api_hal_uid_size(); | ||||
| 
 | ||||
| /* Get const pointer to UID */ | ||||
| const uint8_t* api_hal_uid(); | ||||
							
								
								
									
										24
									
								
								firmware/targets/Inc/api-hal-vcp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								firmware/targets/Inc/api-hal-vcp.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| /* Init VCP HAL
 | ||||
|  * Allocates ring buffer and initializes state | ||||
|  */ | ||||
| void api_hal_vcp_init(); | ||||
| 
 | ||||
| /* Recieve data from VCP
 | ||||
|  * Waits till some data arrives, never returns 0 | ||||
|  * @param buffer - pointer to buffer | ||||
|  * @param size - buffer size | ||||
|  * @return items copied in buffer, 0 if channel closed | ||||
|  */ | ||||
| size_t api_hal_vcp_rx(uint8_t* buffer, size_t size); | ||||
| 
 | ||||
| /* Transmit data to VCP
 | ||||
|  * @param buffer - pointer to buffer | ||||
|  * @param size - buffer size | ||||
|  */ | ||||
| void api_hal_vcp_tx(uint8_t* buffer, size_t size); | ||||
| @ -5,3 +5,6 @@ | ||||
| #include "api-hal-pwm.h" | ||||
| #include "api-hal-task.h" | ||||
| #include "api-hal-tim.h" | ||||
| #include "api-hal-power.h" | ||||
| #include "api-hal-vcp.h" | ||||
| #include "api-hal-uid.h" | ||||
| @ -51,8 +51,8 @@ | ||||
| /* USER CODE BEGIN EXPORTED_DEFINES */ | ||||
| /* Define size for the receive and transmit buffer over CDC */ | ||||
| /* It's up to user to redefine and/or remove those define */ | ||||
| #define APP_RX_DATA_SIZE  2048 | ||||
| #define APP_TX_DATA_SIZE  2048 | ||||
| #define APP_RX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||
| #define APP_TX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||
| 
 | ||||
| /* USER CODE END EXPORTED_DEFINES */ | ||||
| 
 | ||||
|  | ||||
| @ -104,6 +104,7 @@ int main(void) | ||||
|   /* USER CODE BEGIN 2 */ | ||||
|   MX_FATFS_Init(); | ||||
|   delay_us_init_DWT(); | ||||
|   api_hal_vcp_init(); | ||||
|   /* USER CODE END 2 */ | ||||
| 
 | ||||
|   /* Init scheduler */ | ||||
|  | ||||
| @ -120,7 +120,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) | ||||
|     */ | ||||
|     GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12; | ||||
|     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; | ||||
|     GPIO_InitStruct.Pull = GPIO_PULLDOWN; | ||||
|     GPIO_InitStruct.Pull = GPIO_NOPULL; | ||||
|     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; | ||||
|     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3; | ||||
|     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); | ||||
|  | ||||
| @ -51,6 +51,12 @@ | ||||
| 
 | ||||
| /* USER CODE BEGIN PRIVATE_TYPES */ | ||||
| 
 | ||||
| extern void _api_hal_vcp_init(); | ||||
| extern void _api_hal_vcp_deinit(); | ||||
| extern void _api_hal_vcp_control_line(uint8_t state); | ||||
| extern void _api_hal_vcp_rx_callback(char* buffer, size_t size); | ||||
| extern void _api_hal_vcp_tx_complete(size_t size); | ||||
| 
 | ||||
| /* USER CODE END PRIVATE_TYPES */ | ||||
| 
 | ||||
| /**
 | ||||
| @ -156,6 +162,7 @@ static int8_t CDC_Init_FS(void) | ||||
|   /* Set Application Buffers */ | ||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0); | ||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); | ||||
|   _api_hal_vcp_init(); | ||||
|   return (USBD_OK); | ||||
|   /* USER CODE END 3 */ | ||||
| } | ||||
| @ -167,6 +174,7 @@ static int8_t CDC_Init_FS(void) | ||||
| static int8_t CDC_DeInit_FS(void) | ||||
| { | ||||
|   /* USER CODE BEGIN 4 */ | ||||
|   _api_hal_vcp_deinit(); | ||||
|   return (USBD_OK); | ||||
|   /* USER CODE END 4 */ | ||||
| } | ||||
| @ -181,63 +189,34 @@ static int8_t CDC_DeInit_FS(void) | ||||
| static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | ||||
| { | ||||
|   /* USER CODE BEGIN 5 */ | ||||
|   switch(cmd) | ||||
|   { | ||||
|     case CDC_SEND_ENCAPSULATED_COMMAND: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_GET_ENCAPSULATED_RESPONSE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_SET_COMM_FEATURE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_GET_COMM_FEATURE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_CLEAR_COMM_FEATURE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|   /*******************************************************************************/ | ||||
|   /* Line Coding Structure                                                       */ | ||||
|   /*-----------------------------------------------------------------------------*/ | ||||
|   /* Offset | Field       | Size | Value  | Description                          */ | ||||
|   /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/ | ||||
|   /* 4      | bCharFormat |   1  | Number | Stop bits                            */ | ||||
|   /*                                        0 - 1 Stop bit                       */ | ||||
|   /*                                        1 - 1.5 Stop bits                    */ | ||||
|   /*                                        2 - 2 Stop bits                      */ | ||||
|   /* 5      | bParityType |  1   | Number | Parity                               */ | ||||
|   /*                                        0 - None                             */ | ||||
|   /*                                        1 - Odd                              */ | ||||
|   /*                                        2 - Even                             */ | ||||
|   /*                                        3 - Mark                             */ | ||||
|   /*                                        4 - Space                            */ | ||||
|   /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ | ||||
|   /*******************************************************************************/ | ||||
|     case CDC_SET_LINE_CODING: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_GET_LINE_CODING: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_SET_CONTROL_LINE_STATE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_SEND_BREAK: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|   default: | ||||
|     break; | ||||
|   if (cmd == CDC_SEND_ENCAPSULATED_COMMAND) { | ||||
|   } else if (cmd == CDC_GET_ENCAPSULATED_RESPONSE) { | ||||
|   } else if (cmd == CDC_SET_COMM_FEATURE) { | ||||
|   } else if (cmd == CDC_GET_COMM_FEATURE) { | ||||
|   } else if (cmd == CDC_CLEAR_COMM_FEATURE) { | ||||
|   } else if (cmd == CDC_SET_LINE_CODING) { | ||||
|       /*******************************************************************************/ | ||||
|       /* Line Coding Structure                                                       */ | ||||
|       /*-----------------------------------------------------------------------------*/ | ||||
|       /* Offset | Field       | Size | Value  | Description                          */ | ||||
|       /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/ | ||||
|       /* 4      | bCharFormat |   1  | Number | Stop bits                            */ | ||||
|       /*                                        0 - 1 Stop bit                       */ | ||||
|       /*                                        1 - 1.5 Stop bits                    */ | ||||
|       /*                                        2 - 2 Stop bits                      */ | ||||
|       /* 5      | bParityType |  1   | Number | Parity                               */ | ||||
|       /*                                        0 - None                             */ | ||||
|       /*                                        1 - Odd                              */ | ||||
|       /*                                        2 - Even                             */ | ||||
|       /*                                        3 - Mark                             */ | ||||
|       /*                                        4 - Space                            */ | ||||
|       /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ | ||||
|       /*******************************************************************************/ | ||||
|   } else if (cmd == CDC_GET_LINE_CODING) { | ||||
|   } else if (cmd == CDC_SET_CONTROL_LINE_STATE) { | ||||
|     _api_hal_vcp_control_line(((USBD_SetupReqTypedef*)pbuf)->wValue); | ||||
|   } else if (cmd == CDC_SEND_BREAK) { | ||||
|   } else { | ||||
|   } | ||||
| 
 | ||||
|   return (USBD_OK); | ||||
| @ -262,7 +241,7 @@ static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | ||||
| static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) | ||||
| { | ||||
|   /* USER CODE BEGIN 6 */ | ||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]); | ||||
|   _api_hal_vcp_rx_callback((char*)Buf, *Len); | ||||
|   USBD_CDC_ReceivePacket(&hUsbDeviceFS); | ||||
|   return (USBD_OK); | ||||
|   /* USER CODE END 6 */ | ||||
| @ -287,7 +266,8 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) | ||||
|   if (hcdc->TxState != 0){ | ||||
|     return USBD_BUSY; | ||||
|   } | ||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); | ||||
|   memcpy(UserTxBufferFS, Buf, Len); | ||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, Len); | ||||
|   result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); | ||||
|   /* USER CODE END 7 */ | ||||
|   return result; | ||||
| @ -310,8 +290,8 @@ static int8_t CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum) | ||||
|   uint8_t result = USBD_OK; | ||||
|   /* USER CODE BEGIN 13 */ | ||||
|   UNUSED(Buf); | ||||
|   UNUSED(Len); | ||||
|   UNUSED(epnum); | ||||
|   _api_hal_vcp_tx_complete(*Len); | ||||
|   /* USER CODE END 13 */ | ||||
|   return result; | ||||
| } | ||||
|  | ||||
							
								
								
									
										10
									
								
								firmware/targets/f2/api-hal/api-hal-uid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								firmware/targets/f2/api-hal/api-hal-uid.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| #include <api-hal-uid.h> | ||||
| #include <stm32l4xx.h> | ||||
| 
 | ||||
| size_t api_hal_uid_size() { | ||||
|     return 96/8; | ||||
| } | ||||
| 
 | ||||
| const uint8_t* api_hal_uid() { | ||||
|     return (const uint8_t *)UID_BASE; | ||||
| } | ||||
							
								
								
									
										92
									
								
								firmware/targets/f2/api-hal/api-hal-vcp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								firmware/targets/f2/api-hal/api-hal-vcp.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | ||||
| #include <api-hal-vcp.h> | ||||
| #include <usbd_cdc_if.h> | ||||
| #include <flipper_v2.h> | ||||
| #include <stream_buffer.h> | ||||
| 
 | ||||
| #define API_HAL_VCP_RX_BUFFER_SIZE 600 | ||||
| 
 | ||||
| typedef struct { | ||||
|     StreamBufferHandle_t rx_stream; | ||||
|     osSemaphoreId_t tx_semaphore; | ||||
|     volatile bool alive; | ||||
|     volatile bool underrun; | ||||
| } ApiHalVcp; | ||||
| 
 | ||||
| ApiHalVcp api_hal_vcp; | ||||
| 
 | ||||
| static const uint8_t ascii_soh = 0x01; | ||||
| static const uint8_t ascii_eot = 0x04; | ||||
| 
 | ||||
| void _api_hal_vcp_init(); | ||||
| void _api_hal_vcp_deinit(); | ||||
| void _api_hal_vcp_control_line(uint8_t state); | ||||
| void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size); | ||||
| void _api_hal_vcp_tx_complete(size_t size); | ||||
| 
 | ||||
| void api_hal_vcp_init() { | ||||
|     api_hal_vcp.rx_stream = xStreamBufferCreate(API_HAL_VCP_RX_BUFFER_SIZE, 1); | ||||
|     api_hal_vcp.tx_semaphore = osSemaphoreNew(1, 1, NULL); | ||||
|     api_hal_vcp.alive = false; | ||||
|     api_hal_vcp.underrun = false; | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_init() { | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_deinit() { | ||||
|     api_hal_vcp.alive = false; | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_control_line(uint8_t state) { | ||||
|     // bit 0: DTR state, bit 1: RTS state
 | ||||
|     // bool dtr = state & 0b01;
 | ||||
|     bool rts = state & 0b10; | ||||
| 
 | ||||
|     if (rts) { | ||||
|         api_hal_vcp.alive = true; | ||||
|         _api_hal_vcp_rx_callback(&ascii_soh, 1); // SOH
 | ||||
|     } else { | ||||
|         api_hal_vcp.alive = false; | ||||
|         _api_hal_vcp_rx_callback(&ascii_eot, 1); // EOT
 | ||||
|     } | ||||
| 
 | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size) { | ||||
|     BaseType_t xHigherPriorityTaskWoken = pdFALSE; | ||||
|     size_t ret = xStreamBufferSendFromISR(api_hal_vcp.rx_stream, buffer, size, &xHigherPriorityTaskWoken); | ||||
|     if (ret != size) { | ||||
|         api_hal_vcp.underrun = true; | ||||
|     } | ||||
|     portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_tx_complete(size_t size) { | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| size_t api_hal_vcp_rx(uint8_t* buffer, size_t size) { | ||||
|     return xStreamBufferReceive(api_hal_vcp.rx_stream, buffer, size, portMAX_DELAY); | ||||
| } | ||||
| 
 | ||||
| void api_hal_vcp_tx(uint8_t* buffer, size_t size) { | ||||
|     while (size > 0 && api_hal_vcp.alive) { | ||||
|         furi_check(osSemaphoreAcquire(api_hal_vcp.tx_semaphore, osWaitForever) == osOK); | ||||
| 
 | ||||
|         size_t batch_size = size; | ||||
|         if (batch_size > APP_TX_DATA_SIZE) { | ||||
|             batch_size = APP_TX_DATA_SIZE; | ||||
|         } | ||||
| 
 | ||||
|         if (CDC_Transmit_FS(buffer, batch_size) == USBD_OK) { | ||||
|             size -= batch_size; | ||||
|             buffer += batch_size; | ||||
|         } else { | ||||
|             // Shouldn't be there 
 | ||||
|             osDelay(100); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| TOOLCHAIN = arm | ||||
| 
 | ||||
| DEBUG_AGENT		= set -m; st-util -n --semihosting | ||||
| DEBUG_AGENT		= openocd -f interface/stlink-v2.cfg -c "transport select hla_swd" -f target/stm32l4x.cfg -c "init" | ||||
| 
 | ||||
| BOOT_ADDRESS	= 0x08000000 | ||||
| FW_ADDRESS		= 0x08008000 | ||||
|  | ||||
| @ -51,8 +51,8 @@ | ||||
| /* USER CODE BEGIN EXPORTED_DEFINES */ | ||||
| /* Define size for the receive and transmit buffer over CDC */ | ||||
| /* It's up to user to redefine and/or remove those define */ | ||||
| #define APP_RX_DATA_SIZE  2048 | ||||
| #define APP_TX_DATA_SIZE  2048 | ||||
| #define APP_RX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||
| #define APP_TX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||
| 
 | ||||
| /* USER CODE END EXPORTED_DEFINES */ | ||||
| 
 | ||||
|  | ||||
| @ -111,6 +111,7 @@ int main(void) | ||||
|   /* USER CODE BEGIN 2 */ | ||||
|   MX_FATFS_Init(); | ||||
|   delay_us_init_DWT(); | ||||
|   api_hal_vcp_init(); | ||||
|   /* USER CODE END 2 */ | ||||
| 
 | ||||
|   /* Init scheduler */ | ||||
|  | ||||
| @ -51,6 +51,12 @@ | ||||
| 
 | ||||
| /* USER CODE BEGIN PRIVATE_TYPES */ | ||||
| 
 | ||||
| extern void _api_hal_vcp_init(); | ||||
| extern void _api_hal_vcp_deinit(); | ||||
| extern void _api_hal_vcp_control_line(uint8_t state); | ||||
| extern void _api_hal_vcp_rx_callback(char* buffer, size_t size); | ||||
| extern void _api_hal_vcp_tx_complete(size_t size); | ||||
| 
 | ||||
| /* USER CODE END PRIVATE_TYPES */ | ||||
| 
 | ||||
| /**
 | ||||
| @ -156,6 +162,7 @@ static int8_t CDC_Init_FS(void) | ||||
|   /* Set Application Buffers */ | ||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0); | ||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); | ||||
|   _api_hal_vcp_init(); | ||||
|   return (USBD_OK); | ||||
|   /* USER CODE END 3 */ | ||||
| } | ||||
| @ -167,6 +174,7 @@ static int8_t CDC_Init_FS(void) | ||||
| static int8_t CDC_DeInit_FS(void) | ||||
| { | ||||
|   /* USER CODE BEGIN 4 */ | ||||
|   _api_hal_vcp_deinit(); | ||||
|   return (USBD_OK); | ||||
|   /* USER CODE END 4 */ | ||||
| } | ||||
| @ -181,63 +189,34 @@ static int8_t CDC_DeInit_FS(void) | ||||
| static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | ||||
| { | ||||
|   /* USER CODE BEGIN 5 */ | ||||
|   switch(cmd) | ||||
|   { | ||||
|     case CDC_SEND_ENCAPSULATED_COMMAND: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_GET_ENCAPSULATED_RESPONSE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_SET_COMM_FEATURE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_GET_COMM_FEATURE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_CLEAR_COMM_FEATURE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|   /*******************************************************************************/ | ||||
|   /* Line Coding Structure                                                       */ | ||||
|   /*-----------------------------------------------------------------------------*/ | ||||
|   /* Offset | Field       | Size | Value  | Description                          */ | ||||
|   /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/ | ||||
|   /* 4      | bCharFormat |   1  | Number | Stop bits                            */ | ||||
|   /*                                        0 - 1 Stop bit                       */ | ||||
|   /*                                        1 - 1.5 Stop bits                    */ | ||||
|   /*                                        2 - 2 Stop bits                      */ | ||||
|   /* 5      | bParityType |  1   | Number | Parity                               */ | ||||
|   /*                                        0 - None                             */ | ||||
|   /*                                        1 - Odd                              */ | ||||
|   /*                                        2 - Even                             */ | ||||
|   /*                                        3 - Mark                             */ | ||||
|   /*                                        4 - Space                            */ | ||||
|   /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ | ||||
|   /*******************************************************************************/ | ||||
|     case CDC_SET_LINE_CODING: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_GET_LINE_CODING: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_SET_CONTROL_LINE_STATE: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|     case CDC_SEND_BREAK: | ||||
| 
 | ||||
|     break; | ||||
| 
 | ||||
|   default: | ||||
|     break; | ||||
|   if (cmd == CDC_SEND_ENCAPSULATED_COMMAND) { | ||||
|   } else if (cmd == CDC_GET_ENCAPSULATED_RESPONSE) { | ||||
|   } else if (cmd == CDC_SET_COMM_FEATURE) { | ||||
|   } else if (cmd == CDC_GET_COMM_FEATURE) { | ||||
|   } else if (cmd == CDC_CLEAR_COMM_FEATURE) { | ||||
|   } else if (cmd == CDC_SET_LINE_CODING) { | ||||
|       /*******************************************************************************/ | ||||
|       /* Line Coding Structure                                                       */ | ||||
|       /*-----------------------------------------------------------------------------*/ | ||||
|       /* Offset | Field       | Size | Value  | Description                          */ | ||||
|       /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/ | ||||
|       /* 4      | bCharFormat |   1  | Number | Stop bits                            */ | ||||
|       /*                                        0 - 1 Stop bit                       */ | ||||
|       /*                                        1 - 1.5 Stop bits                    */ | ||||
|       /*                                        2 - 2 Stop bits                      */ | ||||
|       /* 5      | bParityType |  1   | Number | Parity                               */ | ||||
|       /*                                        0 - None                             */ | ||||
|       /*                                        1 - Odd                              */ | ||||
|       /*                                        2 - Even                             */ | ||||
|       /*                                        3 - Mark                             */ | ||||
|       /*                                        4 - Space                            */ | ||||
|       /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ | ||||
|       /*******************************************************************************/ | ||||
|   } else if (cmd == CDC_GET_LINE_CODING) { | ||||
|   } else if (cmd == CDC_SET_CONTROL_LINE_STATE) { | ||||
|     _api_hal_vcp_control_line(((USBD_SetupReqTypedef*)pbuf)->wValue); | ||||
|   } else if (cmd == CDC_SEND_BREAK) { | ||||
|   } else { | ||||
|   } | ||||
| 
 | ||||
|   return (USBD_OK); | ||||
| @ -262,7 +241,7 @@ static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | ||||
| static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) | ||||
| { | ||||
|   /* USER CODE BEGIN 6 */ | ||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]); | ||||
|   _api_hal_vcp_rx_callback((char*)Buf, *Len); | ||||
|   USBD_CDC_ReceivePacket(&hUsbDeviceFS); | ||||
|   return (USBD_OK); | ||||
|   /* USER CODE END 6 */ | ||||
| @ -287,7 +266,8 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) | ||||
|   if (hcdc->TxState != 0){ | ||||
|     return USBD_BUSY; | ||||
|   } | ||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); | ||||
|   memcpy(UserTxBufferFS, Buf, Len); | ||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, Len); | ||||
|   result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); | ||||
|   /* USER CODE END 7 */ | ||||
|   return result; | ||||
| @ -310,8 +290,8 @@ static int8_t CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum) | ||||
|   uint8_t result = USBD_OK; | ||||
|   /* USER CODE BEGIN 13 */ | ||||
|   UNUSED(Buf); | ||||
|   UNUSED(Len); | ||||
|   UNUSED(epnum); | ||||
|   _api_hal_vcp_tx_complete(*Len); | ||||
|   /* USER CODE END 13 */ | ||||
|   return result; | ||||
| } | ||||
|  | ||||
							
								
								
									
										10
									
								
								firmware/targets/f3/api-hal/api-hal-uuid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								firmware/targets/f3/api-hal/api-hal-uuid.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| #include <api-hal-uid.h> | ||||
| #include <stm32wbxx.h> | ||||
| 
 | ||||
| size_t api_hal_uid_size() { | ||||
|     return 64/8; | ||||
| } | ||||
| 
 | ||||
| const uint8_t* api_hal_uid() { | ||||
|     return (const uint8_t *)UID64_BASE; | ||||
| } | ||||
							
								
								
									
										92
									
								
								firmware/targets/f3/api-hal/api-hal-vcp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								firmware/targets/f3/api-hal/api-hal-vcp.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | ||||
| #include <api-hal-vcp.h> | ||||
| #include <usbd_cdc_if.h> | ||||
| #include <flipper_v2.h> | ||||
| #include <stream_buffer.h> | ||||
| 
 | ||||
| #define API_HAL_VCP_RX_BUFFER_SIZE 600 | ||||
| 
 | ||||
| typedef struct { | ||||
|     StreamBufferHandle_t rx_stream; | ||||
|     osSemaphoreId_t tx_semaphore; | ||||
|     volatile bool alive; | ||||
|     volatile bool underrun; | ||||
| } ApiHalVcp; | ||||
| 
 | ||||
| ApiHalVcp api_hal_vcp; | ||||
| 
 | ||||
| static const uint8_t ascii_soh = 0x01; | ||||
| static const uint8_t ascii_eot = 0x04; | ||||
| 
 | ||||
| void _api_hal_vcp_init(); | ||||
| void _api_hal_vcp_deinit(); | ||||
| void _api_hal_vcp_control_line(uint8_t state); | ||||
| void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size); | ||||
| void _api_hal_vcp_tx_complete(size_t size); | ||||
| 
 | ||||
| void api_hal_vcp_init() { | ||||
|     api_hal_vcp.rx_stream = xStreamBufferCreate(API_HAL_VCP_RX_BUFFER_SIZE, 1); | ||||
|     api_hal_vcp.tx_semaphore = osSemaphoreNew(1, 1, NULL); | ||||
|     api_hal_vcp.alive = false; | ||||
|     api_hal_vcp.underrun = false; | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_init() { | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_deinit() { | ||||
|     api_hal_vcp.alive = false; | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_control_line(uint8_t state) { | ||||
|     // bit 0: DTR state, bit 1: RTS state
 | ||||
|     // bool dtr = state & 0b01;
 | ||||
|     bool rts = state & 0b10; | ||||
| 
 | ||||
|     if (rts) { | ||||
|         api_hal_vcp.alive = true; | ||||
|         _api_hal_vcp_rx_callback(&ascii_soh, 1); // SOH
 | ||||
|     } else { | ||||
|         api_hal_vcp.alive = false; | ||||
|         _api_hal_vcp_rx_callback(&ascii_eot, 1); // EOT
 | ||||
|     } | ||||
| 
 | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size) { | ||||
|     BaseType_t xHigherPriorityTaskWoken = pdFALSE; | ||||
|     size_t ret = xStreamBufferSendFromISR(api_hal_vcp.rx_stream, buffer, size, &xHigherPriorityTaskWoken); | ||||
|     if (ret != size) { | ||||
|         api_hal_vcp.underrun = true; | ||||
|     } | ||||
|     portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||||
| } | ||||
| 
 | ||||
| void _api_hal_vcp_tx_complete(size_t size) { | ||||
|     osSemaphoreRelease(api_hal_vcp.tx_semaphore); | ||||
| } | ||||
| 
 | ||||
| size_t api_hal_vcp_rx(uint8_t* buffer, size_t size) { | ||||
|     return xStreamBufferReceive(api_hal_vcp.rx_stream, buffer, size, portMAX_DELAY); | ||||
| } | ||||
| 
 | ||||
| void api_hal_vcp_tx(uint8_t* buffer, size_t size) { | ||||
|     while (size > 0 && api_hal_vcp.alive) { | ||||
|         furi_check(osSemaphoreAcquire(api_hal_vcp.tx_semaphore, osWaitForever) == osOK); | ||||
| 
 | ||||
|         size_t batch_size = size; | ||||
|         if (batch_size > APP_TX_DATA_SIZE) { | ||||
|             batch_size = APP_TX_DATA_SIZE; | ||||
|         } | ||||
| 
 | ||||
|         if (CDC_Transmit_FS(buffer, batch_size) == USBD_OK) { | ||||
|             size -= batch_size; | ||||
|             buffer += batch_size; | ||||
|         } else { | ||||
|             // Shouldn't be there 
 | ||||
|             osDelay(100); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,7 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "api-hal-gpio.h" | ||||
| #include "api-hal-delay.h" | ||||
| #include "api-hal-pwm.h" | ||||
| #include "api-hal-task.h" | ||||
| #include "api-hal-tim.h" | ||||
| @ -270,6 +270,7 @@ Mcu.Pin12=PA2 | ||||
| PD0.GPIOParameters=GPIO_Speed,PinState,GPIO_Label | ||||
| Mcu.Pin10=PA0 | ||||
| SH.GPXTI10.ConfNb=1 | ||||
| USB_DEVICE.APP_RX_DATA_SIZE=512 | ||||
| TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE | ||||
| PC3.GPIO_Label=PC3 | ||||
| PA3.PinState=GPIO_PIN_SET | ||||
| @ -453,7 +454,7 @@ OSC_IN.GPIOParameters=GPIO_Label | ||||
| PB12.Locked=true | ||||
| ProjectManager.DeletePrevious=true | ||||
| PB10.Locked=true | ||||
| USB_DEVICE.IPParameters=VirtualMode,VirtualModeFS,CLASS_NAME_FS,MANUFACTURER_STRING,PRODUCT_STRING_CDC_FS | ||||
| USB_DEVICE.IPParameters=VirtualMode,VirtualModeFS,CLASS_NAME_FS,MANUFACTURER_STRING,PRODUCT_STRING_CDC_FS,APP_RX_DATA_SIZE,APP_TX_DATA_SIZE | ||||
| TIM16.Channel=TIM_CHANNEL_1 | ||||
| RCC.AHB2CLKDivider=RCC_SYSCLK_DIV2 | ||||
| RCC.FamilyName=M | ||||
| @ -528,6 +529,7 @@ PA13.Locked=true | ||||
| RF1.Mode=RF1_Activate | ||||
| PB7.Mode=Asynchronous | ||||
| NVIC.EXTI9_5_IRQn=true\:5\:0\:true\:false\:true\:false\:true\:true | ||||
| USB_DEVICE.APP_TX_DATA_SIZE=512 | ||||
| PA14.Signal=SYS_JTCK-SWCLK | ||||
| PB2.GPIO_Label=PB2 | ||||
| PC6.GPIOParameters=GPIO_Label | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| TOOLCHAIN = arm | ||||
| 
 | ||||
| DEBUG_AGENT		= openocd -f interface/stlink-v2.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init" -c "reset halt" | ||||
| DEBUG_AGENT		= openocd -f interface/stlink.cfg -c "transport select hla_swd" -f target/stm32wbx.cfg -c "init" | ||||
| 
 | ||||
| BOOT_ADDRESS	= 0x08000000 | ||||
| FW_ADDRESS		= 0x08008000 | ||||
|  | ||||
							
								
								
									
										2
									
								
								lib/mlib
									
									
									
									
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								lib/mlib
									
									
									
									
									
								
							| @ -1 +1 @@ | ||||
| Subproject commit eb7556f88faf0bbfd6a4ae99a3d53dcbe2064b88 | ||||
| Subproject commit ebf440731d44f5eba926e0f648758676461bbba4 | ||||
| @ -73,7 +73,7 @@ upload: $(OBJ_DIR)/upload | ||||
| debug: flash | ||||
| 	$(DEBUG_AGENT) & echo $$! > $(OBJ_DIR)/agent.PID | ||||
| 	arm-none-eabi-gdb \
 | ||||
| 		-ex "target extended-remote 127.0.0.1:4242" \
 | ||||
| 		-ex "target extended-remote 127.0.0.1:3333" \
 | ||||
| 		-ex "set confirm off" \
 | ||||
| 		-ex "source ../debug/FreeRTOS/FreeRTOS.py" \
 | ||||
| 		$(OBJ_DIR)/$(PROJECT).elf; \
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13