UART echo app (#831)
* HAL: add context to UART IRQ's * Apps: uart echo/log application * Sync api * Another api sync
This commit is contained in:
		
							parent
							
								
									4303945748
								
							
						
					
					
						commit
						013ed64cbb
					
				| @ -20,6 +20,7 @@ extern int32_t desktop_srv(void* p); | ||||
| extern int32_t accessor_app(void* p); | ||||
| extern int32_t archive_app(void* p); | ||||
| extern int32_t bad_usb_app(void* p); | ||||
| extern int32_t uart_echo_app(void* p); | ||||
| extern int32_t blink_test_app(void* p); | ||||
| extern int32_t bt_debug_app(void* p); | ||||
| extern int32_t delay_test_app(void* p); | ||||
| @ -236,6 +237,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | ||||
|     {.app = bad_usb_app, .name = "Bad USB test", .stack_size = 2048, .icon = NULL}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_UART_ECHO | ||||
|     {.app = uart_echo_app, .name = "Uart Echo", .stack_size = 2048, .icon = NULL}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_IRDA_MONITOR | ||||
|     {.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = NULL}, | ||||
| #endif | ||||
|  | ||||
| @ -48,6 +48,7 @@ APP_DISPLAY_TEST = 1 | ||||
| 
 | ||||
| APP_USB_MOUSE = 1 | ||||
| APP_BAD_USB = 1 | ||||
| APP_UART_ECHO = 1 | ||||
| endif | ||||
| 
 | ||||
| 
 | ||||
| @ -132,6 +133,11 @@ CFLAGS		+= -DAPP_USB_TEST | ||||
| SRV_GUI = 1 | ||||
| endif | ||||
| 
 | ||||
| APP_UART_ECHO ?= 0 | ||||
| ifeq ($(APP_UART_ECHO), 1) | ||||
| CFLAGS		+= -DAPP_UART_ECHO | ||||
| SRV_GUI = 1 | ||||
| endif | ||||
| 
 | ||||
| APP_DISPLAY_TEST ?= 0 | ||||
| ifeq ($(APP_DISPLAY_TEST), 1) | ||||
|  | ||||
							
								
								
									
										268
									
								
								applications/debug_tools/uart_echo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								applications/debug_tools/uart_echo.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,268 @@ | ||||
| #include <furi.h> | ||||
| #include <m-string.h> | ||||
| #include <gui/gui.h> | ||||
| #include <notification/notification.h> | ||||
| #include <notification/notification-messages.h> | ||||
| #include <gui/elements.h> | ||||
| #include <stream_buffer.h> | ||||
| #include <furi-hal-uart.h> | ||||
| #include <furi-hal-console.h> | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| 
 | ||||
| #define LINES_ON_SCREEN 6 | ||||
| #define COLUMNS_ON_SCREEN 21 | ||||
| 
 | ||||
| typedef struct UartDumpModel UartDumpModel; | ||||
| 
 | ||||
| typedef struct { | ||||
|     Gui* gui; | ||||
|     NotificationApp* notification; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     View* view; | ||||
|     FuriThread* worker_thread; | ||||
|     StreamBufferHandle_t rx_stream; | ||||
| } UartEchoApp; | ||||
| 
 | ||||
| typedef struct { | ||||
|     string_t text; | ||||
| } ListElement; | ||||
| 
 | ||||
| struct UartDumpModel { | ||||
|     ListElement* list[LINES_ON_SCREEN]; | ||||
|     uint8_t line; | ||||
| 
 | ||||
|     char last_char; | ||||
|     bool escape; | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|     WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
 | ||||
|     WorkerEventStop = (1 << 1), | ||||
|     WorkerEventRx = (1 << 2), | ||||
| } WorkerEventFlags; | ||||
| 
 | ||||
| #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) | ||||
| 
 | ||||
| const NotificationSequence sequence_notification = { | ||||
|     &message_display_on, | ||||
|     &message_green_255, | ||||
|     &message_delay_10, | ||||
|     NULL, | ||||
| }; | ||||
| 
 | ||||
| static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     UartDumpModel* model = _model; | ||||
| 
 | ||||
|     // Prepare canvas
 | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_set_font(canvas, FontKeyboard); | ||||
| 
 | ||||
|     for(size_t i = 0; i < LINES_ON_SCREEN; i++) { | ||||
|         canvas_draw_str( | ||||
|             canvas, | ||||
|             0, | ||||
|             (i + 1) * (canvas_current_font_height(canvas) - 1), | ||||
|             string_get_cstr(model->list[i]->text)); | ||||
| 
 | ||||
|         if(i == model->line) { | ||||
|             uint8_t width = canvas_string_width(canvas, string_get_cstr(model->list[i]->text)); | ||||
| 
 | ||||
|             canvas_draw_box( | ||||
|                 canvas, | ||||
|                 width, | ||||
|                 (i) * (canvas_current_font_height(canvas) - 1) + 2, | ||||
|                 2, | ||||
|                 canvas_current_font_height(canvas) - 2); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool uart_echo_view_input_callback(InputEvent* event, void* context) { | ||||
|     bool consumed = false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| static uint32_t uart_echo_exit(void* context) { | ||||
|     return VIEW_NONE; | ||||
| } | ||||
| 
 | ||||
| static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { | ||||
|     furi_assert(context); | ||||
|     BaseType_t xHigherPriorityTaskWoken = pdFALSE; | ||||
|     UartEchoApp* app = context; | ||||
| 
 | ||||
|     if(ev == UartIrqEventRXNE) { | ||||
|         xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken); | ||||
|         osThreadFlagsSet(furi_thread_get_thread_id(app->worker_thread), WorkerEventRx); | ||||
|         portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void uart_echo_push_to_list(UartDumpModel* model, const char data) { | ||||
|     if(model->escape) { | ||||
|         // escape code end with letter
 | ||||
|         if((data >= 'a' && data <= 'z') || (data >= 'A' && data <= 'Z')) { | ||||
|             model->escape = false; | ||||
|         } | ||||
|     } else if(data == '[' && model->last_char == '\e') { | ||||
|         // "Esc[" is a escape code
 | ||||
|         model->escape = true; | ||||
|     } else if((data >= ' ' && data <= '~') || (data == '\n' || data == '\r')) { | ||||
|         bool new_string_needed = false; | ||||
|         if(string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) { | ||||
|             new_string_needed = true; | ||||
|         } else if((data == '\n' || data == '\r')) { | ||||
|             // pack line breaks
 | ||||
|             if(model->last_char != '\n' && model->last_char != '\r') { | ||||
|                 new_string_needed = true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(new_string_needed) { | ||||
|             if((model->line + 1) < LINES_ON_SCREEN) { | ||||
|                 model->line += 1; | ||||
|             } else { | ||||
|                 ListElement* first = model->list[0]; | ||||
| 
 | ||||
|                 for(size_t i = 1; i < LINES_ON_SCREEN; i++) { | ||||
|                     model->list[i - 1] = model->list[i]; | ||||
|                 } | ||||
| 
 | ||||
|                 string_reset(first->text); | ||||
|                 model->list[model->line] = first; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(data != '\n' && data != '\r') { | ||||
|             string_push_back(model->list[model->line]->text, data); | ||||
|         } | ||||
|     } | ||||
|     model->last_char = data; | ||||
| } | ||||
| 
 | ||||
| static int32_t uart_echo_worker(void* context) { | ||||
|     furi_assert(context); | ||||
|     UartEchoApp* app = context; | ||||
| 
 | ||||
|     while(1) { | ||||
|         uint32_t events = osThreadFlagsWait(WORKER_EVENTS_MASK, osFlagsWaitAny, osWaitForever); | ||||
|         furi_check((events & osFlagsError) == 0); | ||||
| 
 | ||||
|         if(events & WorkerEventStop) break; | ||||
|         if(events & WorkerEventRx) { | ||||
|             size_t length = 0; | ||||
|             do { | ||||
|                 uint8_t data[64]; | ||||
|                 length = xStreamBufferReceive(app->rx_stream, data, 64, 0); | ||||
|                 if(length > 0) { | ||||
|                     furi_hal_uart_tx(FuriHalUartIdUSART1, data, length); | ||||
|                     with_view_model( | ||||
|                         app->view, (UartDumpModel * model) { | ||||
|                             for(size_t i = 0; i < length; i++) { | ||||
|                                 uart_echo_push_to_list(model, data[i]); | ||||
|                             } | ||||
|                             return false; | ||||
|                         }); | ||||
|                 } | ||||
|             } while(length > 0); | ||||
| 
 | ||||
|             notification_message(app->notification, &sequence_notification); | ||||
|             with_view_model( | ||||
|                 app->view, (UartDumpModel * model) { return true; }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static UartEchoApp* uart_echo_app_alloc() { | ||||
|     UartEchoApp* app = furi_alloc(sizeof(UartEchoApp)); | ||||
| 
 | ||||
|     app->rx_stream = xStreamBufferCreate(2048, 1); | ||||
| 
 | ||||
|     // Gui
 | ||||
|     app->gui = furi_record_open("gui"); | ||||
|     app->notification = furi_record_open("notification"); | ||||
| 
 | ||||
|     // View dispatcher
 | ||||
|     app->view_dispatcher = view_dispatcher_alloc(); | ||||
|     view_dispatcher_enable_queue(app->view_dispatcher); | ||||
|     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||
| 
 | ||||
|     // Views
 | ||||
|     app->view = view_alloc(); | ||||
|     view_set_draw_callback(app->view, uart_echo_view_draw_callback); | ||||
|     view_set_input_callback(app->view, uart_echo_view_input_callback); | ||||
|     view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel)); | ||||
|     with_view_model( | ||||
|         app->view, (UartDumpModel * model) { | ||||
|             for(size_t i = 0; i < LINES_ON_SCREEN; i++) { | ||||
|                 model->line = 0; | ||||
|                 model->escape = false; | ||||
|                 model->list[i] = furi_alloc(sizeof(ListElement)); | ||||
|                 string_init(model->list[i]->text); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     view_set_previous_callback(app->view, uart_echo_exit); | ||||
|     view_dispatcher_add_view(app->view_dispatcher, 0, app->view); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, 0); | ||||
| 
 | ||||
|     // Enable uart listener
 | ||||
|     furi_hal_console_disable(); | ||||
|     furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); | ||||
|     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); | ||||
| 
 | ||||
|     app->worker_thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(app->worker_thread, "UsbUartWorker"); | ||||
|     furi_thread_set_stack_size(app->worker_thread, 1024); | ||||
|     furi_thread_set_context(app->worker_thread, app); | ||||
|     furi_thread_set_callback(app->worker_thread, uart_echo_worker); | ||||
|     furi_thread_start(app->worker_thread); | ||||
| 
 | ||||
|     return app; | ||||
| } | ||||
| 
 | ||||
| static void uart_echo_app_free(UartEchoApp* app) { | ||||
|     furi_assert(app); | ||||
| 
 | ||||
|     osThreadFlagsSet(furi_thread_get_thread_id(app->worker_thread), WorkerEventStop); | ||||
|     furi_thread_join(app->worker_thread); | ||||
|     furi_thread_free(app->worker_thread); | ||||
| 
 | ||||
|     furi_hal_console_enable(); | ||||
| 
 | ||||
|     // Free views
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, 0); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         app->view, (UartDumpModel * model) { | ||||
|             for(size_t i = 0; i < LINES_ON_SCREEN; i++) { | ||||
|                 string_clear(model->list[i]->text); | ||||
|                 free(model->list[i]); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|     view_free(app->view); | ||||
|     view_dispatcher_free(app->view_dispatcher); | ||||
| 
 | ||||
|     // Close gui record
 | ||||
|     furi_record_close("gui"); | ||||
|     furi_record_close("notification"); | ||||
|     app->gui = NULL; | ||||
| 
 | ||||
|     vStreamBufferDelete(app->rx_stream); | ||||
| 
 | ||||
|     // Free rest
 | ||||
|     free(app); | ||||
| } | ||||
| 
 | ||||
| int32_t uart_echo_app(void* p) { | ||||
|     UartEchoApp* app = uart_echo_app_alloc(); | ||||
|     view_dispatcher_run(app->view_dispatcher); | ||||
|     uart_echo_app_free(app); | ||||
|     return 0; | ||||
| } | ||||
| @ -57,7 +57,7 @@ static CdcCallbacks cdc_cb = { | ||||
| 
 | ||||
| static int32_t usb_uart_tx_thread(void* context); | ||||
| 
 | ||||
| static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) { | ||||
| static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { | ||||
|     BaseType_t xHigherPriorityTaskWoken = pdFALSE; | ||||
| 
 | ||||
|     if(ev == UartIrqEventRXNE) { | ||||
| @ -94,10 +94,10 @@ static int32_t usb_uart_worker(void* context) { | ||||
|         furi_hal_console_disable(); | ||||
|     } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) { | ||||
|         furi_hal_uart_init(usb_uart->cfg.uart_ch, 115200); | ||||
|         furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb); | ||||
|         furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb, NULL); | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb); | ||||
|     furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb, NULL); | ||||
|     if(usb_uart->cfg.baudrate != 0) | ||||
|         furi_hal_uart_set_br(usb_uart->cfg.uart_ch, usb_uart->cfg.baudrate); | ||||
|     else | ||||
|  | ||||
| @ -24,7 +24,7 @@ void furi_hal_console_init() { | ||||
| } | ||||
| 
 | ||||
| void furi_hal_console_enable() { | ||||
|     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL); | ||||
|     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); | ||||
|     while (!LL_USART_IsActiveFlag_TC(USART1)); | ||||
|     furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); | ||||
|     furi_hal_console_alive = true; | ||||
|  | ||||
| @ -6,7 +6,8 @@ | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| static void (*irq_cb[2])(uint8_t ev, uint8_t data); | ||||
| static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); | ||||
| static void* irq_ctx[2]; | ||||
| 
 | ||||
| static void furi_hal_usart_init(uint32_t baud) { | ||||
|     hal_gpio_init_ex( | ||||
| @ -38,7 +39,8 @@ static void furi_hal_usart_init(uint32_t baud) { | ||||
| 
 | ||||
|     LL_USART_Enable(USART1); | ||||
| 
 | ||||
|     while(!LL_USART_IsActiveFlag_TEACK(USART1)); | ||||
|     while(!LL_USART_IsActiveFlag_TEACK(USART1)) | ||||
|         ; | ||||
| 
 | ||||
|     LL_USART_EnableIT_RXNE_RXFNE(USART1); | ||||
|     LL_USART_EnableIT_IDLE(USART1); | ||||
| @ -74,7 +76,8 @@ static void furi_hal_lpuart_init(uint32_t baud) { | ||||
| 
 | ||||
|     LL_LPUART_Enable(LPUART1); | ||||
| 
 | ||||
|     while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); | ||||
|     while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))) | ||||
|         ; | ||||
| 
 | ||||
|     furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); | ||||
| 
 | ||||
| @ -94,16 +97,19 @@ void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { | ||||
|     if(ch == FuriHalUartIdUSART1) { | ||||
|         if(LL_USART_IsEnabled(USART1)) { | ||||
|             // Wait for transfer complete flag
 | ||||
|             while (!LL_USART_IsActiveFlag_TC(USART1)); | ||||
|             while(!LL_USART_IsActiveFlag_TC(USART1)) | ||||
|                 ; | ||||
|             LL_USART_Disable(USART1); | ||||
|             uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); | ||||
|             LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); | ||||
|             LL_USART_SetBaudRate( | ||||
|                 USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); | ||||
|             LL_USART_Enable(USART1); | ||||
|         } | ||||
|     } else if(ch == FuriHalUartIdLPUART1) { | ||||
|         if(LL_LPUART_IsEnabled(LPUART1)) { | ||||
|             // Wait for transfer complete flag
 | ||||
|             while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); | ||||
|             while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) | ||||
|                 ; | ||||
|             LL_LPUART_Disable(LPUART1); | ||||
|             uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); | ||||
|             if(uartclk / baud > 4095) { | ||||
| @ -119,7 +125,7 @@ void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { | ||||
| } | ||||
| 
 | ||||
| void furi_hal_uart_deinit(FuriHalUartId ch) { | ||||
|     furi_hal_uart_set_irq_cb(ch, NULL); | ||||
|     furi_hal_uart_set_irq_cb(ch, NULL, NULL); | ||||
|     if(ch == FuriHalUartIdUSART1) { | ||||
|         LL_USART_Disable(USART1); | ||||
|         hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
| @ -133,11 +139,11 @@ void furi_hal_uart_deinit(FuriHalUartId ch) { | ||||
| 
 | ||||
| void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { | ||||
|     if(ch == FuriHalUartIdUSART1) { | ||||
|         if (LL_USART_IsEnabled(USART1) == 0) | ||||
|             return; | ||||
|         if(LL_USART_IsEnabled(USART1) == 0) return; | ||||
| 
 | ||||
|         while(buffer_size > 0) { | ||||
|             while (!LL_USART_IsActiveFlag_TXE(USART1)); | ||||
|             while(!LL_USART_IsActiveFlag_TXE(USART1)) | ||||
|                 ; | ||||
| 
 | ||||
|             LL_USART_TransmitData8(USART1, *buffer); | ||||
|             buffer++; | ||||
| @ -145,11 +151,11 @@ void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { | ||||
|         } | ||||
| 
 | ||||
|     } else if(ch == FuriHalUartIdLPUART1) { | ||||
|         if (LL_LPUART_IsEnabled(LPUART1) == 0) | ||||
|             return; | ||||
|         if(LL_LPUART_IsEnabled(LPUART1) == 0) return; | ||||
| 
 | ||||
|         while(buffer_size > 0) { | ||||
|             while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); | ||||
|             while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) | ||||
|                 ; | ||||
| 
 | ||||
|             LL_LPUART_TransmitData8(LPUART1, *buffer); | ||||
| 
 | ||||
| @ -159,14 +165,19 @@ void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)) { | ||||
| void furi_hal_uart_set_irq_cb( | ||||
|     FuriHalUartId ch, | ||||
|     void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), | ||||
|     void* ctx) { | ||||
|     if(cb == NULL) { | ||||
|         if(ch == FuriHalUartIdUSART1) | ||||
|             NVIC_DisableIRQ(USART1_IRQn); | ||||
|         else if(ch == FuriHalUartIdLPUART1) | ||||
|             NVIC_DisableIRQ(LPUART1_IRQn); | ||||
|         irq_cb[ch] = cb; | ||||
|         irq_ctx[ch] = ctx; | ||||
|     } else { | ||||
|         irq_ctx[ch] = ctx; | ||||
|         irq_cb[ch] = cb; | ||||
|         if(ch == FuriHalUartIdUSART1) | ||||
|             NVIC_EnableIRQ(USART1_IRQn); | ||||
| @ -178,9 +189,9 @@ void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint | ||||
| void LPUART1_IRQHandler(void) { | ||||
|     if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { | ||||
|         uint8_t data = LL_LPUART_ReceiveData8(LPUART1); | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data); | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]); | ||||
|     } else if(LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0); | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdLPUART1]); | ||||
|         LL_LPUART_ClearFlag_IDLE(LPUART1); | ||||
|     } else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) { | ||||
|         LL_LPUART_ClearFlag_ORE(LPUART1); | ||||
| @ -191,9 +202,9 @@ void LPUART1_IRQHandler(void) { | ||||
| void USART1_IRQHandler(void) { | ||||
|     if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { | ||||
|         uint8_t data = LL_USART_ReceiveData8(USART1); | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data); | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]); | ||||
|     } else if(LL_USART_IsActiveFlag_IDLE(USART1)) { | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0); | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdUSART1]); | ||||
|         LL_USART_ClearFlag_IDLE(USART1); | ||||
|     } else if(LL_USART_IsActiveFlag_ORE(USART1)) { | ||||
|         LL_USART_ClearFlag_ORE(USART1); | ||||
|  | ||||
| @ -1,3 +1,10 @@ | ||||
| /**
 | ||||
|  * @file furi-hal-uart.h | ||||
|  * @version 1.0 | ||||
|  * @date 2021-11-19 | ||||
|  *  | ||||
|  * UART HAL api interface | ||||
|  */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| @ -7,26 +14,63 @@ | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * UART channels | ||||
|  */ | ||||
| typedef enum { | ||||
|     FuriHalUartIdUSART1, | ||||
|     FuriHalUartIdLPUART1, | ||||
| } FuriHalUartId; | ||||
| 
 | ||||
| /**
 | ||||
|  * UART events | ||||
|  */ | ||||
| typedef enum { | ||||
|     UartIrqEventRXNE, | ||||
|     UartIrqEventIDLE, | ||||
|     //TODO: more events
 | ||||
| } UartIrqEvent; | ||||
| 
 | ||||
| void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud); | ||||
| /**
 | ||||
|  * Init UART | ||||
|  * Configures GPIO to UART function, сonfigures UART hardware, enables UART hardware | ||||
|  * @param channel UART channel | ||||
|  * @param baud baudrate | ||||
|  */ | ||||
| void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud); | ||||
| 
 | ||||
| void furi_hal_uart_deinit(FuriHalUartId ch); | ||||
| /**
 | ||||
|  * Deinit UART | ||||
|  * Configures GPIO to analog, clears callback and callback context, disables UART hardware | ||||
|  * @param channel UART channel | ||||
|  */ | ||||
| void furi_hal_uart_deinit(FuriHalUartId channel); | ||||
| 
 | ||||
| void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud); | ||||
| /**
 | ||||
|  * Changes UART baudrate | ||||
|  * @param channel UART channel | ||||
|  * @param baud baudrate | ||||
|  */ | ||||
| void furi_hal_uart_set_br(FuriHalUartId channel, uint32_t baud); | ||||
| 
 | ||||
