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 | 
							
								
								
									
										24
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @ -13,6 +13,7 @@ jobs: | |||||||
|       - name: Checkout code |       - name: Checkout code | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|         with: |         with: | ||||||
|  |           fetch-depth: 0 | ||||||
|           submodules: true |           submodules: true | ||||||
|       - uses: satackey/action-docker-layer-caching@v0.0.8 |       - uses: satackey/action-docker-layer-caching@v0.0.8 | ||||||
|         continue-on-error: true |         continue-on-error: true | ||||||
| @ -110,12 +111,12 @@ jobs: | |||||||
|       - name: Get bootloader |       - name: Get bootloader | ||||||
|         uses: actions/download-artifact@v2 |         uses: actions/download-artifact@v2 | ||||||
|         with: |         with: | ||||||
|           name: bootloader_f2 |           name: bootloader_f3 | ||||||
|           path: bootloader |           path: bootloader | ||||||
|       - name: Get firmware |       - name: Get firmware | ||||||
|         uses: actions/download-artifact@v2 |         uses: actions/download-artifact@v2 | ||||||
|         with: |         with: | ||||||
|           name: firmware_f2 |           name: firmware_f3 | ||||||
|           path: firmware |           path: firmware | ||||||
|       - name: Upload bootloader |       - name: Upload bootloader | ||||||
|         uses: burnett01/rsync-deployments@4.1 |         uses: burnett01/rsync-deployments@4.1 | ||||||
| @ -145,17 +146,12 @@ jobs: | |||||||
|       - name: Get bootloader |       - name: Get bootloader | ||||||
|         uses: actions/download-artifact@v2 |         uses: actions/download-artifact@v2 | ||||||
|         with: |         with: | ||||||
|           name: bootloader_f2 |           name: bootloader_f3 | ||||||
|           path: bootloader |           path: bootloader | ||||||
|       - name: Get firmware |       - name: Get firmware | ||||||
|         uses: actions/download-artifact@v2 |         uses: actions/download-artifact@v2 | ||||||
|         with: |         with: | ||||||
|           name: firmware_f2 |           name: firmware_f3 | ||||||
|           path: firmware |  | ||||||
|       - name: Get firmware |  | ||||||
|         uses: actions/download-artifact@v2 |  | ||||||
|         with: |  | ||||||
|           name: firmware_f2 |  | ||||||
|           path: firmware  |           path: firmware  | ||||||
|       - name: cp  |       - name: cp  | ||||||
|         run: cp ./bootloader/bootloader.bin full_firmware_latest.bin |         run: cp ./bootloader/bootloader.bin full_firmware_latest.bin | ||||||
| @ -187,12 +183,6 @@ jobs: | |||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|           ref: master  |           ref: master  | ||||||
|           submodules: true       |           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 |       - name: Set test env | ||||||
|         run: echo "NUMBER_OF_COMMITS=$(git rev-list --count HEAD)" >> $GITHUB_ENV |         run: echo "NUMBER_OF_COMMITS=$(git rev-list --count HEAD)" >> $GITHUB_ENV | ||||||
|       - name: Test output NUMBER_OF_COMMITS |       - name: Test output NUMBER_OF_COMMITS | ||||||
| @ -205,12 +195,12 @@ jobs: | |||||||
|       - name: Test output PREP_DATE |       - name: Test output PREP_DATE | ||||||
|         run: echo $PREP_DATE           |         run: echo $PREP_DATE           | ||||||
|       - name: Gen pic |       - 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 |       - name: Upload pic | ||||||
|         uses: burnett01/rsync-deployments@4.1 |         uses: burnett01/rsync-deployments@4.1 | ||||||
|         with: |         with: | ||||||
|           switches: -avzp --delete |           switches: -avzp --delete | ||||||
|           path: out.png |           path: latest-firmware-banner.png | ||||||
|           remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}/" |           remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}/" | ||||||
|           remote_host: ${{ secrets.RSYNC_DEPLOY_HOST }} |           remote_host: ${{ secrets.RSYNC_DEPLOY_HOST }} | ||||||
|           remote_user: ${{ secrets.RSYNC_DEPLOY_USER }} |           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 | # 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. | 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 application_vibro(void* p); | ||||||
| void app_gpio_test(void* p); | void app_gpio_test(void* p); | ||||||
| void app_ibutton(void* p); | void app_ibutton(void* p); | ||||||
|  | void cli_task(void* p); | ||||||
|  | void music_player(void* p); | ||||||
| 
 | 
 | ||||||