| void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size); | ||||
| /**
 | ||||
|  * Transmits data | ||||
|  * @param channel UART channel | ||||
|  * @param buffer data | ||||
|  * @param buffer_size data size (in bytes) | ||||
|  */ | ||||
| void furi_hal_uart_tx(FuriHalUartId channel, uint8_t* buffer, size_t buffer_size); | ||||
| 
 | ||||
| void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)); | ||||
| /**
 | ||||
|  * Sets UART event callback | ||||
|  * @param channel UART channel | ||||
|  * @param callback callback pointer | ||||
|  * @param context callback context | ||||
|  */ | ||||
| void furi_hal_uart_set_irq_cb( | ||||
|     FuriHalUartId channel, | ||||
|     void (*callback)(UartIrqEvent event, uint8_t data, void* context), | ||||
|     void* context); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
| @ -24,32 +24,33 @@ void furi_hal_console_init() { | ||||
| } | ||||
| 
 | ||||
| void furi_hal_console_enable() { | ||||
|     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL); | ||||
|     while (!LL_USART_IsActiveFlag_TC(USART1)); | ||||
|     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); | ||||
|     while(!LL_USART_IsActiveFlag_TC(USART1)) | ||||
|         ; | ||||
|     furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); | ||||
|     furi_hal_console_alive = true; | ||||
| } | ||||
| 
 | ||||
| void furi_hal_console_disable() { | ||||
|     while (!LL_USART_IsActiveFlag_TC(USART1)); | ||||
|     while(!LL_USART_IsActiveFlag_TC(USART1)) | ||||
|         ; | ||||
|     furi_hal_console_alive = false; | ||||
| } | ||||
| 
 | ||||
| void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { | ||||
|     if (!furi_hal_console_alive) | ||||
|         return; | ||||
|     if(!furi_hal_console_alive) return; | ||||
| 
 | ||||
|     UTILS_ENTER_CRITICAL_SECTION(); | ||||
|     // Transmit data
 | ||||
|     furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); | ||||
|     // Wait for TC flag to be raised for last char
 | ||||
|     while (!LL_USART_IsActiveFlag_TC(USART1)); | ||||
|     while(!LL_USART_IsActiveFlag_TC(USART1)) | ||||
|         ; | ||||
|     UTILS_EXIT_CRITICAL_SECTION(); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) { | ||||
|     if (!furi_hal_console_alive) | ||||
|         return; | ||||
|     if(!furi_hal_console_alive) return; | ||||
| 
 | ||||
|     UTILS_ENTER_CRITICAL_SECTION(); | ||||
|     // Transmit data
 | ||||
| @ -57,7 +58,8 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size | ||||
|     // Transmit new line symbols
 | ||||
|     furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2); | ||||
|     // Wait for TC flag to be raised for last char
 | ||||