| const FlipperStartupApp FLIPPER_STARTUP[] = { | const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||||
| #ifdef APP_DISPLAY | #ifdef APP_DISPLAY | ||||||
|     {.app = display_u8g2, .name = "display_u8g2", .libs = {0}}, |     {.app = display_u8g2, .name = "display_u8g2", .libs = {0}}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef APP_CLI | ||||||
|  |     {.app = cli_task, .name = "cli_task", .libs = {0}}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_EXAMPLE_BLINK | #ifdef APP_EXAMPLE_BLINK | ||||||
|     {.app = application_blink, .name = "blink", .libs = {1, FURI_LIB{"input_task"}}}, |     {.app = application_blink, .name = "blink", .libs = {1, FURI_LIB{"input_task"}}}, | ||||||
| #endif | #endif | ||||||
| @ -69,7 +75,7 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_POWER | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_CC1101 | #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"}}}, |     {.app = sd_card_test, .name = "sd_card_test", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef APP_MUSIC_PLAYER | ||||||
|  |     {.app = music_player, .name = "music player", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_GPIO_DEMO | #ifdef APP_GPIO_DEMO | ||||||
|     { |     { | ||||||
|         .app = app_gpio_test, |         .app = app_gpio_test, | ||||||
| @ -173,4 +183,8 @@ const FlipperStartupApp FLIPPER_APPS[] = { | |||||||
| #ifdef BUILD_IBUTTON | #ifdef BUILD_IBUTTON | ||||||
|     {.app = app_ibutton, .name = "ibutton", .libs = {1, FURI_LIB{"gui_task"}}}, |     {.app = app_ibutton, .name = "ibutton", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||||
| #endif | #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_MENU = 1 | ||||||
| APP_NFC  = 1 | APP_NFC  = 1 | ||||||
| APP_POWER = 1 | APP_POWER = 1 | ||||||
|  | APP_CLI = 1 | ||||||
| BUILD_IRDA  = 1 | BUILD_IRDA  = 1 | ||||||
| APP_IRUKAGOTCHI = 1 | APP_IRUKAGOTCHI = 1 | ||||||
| BUILD_EXAMPLE_BLINK = 1 | BUILD_EXAMPLE_BLINK = 1 | ||||||
| @ -22,6 +23,7 @@ BUILD_SPEAKER_DEMO = 1 | |||||||
| BUILD_VIBRO_DEMO = 1 | BUILD_VIBRO_DEMO = 1 | ||||||
| BUILD_SD_TEST = 1 | BUILD_SD_TEST = 1 | ||||||
| BUILD_GPIO_DEMO = 1 | BUILD_GPIO_DEMO = 1 | ||||||
|  | BUILD_MUSIC_PLAYER = 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| APP_NFC ?= 0 | APP_NFC ?= 0 | ||||||
| @ -45,6 +47,13 @@ CFLAGS		+= -DAPP_POWER | |||||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/power/*.c) | C_SOURCES	+= $(wildcard $(APP_DIR)/power/*.c) | ||||||
| endif | 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 | APP_MENU ?= 0 | ||||||
| ifeq ($(APP_MENU), 1) | ifeq ($(APP_MENU), 1) | ||||||
| CFLAGS += -DAPP_MENU | CFLAGS += -DAPP_MENU | ||||||
| @ -238,6 +247,17 @@ CFLAGS		+= -DBUILD_GPIO_DEMO | |||||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/gpio-tester/*.c) | C_SOURCES	+= $(wildcard $(APP_DIR)/gpio-tester/*.c) | ||||||
| endif | 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
 | # device drivers
 | ||||||
| 
 | 
 | ||||||
| APP_GUI	?= 0 | 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; |     nfc->screen = 0; | ||||||
|     widget_enabled_set(nfc->widget, true); |     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) { |     if(nfc->ret == ERR_NONE && !nfc->worker) { | ||||||
|         // TODO change to fuirac_start
 |         // TODO change to fuirac_start
 | ||||||
|         nfc->worker = osThreadNew(nfc_worker_task, nfc, &nfc->worker_attr); |         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) { | 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(); |     st25r3916OscOn(); | ||||||
|     st25r3916TxRxOn(); |     st25r3916TxRxOn(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_field_off_callback(void* context) { | 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(); |     st25r3916TxRxOff(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -158,8 +182,8 @@ void nfc_task(void* p) { | |||||||
|         furiac_exit(NULL); |         furiac_exit(NULL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nfc->ret = rfalNfcInitialize(); |     // TODO only for workaround
 | ||||||
|     rfalLowPowerModeStart(); |     nfc->ret = ERR_WRONG_STATE; | ||||||
| 
 | 
 | ||||||
|     furiac_ready(); |     furiac_ready(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ | |||||||
| #include <gui/widget.h> | #include <gui/widget.h> | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
| #include <api-hal-power.h> | #include <api-hal-power.h> | ||||||
|  | #include <cli/cli.h> | ||||||
| 
 | 
 | ||||||
| struct Power { | struct Power { | ||||||
|     Icon* usb_icon; |     Icon* usb_icon; | ||||||
| @ -17,6 +18,7 @@ struct Power { | |||||||
|     Widget* battery_widget; |     Widget* battery_widget; | ||||||
| 
 | 
 | ||||||
|     ValueMutex* menu_vm; |     ValueMutex* menu_vm; | ||||||
|  |     Cli* cli; | ||||||
|     MenuItem* menu; |     MenuItem* menu; | ||||||
| 
 | 
 | ||||||
|     uint8_t charge; |     uint8_t charge; | ||||||
| @ -54,6 +56,8 @@ Power* power_alloc() { | |||||||
|     power->menu_vm = furi_open("menu"); |     power->menu_vm = furi_open("menu"); | ||||||
|     furi_check(power->menu_vm); |     furi_check(power->menu_vm); | ||||||
| 
 | 
 | ||||||
|  |     power->cli = furi_open("cli"); | ||||||
|  | 
 | ||||||
|     power->menu = menu_item_alloc_menu("Power", NULL); |     power->menu = menu_item_alloc_menu("Power", NULL); | ||||||
|     menu_item_subitem_add( |     menu_item_subitem_add( | ||||||
|         power->menu, menu_item_alloc_function("Poweroff", NULL, power_off_callback, power)); |         power->menu, menu_item_alloc_function("Poweroff", NULL, power_off_callback, power)); | ||||||
| @ -82,10 +86,20 @@ void power_free(Power* power) { | |||||||
|     free(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 power_task(void* p) { | ||||||
|     (void)p; |     (void)p; | ||||||
|     Power* power = power_alloc(); |     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); |     FuriRecordSubscriber* gui_record = furi_open_deprecated("gui", false, false, NULL, NULL, NULL); | ||||||
|     assert(gui_record); |     assert(gui_record); | ||||||
|     GuiApi* gui = furi_take(gui_record); |     GuiApi* gui = furi_take(gui_record); | ||||||
|  | |||||||
| @ -1,5 +1,15 @@ | |||||||
| #include "flipper_v2.h" | #include "flipper_v2.h" | ||||||
| 
 | 
 | ||||||
| bool init_flipper_api(void) { | 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 FuriRecord records[MAX_RECORD_COUNT]; | ||||||
| static size_t current_buffer_idx = 0; | 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
 | // find record pointer by name
 | ||||||
| static FuriRecord* find_record(const char* 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); |     printf("[FURI] creating %s record\n", name); | ||||||
| #endif | #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); |     FuriRecord* record = find_record(name); | ||||||
| 
 | 
 | ||||||
|     if(record != NULL) { |     if(record != NULL) { | ||||||
| @ -69,6 +81,8 @@ bool furi_create_deprecated(const char* name, void* value, size_t size) { | |||||||
| 
 | 
 | ||||||
|     current_buffer_idx++; |     current_buffer_idx++; | ||||||
| 
 | 
 | ||||||
|  |     osMutexRelease(furi_core_mutex); | ||||||
|  | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -87,6 +87,9 @@ typedef struct { | |||||||
|     FlipperAppLibrary libs; |     FlipperAppLibrary libs; | ||||||
| } FlipperStartupApp; | } FlipperStartupApp; | ||||||
| 
 | 
 | ||||||
|  | // Init core
 | ||||||
|  | bool furi_init(void); | ||||||
|  | 
 | ||||||
| /*!
 | /*!
 | ||||||
| Simply starts application. | Simply starts application. | ||||||
| It call app entrypoint with param passed as argument. | 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) { |         if(app_id == INVALID_TASK_ID) { | ||||||
| #ifdef FURI_DEBUG | #ifdef FURI_DEBUG | ||||||
|             printf("[FURIAC] Invalid library name %s\n", lib_name); |             printf("[FURIAC] Invalid library name %s\n", libs->name[i]); | ||||||
| #endif | #endif | ||||||
|         } else { |         } else { | ||||||
|             while(!task_buffer[app_id].ready) { |             while(!task_buffer[app_id].ready) { | ||||||
| #ifdef FURI_DEBUG | #ifdef FURI_DEBUG | ||||||
|                 printf("[FURIAC] waiting for library \"%s\"\n", lib_name); |                 printf("[FURIAC] waiting for library \"%s\"\n", libs->name[i]); | ||||||
| #endif | #endif | ||||||
|                 osDelay(50); |                 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-pwm.h" | ||||||
| #include "api-hal-task.h" | #include "api-hal-task.h" | ||||||
| #include "api-hal-tim.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 */ | /* USER CODE BEGIN EXPORTED_DEFINES */ | ||||||
| /* Define size for the receive and transmit buffer over CDC */ | /* Define size for the receive and transmit buffer over CDC */ | ||||||
| /* It's up to user to redefine and/or remove those define */ | /* It's up to user to redefine and/or remove those define */ | ||||||
| #define APP_RX_DATA_SIZE  2048 | #define APP_RX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||||
| #define APP_TX_DATA_SIZE  2048 | #define APP_TX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||||
| 
 | 
 | ||||||
| /* USER CODE END EXPORTED_DEFINES */ | /* USER CODE END EXPORTED_DEFINES */ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -104,6 +104,7 @@ int main(void) | |||||||
|   /* USER CODE BEGIN 2 */ |   /* USER CODE BEGIN 2 */ | ||||||
|   MX_FATFS_Init(); |   MX_FATFS_Init(); | ||||||
|   delay_us_init_DWT(); |   delay_us_init_DWT(); | ||||||
|  |   api_hal_vcp_init(); | ||||||
|   /* USER CODE END 2 */ |   /* USER CODE END 2 */ | ||||||
| 
 | 
 | ||||||
|   /* Init scheduler */ |   /* 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.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12; | ||||||
|     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; |     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.Speed = GPIO_SPEED_FREQ_VERY_HIGH; | ||||||
|     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3; |     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3; | ||||||
|     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); |     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); | ||||||
|  | |||||||
| @ -51,6 +51,12 @@ | |||||||
| 
 | 
 | ||||||
| /* USER CODE BEGIN PRIVATE_TYPES */ | /* 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 */ | /* USER CODE END PRIVATE_TYPES */ | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -156,6 +162,7 @@ static int8_t CDC_Init_FS(void) | |||||||
|   /* Set Application Buffers */ |   /* Set Application Buffers */ | ||||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0); |   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0); | ||||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); |   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); | ||||||
|  |   _api_hal_vcp_init(); | ||||||
|   return (USBD_OK); |   return (USBD_OK); | ||||||
|   /* USER CODE END 3 */ |   /* USER CODE END 3 */ | ||||||
| } | } | ||||||
| @ -167,6 +174,7 @@ static int8_t CDC_Init_FS(void) | |||||||
| static int8_t CDC_DeInit_FS(void) | static int8_t CDC_DeInit_FS(void) | ||||||
| { | { | ||||||
|   /* USER CODE BEGIN 4 */ |   /* USER CODE BEGIN 4 */ | ||||||
|  |   _api_hal_vcp_deinit(); | ||||||
|   return (USBD_OK); |   return (USBD_OK); | ||||||
|   /* USER CODE END 4 */ |   /* USER CODE END 4 */ | ||||||
| } | } | ||||||
| @ -181,28 +189,12 @@ static int8_t CDC_DeInit_FS(void) | |||||||
| static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | ||||||
| { | { | ||||||
|   /* USER CODE BEGIN 5 */ |   /* USER CODE BEGIN 5 */ | ||||||
|   switch(cmd) |   if (cmd == CDC_SEND_ENCAPSULATED_COMMAND) { | ||||||
|   { |   } else if (cmd == CDC_GET_ENCAPSULATED_RESPONSE) { | ||||||
|     case CDC_SEND_ENCAPSULATED_COMMAND: |   } else if (cmd == CDC_SET_COMM_FEATURE) { | ||||||
| 
 |   } else if (cmd == CDC_GET_COMM_FEATURE) { | ||||||
|     break; |   } else if (cmd == CDC_CLEAR_COMM_FEATURE) { | ||||||
| 
 |   } else if (cmd == CDC_SET_LINE_CODING) { | ||||||
|     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                                                       */ |       /* Line Coding Structure                                                       */ | ||||||
|       /*-----------------------------------------------------------------------------*/ |       /*-----------------------------------------------------------------------------*/ | ||||||
| @ -220,24 +212,11 @@ static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | |||||||
|       /*                                        4 - Space                            */ |       /*                                        4 - Space                            */ | ||||||
|       /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ |       /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ | ||||||
|       /*******************************************************************************/ |       /*******************************************************************************/ | ||||||
|     case CDC_SET_LINE_CODING: |   } else if (cmd == CDC_GET_LINE_CODING) { | ||||||
| 
 |   } else if (cmd == CDC_SET_CONTROL_LINE_STATE) { | ||||||
|     break; |     _api_hal_vcp_control_line(((USBD_SetupReqTypedef*)pbuf)->wValue); | ||||||
| 
 |   } else if (cmd == CDC_SEND_BREAK) { | ||||||
|     case CDC_GET_LINE_CODING: |   } else { | ||||||
| 
 |  | ||||||
|     break; |  | ||||||
| 
 |  | ||||||
|     case CDC_SET_CONTROL_LINE_STATE: |  | ||||||
| 
 |  | ||||||
|     break; |  | ||||||
| 
 |  | ||||||
|     case CDC_SEND_BREAK: |  | ||||||
| 
 |  | ||||||
|     break; |  | ||||||
| 
 |  | ||||||
|   default: |  | ||||||
|     break; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return (USBD_OK); |   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) | static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) | ||||||
| { | { | ||||||
|   /* USER CODE BEGIN 6 */ |   /* USER CODE BEGIN 6 */ | ||||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]); |   _api_hal_vcp_rx_callback((char*)Buf, *Len); | ||||||
|   USBD_CDC_ReceivePacket(&hUsbDeviceFS); |   USBD_CDC_ReceivePacket(&hUsbDeviceFS); | ||||||
|   return (USBD_OK); |   return (USBD_OK); | ||||||
|   /* USER CODE END 6 */ |   /* USER CODE END 6 */ | ||||||
| @ -287,7 +266,8 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) | |||||||
|   if (hcdc->TxState != 0){ |   if (hcdc->TxState != 0){ | ||||||
|     return USBD_BUSY; |     return USBD_BUSY; | ||||||
|   } |   } | ||||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); |   memcpy(UserTxBufferFS, Buf, Len); | ||||||
|  |   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, Len); | ||||||
|   result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); |   result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); | ||||||
|   /* USER CODE END 7 */ |   /* USER CODE END 7 */ | ||||||
|   return result; |   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; |   uint8_t result = USBD_OK; | ||||||
|   /* USER CODE BEGIN 13 */ |   /* USER CODE BEGIN 13 */ | ||||||
|   UNUSED(Buf); |   UNUSED(Buf); | ||||||
|   UNUSED(Len); |  | ||||||
|   UNUSED(epnum); |   UNUSED(epnum); | ||||||
|  |   _api_hal_vcp_tx_complete(*Len); | ||||||
|   /* USER CODE END 13 */ |   /* USER CODE END 13 */ | ||||||
|   return result; |   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 | 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 | BOOT_ADDRESS	= 0x08000000 | ||||||
| FW_ADDRESS		= 0x08008000 | FW_ADDRESS		= 0x08008000 | ||||||
|  | |||||||
| @ -51,8 +51,8 @@ | |||||||
| /* USER CODE BEGIN EXPORTED_DEFINES */ | /* USER CODE BEGIN EXPORTED_DEFINES */ | ||||||
| /* Define size for the receive and transmit buffer over CDC */ | /* Define size for the receive and transmit buffer over CDC */ | ||||||
| /* It's up to user to redefine and/or remove those define */ | /* It's up to user to redefine and/or remove those define */ | ||||||
| #define APP_RX_DATA_SIZE  2048 | #define APP_RX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||||
| #define APP_TX_DATA_SIZE  2048 | #define APP_TX_DATA_SIZE  CDC_DATA_HS_MAX_PACKET_SIZE | ||||||
| 
 | 
 | ||||||