|     while (!LL_USART_IsActiveFlag_TC(USART1)); | ||||
|     while(!LL_USART_IsActiveFlag_TC(USART1)) | ||||
|         ; | ||||
|     UTILS_EXIT_CRITICAL_SECTION(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,8 @@ | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| static void (*irq_cb[2])(uint8_t ev, uint8_t data); | ||||
| static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); | ||||
| static void* irq_ctx[2]; | ||||
| 
 | ||||
| static void furi_hal_usart_init(uint32_t baud) { | ||||
|     hal_gpio_init_ex( | ||||
| @ -38,7 +39,8 @@ static void furi_hal_usart_init(uint32_t baud) { | ||||
| 
 | ||||
|     LL_USART_Enable(USART1); | ||||
| 
 | ||||
|     while(!LL_USART_IsActiveFlag_TEACK(USART1)); | ||||
|     while(!LL_USART_IsActiveFlag_TEACK(USART1)) | ||||
|         ; | ||||
| 
 | ||||
|     LL_USART_EnableIT_RXNE_RXFNE(USART1); | ||||
|     LL_USART_EnableIT_IDLE(USART1); | ||||
| @ -74,7 +76,8 @@ static void furi_hal_lpuart_init(uint32_t baud) { | ||||
| 
 | ||||
|     LL_LPUART_Enable(LPUART1); | ||||
| 
 | ||||
|     while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); | ||||
|     while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))) | ||||
|         ; | ||||
| 
 | ||||
|     furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); | ||||
| 
 | ||||
| @ -94,16 +97,19 @@ void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { | ||||
|     if(ch == FuriHalUartIdUSART1) { | ||||
|         if(LL_USART_IsEnabled(USART1)) { | ||||
|             // Wait for transfer complete flag
 | ||||
|             while (!LL_USART_IsActiveFlag_TC(USART1)); | ||||
|             while(!LL_USART_IsActiveFlag_TC(USART1)) | ||||
|                 ; | ||||
|             LL_USART_Disable(USART1); | ||||
|             uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); | ||||
|             LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); | ||||
|             LL_USART_SetBaudRate( | ||||
|                 USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); | ||||
|             LL_USART_Enable(USART1); | ||||
|         } | ||||
|     } else if(ch == FuriHalUartIdLPUART1) { | ||||
|         if(LL_LPUART_IsEnabled(LPUART1)) { | ||||
|             // Wait for transfer complete flag
 | ||||
|             while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); | ||||
|             while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) | ||||
|                 ; | ||||
|             LL_LPUART_Disable(LPUART1); | ||||
|             uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); | ||||
|             if(uartclk / baud > 4095) { | ||||
| @ -119,7 +125,7 @@ void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { | ||||
| } | ||||
| 
 | ||||
| void furi_hal_uart_deinit(FuriHalUartId ch) { | ||||
|     furi_hal_uart_set_irq_cb(ch, NULL); | ||||
|     furi_hal_uart_set_irq_cb(ch, NULL, NULL); | ||||
|     if(ch == FuriHalUartIdUSART1) { | ||||
|         LL_USART_Disable(USART1); | ||||
|         hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
| @ -133,11 +139,11 @@ void furi_hal_uart_deinit(FuriHalUartId ch) { | ||||
| 
 | ||||
| void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { | ||||
|     if(ch == FuriHalUartIdUSART1) { | ||||
|         if (LL_USART_IsEnabled(USART1) == 0) | ||||
|             return; | ||||
|         if(LL_USART_IsEnabled(USART1) == 0) return; | ||||
| 
 | ||||
|         while(buffer_size > 0) { | ||||
|             while (!LL_USART_IsActiveFlag_TXE(USART1)); | ||||
|             while(!LL_USART_IsActiveFlag_TXE(USART1)) | ||||
|                 ; | ||||
| 
 | ||||
|             LL_USART_TransmitData8(USART1, *buffer); | ||||
|             buffer++; | ||||
| @ -145,11 +151,11 @@ void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { | ||||
|         } | ||||
| 
 | ||||
|     } else if(ch == FuriHalUartIdLPUART1) { | ||||
|         if (LL_LPUART_IsEnabled(LPUART1) == 0) | ||||
|             return; | ||||
|         if(LL_LPUART_IsEnabled(LPUART1) == 0) return; | ||||
| 
 | ||||
|         while(buffer_size > 0) { | ||||
|             while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); | ||||
|             while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) | ||||
|                 ; | ||||
| 
 | ||||
|             LL_LPUART_TransmitData8(LPUART1, *buffer); | ||||
| 
 | ||||
| @ -159,14 +165,19 @@ void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)) { | ||||
| void furi_hal_uart_set_irq_cb( | ||||
|     FuriHalUartId ch, | ||||
|     void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), | ||||
|     void* ctx) { | ||||
|     if(cb == NULL) { | ||||
|         if(ch == FuriHalUartIdUSART1) | ||||
|             NVIC_DisableIRQ(USART1_IRQn); | ||||
|         else if(ch == FuriHalUartIdLPUART1) | ||||
|             NVIC_DisableIRQ(LPUART1_IRQn); | ||||
|         irq_cb[ch] = cb; | ||||
|         irq_ctx[ch] = ctx; | ||||
|     } else { | ||||
|         irq_ctx[ch] = ctx; | ||||
|         irq_cb[ch] = cb; | ||||
|         if(ch == FuriHalUartIdUSART1) | ||||
|             NVIC_EnableIRQ(USART1_IRQn); | ||||
| @ -178,9 +189,9 @@ void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint | ||||
| void LPUART1_IRQHandler(void) { | ||||
|     if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { | ||||
|         uint8_t data = LL_LPUART_ReceiveData8(LPUART1); | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data); | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]); | ||||
|     } else if(LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0); | ||||
|         irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdLPUART1]); | ||||
|         LL_LPUART_ClearFlag_IDLE(LPUART1); | ||||
|     } else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) { | ||||
|         LL_LPUART_ClearFlag_ORE(LPUART1); | ||||
| @ -191,9 +202,9 @@ void LPUART1_IRQHandler(void) { | ||||
| void USART1_IRQHandler(void) { | ||||
|     if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { | ||||
|         uint8_t data = LL_USART_ReceiveData8(USART1); | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data); | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]); | ||||
|     } else if(LL_USART_IsActiveFlag_IDLE(USART1)) { | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0); | ||||
|         irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdUSART1]); | ||||
|         LL_USART_ClearFlag_IDLE(USART1); | ||||
|     } else if(LL_USART_IsActiveFlag_ORE(USART1)) { | ||||
|         LL_USART_ClearFlag_ORE(USART1); | ||||
|  | ||||
| @ -1,3 +1,10 @@ | ||||
| /**
 | ||||
|  * @file furi-hal-uart.h | ||||
|  * @version 1.0 | ||||
|  * @date 2021-11-19 | ||||
|  *  | ||||
|  * UART HAL api interface | ||||
|  */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| @ -7,26 +14,63 @@ | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * UART channels | ||||
|  */ | ||||
| typedef enum { | ||||
|     FuriHalUartIdUSART1, | ||||
|     FuriHalUartIdLPUART1, | ||||
| } FuriHalUartId; | ||||
| 
 | ||||
| /**
 | ||||
|  * UART events | ||||
|  */ | ||||
| typedef enum { | ||||
|     UartIrqEventRXNE, | ||||
|     UartIrqEventIDLE, | ||||
|     //TODO: more events
 | ||||
| } UartIrqEvent; | ||||
| 
 | ||||
| void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud); | ||||
| /**
 | ||||
|  * Init UART | ||||
|  * Configures GPIO to UART function, сonfigures UART hardware, enables UART hardware | ||||
|  * @param channel UART channel | ||||
|  * @param baud baudrate | ||||
|  */ | ||||
| void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud); | ||||
| 
 | ||||