| /* USER CODE END EXPORTED_DEFINES */ | /* USER CODE END EXPORTED_DEFINES */ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -111,6 +111,7 @@ int main(void) | |||||||
|   /* USER CODE BEGIN 2 */ |   /* USER CODE BEGIN 2 */ | ||||||
|   MX_FATFS_Init(); |   MX_FATFS_Init(); | ||||||
|   delay_us_init_DWT(); |   delay_us_init_DWT(); | ||||||
|  |   api_hal_vcp_init(); | ||||||
|   /* USER CODE END 2 */ |   /* USER CODE END 2 */ | ||||||
| 
 | 
 | ||||||
|   /* Init scheduler */ |   /* Init scheduler */ | ||||||
|  | |||||||
| @ -51,6 +51,12 @@ | |||||||
| 
 | 
 | ||||||
| /* USER CODE BEGIN PRIVATE_TYPES */ | /* 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 */ | /* USER CODE END PRIVATE_TYPES */ | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -156,6 +162,7 @@ static int8_t CDC_Init_FS(void) | |||||||
|   /* Set Application Buffers */ |   /* Set Application Buffers */ | ||||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0); |   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0); | ||||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); |   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); | ||||||
|  |   _api_hal_vcp_init(); | ||||||
|   return (USBD_OK); |   return (USBD_OK); | ||||||
|   /* USER CODE END 3 */ |   /* USER CODE END 3 */ | ||||||
| } | } | ||||||
| @ -167,6 +174,7 @@ static int8_t CDC_Init_FS(void) | |||||||
| static int8_t CDC_DeInit_FS(void) | static int8_t CDC_DeInit_FS(void) | ||||||
| { | { | ||||||
|   /* USER CODE BEGIN 4 */ |   /* USER CODE BEGIN 4 */ | ||||||
|  |   _api_hal_vcp_deinit(); | ||||||
|   return (USBD_OK); |   return (USBD_OK); | ||||||
|   /* USER CODE END 4 */ |   /* USER CODE END 4 */ | ||||||
| } | } | ||||||
| @ -181,28 +189,12 @@ static int8_t CDC_DeInit_FS(void) | |||||||
| static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | ||||||
| { | { | ||||||
|   /* USER CODE BEGIN 5 */ |   /* USER CODE BEGIN 5 */ | ||||||
|   switch(cmd) |   if (cmd == CDC_SEND_ENCAPSULATED_COMMAND) { | ||||||
|   { |   } else if (cmd == CDC_GET_ENCAPSULATED_RESPONSE) { | ||||||
|     case CDC_SEND_ENCAPSULATED_COMMAND: |   } else if (cmd == CDC_SET_COMM_FEATURE) { | ||||||
| 
 |   } else if (cmd == CDC_GET_COMM_FEATURE) { | ||||||
|     break; |   } else if (cmd == CDC_CLEAR_COMM_FEATURE) { | ||||||
| 
 |   } else if (cmd == CDC_SET_LINE_CODING) { | ||||||
|     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                                                       */ |       /* Line Coding Structure                                                       */ | ||||||
|       /*-----------------------------------------------------------------------------*/ |       /*-----------------------------------------------------------------------------*/ | ||||||
| @ -220,24 +212,11 @@ static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) | |||||||
|       /*                                        4 - Space                            */ |       /*                                        4 - Space                            */ | ||||||
|       /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ |       /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */ | ||||||
|       /*******************************************************************************/ |       /*******************************************************************************/ | ||||||
|     case CDC_SET_LINE_CODING: |   } else if (cmd == CDC_GET_LINE_CODING) { | ||||||
| 
 |   } else if (cmd == CDC_SET_CONTROL_LINE_STATE) { | ||||||
|     break; |     _api_hal_vcp_control_line(((USBD_SetupReqTypedef*)pbuf)->wValue); | ||||||
| 
 |   } else if (cmd == CDC_SEND_BREAK) { | ||||||
|     case CDC_GET_LINE_CODING: |   } else { | ||||||
| 
 |  | ||||||
|     break; |  | ||||||
| 
 |  | ||||||
|     case CDC_SET_CONTROL_LINE_STATE: |  | ||||||
| 
 |  | ||||||
|     break; |  | ||||||
| 
 |  | ||||||
|     case CDC_SEND_BREAK: |  | ||||||
| 
 |  | ||||||
|     break; |  | ||||||
| 
 |  | ||||||
|   default: |  | ||||||
|     break; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return (USBD_OK); |   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) | static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) | ||||||
| { | { | ||||||
|   /* USER CODE BEGIN 6 */ |   /* USER CODE BEGIN 6 */ | ||||||
|   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]); |   _api_hal_vcp_rx_callback((char*)Buf, *Len); | ||||||
|   USBD_CDC_ReceivePacket(&hUsbDeviceFS); |   USBD_CDC_ReceivePacket(&hUsbDeviceFS); | ||||||
|   return (USBD_OK); |   return (USBD_OK); | ||||||
|   /* USER CODE END 6 */ |   /* USER CODE END 6 */ | ||||||
| @ -287,7 +266,8 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) | |||||||
|   if (hcdc->TxState != 0){ |   if (hcdc->TxState != 0){ | ||||||
|     return USBD_BUSY; |     return USBD_BUSY; | ||||||
|   } |   } | ||||||
|   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); |   memcpy(UserTxBufferFS, Buf, Len); | ||||||
|  |   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, Len); | ||||||
|   result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); |   result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); | ||||||
|   /* USER CODE END 7 */ |   /* USER CODE END 7 */ | ||||||
|   return result; |   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; |   uint8_t result = USBD_OK; | ||||||
|   /* USER CODE BEGIN 13 */ |   /* USER CODE BEGIN 13 */ | ||||||
|   UNUSED(Buf); |   UNUSED(Buf); | ||||||
|   UNUSED(Len); |  | ||||||
|   UNUSED(epnum); |   UNUSED(epnum); | ||||||
|  |   _api_hal_vcp_tx_complete(*Len); | ||||||
|   /* USER CODE END 13 */ |   /* USER CODE END 13 */ | ||||||
|   return result; |   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 | PD0.GPIOParameters=GPIO_Speed,PinState,GPIO_Label | ||||||
| Mcu.Pin10=PA0 | Mcu.Pin10=PA0 | ||||||
| SH.GPXTI10.ConfNb=1 | SH.GPXTI10.ConfNb=1 | ||||||
|  | USB_DEVICE.APP_RX_DATA_SIZE=512 | ||||||
| TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE | TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE | ||||||
| PC3.GPIO_Label=PC3 | PC3.GPIO_Label=PC3 | ||||||
| PA3.PinState=GPIO_PIN_SET | PA3.PinState=GPIO_PIN_SET | ||||||
| @ -453,7 +454,7 @@ OSC_IN.GPIOParameters=GPIO_Label | |||||||
| PB12.Locked=true | PB12.Locked=true | ||||||
| ProjectManager.DeletePrevious=true | ProjectManager.DeletePrevious=true | ||||||
| PB10.Locked=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 | TIM16.Channel=TIM_CHANNEL_1 | ||||||
| RCC.AHB2CLKDivider=RCC_SYSCLK_DIV2 | RCC.AHB2CLKDivider=RCC_SYSCLK_DIV2 | ||||||
| RCC.FamilyName=M | RCC.FamilyName=M | ||||||
| @ -528,6 +529,7 @@ PA13.Locked=true | |||||||
| RF1.Mode=RF1_Activate | RF1.Mode=RF1_Activate | ||||||
| PB7.Mode=Asynchronous | PB7.Mode=Asynchronous | ||||||
| NVIC.EXTI9_5_IRQn=true\:5\:0\:true\:false\:true\:false\:true\:true | 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 | PA14.Signal=SYS_JTCK-SWCLK | ||||||
| PB2.GPIO_Label=PB2 | PB2.GPIO_Label=PB2 | ||||||
| PC6.GPIOParameters=GPIO_Label | PC6.GPIOParameters=GPIO_Label | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| TOOLCHAIN = arm | 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 | BOOT_ADDRESS	= 0x08000000 | ||||||
| FW_ADDRESS		= 0x08008000 | 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: flash | ||||||
| 	$(DEBUG_AGENT) & echo $$! > $(OBJ_DIR)/agent.PID | 	$(DEBUG_AGENT) & echo $$! > $(OBJ_DIR)/agent.PID | ||||||
| 	arm-none-eabi-gdb \
 | 	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 "set confirm off" \
 | ||||||
| 		-ex "source ../debug/FreeRTOS/FreeRTOS.py" \
 | 		-ex "source ../debug/FreeRTOS/FreeRTOS.py" \
 | ||||||
| 		$(OBJ_DIR)/$(PROJECT).elf; \
 | 		$(OBJ_DIR)/$(PROJECT).elf; \
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13