| void furi_hal_uart_deinit(FuriHalUartId ch); | ||||
| /**
 | ||||
|  * Deinit UART | ||||
|  * Configures GPIO to analog, clears callback and callback context, disables UART hardware | ||||
|  * @param channel UART channel | ||||
|  */ | ||||
| void furi_hal_uart_deinit(FuriHalUartId channel); | ||||
| 
 | ||||
| void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud); | ||||
| /**
 | ||||
|  * Changes UART baudrate | ||||
|  * @param channel UART channel | ||||
|  * @param baud baudrate | ||||
|  */ | ||||
| void furi_hal_uart_set_br(FuriHalUartId channel, uint32_t baud); | ||||
| 
 | ||||
| void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size); | ||||
| /**
 | ||||
|  * Transmits data | ||||
|  * @param channel UART channel | ||||
|  * @param buffer data | ||||
|  * @param buffer_size data size (in bytes) | ||||
|  */ | ||||
| void furi_hal_uart_tx(FuriHalUartId channel, uint8_t* buffer, size_t buffer_size); | ||||
| 
 | ||||
| void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)); | ||||
| /**
 | ||||
|  * Sets UART event callback | ||||
|  * @param channel UART channel | ||||
|  * @param callback callback pointer | ||||
|  * @param context callback context | ||||
|  */ | ||||
| void furi_hal_uart_set_irq_cb( | ||||
|     FuriHalUartId channel, | ||||
|     void (*callback)(UartIrqEvent event, uint8_t data, void* context), | ||||
|     void* context); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG