FuriHal: UART refactoring (#3211)
* FuriHal: UART refactoring * ApiSymbols: add furi_record_destroy * FuriHal: cleanup serial API, add logging configuration in RTC * FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate. * FuriHal: RTC logging method documentation * Synchronize API Symbols * Furi: mark HEAP_PRINT_DEBUG as broken * FuriHal: furi_hal_serial, add custom IRQ func * Fix PR review issues * FuriHal: UART add reception DMA (#3220) * FuriHal: add DMA serial rx mode * usb_uart_bridge: switch to working with DMA * Sync api symbol versions * FuriHal: update serial docs and api * FuriHal: Selial added similar API for simple reception mode as with DMA * FuriHal: Update API target H18 * API: ver API H7 * FuriHal: Serial error processing * FuriHal: fix furi_hal_serial set baudrate * Sync api symbols * FuriHal: cleanup serial isr and various flag handling procedures * FuriHal: cleanup and simplify serial API * Debug: update UART Echo serial related flags * FuriHal: update serial API symbols naming * FuriHalSerial: various improvements and PR review fixes * FuriHal: proper ISR function signatures --------- Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com> Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: SkorP <skorpionm@yandex.ru> Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									d73d007797
								
							
						
					
					
						commit
						fc043da9c6
					
				@ -1,13 +1,14 @@
 | 
				
			|||||||
#include <furi.h>
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					#include <furi_hal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <gui/gui.h>
 | 
					#include <gui/gui.h>
 | 
				
			||||||
#include <notification/notification.h>
 | 
					 | 
				
			||||||
#include <notification/notification_messages.h>
 | 
					 | 
				
			||||||
#include <gui/elements.h>
 | 
					#include <gui/elements.h>
 | 
				
			||||||
#include <furi_hal_uart.h>
 | 
					 | 
				
			||||||
#include <furi_hal_console.h>
 | 
					 | 
				
			||||||
#include <gui/view_dispatcher.h>
 | 
					#include <gui/view_dispatcher.h>
 | 
				
			||||||
#include <gui/modules/dialog_ex.h>
 | 
					#include <gui/modules/dialog_ex.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <notification/notification.h>
 | 
				
			||||||
 | 
					#include <notification/notification_messages.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define LINES_ON_SCREEN 6
 | 
					#define LINES_ON_SCREEN 6
 | 
				
			||||||
#define COLUMNS_ON_SCREEN 21
 | 
					#define COLUMNS_ON_SCREEN 21
 | 
				
			||||||
#define TAG "UartEcho"
 | 
					#define TAG "UartEcho"
 | 
				
			||||||
@ -22,6 +23,7 @@ typedef struct {
 | 
				
			|||||||
    View* view;
 | 
					    View* view;
 | 
				
			||||||
    FuriThread* worker_thread;
 | 
					    FuriThread* worker_thread;
 | 
				
			||||||
    FuriStreamBuffer* rx_stream;
 | 
					    FuriStreamBuffer* rx_stream;
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* serial_handle;
 | 
				
			||||||
} UartEchoApp;
 | 
					} UartEchoApp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
@ -39,10 +41,16 @@ struct UartDumpModel {
 | 
				
			|||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
 | 
					    WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
 | 
				
			||||||
    WorkerEventStop = (1 << 1),
 | 
					    WorkerEventStop = (1 << 1),
 | 
				
			||||||
    WorkerEventRx = (1 << 2),
 | 
					    WorkerEventRxData = (1 << 2),
 | 
				
			||||||
 | 
					    WorkerEventRxIdle = (1 << 3),
 | 
				
			||||||
 | 
					    WorkerEventRxOverrunError = (1 << 4),
 | 
				
			||||||
 | 
					    WorkerEventRxFramingError = (1 << 5),
 | 
				
			||||||
 | 
					    WorkerEventRxNoiseError = (1 << 6),
 | 
				
			||||||
} WorkerEventFlags;
 | 
					} WorkerEventFlags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
 | 
					#define WORKER_EVENTS_MASK                                                                 \
 | 
				
			||||||
 | 
					    (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \
 | 
				
			||||||
 | 
					     WorkerEventRxFramingError | WorkerEventRxNoiseError)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const NotificationSequence sequence_notification = {
 | 
					const NotificationSequence sequence_notification = {
 | 
				
			||||||
    &message_display_backlight_on,
 | 
					    &message_display_backlight_on,
 | 
				
			||||||
@ -91,14 +99,39 @@ static uint32_t uart_echo_exit(void* context) {
 | 
				
			|||||||
    return VIEW_NONE;
 | 
					    return VIEW_NONE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
 | 
					static void
 | 
				
			||||||
 | 
					    uart_echo_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) {
 | 
				
			||||||
    furi_assert(context);
 | 
					    furi_assert(context);
 | 
				
			||||||
 | 
					    UNUSED(handle);
 | 
				
			||||||
    UartEchoApp* app = context;
 | 
					    UartEchoApp* app = context;
 | 
				
			||||||
 | 
					    volatile FuriHalSerialRxEvent event_copy = event;
 | 
				
			||||||
 | 
					    UNUSED(event_copy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(ev == UartIrqEventRXNE) {
 | 
					    WorkerEventFlags flag = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(event & FuriHalSerialRxEventData) {
 | 
				
			||||||
 | 
					        uint8_t data = furi_hal_serial_async_rx(handle);
 | 
				
			||||||
        furi_stream_buffer_send(app->rx_stream, &data, 1, 0);
 | 
					        furi_stream_buffer_send(app->rx_stream, &data, 1, 0);
 | 
				
			||||||
        furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx);
 | 
					        flag |= WorkerEventRxData;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(event & FuriHalSerialRxEventIdle) {
 | 
				
			||||||
 | 
					        //idle line detected, packet transmission may have ended
 | 
				
			||||||
 | 
					        flag |= WorkerEventRxIdle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //error detected
 | 
				
			||||||
 | 
					    if(event & FuriHalSerialRxEventFrameError) {
 | 
				
			||||||
 | 
					        flag |= WorkerEventRxFramingError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(event & FuriHalSerialRxEventNoiseError) {
 | 
				
			||||||
 | 
					        flag |= WorkerEventRxNoiseError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(event & FuriHalSerialRxEventOverrunError) {
 | 
				
			||||||
 | 
					        flag |= WorkerEventRxOverrunError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_thread_flags_set(furi_thread_get_id(app->worker_thread), flag);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void uart_echo_push_to_list(UartDumpModel* model, const char data) {
 | 
					static void uart_echo_push_to_list(UartDumpModel* model, const char data) {
 | 
				
			||||||
@ -153,13 +186,13 @@ static int32_t uart_echo_worker(void* context) {
 | 
				
			|||||||
        furi_check((events & FuriFlagError) == 0);
 | 
					        furi_check((events & FuriFlagError) == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(events & WorkerEventStop) break;
 | 
					        if(events & WorkerEventStop) break;
 | 
				
			||||||
        if(events & WorkerEventRx) {
 | 
					        if(events & WorkerEventRxData) {
 | 
				
			||||||
            size_t length = 0;
 | 
					            size_t length = 0;
 | 
				
			||||||
            do {
 | 
					            do {
 | 
				
			||||||
                uint8_t data[64];
 | 
					                uint8_t data[64];
 | 
				
			||||||
                length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0);
 | 
					                length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0);
 | 
				
			||||||
                if(length > 0) {
 | 
					                if(length > 0) {
 | 
				
			||||||
                    furi_hal_uart_tx(FuriHalUartIdUSART1, data, length);
 | 
					                    furi_hal_serial_tx(app->serial_handle, data, length);
 | 
				
			||||||
                    with_view_model(
 | 
					                    with_view_model(
 | 
				
			||||||
                        app->view,
 | 
					                        app->view,
 | 
				
			||||||
                        UartDumpModel * model,
 | 
					                        UartDumpModel * model,
 | 
				
			||||||
@ -176,6 +209,23 @@ static int32_t uart_echo_worker(void* context) {
 | 
				
			|||||||
            with_view_model(
 | 
					            with_view_model(
 | 
				
			||||||
                app->view, UartDumpModel * model, { UNUSED(model); }, true);
 | 
					                app->view, UartDumpModel * model, { UNUSED(model); }, true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(events & WorkerEventRxIdle) {
 | 
				
			||||||
 | 
					            furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(events &
 | 
				
			||||||
 | 
					           (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) {
 | 
				
			||||||
 | 
					            if(events & WorkerEventRxOverrunError) {
 | 
				
			||||||
 | 
					                furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect ORE\r\n", 14);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(events & WorkerEventRxFramingError) {
 | 
				
			||||||
 | 
					                furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect FE\r\n", 13);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(events & WorkerEventRxNoiseError) {
 | 
				
			||||||
 | 
					                furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
@ -221,9 +271,11 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
 | 
				
			|||||||
    furi_thread_start(app->worker_thread);
 | 
					    furi_thread_start(app->worker_thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Enable uart listener
 | 
					    // Enable uart listener
 | 
				
			||||||
    furi_hal_console_disable();
 | 
					    app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart);
 | 
				
			||||||
    furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate);
 | 
					    furi_check(app->serial_handle);
 | 
				
			||||||
    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
 | 
					    furi_hal_serial_init(app->serial_handle, baudrate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_async_rx_start(app->serial_handle, uart_echo_on_irq_cb, app, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return app;
 | 
					    return app;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -231,12 +283,13 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
 | 
				
			|||||||
static void uart_echo_app_free(UartEchoApp* app) {
 | 
					static void uart_echo_app_free(UartEchoApp* app) {
 | 
				
			||||||
    furi_assert(app);
 | 
					    furi_assert(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);
 | 
					    furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);
 | 
				
			||||||
    furi_thread_join(app->worker_thread);
 | 
					    furi_thread_join(app->worker_thread);
 | 
				
			||||||
    furi_thread_free(app->worker_thread);
 | 
					    furi_thread_free(app->worker_thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_deinit(app->serial_handle);
 | 
				
			||||||
 | 
					    furi_hal_serial_control_release(app->serial_handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Free views
 | 
					    // Free views
 | 
				
			||||||
    view_dispatcher_remove_view(app->view_dispatcher, 0);
 | 
					    view_dispatcher_remove_view(app->view_dispatcher, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ App(
 | 
				
			|||||||
    name="GPIO",
 | 
					    name="GPIO",
 | 
				
			||||||
    apptype=FlipperAppType.MENUEXTERNAL,
 | 
					    apptype=FlipperAppType.MENUEXTERNAL,
 | 
				
			||||||
    entry_point="gpio_app",
 | 
					    entry_point="gpio_app",
 | 
				
			||||||
    stack_size=1 * 1024,
 | 
					    stack_size=2 * 1024,
 | 
				
			||||||
    icon="A_GPIO_14",
 | 
					    icon="A_GPIO_14",
 | 
				
			||||||
    order=50,
 | 
					    order=50,
 | 
				
			||||||
    fap_libs=["assets"],
 | 
					    fap_libs=["assets"],
 | 
				
			||||||
 | 
				
			|||||||
@ -46,7 +46,7 @@ void line_ensure_flow_invariant(GpioApp* app) {
 | 
				
			|||||||
    // selected. This function enforces that invariant by resetting flow_pins
 | 
					    // selected. This function enforces that invariant by resetting flow_pins
 | 
				
			||||||
    // to None if it is configured to 16,15 when LPUART is selected.
 | 
					    // to None if it is configured to 16,15 when LPUART is selected.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
 | 
					    uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalSerialIdLpuart ? 3 : 4;
 | 
				
			||||||
    VariableItem* item = app->var_item_flow;
 | 
					    VariableItem* item = app->var_item_flow;
 | 
				
			||||||
    variable_item_set_values_count(item, available_flow_pins);
 | 
					    variable_item_set_values_count(item, available_flow_pins);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -77,9 +77,9 @@ static void line_port_cb(VariableItem* item) {
 | 
				
			|||||||
    variable_item_set_current_value_text(item, uart_ch[index]);
 | 
					    variable_item_set_current_value_text(item, uart_ch[index]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(index == 0)
 | 
					    if(index == 0)
 | 
				
			||||||
        app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
 | 
					        app->usb_uart_cfg->uart_ch = FuriHalSerialIdUsart;
 | 
				
			||||||
    else if(index == 1)
 | 
					    else if(index == 1)
 | 
				
			||||||
        app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
 | 
					        app->usb_uart_cfg->uart_ch = FuriHalSerialIdLpuart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    line_ensure_flow_invariant(app);
 | 
					    line_ensure_flow_invariant(app);
 | 
				
			||||||
    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
 | 
					    view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
 | 
				
			||||||
 | 
				
			|||||||
@ -29,17 +29,18 @@ typedef enum {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    WorkerEvtTxStop = (1 << 2),
 | 
					    WorkerEvtTxStop = (1 << 2),
 | 
				
			||||||
    WorkerEvtCdcRx = (1 << 3),
 | 
					    WorkerEvtCdcRx = (1 << 3),
 | 
				
			||||||
 | 
					    WorkerEvtCdcTxComplete = (1 << 4),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WorkerEvtCfgChange = (1 << 4),
 | 
					    WorkerEvtCfgChange = (1 << 5),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WorkerEvtLineCfgSet = (1 << 5),
 | 
					    WorkerEvtLineCfgSet = (1 << 6),
 | 
				
			||||||
    WorkerEvtCtrlLineSet = (1 << 6),
 | 
					    WorkerEvtCtrlLineSet = (1 << 7),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} WorkerEvtFlags;
 | 
					} WorkerEvtFlags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define WORKER_ALL_RX_EVENTS                                                      \
 | 
					#define WORKER_ALL_RX_EVENTS                                                      \
 | 
				
			||||||
    (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \
 | 
					    (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \
 | 
				
			||||||
     WorkerEvtCtrlLineSet)
 | 
					     WorkerEvtCtrlLineSet | WorkerEvtCdcTxComplete)
 | 
				
			||||||
#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx)
 | 
					#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct UsbUartBridge {
 | 
					struct UsbUartBridge {
 | 
				
			||||||
@ -50,6 +51,7 @@ struct UsbUartBridge {
 | 
				
			|||||||
    FuriThread* tx_thread;
 | 
					    FuriThread* tx_thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    FuriStreamBuffer* rx_stream;
 | 
					    FuriStreamBuffer* rx_stream;
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* serial_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    FuriMutex* usb_mutex;
 | 
					    FuriMutex* usb_mutex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -80,11 +82,23 @@ static const CdcCallbacks cdc_cb = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static int32_t usb_uart_tx_thread(void* context);
 | 
					static int32_t usb_uart_tx_thread(void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
 | 
					static void usb_uart_on_irq_rx_dma_cb(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialRxEvent ev,
 | 
				
			||||||
 | 
					    size_t size,
 | 
				
			||||||
 | 
					    void* context) {
 | 
				
			||||||
    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
 | 
					    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(ev == UartIrqEventRXNE) {
 | 
					    if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) {
 | 
				
			||||||
        furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0);
 | 
					        uint8_t data[FURI_HAL_SERIAL_DMA_BUFFER_SIZE] = {0};
 | 
				
			||||||
 | 
					        while(size) {
 | 
				
			||||||
 | 
					            size_t ret = furi_hal_serial_dma_rx(
 | 
				
			||||||
 | 
					                handle,
 | 
				
			||||||
 | 
					                data,
 | 
				
			||||||
 | 
					                (size > FURI_HAL_SERIAL_DMA_BUFFER_SIZE) ? FURI_HAL_SERIAL_DMA_BUFFER_SIZE : size);
 | 
				
			||||||
 | 
					            furi_stream_buffer_send(usb_uart->rx_stream, data, ret, 0);
 | 
				
			||||||
 | 
					            size -= ret;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
        furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone);
 | 
					        furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -116,32 +130,33 @@ static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) {
 | 
					static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) {
 | 
				
			||||||
    if(uart_ch == FuriHalUartIdUSART1) {
 | 
					    furi_assert(!usb_uart->serial_handle);
 | 
				
			||||||
        furi_hal_console_disable();
 | 
					
 | 
				
			||||||
    } else if(uart_ch == FuriHalUartIdLPUART1) {
 | 
					    usb_uart->serial_handle = furi_hal_serial_control_acquire(uart_ch);
 | 
				
			||||||
        furi_hal_uart_init(uart_ch, 115200);
 | 
					    furi_assert(usb_uart->serial_handle);
 | 
				
			||||||
    }
 | 
					
 | 
				
			||||||
    furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart);
 | 
					    furi_hal_serial_init(usb_uart->serial_handle, 115200);
 | 
				
			||||||
 | 
					    furi_hal_serial_dma_rx_start(
 | 
				
			||||||
 | 
					        usb_uart->serial_handle, usb_uart_on_irq_rx_dma_cb, usb_uart, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void usb_uart_serial_deinit(UsbUartBridge* usb_uart, uint8_t uart_ch) {
 | 
					static void usb_uart_serial_deinit(UsbUartBridge* usb_uart) {
 | 
				
			||||||
    UNUSED(usb_uart);
 | 
					    furi_assert(usb_uart->serial_handle);
 | 
				
			||||||
    furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL);
 | 
					
 | 
				
			||||||
    if(uart_ch == FuriHalUartIdUSART1)
 | 
					    furi_hal_serial_deinit(usb_uart->serial_handle);
 | 
				
			||||||
        furi_hal_console_enable();
 | 
					    furi_hal_serial_control_release(usb_uart->serial_handle);
 | 
				
			||||||
    else if(uart_ch == FuriHalUartIdLPUART1)
 | 
					    usb_uart->serial_handle = NULL;
 | 
				
			||||||
        furi_hal_uart_deinit(uart_ch);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) {
 | 
					static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) {
 | 
				
			||||||
    if(baudrate != 0) {
 | 
					    if(baudrate != 0) {
 | 
				
			||||||
        furi_hal_uart_set_br(usb_uart->cfg.uart_ch, baudrate);
 | 
					        furi_hal_serial_set_br(usb_uart->serial_handle, baudrate);
 | 
				
			||||||
        usb_uart->st.baudrate_cur = baudrate;
 | 
					        usb_uart->st.baudrate_cur = baudrate;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        struct usb_cdc_line_coding* line_cfg =
 | 
					        struct usb_cdc_line_coding* line_cfg =
 | 
				
			||||||
            furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch);
 | 
					            furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch);
 | 
				
			||||||
        if(line_cfg->dwDTERate > 0) {
 | 
					        if(line_cfg->dwDTERate > 0) {
 | 
				
			||||||
            furi_hal_uart_set_br(usb_uart->cfg.uart_ch, line_cfg->dwDTERate);
 | 
					            furi_hal_serial_set_br(usb_uart->serial_handle, line_cfg->dwDTERate);
 | 
				
			||||||
            usb_uart->st.baudrate_cur = line_cfg->dwDTERate;
 | 
					            usb_uart->st.baudrate_cur = line_cfg->dwDTERate;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -191,7 +206,7 @@ static int32_t usb_uart_worker(void* context) {
 | 
				
			|||||||
            furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
 | 
					            furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
 | 
				
			||||||
        furi_check(!(events & FuriFlagError));
 | 
					        furi_check(!(events & FuriFlagError));
 | 
				
			||||||
        if(events & WorkerEvtStop) break;
 | 
					        if(events & WorkerEvtStop) break;
 | 
				
			||||||
        if(events & WorkerEvtRxDone) {
 | 
					        if(events & (WorkerEvtRxDone | WorkerEvtCdcTxComplete)) {
 | 
				
			||||||
            size_t len = furi_stream_buffer_receive(
 | 
					            size_t len = furi_stream_buffer_receive(
 | 
				
			||||||
                usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0);
 | 
					                usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0);
 | 
				
			||||||
            if(len > 0) {
 | 
					            if(len > 0) {
 | 
				
			||||||
@ -223,7 +238,7 @@ static int32_t usb_uart_worker(void* context) {
 | 
				
			|||||||
                furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);
 | 
					                furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);
 | 
				
			||||||
                furi_thread_join(usb_uart->tx_thread);
 | 
					                furi_thread_join(usb_uart->tx_thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
 | 
					                usb_uart_serial_deinit(usb_uart);
 | 
				
			||||||
                usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch);
 | 
					                usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch;
 | 
					                usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch;
 | 
				
			||||||
@ -274,7 +289,7 @@ static int32_t usb_uart_worker(void* context) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
 | 
					    usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
 | 
				
			||||||
    usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
 | 
					    usb_uart_serial_deinit(usb_uart);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
					    furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -320,18 +335,10 @@ static int32_t usb_uart_tx_thread(void* context) {
 | 
				
			|||||||
                if(usb_uart->cfg.software_de_re != 0)
 | 
					                if(usb_uart->cfg.software_de_re != 0)
 | 
				
			||||||
                    furi_hal_gpio_write(USB_USART_DE_RE_PIN, false);
 | 
					                    furi_hal_gpio_write(USB_USART_DE_RE_PIN, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len);
 | 
					                furi_hal_serial_tx(usb_uart->serial_handle, data, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if(usb_uart->cfg.software_de_re != 0) {
 | 
					                if(usb_uart->cfg.software_de_re != 0) {
 | 
				
			||||||
                    //TODO: FL-3276 port to new USART API
 | 
					                    furi_hal_serial_tx_wait_complete(usb_uart->serial_handle);
 | 
				
			||||||
                    if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) {
 | 
					 | 
				
			||||||
                        while(!LL_USART_IsActiveFlag_TC(USART1))
 | 
					 | 
				
			||||||
                            ;
 | 
					 | 
				
			||||||
                    } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
                        while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
 | 
					 | 
				
			||||||
                            ;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);
 | 
					                    furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -345,6 +352,7 @@ static int32_t usb_uart_tx_thread(void* context) {
 | 
				
			|||||||
static void vcp_on_cdc_tx_complete(void* context) {
 | 
					static void vcp_on_cdc_tx_complete(void* context) {
 | 
				
			||||||
    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
 | 
					    UsbUartBridge* usb_uart = (UsbUartBridge*)context;
 | 
				
			||||||
    furi_semaphore_release(usb_uart->tx_sem);
 | 
					    furi_semaphore_release(usb_uart->tx_sem);
 | 
				
			||||||
 | 
					    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCdcTxComplete);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void vcp_on_cdc_rx(void* context) {
 | 
					static void vcp_on_cdc_rx(void* context) {
 | 
				
			||||||
 | 
				
			|||||||
@ -8,8 +8,6 @@
 | 
				
			|||||||
#include <furi_hal_usb_hid_u2f.h>
 | 
					#include <furi_hal_usb_hid_u2f.h>
 | 
				
			||||||
#include <storage/storage.h>
 | 
					#include <storage/storage.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <furi_hal_console.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define TAG "U2fHid"
 | 
					#define TAG "U2fHid"
 | 
				
			||||||
#define WORKER_TAG TAG "Worker"
 | 
					#define WORKER_TAG TAG "Worker"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -211,7 +211,12 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) {
 | 
				
			|||||||
    furi_log_level_to_string(furi_log_get_level(), ¤t_level);
 | 
					    furi_log_level_to_string(furi_log_get_level(), ¤t_level);
 | 
				
			||||||
    printf("Current log level: %s\r\n", current_level);
 | 
					    printf("Current log level: %s\r\n", current_level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring);
 | 
					    FuriLogHandler log_handler = {
 | 
				
			||||||
 | 
					        .callback = cli_command_log_tx_callback,
 | 
				
			||||||
 | 
					        .context = ring,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_log_add_handler(log_handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    printf("Use <log ?> to list available log levels\r\n");
 | 
					    printf("Use <log ?> to list available log levels\r\n");
 | 
				
			||||||
    printf("Press CTRL+C to stop...\r\n");
 | 
					    printf("Press CTRL+C to stop...\r\n");
 | 
				
			||||||
@ -220,7 +225,7 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) {
 | 
				
			|||||||
        cli_write(cli, buffer, ret);
 | 
					        cli_write(cli, buffer, ret);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_console_set_tx_callback(NULL, NULL);
 | 
					    furi_log_remove_handler(log_handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(restore_log_level) {
 | 
					    if(restore_log_level) {
 | 
				
			||||||
        // There will be strange behaviour if log level is set from settings while log command is running
 | 
					        // There will be strange behaviour if log level is set from settings while log command is running
 | 
				
			||||||
 | 
				
			|||||||
@ -24,12 +24,56 @@ const uint32_t log_level_value[] = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void log_level_changed(VariableItem* item) {
 | 
					static void log_level_changed(VariableItem* item) {
 | 
				
			||||||
    // SystemSettings* app = variable_item_get_context(item);
 | 
					 | 
				
			||||||
    uint8_t index = variable_item_get_current_value_index(item);
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
    variable_item_set_current_value_text(item, log_level_text[index]);
 | 
					    variable_item_set_current_value_text(item, log_level_text[index]);
 | 
				
			||||||
    furi_hal_rtc_set_log_level(log_level_value[index]);
 | 
					    furi_hal_rtc_set_log_level(log_level_value[index]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* const log_device_text[] = {
 | 
				
			||||||
 | 
					    "USART",
 | 
				
			||||||
 | 
					    "LPUART",
 | 
				
			||||||
 | 
					    "None",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint32_t log_device_value[] = {
 | 
				
			||||||
 | 
					    FuriHalRtcLogDeviceUsart,
 | 
				
			||||||
 | 
					    FuriHalRtcLogDeviceLpuart,
 | 
				
			||||||
 | 
					    FuriHalRtcLogDeviceNone};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void log_device_changed(VariableItem* item) {
 | 
				
			||||||
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
 | 
					    variable_item_set_current_value_text(item, log_device_text[index]);
 | 
				
			||||||
 | 
					    furi_hal_rtc_set_log_device(log_device_value[index]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* const log_baud_rate_text[] = {
 | 
				
			||||||
 | 
					    "9600",
 | 
				
			||||||
 | 
					    "38400",
 | 
				
			||||||
 | 
					    "57600",
 | 
				
			||||||
 | 
					    "115200",
 | 
				
			||||||
 | 
					    "230400",
 | 
				
			||||||
 | 
					    "460800",
 | 
				
			||||||
 | 
					    "921600",
 | 
				
			||||||
 | 
					    "1843200",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint32_t log_baud_rate_value[] = {
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate9600,
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate38400,
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate57600,
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate115200,
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate230400,
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate460800,
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate921600,
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate1843200,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void log_baud_rate_changed(VariableItem* item) {
 | 
				
			||||||
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
 | 
					    variable_item_set_current_value_text(item, log_baud_rate_text[index]);
 | 
				
			||||||
 | 
					    furi_hal_rtc_set_log_baud_rate(log_baud_rate_value[index]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char* const debug_text[] = {
 | 
					const char* const debug_text[] = {
 | 
				
			||||||
    "OFF",
 | 
					    "OFF",
 | 
				
			||||||
    "ON",
 | 
					    "ON",
 | 
				
			||||||
@ -64,7 +108,6 @@ const uint32_t heap_trace_mode_value[] = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void heap_trace_mode_changed(VariableItem* item) {
 | 
					static void heap_trace_mode_changed(VariableItem* item) {
 | 
				
			||||||
    // SystemSettings* app = variable_item_get_context(item);
 | 
					 | 
				
			||||||
    uint8_t index = variable_item_get_current_value_index(item);
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
    variable_item_set_current_value_text(item, heap_trace_mode_text[index]);
 | 
					    variable_item_set_current_value_text(item, heap_trace_mode_text[index]);
 | 
				
			||||||
    furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]);
 | 
					    furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]);
 | 
				
			||||||
@ -81,7 +124,6 @@ const uint32_t mesurement_units_value[] = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void mesurement_units_changed(VariableItem* item) {
 | 
					static void mesurement_units_changed(VariableItem* item) {
 | 
				
			||||||
    // SystemSettings* app = variable_item_get_context(item);
 | 
					 | 
				
			||||||
    uint8_t index = variable_item_get_current_value_index(item);
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
    variable_item_set_current_value_text(item, mesurement_units_text[index]);
 | 
					    variable_item_set_current_value_text(item, mesurement_units_text[index]);
 | 
				
			||||||
    locale_set_measurement_unit(mesurement_units_value[index]);
 | 
					    locale_set_measurement_unit(mesurement_units_value[index]);
 | 
				
			||||||
@ -98,7 +140,6 @@ const uint32_t time_format_value[] = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void time_format_changed(VariableItem* item) {
 | 
					static void time_format_changed(VariableItem* item) {
 | 
				
			||||||
    // SystemSettings* app = variable_item_get_context(item);
 | 
					 | 
				
			||||||
    uint8_t index = variable_item_get_current_value_index(item);
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
    variable_item_set_current_value_text(item, time_format_text[index]);
 | 
					    variable_item_set_current_value_text(item, time_format_text[index]);
 | 
				
			||||||
    locale_set_time_format(time_format_value[index]);
 | 
					    locale_set_time_format(time_format_value[index]);
 | 
				
			||||||
@ -117,7 +158,6 @@ const uint32_t date_format_value[] = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void date_format_changed(VariableItem* item) {
 | 
					static void date_format_changed(VariableItem* item) {
 | 
				
			||||||
    // SystemSettings* app = variable_item_get_context(item);
 | 
					 | 
				
			||||||
    uint8_t index = variable_item_get_current_value_index(item);
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
    variable_item_set_current_value_text(item, date_format_text[index]);
 | 
					    variable_item_set_current_value_text(item, date_format_text[index]);
 | 
				
			||||||
    locale_set_date_format(date_format_value[index]);
 | 
					    locale_set_date_format(date_format_value[index]);
 | 
				
			||||||
@ -227,6 +267,24 @@ SystemSettings* system_settings_alloc() {
 | 
				
			|||||||
    variable_item_set_current_value_index(item, value_index);
 | 
					    variable_item_set_current_value_index(item, value_index);
 | 
				
			||||||
    variable_item_set_current_value_text(item, log_level_text[value_index]);
 | 
					    variable_item_set_current_value_text(item, log_level_text[value_index]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = variable_item_list_add(
 | 
				
			||||||
 | 
					        app->var_item_list, "Log Device", COUNT_OF(log_device_text), log_device_changed, app);
 | 
				
			||||||
 | 
					    value_index = value_index_uint32(
 | 
				
			||||||
 | 
					        furi_hal_rtc_get_log_device(), log_device_value, COUNT_OF(log_device_text));
 | 
				
			||||||
 | 
					    variable_item_set_current_value_index(item, value_index);
 | 
				
			||||||
 | 
					    variable_item_set_current_value_text(item, log_device_text[value_index]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    item = variable_item_list_add(
 | 
				
			||||||
 | 
					        app->var_item_list,
 | 
				
			||||||
 | 
					        "Log Baud Rate",
 | 
				
			||||||
 | 
					        COUNT_OF(log_baud_rate_text),
 | 
				
			||||||
 | 
					        log_baud_rate_changed,
 | 
				
			||||||
 | 
					        app);
 | 
				
			||||||
 | 
					    value_index = value_index_uint32(
 | 
				
			||||||
 | 
					        furi_hal_rtc_get_log_baud_rate(), log_baud_rate_value, COUNT_OF(log_baud_rate_text));
 | 
				
			||||||
 | 
					    variable_item_set_current_value_index(item, value_index);
 | 
				
			||||||
 | 
					    variable_item_set_current_value_text(item, log_baud_rate_text[value_index]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    item = variable_item_list_add(
 | 
					    item = variable_item_list_add(
 | 
				
			||||||
        app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app);
 | 
					        app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app);
 | 
				
			||||||
    value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0;
 | 
					    value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0;
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,7 @@ When not using the API, these peripherals MUST be enabled by the user code and t
 | 
				
			|||||||
| SPI2          | --                    |
 | 
					| SPI2          | --                    |
 | 
				
			||||||
| I2C1          | `furi_hal_i2c.h`      |
 | 
					| I2C1          | `furi_hal_i2c.h`      |
 | 
				
			||||||
| I2C3          | --                    |
 | 
					| I2C3          | --                    |
 | 
				
			||||||
| USART1        | `furi_hal_uart.h`     |
 | 
					| USART1        | `furi_hal_serial.h`   |
 | 
				
			||||||
| LPUART1       | --                    |
 | 
					| LPUART1       | --                    |
 | 
				
			||||||
| USB           | `furi_hal_usb.h`      |
 | 
					| USB           | `furi_hal_usb.h`      |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -102,8 +102,8 @@ Below is the list of DMA channels and their usage by the system.
 | 
				
			|||||||
|  --   | 3         |           |                           |
 | 
					|  --   | 3         |           |                           |
 | 
				
			||||||
|  --   | 4         | yes       | pulse reader              |
 | 
					|  --   | 4         | yes       | pulse reader              |
 | 
				
			||||||
|  --   | 5         |           |                           |
 | 
					|  --   | 5         |           |                           |
 | 
				
			||||||
|  --   | 6         |           |                           |
 | 
					|  --   | 6         | yes       | USART_Rx                  |
 | 
				
			||||||
|  --   | 7         |           |                           |
 | 
					|  --   | 7         | yes       | LPUART_Rx                 |
 | 
				
			||||||
| DMA2  | 1         | yes       | infrared, lfrfid, subghz, |
 | 
					| DMA2  | 1         | yes       | infrared, lfrfid, subghz, |
 | 
				
			||||||
|  --   | 2         | yes       | --                        |
 | 
					|  --   | 2         | yes       | --                        |
 | 
				
			||||||
|  --   | 3         | yes       | cc1101_ext                |
 | 
					|  --   | 3         | yes       | cc1101_ext                |
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@
 | 
				
			|||||||
#include "common_defines.h"
 | 
					#include "common_defines.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stm32wbxx.h>
 | 
					#include <stm32wbxx.h>
 | 
				
			||||||
#include <furi_hal_console.h>
 | 
					 | 
				
			||||||
#include <furi_hal_power.h>
 | 
					#include <furi_hal_power.h>
 | 
				
			||||||
#include <furi_hal_rtc.h>
 | 
					#include <furi_hal_rtc.h>
 | 
				
			||||||
#include <furi_hal_debug.h>
 | 
					#include <furi_hal_debug.h>
 | 
				
			||||||
@ -59,69 +58,69 @@ extern size_t xPortGetTotalHeapSize(void);
 | 
				
			|||||||
static void __furi_put_uint32_as_text(uint32_t data) {
 | 
					static void __furi_put_uint32_as_text(uint32_t data) {
 | 
				
			||||||
    char tmp_str[] = "-2147483648";
 | 
					    char tmp_str[] = "-2147483648";
 | 
				
			||||||
    itoa(data, tmp_str, 10);
 | 
					    itoa(data, tmp_str, 10);
 | 
				
			||||||
    furi_hal_console_puts(tmp_str);
 | 
					    furi_log_puts(tmp_str);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __furi_put_uint32_as_hex(uint32_t data) {
 | 
					static void __furi_put_uint32_as_hex(uint32_t data) {
 | 
				
			||||||
    char tmp_str[] = "0xFFFFFFFF";
 | 
					    char tmp_str[] = "0xFFFFFFFF";
 | 
				
			||||||
    itoa(data, tmp_str, 16);
 | 
					    itoa(data, tmp_str, 16);
 | 
				
			||||||
    furi_hal_console_puts(tmp_str);
 | 
					    furi_log_puts(tmp_str);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __furi_print_register_info() {
 | 
					static void __furi_print_register_info() {
 | 
				
			||||||
    // Print registers
 | 
					    // Print registers
 | 
				
			||||||
    for(uint8_t i = 0; i < 12; i++) {
 | 
					    for(uint8_t i = 0; i < 12; i++) {
 | 
				
			||||||
        furi_hal_console_puts("\r\n\tr");
 | 
					        furi_log_puts("\r\n\tr");
 | 
				
			||||||
        __furi_put_uint32_as_text(i);
 | 
					        __furi_put_uint32_as_text(i);
 | 
				
			||||||
        furi_hal_console_puts(" : ");
 | 
					        furi_log_puts(" : ");
 | 
				
			||||||
        __furi_put_uint32_as_hex(__furi_check_registers[i]);
 | 
					        __furi_put_uint32_as_hex(__furi_check_registers[i]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_console_puts("\r\n\tlr : ");
 | 
					    furi_log_puts("\r\n\tlr : ");
 | 
				
			||||||
    __furi_put_uint32_as_hex(__furi_check_registers[12]);
 | 
					    __furi_put_uint32_as_hex(__furi_check_registers[12]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __furi_print_stack_info() {
 | 
					static void __furi_print_stack_info() {
 | 
				
			||||||
    furi_hal_console_puts("\r\n\tstack watermark: ");
 | 
					    furi_log_puts("\r\n\tstack watermark: ");
 | 
				
			||||||
    __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4);
 | 
					    __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __furi_print_bt_stack_info() {
 | 
					static void __furi_print_bt_stack_info() {
 | 
				
			||||||
    const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info();
 | 
					    const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info();
 | 
				
			||||||
    if(fault_info == NULL) {
 | 
					    if(fault_info == NULL) {
 | 
				
			||||||
        furi_hal_console_puts("\r\n\tcore2: not faulted");
 | 
					        furi_log_puts("\r\n\tcore2: not faulted");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: ");
 | 
					        furi_log_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: ");
 | 
				
			||||||
        __furi_put_uint32_as_hex(fault_info->source_pc);
 | 
					        __furi_put_uint32_as_hex(fault_info->source_pc);
 | 
				
			||||||
        furi_hal_console_puts("\r\n\tLR: ");
 | 
					        furi_log_puts("\r\n\tLR: ");
 | 
				
			||||||
        __furi_put_uint32_as_hex(fault_info->source_lr);
 | 
					        __furi_put_uint32_as_hex(fault_info->source_lr);
 | 
				
			||||||
        furi_hal_console_puts("\r\n\tSP: ");
 | 
					        furi_log_puts("\r\n\tSP: ");
 | 
				
			||||||
        __furi_put_uint32_as_hex(fault_info->source_sp);
 | 
					        __furi_put_uint32_as_hex(fault_info->source_sp);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __furi_print_heap_info() {
 | 
					static void __furi_print_heap_info() {
 | 
				
			||||||
    furi_hal_console_puts("\r\n\t     heap total: ");
 | 
					    furi_log_puts("\r\n\t     heap total: ");
 | 
				
			||||||
    __furi_put_uint32_as_text(xPortGetTotalHeapSize());
 | 
					    __furi_put_uint32_as_text(xPortGetTotalHeapSize());
 | 
				
			||||||
    furi_hal_console_puts("\r\n\t      heap free: ");
 | 
					    furi_log_puts("\r\n\t      heap free: ");
 | 
				
			||||||
    __furi_put_uint32_as_text(xPortGetFreeHeapSize());
 | 
					    __furi_put_uint32_as_text(xPortGetFreeHeapSize());
 | 
				
			||||||
    furi_hal_console_puts("\r\n\t heap watermark: ");
 | 
					    furi_log_puts("\r\n\t heap watermark: ");
 | 
				
			||||||
    __furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize());
 | 
					    __furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __furi_print_name(bool isr) {
 | 
					static void __furi_print_name(bool isr) {
 | 
				
			||||||
    if(isr) {
 | 
					    if(isr) {
 | 
				
			||||||
        furi_hal_console_puts("[ISR ");
 | 
					        furi_log_puts("[ISR ");
 | 
				
			||||||
        __furi_put_uint32_as_text(__get_IPSR());
 | 
					        __furi_put_uint32_as_text(__get_IPSR());
 | 
				
			||||||
        furi_hal_console_puts("] ");
 | 
					        furi_log_puts("] ");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const char* name = pcTaskGetName(NULL);
 | 
					        const char* name = pcTaskGetName(NULL);
 | 
				
			||||||
        if(name == NULL) {
 | 
					        if(name == NULL) {
 | 
				
			||||||
            furi_hal_console_puts("[main] ");
 | 
					            furi_log_puts("[main] ");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            furi_hal_console_puts("[");
 | 
					            furi_log_puts("[");
 | 
				
			||||||
            furi_hal_console_puts(name);
 | 
					            furi_log_puts(name);
 | 
				
			||||||
            furi_hal_console_puts("] ");
 | 
					            furi_log_puts("] ");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -140,9 +139,9 @@ FURI_NORETURN void __furi_crash_implementation() {
 | 
				
			|||||||
        __furi_check_message = "furi_check failed";
 | 
					        __furi_check_message = "furi_check failed";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_console_puts("\r\n\033[0;31m[CRASH]");
 | 
					    furi_log_puts("\r\n\033[0;31m[CRASH]");
 | 
				
			||||||
    __furi_print_name(isr);
 | 
					    __furi_print_name(isr);
 | 
				
			||||||
    furi_hal_console_puts(__furi_check_message);
 | 
					    furi_log_puts(__furi_check_message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __furi_print_register_info();
 | 
					    __furi_print_register_info();
 | 
				
			||||||
    if(!isr) {
 | 
					    if(!isr) {
 | 
				
			||||||
@ -157,8 +156,8 @@ FURI_NORETURN void __furi_crash_implementation() {
 | 
				
			|||||||
#ifdef FURI_NDEBUG
 | 
					#ifdef FURI_NDEBUG
 | 
				
			||||||
    if(debug) {
 | 
					    if(debug) {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
        furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n");
 | 
					        furi_log_puts("\r\nSystem halted. Connect debugger for more info\r\n");
 | 
				
			||||||
        furi_hal_console_puts("\033[0m\r\n");
 | 
					        furi_log_puts("\033[0m\r\n");
 | 
				
			||||||
        furi_hal_debug_enable();
 | 
					        furi_hal_debug_enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        RESTORE_REGISTERS_AND_HALT_MCU(debug);
 | 
					        RESTORE_REGISTERS_AND_HALT_MCU(debug);
 | 
				
			||||||
@ -169,8 +168,8 @@ FURI_NORETURN void __furi_crash_implementation() {
 | 
				
			|||||||
            ptr = (uint32_t) "Check serial logs";
 | 
					            ptr = (uint32_t) "Check serial logs";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        furi_hal_rtc_set_fault_data(ptr);
 | 
					        furi_hal_rtc_set_fault_data(ptr);
 | 
				
			||||||
        furi_hal_console_puts("\r\nRebooting system.\r\n");
 | 
					        furi_log_puts("\r\nRebooting system.\r\n");
 | 
				
			||||||
        furi_hal_console_puts("\033[0m\r\n");
 | 
					        furi_log_puts("\033[0m\r\n");
 | 
				
			||||||
        furi_hal_power_reset();
 | 
					        furi_hal_power_reset();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@ -187,11 +186,11 @@ FURI_NORETURN void __furi_halt_implementation() {
 | 
				
			|||||||
        __furi_check_message = "System halt requested.";
 | 
					        __furi_check_message = "System halt requested.";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_console_puts("\r\n\033[0;31m[HALT]");
 | 
					    furi_log_puts("\r\n\033[0;31m[HALT]");
 | 
				
			||||||
    __furi_print_name(isr);
 | 
					    __furi_print_name(isr);
 | 
				
			||||||
    furi_hal_console_puts(__furi_check_message);
 | 
					    furi_log_puts(__furi_check_message);
 | 
				
			||||||
    furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n");
 | 
					    furi_log_puts("\r\nSystem halted. Bye-bye!\r\n");
 | 
				
			||||||
    furi_hal_console_puts("\033[0m\r\n");
 | 
					    furi_log_puts("\033[0m\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Check if debug enabled by DAP
 | 
					    // Check if debug enabled by DAP
 | 
				
			||||||
    // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en
 | 
					    // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										107
									
								
								furi/core/log.c
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								furi/core/log.c
									
									
									
									
									
								
							@ -2,17 +2,19 @@
 | 
				
			|||||||
#include "check.h"
 | 
					#include "check.h"
 | 
				
			||||||
#include "mutex.h"
 | 
					#include "mutex.h"
 | 
				
			||||||
#include <furi_hal.h>
 | 
					#include <furi_hal.h>
 | 
				
			||||||
 | 
					#include <m-list.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LIST_DEF(FuriLogHandlersList, FuriLogHandler, M_POD_OPLIST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define FURI_LOG_LEVEL_DEFAULT FuriLogLevelInfo
 | 
					#define FURI_LOG_LEVEL_DEFAULT FuriLogLevelInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    FuriLogLevel log_level;
 | 
					    FuriLogLevel log_level;
 | 
				
			||||||
    FuriLogPuts puts;
 | 
					 | 
				
			||||||
    FuriLogTimestamp timestamp;
 | 
					 | 
				
			||||||
    FuriMutex* mutex;
 | 
					    FuriMutex* mutex;
 | 
				
			||||||
 | 
					    FuriLogHandlersList_t tx_handlers;
 | 
				
			||||||
} FuriLogParams;
 | 
					} FuriLogParams;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static FuriLogParams furi_log;
 | 
					static FuriLogParams furi_log = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    const char* str;
 | 
					    const char* str;
 | 
				
			||||||
@ -32,9 +34,77 @@ static const FuriLogLevelDescription FURI_LOG_LEVEL_DESCRIPTIONS[] = {
 | 
				
			|||||||
void furi_log_init() {
 | 
					void furi_log_init() {
 | 
				
			||||||
    // Set default logging parameters
 | 
					    // Set default logging parameters
 | 
				
			||||||
    furi_log.log_level = FURI_LOG_LEVEL_DEFAULT;
 | 
					    furi_log.log_level = FURI_LOG_LEVEL_DEFAULT;
 | 
				
			||||||
    furi_log.puts = furi_hal_console_puts;
 | 
					    furi_log.mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
 | 
				
			||||||
    furi_log.timestamp = furi_get_tick;
 | 
					    FuriLogHandlersList_init(furi_log.tx_handlers);
 | 
				
			||||||
    furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool furi_log_add_handler(FuriLogHandler handler) {
 | 
				
			||||||
 | 
					    furi_check(handler.callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool ret = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriLogHandlersList_it_t it;
 | 
				
			||||||
 | 
					    FuriLogHandlersList_it(it, furi_log.tx_handlers);
 | 
				
			||||||
 | 
					    while(!FuriLogHandlersList_end_p(it)) {
 | 
				
			||||||
 | 
					        if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) {
 | 
				
			||||||
 | 
					            ret = false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            FuriLogHandlersList_next(it);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(ret) {
 | 
				
			||||||
 | 
					        FuriLogHandlersList_push_back(furi_log.tx_handlers, handler);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_mutex_release(furi_log.mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool furi_log_remove_handler(FuriLogHandler handler) {
 | 
				
			||||||
 | 
					    bool ret = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriLogHandlersList_it_t it;
 | 
				
			||||||
 | 
					    FuriLogHandlersList_it(it, furi_log.tx_handlers);
 | 
				
			||||||
 | 
					    while(!FuriLogHandlersList_end_p(it)) {
 | 
				
			||||||
 | 
					        if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) {
 | 
				
			||||||
 | 
					            FuriLogHandlersList_remove(furi_log.tx_handlers, it);
 | 
				
			||||||
 | 
					            ret = true;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            FuriLogHandlersList_next(it);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_mutex_release(furi_log.mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_log_tx(const uint8_t* data, size_t size) {
 | 
				
			||||||
 | 
					    if(!FURI_IS_ISR()) {
 | 
				
			||||||
 | 
					        furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if(furi_mutex_get_owner(furi_log.mutex)) return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriLogHandlersList_it_t it;
 | 
				
			||||||
 | 
					    FuriLogHandlersList_it(it, furi_log.tx_handlers);
 | 
				
			||||||
 | 
					    while(!FuriLogHandlersList_end_p(it)) {
 | 
				
			||||||
 | 
					        FuriLogHandlersList_ref(it)->callback(data, size, FuriLogHandlersList_ref(it)->context);
 | 
				
			||||||
 | 
					        FuriLogHandlersList_next(it);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(!FURI_IS_ISR()) furi_mutex_release(furi_log.mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_log_puts(const char* data) {
 | 
				
			||||||
 | 
					    furi_check(data);
 | 
				
			||||||
 | 
					    furi_log_tx((const uint8_t*)data, strlen(data));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) {
 | 
					void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) {
 | 
				
			||||||
@ -72,13 +142,8 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Timestamp
 | 
					        // Timestamp
 | 
				
			||||||
        furi_string_printf(
 | 
					        furi_string_printf(
 | 
				
			||||||
            string,
 | 
					            string, "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, furi_get_tick(), color, log_letter, tag);
 | 
				
			||||||
            "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET,
 | 
					        furi_log_puts(furi_string_get_cstr(string));
 | 
				
			||||||
            furi_log.timestamp(),
 | 
					 | 
				
			||||||
            color,
 | 
					 | 
				
			||||||
            log_letter,
 | 
					 | 
				
			||||||
            tag);
 | 
					 | 
				
			||||||
        furi_log.puts(furi_string_get_cstr(string));
 | 
					 | 
				
			||||||
        furi_string_reset(string);
 | 
					        furi_string_reset(string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        va_list args;
 | 
					        va_list args;
 | 
				
			||||||
@ -86,10 +151,10 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form
 | 
				
			|||||||
        furi_string_vprintf(string, format, args);
 | 
					        furi_string_vprintf(string, format, args);
 | 
				
			||||||
        va_end(args);
 | 
					        va_end(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        furi_log.puts(furi_string_get_cstr(string));
 | 
					        furi_log_puts(furi_string_get_cstr(string));
 | 
				
			||||||
        furi_string_free(string);
 | 
					        furi_string_free(string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        furi_log.puts("\r\n");
 | 
					        furi_log_puts("\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        furi_mutex_release(furi_log.mutex);
 | 
					        furi_mutex_release(furi_log.mutex);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -105,7 +170,7 @@ void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) {
 | 
				
			|||||||
        furi_string_vprintf(string, format, args);
 | 
					        furi_string_vprintf(string, format, args);
 | 
				
			||||||
        va_end(args);
 | 
					        va_end(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        furi_log.puts(furi_string_get_cstr(string));
 | 
					        furi_log_puts(furi_string_get_cstr(string));
 | 
				
			||||||
        furi_string_free(string);
 | 
					        furi_string_free(string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        furi_mutex_release(furi_log.mutex);
 | 
					        furi_mutex_release(furi_log.mutex);
 | 
				
			||||||
@ -123,16 +188,6 @@ FuriLogLevel furi_log_get_level(void) {
 | 
				
			|||||||
    return furi_log.log_level;
 | 
					    return furi_log.log_level;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void furi_log_set_puts(FuriLogPuts puts) {
 | 
					 | 
				
			||||||
    furi_assert(puts);
 | 
					 | 
				
			||||||
    furi_log.puts = puts;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_log_set_timestamp(FuriLogTimestamp timestamp) {
 | 
					 | 
				
			||||||
    furi_assert(timestamp);
 | 
					 | 
				
			||||||
    furi_log.timestamp = timestamp;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool furi_log_level_to_string(FuriLogLevel level, const char** str) {
 | 
					bool furi_log_level_to_string(FuriLogLevel level, const char** str) {
 | 
				
			||||||
    for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) {
 | 
					    for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) {
 | 
				
			||||||
        if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) {
 | 
					        if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) {
 | 
				
			||||||
 | 
				
			|||||||
@ -39,11 +39,44 @@ typedef enum {
 | 
				
			|||||||
#define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE)
 | 
					#define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE)
 | 
				
			||||||
#define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE)
 | 
					#define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void (*FuriLogPuts)(const char* data);
 | 
					typedef void (*FuriLogHandlerCallback)(const uint8_t* data, size_t size, void* context);
 | 
				
			||||||
typedef uint32_t (*FuriLogTimestamp)(void);
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    FuriLogHandlerCallback callback;
 | 
				
			||||||
 | 
					    void* context;
 | 
				
			||||||
 | 
					} FuriLogHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Initialize logging */
 | 
					/** Initialize logging */
 | 
				
			||||||
void furi_log_init();
 | 
					void furi_log_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Add log TX callback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  callback  The callback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     true on success, false otherwise
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool furi_log_add_handler(FuriLogHandler handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Remove log TX callback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  callback  The callback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     true on success, false otherwise
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool furi_log_remove_handler(FuriLogHandler handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Transmit data through log IO callbacks
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  data  The data
 | 
				
			||||||
 | 
					 * @param[in]  size  The size
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_log_tx(const uint8_t* data, size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Transmit data through log IO callbacks
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  data  The data, null-terminated C-string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_log_puts(const char* data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Print log record
 | 
					/** Print log record
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
@ -74,19 +107,7 @@ void furi_log_set_level(FuriLogLevel level);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     The furi log level.
 | 
					 * @return     The furi log level.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
FuriLogLevel furi_log_get_level();
 | 
					FuriLogLevel furi_log_get_level(void);
 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Set log output callback
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param[in]  puts  The puts callback
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void furi_log_set_puts(FuriLogPuts puts);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Set timestamp callback
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param[in]  timestamp  The timestamp callback
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void furi_log_set_timestamp(FuriLogTimestamp timestamp);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Log level to string
 | 
					/** Log level to string
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,7 @@
 | 
				
			|||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <stm32wbxx.h>
 | 
					#include <stm32wbxx.h>
 | 
				
			||||||
#include <furi_hal_console.h>
 | 
					#include <core/log.h>
 | 
				
			||||||
#include <core/common_defines.h>
 | 
					#include <core/common_defines.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
 | 
					/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
 | 
				
			||||||
@ -52,6 +52,10 @@ task.h is included from an application file. */
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
 | 
					#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HEAP_PRINT_DEBUG
 | 
				
			||||||
 | 
					#error This feature is broken, logging transport must be replaced with RTT
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if(configSUPPORT_DYNAMIC_ALLOCATION == 0)
 | 
					#if(configSUPPORT_DYNAMIC_ALLOCATION == 0)
 | 
				
			||||||
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
 | 
					#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@ -286,13 +290,13 @@ static void print_heap_init() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // {PHStart|heap_start|heap_end}
 | 
					    // {PHStart|heap_start|heap_end}
 | 
				
			||||||
    FURI_CRITICAL_ENTER();
 | 
					    FURI_CRITICAL_ENTER();
 | 
				
			||||||
    furi_hal_console_puts("{PHStart|");
 | 
					    furi_log_puts("{PHStart|");
 | 
				
			||||||
    ultoa(heap_start, tmp_str, 16);
 | 
					    ultoa(heap_start, tmp_str, 16);
 | 
				
			||||||
    furi_hal_console_puts(tmp_str);
 | 
					    furi_log_puts(tmp_str);
 | 
				
			||||||
    furi_hal_console_puts("|");
 | 
					    furi_log_puts("|");
 | 
				
			||||||
    ultoa(heap_end, tmp_str, 16);
 | 
					    ultoa(heap_end, tmp_str, 16);
 | 
				
			||||||
    furi_hal_console_puts(tmp_str);
 | 
					    furi_log_puts(tmp_str);
 | 
				
			||||||
    furi_hal_console_puts("}\r\n");
 | 
					    furi_log_puts("}\r\n");
 | 
				
			||||||
    FURI_CRITICAL_EXIT();
 | 
					    FURI_CRITICAL_EXIT();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -305,15 +309,15 @@ static void print_heap_malloc(void* ptr, size_t size) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // {thread name|m|address|size}
 | 
					    // {thread name|m|address|size}
 | 
				
			||||||
    FURI_CRITICAL_ENTER();
 | 
					    FURI_CRITICAL_ENTER();
 | 
				
			||||||
    furi_hal_console_puts("{");
 | 
					    furi_log_puts("{");
 | 
				
			||||||
    furi_hal_console_puts(name);
 | 
					    furi_log_puts(name);
 | 
				
			||||||
    furi_hal_console_puts("|m|0x");
 | 
					    furi_log_puts("|m|0x");
 | 
				
			||||||
    ultoa((unsigned long)ptr, tmp_str, 16);
 | 
					    ultoa((unsigned long)ptr, tmp_str, 16);
 | 
				
			||||||
    furi_hal_console_puts(tmp_str);
 | 
					    furi_log_puts(tmp_str);
 | 
				
			||||||
    furi_hal_console_puts("|");
 | 
					    furi_log_puts("|");
 | 
				
			||||||
    utoa(size, tmp_str, 10);
 | 
					    utoa(size, tmp_str, 10);
 | 
				
			||||||
    furi_hal_console_puts(tmp_str);
 | 
					    furi_log_puts(tmp_str);
 | 
				
			||||||
    furi_hal_console_puts("}\r\n");
 | 
					    furi_log_puts("}\r\n");
 | 
				
			||||||
    FURI_CRITICAL_EXIT();
 | 
					    FURI_CRITICAL_EXIT();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -326,12 +330,12 @@ static void print_heap_free(void* ptr) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // {thread name|f|address}
 | 
					    // {thread name|f|address}
 | 
				
			||||||
    FURI_CRITICAL_ENTER();
 | 
					    FURI_CRITICAL_ENTER();
 | 
				
			||||||
    furi_hal_console_puts("{");
 | 
					    furi_log_puts("{");
 | 
				
			||||||
    furi_hal_console_puts(name);
 | 
					    furi_log_puts(name);
 | 
				
			||||||
    furi_hal_console_puts("|f|0x");
 | 
					    furi_log_puts("|f|0x");
 | 
				
			||||||
    ultoa((unsigned long)ptr, tmp_str, 16);
 | 
					    ultoa((unsigned long)ptr, tmp_str, 16);
 | 
				
			||||||
    furi_hal_console_puts(tmp_str);
 | 
					    furi_log_puts(tmp_str);
 | 
				
			||||||
    furi_hal_console_puts("}\r\n");
 | 
					    furi_log_puts("}\r\n");
 | 
				
			||||||
    FURI_CRITICAL_EXIT();
 | 
					    FURI_CRITICAL_EXIT();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -114,8 +114,10 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
 | 
					    hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) {
 | 
					    if((hMutex == NULL)) {
 | 
				
			||||||
        owner = 0;
 | 
					        owner = 0;
 | 
				
			||||||
 | 
					    } else if(FURI_IS_IRQ_MODE()) {
 | 
				
			||||||
 | 
					        owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);
 | 
					        owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "log.h"
 | 
					#include "log.h"
 | 
				
			||||||
#include <furi_hal_rtc.h>
 | 
					#include <furi_hal_rtc.h>
 | 
				
			||||||
#include <furi_hal_console.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <FreeRTOS.h>
 | 
					#include <FreeRTOS.h>
 | 
				
			||||||
#include <task.h>
 | 
					#include <task.h>
 | 
				
			||||||
@ -570,7 +569,7 @@ static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, s
 | 
				
			|||||||
    if(thread->output.write_callback != NULL) {
 | 
					    if(thread->output.write_callback != NULL) {
 | 
				
			||||||
        thread->output.write_callback(data, size);
 | 
					        thread->output.write_callback(data, size);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        furi_hal_console_tx((const uint8_t*)data, size);
 | 
					        furi_log_tx((const uint8_t*)data, size);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return size;
 | 
					    return size;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,52 +1,55 @@
 | 
				
			|||||||
#include "value_index.h"
 | 
					#include "value_index.h"
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) {
 | 
					size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count) {
 | 
				
			||||||
    int64_t last_value = INT64_MIN;
 | 
					    size_t index = 0;
 | 
				
			||||||
    uint8_t index = 0;
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < values_count; i++) {
 | 
					 | 
				
			||||||
        if((value >= last_value) && (value <= values[i])) {
 | 
					 | 
				
			||||||
            index = i;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        last_value = values[i];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return index;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) {
 | 
					    for(size_t i = 0; i < values_count; i++) {
 | 
				
			||||||
    int64_t last_value = INT64_MIN;
 | 
					 | 
				
			||||||
    uint8_t index = 0;
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < values_count; i++) {
 | 
					 | 
				
			||||||
        if((value >= last_value) && (value <= values[i])) {
 | 
					 | 
				
			||||||
            index = i;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        last_value = values[i];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return index;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint8_t value_index_float(const float value, const float values[], uint8_t values_count) {
 | 
					 | 
				
			||||||
    const float epsilon = 0.01f;
 | 
					 | 
				
			||||||
    float last_value = values[0];
 | 
					 | 
				
			||||||
    uint8_t index = 0;
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < values_count; i++) {
 | 
					 | 
				
			||||||
        if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) {
 | 
					 | 
				
			||||||
            index = i;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        last_value = values[i];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return index;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count) {
 | 
					 | 
				
			||||||
    uint8_t index = 0;
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < values_count; i++) {
 | 
					 | 
				
			||||||
        if(value == values[i]) {
 | 
					        if(value == values[i]) {
 | 
				
			||||||
            index = i;
 | 
					            index = i;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return index;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count) {
 | 
				
			||||||
 | 
					    size_t index = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(size_t i = 0; i < values_count; i++) {
 | 
				
			||||||
 | 
					        if(value == values[i]) {
 | 
				
			||||||
 | 
					            index = i;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return index;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t value_index_float(const float value, const float values[], size_t values_count) {
 | 
				
			||||||
 | 
					    size_t index = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(size_t i = 0; i < values_count; i++) {
 | 
				
			||||||
 | 
					        const float epsilon = fabsf(values[i] * 0.01f);
 | 
				
			||||||
 | 
					        if(fabsf(values[i] - value) <= epsilon) {
 | 
				
			||||||
 | 
					            index = i;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return index;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t value_index_bool(const bool value, const bool values[], size_t values_count) {
 | 
				
			||||||
 | 
					    size_t index = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(size_t i = 0; i < values_count; i++) {
 | 
				
			||||||
 | 
					        if(value == values[i]) {
 | 
				
			||||||
 | 
					            index = i;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return index;
 | 
					    return index;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
@ -18,7 +19,7 @@ extern "C" {
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return value's index.
 | 
					 * @return value's index.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count);
 | 
					size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get the index of a uint32_t array element which is closest to the given value.
 | 
					/** Get the index of a uint32_t array element which is closest to the given value.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -31,7 +32,7 @@ uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t v
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return value's index.
 | 
					 * @return value's index.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count);
 | 
					size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get the index of a float array element which is closest to the given value.
 | 
					/** Get the index of a float array element which is closest to the given value.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -44,7 +45,7 @@ uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return value's index.
 | 
					 * @return value's index.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint8_t value_index_float(const float value, const float values[], uint8_t values_count);
 | 
					size_t value_index_float(const float value, const float values[], size_t values_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get the index of a bool array element which is equal to the given value.
 | 
					/** Get the index of a bool array element which is equal to the given value.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -57,7 +58,7 @@ uint8_t value_index_float(const float value, const float values[], uint8_t value
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return value's index.
 | 
					 * @return value's index.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count);
 | 
					size_t value_index_bool(const bool value, const bool values[], size_t values_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
entry,status,name,type,params
 | 
					entry,status,name,type,params
 | 
				
			||||||
Version,+,50.1,,
 | 
					Version,+,51.0,,
 | 
				
			||||||
Header,+,applications/services/bt/bt_service/bt.h,,
 | 
					Header,+,applications/services/bt/bt_service/bt.h,,
 | 
				
			||||||
Header,+,applications/services/cli/cli.h,,
 | 
					Header,+,applications/services/cli/cli.h,,
 | 
				
			||||||
Header,+,applications/services/cli/cli_vcp.h,,
 | 
					Header,+,applications/services/cli/cli_vcp.h,,
 | 
				
			||||||
@ -160,7 +160,6 @@ Header,+,targets/f18/furi_hal/furi_hal_spi_config.h,,
 | 
				
			|||||||
Header,+,targets/f18/furi_hal/furi_hal_target_hw.h,,
 | 
					Header,+,targets/f18/furi_hal/furi_hal_target_hw.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_bus.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_bus.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_clock.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_clock.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_console.h,,
 | 
					 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_dma.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_dma.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_flash.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_flash.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_gpio.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_gpio.h,,
 | 
				
			||||||
@ -170,8 +169,11 @@ Header,+,targets/f7/furi_hal/furi_hal_idle_timer.h,,
 | 
				
			|||||||
Header,+,targets/f7/furi_hal/furi_hal_interrupt.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_interrupt.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_os.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_os.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_pwm.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_pwm.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_rtc.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_serial.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_uart.h,,
 | 
					 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,,
 | 
				
			||||||
Header,+,targets/f7/platform_specific/intrinsic_export.h,,
 | 
					Header,+,targets/f7/platform_specific/intrinsic_export.h,,
 | 
				
			||||||
Header,+,targets/f7/platform_specific/math_wrapper.h,,
 | 
					Header,+,targets/f7/platform_specific/math_wrapper.h,,
 | 
				
			||||||
@ -190,7 +192,6 @@ Header,+,targets/furi_hal_include/furi_hal_mpu.h,,
 | 
				
			|||||||
Header,+,targets/furi_hal_include/furi_hal_power.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_power.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_random.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_random.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_region.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_region.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_rtc.h,,
 | 
					 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_sd.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_sd.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_speaker.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_speaker.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_spi.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_spi.h,,
 | 
				
			||||||
@ -1057,14 +1058,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void,
 | 
				
			|||||||
Function,-,furi_hal_clock_switch_hse2pll,_Bool,
 | 
					Function,-,furi_hal_clock_switch_hse2pll,_Bool,
 | 
				
			||||||
Function,-,furi_hal_clock_switch_hsi2hse,void,
 | 
					Function,-,furi_hal_clock_switch_hsi2hse,void,
 | 
				
			||||||
Function,-,furi_hal_clock_switch_pll2hse,_Bool,
 | 
					Function,-,furi_hal_clock_switch_pll2hse,_Bool,
 | 
				
			||||||
Function,+,furi_hal_console_disable,void,
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_enable,void,
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_init,void,
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_printf,void,"const char[], ..."
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_puts,void,const char*
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*"
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize"
 | 
					Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize"
 | 
				
			||||||
Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp
 | 
					Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp
 | 
				
			||||||
Function,+,furi_hal_cortex_delay_us,void,uint32_t
 | 
					Function,+,furi_hal_cortex_delay_us,void,uint32_t
 | 
				
			||||||
@ -1239,6 +1232,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode,
 | 
				
			|||||||
Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat,
 | 
					Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat,
 | 
					Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits,
 | 
					Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits,
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate,
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_log_level,uint8_t,
 | 
					Function,+,furi_hal_rtc_get_log_level,uint8_t,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_pin_fails,uint32_t,
 | 
					Function,+,furi_hal_rtc_get_pin_fails,uint32_t,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister
 | 
					Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister
 | 
				
			||||||
@ -1257,6 +1252,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode
 | 
				
			|||||||
Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat
 | 
					Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat
 | 
				
			||||||
Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat
 | 
					Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat
 | 
				
			||||||
Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits
 | 
					Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice
 | 
				
			||||||
Function,+,furi_hal_rtc_set_log_level,void,uint8_t
 | 
					Function,+,furi_hal_rtc_set_log_level,void,uint8_t
 | 
				
			||||||
Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
 | 
					Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
 | 
				
			||||||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
					Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
				
			||||||
@ -1271,6 +1268,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
 | 
				
			|||||||
Function,+,furi_hal_sd_presence_init,void,
 | 
					Function,+,furi_hal_sd_presence_init,void,
 | 
				
			||||||
Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
 | 
					Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
 | 
					Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_deinit,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_init,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_resume,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_suspend,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle*
 | 
				
			||||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
					Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
				
			||||||
Function,-,furi_hal_speaker_deinit,void,
 | 
					Function,-,furi_hal_speaker_deinit,void,
 | 
				
			||||||
Function,-,furi_hal_speaker_init,void,
 | 
					Function,-,furi_hal_speaker_init,void,
 | 
				
			||||||
@ -1294,13 +1311,6 @@ Function,-,furi_hal_spi_config_init_early,void,
 | 
				
			|||||||
Function,-,furi_hal_spi_dma_init,void,
 | 
					Function,-,furi_hal_spi_dma_init,void,
 | 
				
			||||||
Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle*
 | 
					Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle*
 | 
				
			||||||
Function,+,furi_hal_switch,void,void*
 | 
					Function,+,furi_hal_switch,void,void*
 | 
				
			||||||
Function,+,furi_hal_uart_deinit,void,FuriHalUartId
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_resume,void,FuriHalUartId
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*"
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_suspend,void,FuriHalUartId
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_usb_disable,void,
 | 
					Function,+,furi_hal_usb_disable,void,
 | 
				
			||||||
Function,+,furi_hal_usb_enable,void,
 | 
					Function,+,furi_hal_usb_enable,void,
 | 
				
			||||||
Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*,
 | 
					Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*,
 | 
				
			||||||
@ -1346,15 +1356,17 @@ Function,+,furi_kernel_is_running,_Bool,
 | 
				
			|||||||
Function,+,furi_kernel_lock,int32_t,
 | 
					Function,+,furi_kernel_lock,int32_t,
 | 
				
			||||||
Function,+,furi_kernel_restore_lock,int32_t,int32_t
 | 
					Function,+,furi_kernel_restore_lock,int32_t,int32_t
 | 
				
			||||||
Function,+,furi_kernel_unlock,int32_t,
 | 
					Function,+,furi_kernel_unlock,int32_t,
 | 
				
			||||||
 | 
					Function,+,furi_log_add_handler,_Bool,FuriLogHandler
 | 
				
			||||||
Function,+,furi_log_get_level,FuriLogLevel,
 | 
					Function,+,furi_log_get_level,FuriLogLevel,
 | 
				
			||||||
Function,-,furi_log_init,void,
 | 
					Function,-,furi_log_init,void,
 | 
				
			||||||
Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*"
 | 
					Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*"
 | 
				
			||||||
Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**"
 | 
					Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**"
 | 
				
			||||||
Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..."
 | 
					Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..."
 | 
				
			||||||
Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..."
 | 
					Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..."
 | 
				
			||||||
 | 
					Function,+,furi_log_puts,void,const char*
 | 
				
			||||||
 | 
					Function,+,furi_log_remove_handler,_Bool,FuriLogHandler
 | 
				
			||||||
Function,+,furi_log_set_level,void,FuriLogLevel
 | 
					Function,+,furi_log_set_level,void,FuriLogLevel
 | 
				
			||||||
Function,-,furi_log_set_puts,void,FuriLogPuts
 | 
					Function,+,furi_log_tx,void,"const uint8_t*, size_t"
 | 
				
			||||||
Function,-,furi_log_set_timestamp,void,FuriLogTimestamp
 | 
					 | 
				
			||||||
Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t"
 | 
					Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t"
 | 
				
			||||||
Function,+,furi_message_queue_free,void,FuriMessageQueue*
 | 
					Function,+,furi_message_queue_free,void,FuriMessageQueue*
 | 
				
			||||||
Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t"
 | 
					Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t"
 | 
				
			||||||
@ -2418,10 +2430,10 @@ Function,-,utoa,char*,"unsigned, char*, int"
 | 
				
			|||||||
Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*"
 | 
					Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*"
 | 
				
			||||||
Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*"
 | 
					Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*"
 | 
				
			||||||
Function,+,validator_is_file_free,void,ValidatorIsFile*
 | 
					Function,+,validator_is_file_free,void,ValidatorIsFile*
 | 
				
			||||||
Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t"
 | 
					Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t"
 | 
				
			||||||
Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t"
 | 
					Function,+,value_index_float,size_t,"const float, const float[], size_t"
 | 
				
			||||||
Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t"
 | 
					Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t"
 | 
				
			||||||
Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t"
 | 
					Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t"
 | 
				
			||||||
Function,+,variable_item_get_context,void*,VariableItem*
 | 
					Function,+,variable_item_get_context,void*,VariableItem*
 | 
				
			||||||
Function,+,variable_item_get_current_value_index,uint8_t,VariableItem*
 | 
					Function,+,variable_item_get_current_value_index,uint8_t,VariableItem*
 | 
				
			||||||
Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*"
 | 
					Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*"
 | 
				
			||||||
 | 
				
			|||||||
		
		
			
  | 
@ -33,7 +33,7 @@ void furi_hal_init() {
 | 
				
			|||||||
    furi_hal_mpu_init();
 | 
					    furi_hal_mpu_init();
 | 
				
			||||||
    furi_hal_clock_init();
 | 
					    furi_hal_clock_init();
 | 
				
			||||||
    furi_hal_random_init();
 | 
					    furi_hal_random_init();
 | 
				
			||||||
    furi_hal_console_init();
 | 
					    furi_hal_serial_control_init();
 | 
				
			||||||
    furi_hal_rtc_init();
 | 
					    furi_hal_rtc_init();
 | 
				
			||||||
    furi_hal_interrupt_init();
 | 
					    furi_hal_interrupt_init();
 | 
				
			||||||
    furi_hal_flash_init();
 | 
					    furi_hal_flash_init();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
entry,status,name,type,params
 | 
					entry,status,name,type,params
 | 
				
			||||||
Version,+,50.1,,
 | 
					Version,+,51.0,,
 | 
				
			||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
 | 
					Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
 | 
				
			||||||
Header,+,applications/services/bt/bt_service/bt.h,,
 | 
					Header,+,applications/services/bt/bt_service/bt.h,,
 | 
				
			||||||
Header,+,applications/services/cli/cli.h,,
 | 
					Header,+,applications/services/cli/cli.h,,
 | 
				
			||||||
@ -221,7 +221,6 @@ Header,+,lib/toolbox/value_index.h,,
 | 
				
			|||||||
Header,+,lib/toolbox/version.h,,
 | 
					Header,+,lib/toolbox/version.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_bus.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_bus.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_clock.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_clock.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_console.h,,
 | 
					 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_dma.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_dma.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_flash.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_flash.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_gpio.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_gpio.h,,
 | 
				
			||||||
@ -234,11 +233,14 @@ Header,+,targets/f7/furi_hal/furi_hal_os.h,,
 | 
				
			|||||||
Header,+,targets/f7/furi_hal/furi_hal_pwm.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_pwm.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_resources.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_resources.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_rfid.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_rfid.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_rtc.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_serial.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,,
 | 
				
			||||||
 | 
					Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_spi_config.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_spi_config.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_subghz.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_subghz.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_target_hw.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_target_hw.h,,
 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_uart.h,,
 | 
					 | 
				
			||||||
Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,,
 | 
					Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,,
 | 
				
			||||||
Header,+,targets/f7/platform_specific/intrinsic_export.h,,
 | 
					Header,+,targets/f7/platform_specific/intrinsic_export.h,,
 | 
				
			||||||
Header,+,targets/f7/platform_specific/math_wrapper.h,,
 | 
					Header,+,targets/f7/platform_specific/math_wrapper.h,,
 | 
				
			||||||
@ -259,7 +261,6 @@ Header,+,targets/furi_hal_include/furi_hal_nfc.h,,
 | 
				
			|||||||
Header,+,targets/furi_hal_include/furi_hal_power.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_power.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_random.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_random.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_region.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_region.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_rtc.h,,
 | 
					 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_sd.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_sd.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_speaker.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_speaker.h,,
 | 
				
			||||||
Header,+,targets/furi_hal_include/furi_hal_spi.h,,
 | 
					Header,+,targets/furi_hal_include/furi_hal_spi.h,,
 | 
				
			||||||
@ -1146,14 +1147,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void,
 | 
				
			|||||||
Function,-,furi_hal_clock_switch_hse2pll,_Bool,
 | 
					Function,-,furi_hal_clock_switch_hse2pll,_Bool,
 | 
				
			||||||
Function,-,furi_hal_clock_switch_hsi2hse,void,
 | 
					Function,-,furi_hal_clock_switch_hsi2hse,void,
 | 
				
			||||||
Function,-,furi_hal_clock_switch_pll2hse,_Bool,
 | 
					Function,-,furi_hal_clock_switch_pll2hse,_Bool,
 | 
				
			||||||
Function,+,furi_hal_console_disable,void,
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_enable,void,
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_init,void,
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_printf,void,"const char[], ..."
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_puts,void,const char*
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*"
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize"
 | 
					Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize"
 | 
				
			||||||
Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp
 | 
					Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp
 | 
				
			||||||
Function,+,furi_hal_cortex_delay_us,void,uint32_t
 | 
					Function,+,furi_hal_cortex_delay_us,void,uint32_t
 | 
				
			||||||
@ -1405,6 +1398,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode,
 | 
				
			|||||||
Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat,
 | 
					Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat,
 | 
					Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits,
 | 
					Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits,
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate,
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_log_level,uint8_t,
 | 
					Function,+,furi_hal_rtc_get_log_level,uint8_t,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_pin_fails,uint32_t,
 | 
					Function,+,furi_hal_rtc_get_pin_fails,uint32_t,
 | 
				
			||||||
Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister
 | 
					Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister
 | 
				
			||||||
@ -1423,6 +1418,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode
 | 
				
			|||||||
Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat
 | 
					Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat
 | 
				
			||||||
Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat
 | 
					Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat
 | 
				
			||||||
Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits
 | 
					Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate
 | 
				
			||||||
 | 
					Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice
 | 
				
			||||||
Function,+,furi_hal_rtc_set_log_level,void,uint8_t
 | 
					Function,+,furi_hal_rtc_set_log_level,void,uint8_t
 | 
				
			||||||
Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
 | 
					Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
 | 
				
			||||||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
					Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
 | 
				
			||||||
@ -1437,6 +1434,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
 | 
				
			|||||||
Function,+,furi_hal_sd_presence_init,void,
 | 
					Function,+,furi_hal_sd_presence_init,void,
 | 
				
			||||||
Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
 | 
					Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
 | 
					Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_deinit,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_init,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_resume,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_control_suspend,void,
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle*
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t"
 | 
				
			||||||
 | 
					Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle*
 | 
				
			||||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
					Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
 | 
				
			||||||
Function,-,furi_hal_speaker_deinit,void,
 | 
					Function,-,furi_hal_speaker_deinit,void,
 | 
				
			||||||
Function,-,furi_hal_speaker_init,void,
 | 
					Function,-,furi_hal_speaker_init,void,
 | 
				
			||||||
@ -1490,13 +1507,6 @@ Function,+,furi_hal_subghz_stop_async_tx,void,
 | 
				
			|||||||
Function,+,furi_hal_subghz_tx,_Bool,
 | 
					Function,+,furi_hal_subghz_tx,_Bool,
 | 
				
			||||||
Function,+,furi_hal_subghz_write_packet,void,"const uint8_t*, uint8_t"
 | 
					Function,+,furi_hal_subghz_write_packet,void,"const uint8_t*, uint8_t"
 | 
				
			||||||
Function,+,furi_hal_switch,void,void*
 | 
					Function,+,furi_hal_switch,void,void*
 | 
				
			||||||
Function,+,furi_hal_uart_deinit,void,FuriHalUartId
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_resume,void,FuriHalUartId
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*"
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_suspend,void,FuriHalUartId
 | 
					 | 
				
			||||||
Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t"
 | 
					 | 
				
			||||||
Function,+,furi_hal_usb_disable,void,
 | 
					Function,+,furi_hal_usb_disable,void,
 | 
				
			||||||
Function,+,furi_hal_usb_enable,void,
 | 
					Function,+,furi_hal_usb_enable,void,
 | 
				
			||||||
Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*,
 | 
					Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*,
 | 
				
			||||||
@ -1542,15 +1552,17 @@ Function,+,furi_kernel_is_running,_Bool,
 | 
				
			|||||||
Function,+,furi_kernel_lock,int32_t,
 | 
					Function,+,furi_kernel_lock,int32_t,
 | 
				
			||||||
Function,+,furi_kernel_restore_lock,int32_t,int32_t
 | 
					Function,+,furi_kernel_restore_lock,int32_t,int32_t
 | 
				
			||||||
Function,+,furi_kernel_unlock,int32_t,
 | 
					Function,+,furi_kernel_unlock,int32_t,
 | 
				
			||||||
 | 
					Function,+,furi_log_add_handler,_Bool,FuriLogHandler
 | 
				
			||||||
Function,+,furi_log_get_level,FuriLogLevel,
 | 
					Function,+,furi_log_get_level,FuriLogLevel,
 | 
				
			||||||
Function,-,furi_log_init,void,
 | 
					Function,-,furi_log_init,void,
 | 
				
			||||||
Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*"
 | 
					Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*"
 | 
				
			||||||
Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**"
 | 
					Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**"
 | 
				
			||||||
Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..."
 | 
					Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..."
 | 
				
			||||||
Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..."
 | 
					Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..."
 | 
				
			||||||
 | 
					Function,+,furi_log_puts,void,const char*
 | 
				
			||||||
 | 
					Function,+,furi_log_remove_handler,_Bool,FuriLogHandler
 | 
				
			||||||
Function,+,furi_log_set_level,void,FuriLogLevel
 | 
					Function,+,furi_log_set_level,void,FuriLogLevel
 | 
				
			||||||
Function,-,furi_log_set_puts,void,FuriLogPuts
 | 
					Function,+,furi_log_tx,void,"const uint8_t*, size_t"
 | 
				
			||||||
Function,-,furi_log_set_timestamp,void,FuriLogTimestamp
 | 
					 | 
				
			||||||
Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t"
 | 
					Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t"
 | 
				
			||||||
Function,+,furi_message_queue_free,void,FuriMessageQueue*
 | 
					Function,+,furi_message_queue_free,void,FuriMessageQueue*
 | 
				
			||||||
Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t"
 | 
					Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t"
 | 
				
			||||||
@ -3202,10 +3214,10 @@ Function,-,utoa,char*,"unsigned, char*, int"
 | 
				
			|||||||
Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*"
 | 
					Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*"
 | 
				
			||||||
Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*"
 | 
					Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*"
 | 
				
			||||||
Function,+,validator_is_file_free,void,ValidatorIsFile*
 | 
					Function,+,validator_is_file_free,void,ValidatorIsFile*
 | 
				
			||||||
Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t"
 | 
					Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t"
 | 
				
			||||||
Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t"
 | 
					Function,+,value_index_float,size_t,"const float, const float[], size_t"
 | 
				
			||||||
Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t"
 | 
					Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t"
 | 
				
			||||||
Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t"
 | 
					Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t"
 | 
				
			||||||
Function,+,variable_item_get_context,void*,VariableItem*
 | 
					Function,+,variable_item_get_context,void*,VariableItem*
 | 
				
			||||||
Function,+,variable_item_get_current_value_index,uint8_t,VariableItem*
 | 
					Function,+,variable_item_get_current_value_index,uint8_t,VariableItem*
 | 
				
			||||||
Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*"
 | 
					Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*"
 | 
				
			||||||
 | 
				
			|||||||
		
		
			
  | 
@ -33,7 +33,7 @@ void furi_hal_init() {
 | 
				
			|||||||
    furi_hal_mpu_init();
 | 
					    furi_hal_mpu_init();
 | 
				
			||||||
    furi_hal_clock_init();
 | 
					    furi_hal_clock_init();
 | 
				
			||||||
    furi_hal_random_init();
 | 
					    furi_hal_random_init();
 | 
				
			||||||
    furi_hal_console_init();
 | 
					    furi_hal_serial_control_init();
 | 
				
			||||||
    furi_hal_rtc_init();
 | 
					    furi_hal_rtc_init();
 | 
				
			||||||
    furi_hal_interrupt_init();
 | 
					    furi_hal_interrupt_init();
 | 
				
			||||||
    furi_hal_flash_init();
 | 
					    furi_hal_flash_init();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,99 +0,0 @@
 | 
				
			|||||||
#include <furi_hal_console.h>
 | 
					 | 
				
			||||||
#include <furi_hal_uart.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stm32wbxx_ll_gpio.h>
 | 
					 | 
				
			||||||
#include <stm32wbxx_ll_usart.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <furi.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define TAG "FuriHalConsole"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef HEAP_PRINT_DEBUG
 | 
					 | 
				
			||||||
#define CONSOLE_BAUDRATE 1843200
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#define CONSOLE_BAUDRATE 230400
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    bool alive;
 | 
					 | 
				
			||||||
    FuriHalConsoleTxCallback tx_callback;
 | 
					 | 
				
			||||||
    void* tx_callback_context;
 | 
					 | 
				
			||||||
} FuriHalConsole;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FuriHalConsole furi_hal_console = {
 | 
					 | 
				
			||||||
    .alive = false,
 | 
					 | 
				
			||||||
    .tx_callback = NULL,
 | 
					 | 
				
			||||||
    .tx_callback_context = NULL,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_init() {
 | 
					 | 
				
			||||||
    furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
 | 
					 | 
				
			||||||
    furi_hal_console.alive = true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_enable() {
 | 
					 | 
				
			||||||
    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))
 | 
					 | 
				
			||||||
        ;
 | 
					 | 
				
			||||||
    furi_hal_console.alive = false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) {
 | 
					 | 
				
			||||||
    FURI_CRITICAL_ENTER();
 | 
					 | 
				
			||||||
    furi_hal_console.tx_callback = callback;
 | 
					 | 
				
			||||||
    furi_hal_console.tx_callback_context = context;
 | 
					 | 
				
			||||||
    FURI_CRITICAL_EXIT();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
 | 
					 | 
				
			||||||
    if(!furi_hal_console.alive) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FURI_CRITICAL_ENTER();
 | 
					 | 
				
			||||||
    // Transmit data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(furi_hal_console.tx_callback) {
 | 
					 | 
				
			||||||
        furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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))
 | 
					 | 
				
			||||||
        ;
 | 
					 | 
				
			||||||
    FURI_CRITICAL_EXIT();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) {
 | 
					 | 
				
			||||||
    if(!furi_hal_console.alive) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FURI_CRITICAL_ENTER();
 | 
					 | 
				
			||||||
    // Transmit data
 | 
					 | 
				
			||||||
    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, 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))
 | 
					 | 
				
			||||||
        ;
 | 
					 | 
				
			||||||
    FURI_CRITICAL_EXIT();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_printf(const char format[], ...) {
 | 
					 | 
				
			||||||
    FuriString* string;
 | 
					 | 
				
			||||||
    va_list args;
 | 
					 | 
				
			||||||
    va_start(args, format);
 | 
					 | 
				
			||||||
    string = furi_string_alloc_vprintf(format, args);
 | 
					 | 
				
			||||||
    va_end(args);
 | 
					 | 
				
			||||||
    furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string));
 | 
					 | 
				
			||||||
    furi_string_free(string);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_puts(const char* data) {
 | 
					 | 
				
			||||||
    furi_hal_console_tx((const uint8_t*)data, strlen(data));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,37 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stddef.h>
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <stdio.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __cplusplus
 | 
					 | 
				
			||||||
extern "C" {
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_init();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_enable();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_disable();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Printf-like plain uart interface
 | 
					 | 
				
			||||||
 * @warning Will not work in ISR context
 | 
					 | 
				
			||||||
 * @param format 
 | 
					 | 
				
			||||||
 * @param ... 
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_console_puts(const char* data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __cplusplus
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -59,6 +59,12 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = {
 | 
				
			|||||||
    // LPTIMx
 | 
					    // LPTIMx
 | 
				
			||||||
    [FuriHalInterruptIdLpTim1] = LPTIM1_IRQn,
 | 
					    [FuriHalInterruptIdLpTim1] = LPTIM1_IRQn,
 | 
				
			||||||
    [FuriHalInterruptIdLpTim2] = LPTIM2_IRQn,
 | 
					    [FuriHalInterruptIdLpTim2] = LPTIM2_IRQn,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // UARTx
 | 
				
			||||||
 | 
					    [FuriHalInterruptIdUart1] = USART1_IRQn,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // LPUARTx
 | 
				
			||||||
 | 
					    [FuriHalInterruptIdLpUart1] = LPUART1_IRQn,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__attribute__((always_inline)) static inline void
 | 
					__attribute__((always_inline)) static inline void
 | 
				
			||||||
@ -329,3 +335,11 @@ void LPTIM1_IRQHandler() {
 | 
				
			|||||||
void LPTIM2_IRQHandler() {
 | 
					void LPTIM2_IRQHandler() {
 | 
				
			||||||
    furi_hal_interrupt_call(FuriHalInterruptIdLpTim2);
 | 
					    furi_hal_interrupt_call(FuriHalInterruptIdLpTim2);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void USART1_IRQHandler(void) {
 | 
				
			||||||
 | 
					    furi_hal_interrupt_call(FuriHalInterruptIdUart1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LPUART1_IRQHandler(void) {
 | 
				
			||||||
 | 
					    furi_hal_interrupt_call(FuriHalInterruptIdLpUart1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -49,6 +49,12 @@ typedef enum {
 | 
				
			|||||||
    FuriHalInterruptIdLpTim1,
 | 
					    FuriHalInterruptIdLpTim1,
 | 
				
			||||||
    FuriHalInterruptIdLpTim2,
 | 
					    FuriHalInterruptIdLpTim2,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //UARTx
 | 
				
			||||||
 | 
					    FuriHalInterruptIdUart1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //LPUARTx
 | 
				
			||||||
 | 
					    FuriHalInterruptIdLpUart1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Service value
 | 
					    // Service value
 | 
				
			||||||
    FuriHalInterruptIdMax,
 | 
					    FuriHalInterruptIdMax,
 | 
				
			||||||
} FuriHalInterruptId;
 | 
					} FuriHalInterruptId;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
#include <furi_hal_os.h>
 | 
					#include <furi_hal_os.h>
 | 
				
			||||||
#include <furi_hal_clock.h>
 | 
					#include <furi_hal_clock.h>
 | 
				
			||||||
#include <furi_hal_console.h>
 | 
					 | 
				
			||||||
#include <furi_hal_power.h>
 | 
					#include <furi_hal_power.h>
 | 
				
			||||||
#include <furi_hal_gpio.h>
 | 
					#include <furi_hal_gpio.h>
 | 
				
			||||||
#include <furi_hal_resources.h>
 | 
					#include <furi_hal_resources.h>
 | 
				
			||||||
@ -208,8 +207,8 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) {
 | 
					void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) {
 | 
				
			||||||
    UNUSED(xTask);
 | 
					    UNUSED(xTask);
 | 
				
			||||||
    furi_hal_console_puts("\r\n\r\n stack overflow in ");
 | 
					    furi_log_puts("\r\n\r\n stack overflow in ");
 | 
				
			||||||
    furi_hal_console_puts(pcTaskName);
 | 
					    furi_log_puts(pcTaskName);
 | 
				
			||||||
    furi_hal_console_puts("\r\n\r\n");
 | 
					    furi_log_puts("\r\n\r\n");
 | 
				
			||||||
    furi_crash("StackOverflow");
 | 
					    furi_crash("StackOverflow");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
#include <furi_hal_bt.h>
 | 
					#include <furi_hal_bt.h>
 | 
				
			||||||
#include <furi_hal_vibro.h>
 | 
					#include <furi_hal_vibro.h>
 | 
				
			||||||
#include <furi_hal_resources.h>
 | 
					#include <furi_hal_resources.h>
 | 
				
			||||||
#include <furi_hal_uart.h>
 | 
					#include <furi_hal_serial_control.h>
 | 
				
			||||||
#include <furi_hal_rtc.h>
 | 
					#include <furi_hal_rtc.h>
 | 
				
			||||||
#include <furi_hal_debug.h>
 | 
					#include <furi_hal_debug.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -178,14 +178,12 @@ static inline void furi_hal_power_light_sleep() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static inline void furi_hal_power_suspend_aux_periphs() {
 | 
					static inline void furi_hal_power_suspend_aux_periphs() {
 | 
				
			||||||
    // Disable USART
 | 
					    // Disable USART
 | 
				
			||||||
    furi_hal_uart_suspend(FuriHalUartIdUSART1);
 | 
					    furi_hal_serial_control_suspend();
 | 
				
			||||||
    furi_hal_uart_suspend(FuriHalUartIdLPUART1);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void furi_hal_power_resume_aux_periphs() {
 | 
					static inline void furi_hal_power_resume_aux_periphs() {
 | 
				
			||||||
    // Re-enable USART
 | 
					    // Re-enable USART
 | 
				
			||||||
    furi_hal_uart_resume(FuriHalUartIdUSART1);
 | 
					    furi_hal_serial_control_resume();
 | 
				
			||||||
    furi_hal_uart_resume(FuriHalUartIdLPUART1);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void furi_hal_power_deep_sleep() {
 | 
					static inline void furi_hal_power_deep_sleep() {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
#include <furi_hal_rtc.h>
 | 
					#include <furi_hal_rtc.h>
 | 
				
			||||||
#include <furi_hal_light.h>
 | 
					#include <furi_hal_light.h>
 | 
				
			||||||
#include <furi_hal_debug.h>
 | 
					#include <furi_hal_debug.h>
 | 
				
			||||||
 | 
					#include <furi_hal_serial_control.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stm32wbxx_ll_pwr.h>
 | 
					#include <stm32wbxx_ll_pwr.h>
 | 
				
			||||||
#include <stm32wbxx_ll_bus.h>
 | 
					#include <stm32wbxx_ll_bus.h>
 | 
				
			||||||
@ -34,7 +35,9 @@ typedef struct {
 | 
				
			|||||||
    FuriHalRtcLocaleUnits locale_units : 1;
 | 
					    FuriHalRtcLocaleUnits locale_units : 1;
 | 
				
			||||||
    FuriHalRtcLocaleTimeFormat locale_timeformat : 1;
 | 
					    FuriHalRtcLocaleTimeFormat locale_timeformat : 1;
 | 
				
			||||||
    FuriHalRtcLocaleDateFormat locale_dateformat : 2;
 | 
					    FuriHalRtcLocaleDateFormat locale_dateformat : 2;
 | 
				
			||||||
    uint8_t reserved : 6;
 | 
					    FuriHalRtcLogDevice log_device : 2;
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate log_baud_rate : 3;
 | 
				
			||||||
 | 
					    uint8_t reserved : 1;
 | 
				
			||||||
} SystemReg;
 | 
					} SystemReg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch");
 | 
					_Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch");
 | 
				
			||||||
@ -51,6 +54,24 @@ static const uint8_t furi_hal_rtc_days_per_month[2][FURI_HAL_RTC_MONTHS_COUNT] =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366};
 | 
					static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const FuriHalSerialId furi_hal_rtc_log_devices[] = {
 | 
				
			||||||
 | 
					    [FuriHalRtcLogDeviceUsart] = FuriHalSerialIdUsart,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogDeviceLpuart] = FuriHalSerialIdLpuart,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogDeviceReserved] = FuriHalSerialIdMax,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogDeviceNone] = FuriHalSerialIdMax,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint32_t furi_hal_rtc_log_baud_rates[] = {
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate230400] = 230400,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate9600] = 9600,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate38400] = 38400,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate57600] = 57600,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate115200] = 115200,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate460800] = 460800,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate921600] = 921600,
 | 
				
			||||||
 | 
					    [FuriHalRtcLogBaudRate1843200] = 1843200,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void furi_hal_rtc_reset() {
 | 
					static void furi_hal_rtc_reset() {
 | 
				
			||||||
    LL_RCC_ForceBackupDomainReset();
 | 
					    LL_RCC_ForceBackupDomainReset();
 | 
				
			||||||
    LL_RCC_ReleaseBackupDomainReset();
 | 
					    LL_RCC_ReleaseBackupDomainReset();
 | 
				
			||||||
@ -153,6 +174,9 @@ void furi_hal_rtc_init() {
 | 
				
			|||||||
    LL_RTC_Init(RTC, &RTC_InitStruct);
 | 
					    LL_RTC_Init(RTC, &RTC_InitStruct);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_log_set_level(furi_hal_rtc_get_log_level());
 | 
					    furi_log_set_level(furi_hal_rtc_get_log_level());
 | 
				
			||||||
 | 
					    furi_hal_serial_control_set_logging_config(
 | 
				
			||||||
 | 
					        furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()],
 | 
				
			||||||
 | 
					        furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    FURI_LOG_I(TAG, "Init OK");
 | 
					    FURI_LOG_I(TAG, "Init OK");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -199,6 +223,40 @@ uint8_t furi_hal_rtc_get_log_level() {
 | 
				
			|||||||
    return data->log_level;
 | 
					    return data->log_level;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device) {
 | 
				
			||||||
 | 
					    uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
 | 
				
			||||||
 | 
					    SystemReg* data = (SystemReg*)&data_reg;
 | 
				
			||||||
 | 
					    data->log_device = device;
 | 
				
			||||||
 | 
					    furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_control_set_logging_config(
 | 
				
			||||||
 | 
					        furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()],
 | 
				
			||||||
 | 
					        furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FuriHalRtcLogDevice furi_hal_rtc_get_log_device() {
 | 
				
			||||||
 | 
					    uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
 | 
				
			||||||
 | 
					    SystemReg* data = (SystemReg*)&data_reg;
 | 
				
			||||||
 | 
					    return data->log_device;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate) {
 | 
				
			||||||
 | 
					    uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
 | 
				
			||||||
 | 
					    SystemReg* data = (SystemReg*)&data_reg;
 | 
				
			||||||
 | 
					    data->log_baud_rate = baud_rate;
 | 
				
			||||||
 | 
					    furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_control_set_logging_config(
 | 
				
			||||||
 | 
					        furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()],
 | 
				
			||||||
 | 
					        furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate() {
 | 
				
			||||||
 | 
					    uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
 | 
				
			||||||
 | 
					    SystemReg* data = (SystemReg*)&data_reg;
 | 
				
			||||||
 | 
					    return data->log_baud_rate;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) {
 | 
					void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) {
 | 
				
			||||||
    uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
 | 
					    uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
 | 
				
			||||||
    SystemReg* data = (SystemReg*)&data_reg;
 | 
					    SystemReg* data = (SystemReg*)&data_reg;
 | 
				
			||||||
 | 
				
			|||||||
@ -64,32 +64,50 @@ typedef enum {
 | 
				
			|||||||
} FuriHalRtcRegister;
 | 
					} FuriHalRtcRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    FuriHalRtcLocaleUnitsMetric = 0, /**< Metric measurement units */
 | 
					    FuriHalRtcLocaleUnitsMetric = 0x0, /**< Metric measurement units */
 | 
				
			||||||
    FuriHalRtcLocaleUnitsImperial = 1, /**< Imperial measurement units */
 | 
					    FuriHalRtcLocaleUnitsImperial = 0x1, /**< Imperial measurement units */
 | 
				
			||||||
} FuriHalRtcLocaleUnits;
 | 
					} FuriHalRtcLocaleUnits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    FuriHalRtcLocaleTimeFormat24h = 0, /**< 24-hour format */
 | 
					    FuriHalRtcLocaleTimeFormat24h = 0x0, /**< 24-hour format */
 | 
				
			||||||
    FuriHalRtcLocaleTimeFormat12h = 1, /**< 12-hour format */
 | 
					    FuriHalRtcLocaleTimeFormat12h = 0x1, /**< 12-hour format */
 | 
				
			||||||
} FuriHalRtcLocaleTimeFormat;
 | 
					} FuriHalRtcLocaleTimeFormat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    FuriHalRtcLocaleDateFormatDMY = 0, /**< Day/Month/Year */
 | 
					    FuriHalRtcLocaleDateFormatDMY = 0x0, /**< Day/Month/Year */
 | 
				
			||||||
    FuriHalRtcLocaleDateFormatMDY = 1, /**< Month/Day/Year */
 | 
					    FuriHalRtcLocaleDateFormatMDY = 0x1, /**< Month/Day/Year */
 | 
				
			||||||
    FuriHalRtcLocaleDateFormatYMD = 2, /**< Year/Month/Day */
 | 
					    FuriHalRtcLocaleDateFormatYMD = 0x2, /**< Year/Month/Day */
 | 
				
			||||||
} FuriHalRtcLocaleDateFormat;
 | 
					} FuriHalRtcLocaleDateFormat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    FuriHalRtcLogDeviceUsart = 0x0, /**< Default: USART */
 | 
				
			||||||
 | 
					    FuriHalRtcLogDeviceLpuart = 0x1, /**< Default: LPUART */
 | 
				
			||||||
 | 
					    FuriHalRtcLogDeviceReserved = 0x2, /**< Reserved for future use */
 | 
				
			||||||
 | 
					    FuriHalRtcLogDeviceNone = 0x3, /**< None, disable serial logging */
 | 
				
			||||||
 | 
					} FuriHalRtcLogDevice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate230400 = 0x0, /**< 230400 baud */
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate9600 = 0x1, /**< 9600 baud */
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate38400 = 0x2, /**< 38400 baud */
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate57600 = 0x3, /**< 57600 baud */
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate115200 = 0x4, /**< 115200 baud */
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate460800 = 0x5, /**< 460800 baud */
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate921600 = 0x6, /**< 921600 baud */
 | 
				
			||||||
 | 
					    FuriHalRtcLogBaudRate1843200 = 0x7, /**< 1843200 baud */
 | 
				
			||||||
 | 
					} FuriHalRtcLogBaudRate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Early initialization */
 | 
					/** Early initialization */
 | 
				
			||||||
void furi_hal_rtc_init_early();
 | 
					void furi_hal_rtc_init_early(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Early de-initialization */
 | 
					/** Early de-initialization */
 | 
				
			||||||
void furi_hal_rtc_deinit_early();
 | 
					void furi_hal_rtc_deinit_early(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Initialize RTC subsystem */
 | 
					/** Initialize RTC subsystem */
 | 
				
			||||||
void furi_hal_rtc_init();
 | 
					void furi_hal_rtc_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Force sync shadow registers */
 | 
					/** Force sync shadow registers */
 | 
				
			||||||
void furi_hal_rtc_sync_shadow();
 | 
					void furi_hal_rtc_sync_shadow(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Reset ALL RTC registers content */
 | 
					/** Reset ALL RTC registers content */
 | 
				
			||||||
void furi_hal_rtc_reset_registers();
 | 
					void furi_hal_rtc_reset_registers();
 | 
				
			||||||
@ -119,7 +137,31 @@ void furi_hal_rtc_set_log_level(uint8_t level);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     The Log Level value
 | 
					 * @return     The Log Level value
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint8_t furi_hal_rtc_get_log_level();
 | 
					uint8_t furi_hal_rtc_get_log_level(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Set logging device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  device  The device
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Get logging device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     The furi hal rtc log device.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriHalRtcLogDevice furi_hal_rtc_get_log_device(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Set logging baud rate
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  baud_rate  The baud rate
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Get logging baud rate
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     The furi hal rtc log baud rate.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set RTC Flag
 | 
					/** Set RTC Flag
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -151,7 +193,7 @@ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     The RTC boot mode.
 | 
					 * @return     The RTC boot mode.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
FuriHalRtcBootMode furi_hal_rtc_get_boot_mode();
 | 
					FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set Heap Track mode
 | 
					/** Set Heap Track mode
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -163,7 +205,7 @@ void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     The RTC heap track mode.
 | 
					 * @return     The RTC heap track mode.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode();
 | 
					FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set locale units
 | 
					/** Set locale units
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -175,7 +217,7 @@ void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     The RTC Locale Units.
 | 
					 * @return     The RTC Locale Units.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units();
 | 
					FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set RTC Locale Time Format
 | 
					/** Set RTC Locale Time Format
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -187,7 +229,7 @@ void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     The RTC Locale Time Format.
 | 
					 * @return     The RTC Locale Time Format.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat();
 | 
					FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set RTC Locale Date Format
 | 
					/** Set RTC Locale Date Format
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -199,7 +241,7 @@ void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     The RTC Locale Date Format
 | 
					 * @return     The RTC Locale Date Format
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat();
 | 
					FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set RTC Date Time
 | 
					/** Set RTC Date Time
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -231,7 +273,7 @@ void furi_hal_rtc_set_fault_data(uint32_t value);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     RTC Fault Data value
 | 
					 * @return     RTC Fault Data value
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint32_t furi_hal_rtc_get_fault_data();
 | 
					uint32_t furi_hal_rtc_get_fault_data(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Set Pin Fails count
 | 
					/** Set Pin Fails count
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -243,13 +285,13 @@ void furi_hal_rtc_set_pin_fails(uint32_t value);
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     Pin Fails Count
 | 
					 * @return     Pin Fails Count
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint32_t furi_hal_rtc_get_pin_fails();
 | 
					uint32_t furi_hal_rtc_get_pin_fails(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get UNIX Timestamp
 | 
					/** Get UNIX Timestamp
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return     Unix Timestamp in seconds from UNIX epoch start
 | 
					 * @return     Unix Timestamp in seconds from UNIX epoch start
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
uint32_t furi_hal_rtc_get_timestamp();
 | 
					uint32_t furi_hal_rtc_get_timestamp(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Convert DateTime to UNIX timestamp
 | 
					/** Convert DateTime to UNIX timestamp
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
							
								
								
									
										838
									
								
								targets/f7/furi_hal/furi_hal_serial.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										838
									
								
								targets/f7/furi_hal/furi_hal_serial.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,838 @@
 | 
				
			|||||||
 | 
					#include <furi_hal_serial.h>
 | 
				
			||||||
 | 
					#include "furi_hal_serial_types_i.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <stm32wbxx_ll_lpuart.h>
 | 
				
			||||||
 | 
					#include <stm32wbxx_ll_usart.h>
 | 
				
			||||||
 | 
					#include <stm32wbxx_ll_rcc.h>
 | 
				
			||||||
 | 
					#include <stm32wbxx_ll_dma.h>
 | 
				
			||||||
 | 
					#include <furi_hal_resources.h>
 | 
				
			||||||
 | 
					#include <furi_hal_interrupt.h>
 | 
				
			||||||
 | 
					#include <furi_hal_bus.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FURI_HAL_SERIAL_USART_OVERSAMPLING LL_USART_OVERSAMPLING_16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FURI_HAL_SERIAL_USART_DMA_INSTANCE (DMA1)
 | 
				
			||||||
 | 
					#define FURI_HAL_SERIAL_USART_DMA_CHANNEL (LL_DMA_CHANNEL_6)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FURI_HAL_SERIAL_LPUART_DMA_INSTANCE (DMA1)
 | 
				
			||||||
 | 
					#define FURI_HAL_SERIAL_LPUART_DMA_CHANNEL (LL_DMA_CHANNEL_7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    uint8_t* buffer_rx_ptr;
 | 
				
			||||||
 | 
					    size_t buffer_rx_index_write;
 | 
				
			||||||
 | 
					    size_t buffer_rx_index_read;
 | 
				
			||||||
 | 
					    bool enabled;
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle;
 | 
				
			||||||
 | 
					    FuriHalSerialAsyncRxCallback rx_byte_callback;
 | 
				
			||||||
 | 
					    FuriHalSerialDmaRxCallback rx_dma_callback;
 | 
				
			||||||
 | 
					    void* context;
 | 
				
			||||||
 | 
					} FuriHalSerial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_async_rx_configure(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialAsyncRxCallback callback,
 | 
				
			||||||
 | 
					    void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_usart_irq_callback(void* context) {
 | 
				
			||||||
 | 
					    UNUSED(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialRxEvent event = 0;
 | 
				
			||||||
 | 
					    // Notification flags
 | 
				
			||||||
 | 
					    if(USART1->ISR & USART_ISR_RXNE_RXFNE) {
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(USART1->ISR & USART_ISR_IDLE) {
 | 
				
			||||||
 | 
					        USART1->ICR = USART_ICR_IDLECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventIdle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Error flags
 | 
				
			||||||
 | 
					    if(USART1->ISR & USART_ISR_ORE) {
 | 
				
			||||||
 | 
					        USART1->ICR = USART_ICR_ORECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventOverrunError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(USART1->ISR & USART_ISR_NE) {
 | 
				
			||||||
 | 
					        USART1->ICR = USART_ICR_NECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventNoiseError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(USART1->ISR & USART_ISR_FE) {
 | 
				
			||||||
 | 
					        USART1->ICR = USART_ICR_FECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventFrameError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(USART1->ISR & USART_ISR_PE) {
 | 
				
			||||||
 | 
					        USART1->ICR = USART_ICR_PECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventFrameError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL) {
 | 
				
			||||||
 | 
					        if(furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback(
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdUsart].handle,
 | 
				
			||||||
 | 
					                event,
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdUsart].context);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback(
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdUsart].handle,
 | 
				
			||||||
 | 
					                event,
 | 
				
			||||||
 | 
					                furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart),
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdUsart].context);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_usart_dma_rx_isr(void* context) {
 | 
				
			||||||
 | 
					    UNUSED(context);
 | 
				
			||||||
 | 
					#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) {
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write =
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_DMA_BUFFER_SIZE -
 | 
				
			||||||
 | 
					            LL_DMA_GetDataLength(
 | 
				
			||||||
 | 
					                FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        if((furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read >
 | 
				
			||||||
 | 
					            furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write) ||
 | 
				
			||||||
 | 
					           (furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read <
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) {
 | 
				
			||||||
 | 
					            if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) {
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback(
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdUsart].handle,
 | 
				
			||||||
 | 
					                    FuriHalSerialRxEventData,
 | 
				
			||||||
 | 
					                    furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart),
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdUsart].context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) {
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read <
 | 
				
			||||||
 | 
					           FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) {
 | 
				
			||||||
 | 
					            if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) {
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback(
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdUsart].handle,
 | 
				
			||||||
 | 
					                    FuriHalSerialRxEventData,
 | 
				
			||||||
 | 
					                    furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart),
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdUsart].context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#error Update this code. Would you kindly?
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_usart_init_dma_rx(void) {
 | 
				
			||||||
 | 
					    /* USART1_RX_DMA Init */
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL);
 | 
				
			||||||
 | 
					    furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = 0;
 | 
				
			||||||
 | 
					    furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read = 0;
 | 
				
			||||||
 | 
					    furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
 | 
				
			||||||
 | 
					    LL_DMA_SetMemoryAddress(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        (uint32_t)furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr);
 | 
				
			||||||
 | 
					    LL_DMA_SetPeriphAddress(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        (uint32_t) & (USART1->RDR));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_DMA_ConfigTransfer(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |
 | 
				
			||||||
 | 
					            LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE |
 | 
				
			||||||
 | 
					            LL_DMA_PRIORITY_HIGH);
 | 
				
			||||||
 | 
					    LL_DMA_SetDataLength(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
 | 
				
			||||||
 | 
					    LL_DMA_SetPeriphRequest(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_USART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        LL_DMAMUX_REQ_USART1_RX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, furi_hal_serial_usart_dma_rx_isr, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE))
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE))
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE))
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#error Update this code. Would you kindly?
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					    LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_DMA_EnableChannel(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					    LL_USART_EnableDMAReq_RX(USART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_USART_EnableIT_IDLE(USART1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_usart_deinit_dma_rx(void) {
 | 
				
			||||||
 | 
					    if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr != NULL) {
 | 
				
			||||||
 | 
					        LL_DMA_DisableChannel(
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        LL_USART_DisableDMAReq_RX(USART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LL_USART_DisableIT_IDLE(USART1);
 | 
				
			||||||
 | 
					        LL_DMA_DisableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        LL_DMA_DisableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LL_DMA_DeInit(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, NULL, NULL);
 | 
				
			||||||
 | 
					        free(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr);
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_usart_init(FuriHalSerialHandle* handle, uint32_t baud) {
 | 
				
			||||||
 | 
					    furi_hal_bus_enable(FuriHalBusUSART1);
 | 
				
			||||||
 | 
					    LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_gpio_init_ex(
 | 
				
			||||||
 | 
					        &gpio_usart_tx,
 | 
				
			||||||
 | 
					        GpioModeAltFunctionPushPull,
 | 
				
			||||||
 | 
					        GpioPullUp,
 | 
				
			||||||
 | 
					        GpioSpeedVeryHigh,
 | 
				
			||||||
 | 
					        GpioAltFn7USART1);
 | 
				
			||||||
 | 
					    furi_hal_gpio_init_ex(
 | 
				
			||||||
 | 
					        &gpio_usart_rx,
 | 
				
			||||||
 | 
					        GpioModeAltFunctionPushPull,
 | 
				
			||||||
 | 
					        GpioPullUp,
 | 
				
			||||||
 | 
					        GpioSpeedVeryHigh,
 | 
				
			||||||
 | 
					        GpioAltFn7USART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_USART_InitTypeDef USART_InitStruct;
 | 
				
			||||||
 | 
					    USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
 | 
				
			||||||
 | 
					    USART_InitStruct.BaudRate = baud;
 | 
				
			||||||
 | 
					    USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
 | 
				
			||||||
 | 
					    USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
 | 
				
			||||||
 | 
					    USART_InitStruct.Parity = LL_USART_PARITY_NONE;
 | 
				
			||||||
 | 
					    USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
 | 
				
			||||||
 | 
					    USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
 | 
				
			||||||
 | 
					    USART_InitStruct.OverSampling = FURI_HAL_SERIAL_USART_OVERSAMPLING;
 | 
				
			||||||
 | 
					    LL_USART_Init(USART1, &USART_InitStruct);
 | 
				
			||||||
 | 
					    LL_USART_EnableFIFO(USART1);
 | 
				
			||||||
 | 
					    LL_USART_ConfigAsyncMode(USART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_USART_Enable(USART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1))
 | 
				
			||||||
 | 
					        ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_set_br(handle, baud);
 | 
				
			||||||
 | 
					    LL_USART_DisableIT_ERROR(USART1);
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].enabled = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_lpuart_irq_callback(void* context) {
 | 
				
			||||||
 | 
					    UNUSED(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialRxEvent event = 0;
 | 
				
			||||||
 | 
					    // Notification flags
 | 
				
			||||||
 | 
					    if(LPUART1->ISR & USART_ISR_RXNE_RXFNE) {
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(LPUART1->ISR & USART_ISR_IDLE) {
 | 
				
			||||||
 | 
					        LPUART1->ICR = USART_ICR_IDLECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventIdle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Error flags
 | 
				
			||||||
 | 
					    if(LPUART1->ISR & USART_ISR_ORE) {
 | 
				
			||||||
 | 
					        LPUART1->ICR = USART_ICR_ORECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventOverrunError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(LPUART1->ISR & USART_ISR_NE) {
 | 
				
			||||||
 | 
					        LPUART1->ICR = USART_ICR_NECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventNoiseError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(LPUART1->ISR & USART_ISR_FE) {
 | 
				
			||||||
 | 
					        LPUART1->ICR = USART_ICR_FECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventFrameError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(LPUART1->ISR & USART_ISR_PE) {
 | 
				
			||||||
 | 
					        LPUART1->ICR = USART_ICR_PECF;
 | 
				
			||||||
 | 
					        event |= FuriHalSerialRxEventFrameError;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL) {
 | 
				
			||||||
 | 
					        if(furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback(
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdLpuart].handle,
 | 
				
			||||||
 | 
					                event,
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdLpuart].context);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback(
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdLpuart].handle,
 | 
				
			||||||
 | 
					                event,
 | 
				
			||||||
 | 
					                furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart),
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdLpuart].context);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_lpuart_dma_rx_isr(void* context) {
 | 
				
			||||||
 | 
					    UNUSED(context);
 | 
				
			||||||
 | 
					#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) {
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write =
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_DMA_BUFFER_SIZE -
 | 
				
			||||||
 | 
					            LL_DMA_GetDataLength(
 | 
				
			||||||
 | 
					                FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        if((furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read >
 | 
				
			||||||
 | 
					            furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write) ||
 | 
				
			||||||
 | 
					           (furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read <
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) {
 | 
				
			||||||
 | 
					            if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) {
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback(
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdLpuart].handle,
 | 
				
			||||||
 | 
					                    FuriHalSerialRxEventData,
 | 
				
			||||||
 | 
					                    furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart),
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdLpuart].context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) {
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read <
 | 
				
			||||||
 | 
					           FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) {
 | 
				
			||||||
 | 
					            if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) {
 | 
				
			||||||
 | 
					                furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback(
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdLpuart].handle,
 | 
				
			||||||
 | 
					                    FuriHalSerialRxEventData,
 | 
				
			||||||
 | 
					                    furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart),
 | 
				
			||||||
 | 
					                    furi_hal_serial[FuriHalSerialIdLpuart].context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#error Update this code. Would you kindly?
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_lpuart_init_dma_rx(void) {
 | 
				
			||||||
 | 
					    /* LPUART1_RX_DMA Init */
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL);
 | 
				
			||||||
 | 
					    furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = 0;
 | 
				
			||||||
 | 
					    furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read = 0;
 | 
				
			||||||
 | 
					    furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
 | 
				
			||||||
 | 
					    LL_DMA_SetMemoryAddress(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        (uint32_t)furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr);
 | 
				
			||||||
 | 
					    LL_DMA_SetPeriphAddress(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        (uint32_t) & (LPUART1->RDR));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_DMA_ConfigTransfer(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |
 | 
				
			||||||
 | 
					            LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE |
 | 
				
			||||||
 | 
					            LL_DMA_PRIORITY_HIGH);
 | 
				
			||||||
 | 
					    LL_DMA_SetDataLength(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
 | 
				
			||||||
 | 
					    LL_DMA_SetPeriphRequest(
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
 | 
				
			||||||
 | 
					        FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
 | 
				
			||||||
 | 
					        LL_DMAMUX_REQ_LPUART1_RX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, furi_hal_serial_lpuart_dma_rx_isr, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE))
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE))
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					    if(LL_DMA_IsActiveFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE))
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#error Update this code. Would you kindly?
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					    LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_DMA_EnableChannel(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					    LL_USART_EnableDMAReq_RX(LPUART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_USART_EnableIT_IDLE(LPUART1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_lpuart_deinit_dma_rx(void) {
 | 
				
			||||||
 | 
					    if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr != NULL) {
 | 
				
			||||||
 | 
					        LL_DMA_DisableChannel(
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        LL_USART_DisableDMAReq_RX(LPUART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LL_USART_DisableIT_IDLE(LPUART1);
 | 
				
			||||||
 | 
					        LL_DMA_DisableIT_TC(
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        LL_DMA_DisableIT_HT(
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					        LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LL_DMA_DeInit(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					        furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, NULL, NULL);
 | 
				
			||||||
 | 
					        free(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr);
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_lpuart_init(FuriHalSerialHandle* handle, uint32_t baud) {
 | 
				
			||||||
 | 
					    furi_hal_bus_enable(FuriHalBusLPUART1);
 | 
				
			||||||
 | 
					    LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_gpio_init_ex(
 | 
				
			||||||
 | 
					        &gpio_ext_pc0,
 | 
				
			||||||
 | 
					        GpioModeAltFunctionPushPull,
 | 
				
			||||||
 | 
					        GpioPullUp,
 | 
				
			||||||
 | 
					        GpioSpeedVeryHigh,
 | 
				
			||||||
 | 
					        GpioAltFn8LPUART1);
 | 
				
			||||||
 | 
					    furi_hal_gpio_init_ex(
 | 
				
			||||||
 | 
					        &gpio_ext_pc1,
 | 
				
			||||||
 | 
					        GpioModeAltFunctionPushPull,
 | 
				
			||||||
 | 
					        GpioPullUp,
 | 
				
			||||||
 | 
					        GpioSpeedVeryHigh,
 | 
				
			||||||
 | 
					        GpioAltFn8LPUART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_LPUART_InitTypeDef LPUART_InitStruct;
 | 
				
			||||||
 | 
					    LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
 | 
				
			||||||
 | 
					    LPUART_InitStruct.BaudRate = baud;
 | 
				
			||||||
 | 
					    LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
 | 
				
			||||||
 | 
					    LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
 | 
				
			||||||
 | 
					    LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
 | 
				
			||||||
 | 
					    LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
 | 
				
			||||||
 | 
					    LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
 | 
				
			||||||
 | 
					    LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
 | 
				
			||||||
 | 
					    LL_LPUART_EnableFIFO(LPUART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LL_LPUART_Enable(LPUART1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1))
 | 
				
			||||||
 | 
					        ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_set_br(handle, baud);
 | 
				
			||||||
 | 
					    LL_LPUART_DisableIT_ERROR(LPUART1);
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].enabled = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        furi_hal_serial_lpuart_init(handle, baud);
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        furi_hal_serial_usart_init(handle, baud);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) {
 | 
				
			||||||
 | 
					    uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
 | 
				
			||||||
 | 
					    uint32_t divisor = (uartclk / baud);
 | 
				
			||||||
 | 
					    uint32_t prescaler = 0;
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(FURI_HAL_SERIAL_USART_OVERSAMPLING == LL_USART_OVERSAMPLING_16) {
 | 
				
			||||||
 | 
					            divisor = (divisor / 16) >> 12;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            divisor = (divisor / 8) >> 12;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(divisor < 1) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV1;
 | 
				
			||||||
 | 
					        } else if(divisor < 2) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV2;
 | 
				
			||||||
 | 
					        } else if(divisor < 4) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV4;
 | 
				
			||||||
 | 
					        } else if(divisor < 6) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV6;
 | 
				
			||||||
 | 
					        } else if(divisor < 8) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV8;
 | 
				
			||||||
 | 
					        } else if(divisor < 10) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV10;
 | 
				
			||||||
 | 
					        } else if(divisor < 12) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV12;
 | 
				
			||||||
 | 
					        } else if(divisor < 16) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV16;
 | 
				
			||||||
 | 
					        } else if(divisor < 32) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV32;
 | 
				
			||||||
 | 
					        } else if(divisor < 64) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV64;
 | 
				
			||||||
 | 
					        } else if(divisor < 128) {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV128;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            prescaler = LL_USART_PRESCALER_DIV256;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        divisor >>= 12;
 | 
				
			||||||
 | 
					        if(divisor < 1) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV1;
 | 
				
			||||||
 | 
					        } else if(divisor < 2) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV2;
 | 
				
			||||||
 | 
					        } else if(divisor < 4) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV4;
 | 
				
			||||||
 | 
					        } else if(divisor < 6) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV6;
 | 
				
			||||||
 | 
					        } else if(divisor < 8) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV8;
 | 
				
			||||||
 | 
					        } else if(divisor < 10) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV10;
 | 
				
			||||||
 | 
					        } else if(divisor < 12) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV12;
 | 
				
			||||||
 | 
					        } else if(divisor < 16) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV16;
 | 
				
			||||||
 | 
					        } else if(divisor < 32) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV32;
 | 
				
			||||||
 | 
					        } else if(divisor < 64) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV64;
 | 
				
			||||||
 | 
					        } else if(divisor < 128) {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV128;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            prescaler = LL_LPUART_PRESCALER_DIV256;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return prescaler;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    uint32_t prescaler = furi_hal_serial_get_prescaler(handle, baud);
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(LL_USART_IsEnabled(USART1)) {
 | 
				
			||||||
 | 
					            // Wait for transfer complete flag
 | 
				
			||||||
 | 
					            while(!LL_USART_IsActiveFlag_TC(USART1))
 | 
				
			||||||
 | 
					                ;
 | 
				
			||||||
 | 
					            LL_USART_Disable(USART1);
 | 
				
			||||||
 | 
					            uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
 | 
				
			||||||
 | 
					            LL_USART_SetPrescaler(USART1, prescaler);
 | 
				
			||||||
 | 
					            LL_USART_SetBaudRate(
 | 
				
			||||||
 | 
					                USART1, uartclk, prescaler, FURI_HAL_SERIAL_USART_OVERSAMPLING, baud);
 | 
				
			||||||
 | 
					            LL_USART_Enable(USART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        if(LL_LPUART_IsEnabled(LPUART1)) {
 | 
				
			||||||
 | 
					            // Wait for transfer complete flag
 | 
				
			||||||
 | 
					            while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
 | 
				
			||||||
 | 
					                ;
 | 
				
			||||||
 | 
					            LL_LPUART_Disable(LPUART1);
 | 
				
			||||||
 | 
					            uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE);
 | 
				
			||||||
 | 
					            LL_LPUART_SetPrescaler(LPUART1, prescaler);
 | 
				
			||||||
 | 
					            LL_LPUART_SetBaudRate(LPUART1, uartclk, prescaler, baud);
 | 
				
			||||||
 | 
					            LL_LPUART_Enable(LPUART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_deinit(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    furi_hal_serial_async_rx_configure(handle, NULL, NULL);
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) {
 | 
				
			||||||
 | 
					            furi_hal_bus_disable(FuriHalBusUSART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(LL_USART_IsEnabled(USART1)) {
 | 
				
			||||||
 | 
					            LL_USART_Disable(USART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        furi_hal_serial_usart_deinit_dma_rx();
 | 
				
			||||||
 | 
					        furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
				
			||||||
 | 
					        furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) {
 | 
				
			||||||
 | 
					            furi_hal_bus_disable(FuriHalBusLPUART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(LL_LPUART_IsEnabled(LPUART1)) {
 | 
				
			||||||
 | 
					            LL_LPUART_Disable(LPUART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        furi_hal_serial_lpuart_deinit_dma_rx();
 | 
				
			||||||
 | 
					        furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
				
			||||||
 | 
					        furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        furi_crash();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].enabled = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_suspend(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdLpuart && LL_LPUART_IsEnabled(LPUART1)) {
 | 
				
			||||||
 | 
					        LL_LPUART_Disable(LPUART1);
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdUsart && LL_USART_IsEnabled(USART1)) {
 | 
				
			||||||
 | 
					        LL_USART_Disable(USART1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].enabled = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_resume(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    if(!furi_hal_serial[handle->id].enabled) {
 | 
				
			||||||
 | 
					        if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					            LL_LPUART_Enable(LPUART1);
 | 
				
			||||||
 | 
					        } else if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					            LL_USART_Enable(USART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        furi_hal_serial[handle->id].enabled = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(LL_USART_IsEnabled(USART1) == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while(buffer_size > 0) {
 | 
				
			||||||
 | 
					            while(!LL_USART_IsActiveFlag_TXE(USART1))
 | 
				
			||||||
 | 
					                ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LL_USART_TransmitData8(USART1, *buffer);
 | 
				
			||||||
 | 
					            buffer++;
 | 
				
			||||||
 | 
					            buffer_size--;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        if(LL_LPUART_IsEnabled(LPUART1) == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while(buffer_size > 0) {
 | 
				
			||||||
 | 
					            while(!LL_LPUART_IsActiveFlag_TXE(LPUART1))
 | 
				
			||||||
 | 
					                ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LL_LPUART_TransmitData8(LPUART1, *buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            buffer++;
 | 
				
			||||||
 | 
					            buffer_size--;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(LL_USART_IsEnabled(USART1) == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while(!LL_USART_IsActiveFlag_TC(USART1))
 | 
				
			||||||
 | 
					            ;
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        if(LL_LPUART_IsEnabled(LPUART1) == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
 | 
				
			||||||
 | 
					            ;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_event_init(FuriHalSerialHandle* handle, bool report_errors) {
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        LL_USART_EnableIT_IDLE(USART1);
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        LL_LPUART_EnableIT_IDLE(LPUART1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(report_errors) {
 | 
				
			||||||
 | 
					        if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					            LL_USART_EnableIT_ERROR(USART1);
 | 
				
			||||||
 | 
					        } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					            LL_LPUART_EnableIT_ERROR(LPUART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_event_deinit(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(LL_USART_IsEnabledIT_IDLE(USART1)) LL_USART_DisableIT_IDLE(USART1);
 | 
				
			||||||
 | 
					        if(LL_USART_IsEnabledIT_ERROR(USART1)) LL_USART_DisableIT_ERROR(USART1);
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        if(LL_LPUART_IsEnabledIT_IDLE(LPUART1)) LL_LPUART_DisableIT_IDLE(LPUART1);
 | 
				
			||||||
 | 
					        if(LL_LPUART_IsEnabledIT_ERROR(LPUART1)) LL_LPUART_DisableIT_ERROR(LPUART1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_async_rx_configure(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialAsyncRxCallback callback,
 | 
				
			||||||
 | 
					    void* context) {
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial_usart_deinit_dma_rx();
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(
 | 
				
			||||||
 | 
					                FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL);
 | 
				
			||||||
 | 
					            LL_USART_EnableIT_RXNE_RXFNE(USART1);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL);
 | 
				
			||||||
 | 
					            furi_hal_serial_usart_deinit_dma_rx();
 | 
				
			||||||
 | 
					            LL_USART_DisableIT_RXNE_RXFNE(USART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        if(callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial_lpuart_deinit_dma_rx();
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(
 | 
				
			||||||
 | 
					                FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL);
 | 
				
			||||||
 | 
					            LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL);
 | 
				
			||||||
 | 
					            furi_hal_serial_lpuart_deinit_dma_rx();
 | 
				
			||||||
 | 
					            LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].rx_byte_callback = callback;
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].handle = handle;
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].rx_dma_callback = NULL;
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].context = context;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_async_rx_start(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialAsyncRxCallback callback,
 | 
				
			||||||
 | 
					    void* context,
 | 
				
			||||||
 | 
					    bool report_errors) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    furi_check(callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_event_init(handle, report_errors);
 | 
				
			||||||
 | 
					    furi_hal_serial_async_rx_configure(handle, callback, context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Assign different functions to different UARTs
 | 
				
			||||||
 | 
					    furi_check(
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback !=
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    furi_hal_serial_event_deinit(handle);
 | 
				
			||||||
 | 
					    furi_hal_serial_async_rx_configure(handle, NULL, NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(FURI_IS_IRQ_MODE());
 | 
				
			||||||
 | 
					    furi_assert(handle->id < FuriHalSerialIdMax);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        return LL_USART_ReceiveData8(USART1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return LL_LPUART_ReceiveData8(LPUART1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch) {
 | 
				
			||||||
 | 
					    size_t dma_remain = 0;
 | 
				
			||||||
 | 
					    if(ch == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        dma_remain = LL_DMA_GetDataLength(
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					    } else if(ch == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        dma_remain = LL_DMA_GetDataLength(
 | 
				
			||||||
 | 
					            FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        furi_crash();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial[ch].buffer_rx_index_write = FURI_HAL_SERIAL_DMA_BUFFER_SIZE - dma_remain;
 | 
				
			||||||
 | 
					    if(furi_hal_serial[ch].buffer_rx_index_write >= furi_hal_serial[ch].buffer_rx_index_read) {
 | 
				
			||||||
 | 
					        return furi_hal_serial[ch].buffer_rx_index_write -
 | 
				
			||||||
 | 
					               furi_hal_serial[ch].buffer_rx_index_read;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return FURI_HAL_SERIAL_DMA_BUFFER_SIZE - furi_hal_serial[ch].buffer_rx_index_read +
 | 
				
			||||||
 | 
					               furi_hal_serial[ch].buffer_rx_index_write;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint8_t furi_hal_serial_dma_rx_read_byte(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    uint8_t data = 0;
 | 
				
			||||||
 | 
					    data =
 | 
				
			||||||
 | 
					        furi_hal_serial[handle->id].buffer_rx_ptr[furi_hal_serial[handle->id].buffer_rx_index_read];
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].buffer_rx_index_read++;
 | 
				
			||||||
 | 
					    if(furi_hal_serial[handle->id].buffer_rx_index_read >= FURI_HAL_SERIAL_DMA_BUFFER_SIZE) {
 | 
				
			||||||
 | 
					        furi_hal_serial[handle->id].buffer_rx_index_read = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len) {
 | 
				
			||||||
 | 
					    furi_check(FURI_IS_IRQ_MODE());
 | 
				
			||||||
 | 
					    furi_assert(furi_hal_serial[handle->id].buffer_rx_ptr != NULL);
 | 
				
			||||||
 | 
					    size_t i = 0;
 | 
				
			||||||
 | 
					    size_t available = furi_hal_serial_dma_bytes_available(handle->id);
 | 
				
			||||||
 | 
					    if(available < len) {
 | 
				
			||||||
 | 
					        len = available;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for(i = 0; i < len; i++) {
 | 
				
			||||||
 | 
					        data[i] = furi_hal_serial_dma_rx_read_byte(handle);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return i;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_dma_configure(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialDmaRxCallback callback,
 | 
				
			||||||
 | 
					    void* context) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(handle->id == FuriHalSerialIdUsart) {
 | 
				
			||||||
 | 
					        if(callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial_usart_init_dma_rx();
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(
 | 
				
			||||||
 | 
					                FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            LL_USART_DisableIT_RXNE_RXFNE(USART1);
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL);
 | 
				
			||||||
 | 
					            furi_hal_serial_usart_deinit_dma_rx();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if(handle->id == FuriHalSerialIdLpuart) {
 | 
				
			||||||
 | 
					        if(callback) {
 | 
				
			||||||
 | 
					            furi_hal_serial_lpuart_init_dma_rx();
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(
 | 
				
			||||||
 | 
					                FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
 | 
				
			||||||
 | 
					            furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL);
 | 
				
			||||||
 | 
					            furi_hal_serial_lpuart_deinit_dma_rx();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].rx_byte_callback = NULL;
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].handle = handle;
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].rx_dma_callback = callback;
 | 
				
			||||||
 | 
					    furi_hal_serial[handle->id].context = context;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_dma_rx_start(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialDmaRxCallback callback,
 | 
				
			||||||
 | 
					    void* context,
 | 
				
			||||||
 | 
					    bool report_errors) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    furi_check(callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_serial_event_init(handle, report_errors);
 | 
				
			||||||
 | 
					    furi_hal_serial_dma_configure(handle, callback, context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Assign different functions to different UARTs
 | 
				
			||||||
 | 
					    furi_check(
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback !=
 | 
				
			||||||
 | 
					        furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					    furi_hal_serial_event_deinit(handle);
 | 
				
			||||||
 | 
					    furi_hal_serial_dma_configure(handle, NULL, NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										189
									
								
								targets/f7/furi_hal/furi_hal_serial.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								targets/f7/furi_hal/furi_hal_serial.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @file furi_hal_serial.h
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Serial HAL API
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "furi_hal_serial_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Initialize Serial
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Configures GPIO, configures and enables transceiver.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 * @param      baud    baud rate
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** De-initialize Serial
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Configures GPIO to analog, clears callback and callback context, disables
 | 
				
			||||||
 | 
					 * hardware
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_deinit(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Suspend operation
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Suspend hardware, settings and callbacks are preserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_suspend(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Resume operation
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Resumes hardware from suspended state
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_resume(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Changes baud rate
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 * @param      baud    baud rate
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Transmits data in semi-blocking mode
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Fills transmission pipe with data, returns as soon as all bytes from buffer
 | 
				
			||||||
 | 
					 * are in the pipe.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Real transmission will be completed later. Use
 | 
				
			||||||
 | 
					 * `furi_hal_serial_tx_wait_complete` to wait for completion if you need it.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle       Serial handle
 | 
				
			||||||
 | 
					 * @param      buffer       data
 | 
				
			||||||
 | 
					 * @param      buffer_size  data size (in bytes)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Wait until transmission is completed
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Ensures that all data has been sent.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Serial RX events */
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    FuriHalSerialRxEventData = (1 << 0), /**< Data: new data available */
 | 
				
			||||||
 | 
					    FuriHalSerialRxEventIdle = (1 << 1), /**< Idle: bus idle detected */
 | 
				
			||||||
 | 
					    FuriHalSerialRxEventFrameError = (1 << 2), /**< Framing Error: incorrect frame detected */
 | 
				
			||||||
 | 
					    FuriHalSerialRxEventNoiseError = (1 << 3), /**< Noise Error: noise on the line detected */
 | 
				
			||||||
 | 
					    FuriHalSerialRxEventOverrunError = (1 << 4), /**< Overrun Error: no space for received data */
 | 
				
			||||||
 | 
					} FuriHalSerialRxEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Receive callback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @warning    Callback will be called in interrupt context, ensure thread
 | 
				
			||||||
 | 
					 *             safety on your side.
 | 
				
			||||||
 | 
					 * @param      handle   Serial handle
 | 
				
			||||||
 | 
					 * @param      event    FuriHalSerialRxEvent
 | 
				
			||||||
 | 
					 * @param      context  Callback context provided earlier
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef void (*FuriHalSerialAsyncRxCallback)(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialRxEvent event,
 | 
				
			||||||
 | 
					    void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Start and sets Serial Receive callback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @warning    Callback will be called in interrupt context, ensure thread
 | 
				
			||||||
 | 
					 *             safety on your side
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle         Serial handle
 | 
				
			||||||
 | 
					 * @param      callback       callback pointer
 | 
				
			||||||
 | 
					 * @param      context        callback context
 | 
				
			||||||
 | 
					 * @param[in]  report_errors  report RX error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_async_rx_start(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialAsyncRxCallback callback,
 | 
				
			||||||
 | 
					    void* context,
 | 
				
			||||||
 | 
					    bool report_errors);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Stop Serial Receive
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle    Serial handle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Get data Serial receive
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @warning    This function must be called only from the callback
 | 
				
			||||||
 | 
					 *             FuriHalSerialAsyncRxCallback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     data
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* DMA based Serial API */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FURI_HAL_SERIAL_DMA_BUFFER_SIZE (256u)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Receive DMA callback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @warning    DMA Callback will be called in interrupt context, ensure thread
 | 
				
			||||||
 | 
					 *             safety on your side.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle    Serial handle
 | 
				
			||||||
 | 
					 * @param      event     FuriHalSerialDmaRxEvent
 | 
				
			||||||
 | 
					 * @param      data_len  Received data
 | 
				
			||||||
 | 
					 * @param      context   Callback context provided earlier
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef void (*FuriHalSerialDmaRxCallback)(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialRxEvent event,
 | 
				
			||||||
 | 
					    size_t data_len,
 | 
				
			||||||
 | 
					    void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Start and sets Serial event callback receive DMA
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle         Serial handle
 | 
				
			||||||
 | 
					 * @param      callback       callback pointer
 | 
				
			||||||
 | 
					 * @param      context        callback context
 | 
				
			||||||
 | 
					 * @param[in]  report_errors  report RX error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_dma_rx_start(
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle,
 | 
				
			||||||
 | 
					    FuriHalSerialDmaRxCallback callback,
 | 
				
			||||||
 | 
					    void* context,
 | 
				
			||||||
 | 
					    bool report_errors);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Stop Serial receive DMA
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Get data Serial receive DMA
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @warning    This function must be called only from the callback
 | 
				
			||||||
 | 
					 *             FuriHalSerialDmaRxCallback
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  Serial handle
 | 
				
			||||||
 | 
					 * @param      data    pointer to data buffer
 | 
				
			||||||
 | 
					 * @param      len     get data size (in bytes)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     size actual data receive (in bytes)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										233
									
								
								targets/f7/furi_hal/furi_hal_serial_control.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								targets/f7/furi_hal/furi_hal_serial_control.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,233 @@
 | 
				
			|||||||
 | 
					#include "furi_hal_serial_control.h"
 | 
				
			||||||
 | 
					#include "furi_hal_serial_types_i.h"
 | 
				
			||||||
 | 
					#include "furi_hal_serial.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					#include <toolbox/api_lock.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TAG "FuriHalSerialControl"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageTypeStop,
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageTypeSuspend,
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageTypeResume,
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageTypeAcquire,
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageTypeRelease,
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageTypeLogging,
 | 
				
			||||||
 | 
					} FuriHalSerialControlMessageType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageType type;
 | 
				
			||||||
 | 
					    FuriApiLock api_lock;
 | 
				
			||||||
 | 
					    void* input;
 | 
				
			||||||
 | 
					    void* output;
 | 
				
			||||||
 | 
					} FuriHalSerialControlMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    const FuriHalSerialId id;
 | 
				
			||||||
 | 
					    const uint32_t baud_rate;
 | 
				
			||||||
 | 
					} FuriHalSerialControlMessageInputLogging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    FuriHalSerialHandle handles[FuriHalSerialIdMax];
 | 
				
			||||||
 | 
					    FuriMessageQueue* queue;
 | 
				
			||||||
 | 
					    FuriThread* thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Logging
 | 
				
			||||||
 | 
					    FuriHalSerialId log_config_serial_id;
 | 
				
			||||||
 | 
					    uint32_t log_config_serial_baud_rate;
 | 
				
			||||||
 | 
					    FuriLogHandler log_handler;
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* log_serial;
 | 
				
			||||||
 | 
					} FuriHalSerialControl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FuriHalSerialControl* furi_hal_serial_control = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_control_log_callback(const uint8_t* data, size_t size, void* context) {
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* handle = context;
 | 
				
			||||||
 | 
					    furi_hal_serial_tx(handle, data, size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    if(furi_hal_serial_control->log_serial) {
 | 
				
			||||||
 | 
					        furi_log_remove_handler(furi_hal_serial_control->log_handler);
 | 
				
			||||||
 | 
					        furi_hal_serial_deinit(furi_hal_serial_control->log_serial);
 | 
				
			||||||
 | 
					        furi_hal_serial_control->log_serial = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(handle) {
 | 
				
			||||||
 | 
					        furi_hal_serial_control->log_serial = handle;
 | 
				
			||||||
 | 
					        furi_hal_serial_init(
 | 
				
			||||||
 | 
					            furi_hal_serial_control->log_serial,
 | 
				
			||||||
 | 
					            furi_hal_serial_control->log_config_serial_baud_rate);
 | 
				
			||||||
 | 
					        furi_hal_serial_control->log_handler.callback = furi_hal_serial_control_log_callback;
 | 
				
			||||||
 | 
					        furi_hal_serial_control->log_handler.context = furi_hal_serial_control->log_serial;
 | 
				
			||||||
 | 
					        furi_log_add_handler(furi_hal_serial_control->log_handler);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int32_t furi_hal_serial_control_thread(void* args) {
 | 
				
			||||||
 | 
					    UNUSED(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool should_continue = true;
 | 
				
			||||||
 | 
					    while(should_continue || furi_message_queue_get_count(furi_hal_serial_control->queue) > 0) {
 | 
				
			||||||
 | 
					        FuriHalSerialControlMessage message = {0};
 | 
				
			||||||
 | 
					        FuriStatus status =
 | 
				
			||||||
 | 
					            furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever);
 | 
				
			||||||
 | 
					        furi_check(status == FuriStatusOk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(message.type == FuriHalSerialControlMessageTypeStop) {
 | 
				
			||||||
 | 
					            should_continue = false;
 | 
				
			||||||
 | 
					        } else if(message.type == FuriHalSerialControlMessageTypeSuspend) {
 | 
				
			||||||
 | 
					            for(size_t i = 0; i < FuriHalSerialIdMax; i++) {
 | 
				
			||||||
 | 
					                furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]);
 | 
				
			||||||
 | 
					                furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            api_lock_unlock(message.api_lock);
 | 
				
			||||||
 | 
					        } else if(message.type == FuriHalSerialControlMessageTypeResume) {
 | 
				
			||||||
 | 
					            for(size_t i = 0; i < FuriHalSerialIdMax; i++) {
 | 
				
			||||||
 | 
					                furi_hal_serial_resume(&furi_hal_serial_control->handles[i]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            api_lock_unlock(message.api_lock);
 | 
				
			||||||
 | 
					        } else if(message.type == FuriHalSerialControlMessageTypeAcquire) {
 | 
				
			||||||
 | 
					            FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input;
 | 
				
			||||||
 | 
					            if(furi_hal_serial_control->handles[serial_id].in_use) {
 | 
				
			||||||
 | 
					                *(FuriHalSerialHandle**)message.output = NULL;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Logging
 | 
				
			||||||
 | 
					                if(furi_hal_serial_control->log_config_serial_id == serial_id) {
 | 
				
			||||||
 | 
					                    furi_hal_serial_control_log_set_handle(NULL);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Return handle
 | 
				
			||||||
 | 
					                furi_hal_serial_control->handles[serial_id].in_use = true;
 | 
				
			||||||
 | 
					                *(FuriHalSerialHandle**)message.output =
 | 
				
			||||||
 | 
					                    &furi_hal_serial_control->handles[serial_id];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            api_lock_unlock(message.api_lock);
 | 
				
			||||||
 | 
					        } else if(message.type == FuriHalSerialControlMessageTypeRelease) {
 | 
				
			||||||
 | 
					            FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input;
 | 
				
			||||||
 | 
					            furi_assert(handle->in_use);
 | 
				
			||||||
 | 
					            furi_hal_serial_deinit(handle);
 | 
				
			||||||
 | 
					            handle->in_use = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Return back logging
 | 
				
			||||||
 | 
					            if(furi_hal_serial_control->log_config_serial_id == handle->id) {
 | 
				
			||||||
 | 
					                furi_hal_serial_control_log_set_handle(handle);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            api_lock_unlock(message.api_lock);
 | 
				
			||||||
 | 
					        } else if(message.type == FuriHalSerialControlMessageTypeLogging) {
 | 
				
			||||||
 | 
					            // Set new configuration
 | 
				
			||||||
 | 
					            FuriHalSerialControlMessageInputLogging* message_input = message.input;
 | 
				
			||||||
 | 
					            furi_hal_serial_control->log_config_serial_id = message_input->id;
 | 
				
			||||||
 | 
					            furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate;
 | 
				
			||||||
 | 
					            // Apply new configuration
 | 
				
			||||||
 | 
					            FuriHalSerialHandle* handle = NULL;
 | 
				
			||||||
 | 
					            if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) {
 | 
				
			||||||
 | 
					                handle = &furi_hal_serial_control
 | 
				
			||||||
 | 
					                              ->handles[furi_hal_serial_control->log_config_serial_id];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            furi_hal_serial_control_log_set_handle(handle);
 | 
				
			||||||
 | 
					            api_lock_unlock(message.api_lock);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            furi_crash("Invalid parameter");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_control_init(void) {
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial_control == NULL);
 | 
				
			||||||
 | 
					    // Allocate resources
 | 
				
			||||||
 | 
					    furi_hal_serial_control = malloc(sizeof(FuriHalSerialControl));
 | 
				
			||||||
 | 
					    furi_hal_serial_control->handles[FuriHalSerialIdUsart].id = FuriHalSerialIdUsart;
 | 
				
			||||||
 | 
					    furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart;
 | 
				
			||||||
 | 
					    furi_hal_serial_control->queue =
 | 
				
			||||||
 | 
					        furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage));
 | 
				
			||||||
 | 
					    furi_hal_serial_control->thread =
 | 
				
			||||||
 | 
					        furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL);
 | 
				
			||||||
 | 
					    furi_thread_mark_as_service(furi_hal_serial_control->thread);
 | 
				
			||||||
 | 
					    furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest);
 | 
				
			||||||
 | 
					    furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax;
 | 
				
			||||||
 | 
					    // Start control plane thread
 | 
				
			||||||
 | 
					    furi_thread_start(furi_hal_serial_control->thread);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_control_deinit(void) {
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial_control);
 | 
				
			||||||
 | 
					    // Stop control plane thread
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessage message;
 | 
				
			||||||
 | 
					    message.type = FuriHalSerialControlMessageTypeStop;
 | 
				
			||||||
 | 
					    furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
 | 
				
			||||||
 | 
					    furi_thread_join(furi_hal_serial_control->thread);
 | 
				
			||||||
 | 
					    // Release resources
 | 
				
			||||||
 | 
					    furi_thread_free(furi_hal_serial_control->thread);
 | 
				
			||||||
 | 
					    furi_message_queue_free(furi_hal_serial_control->queue);
 | 
				
			||||||
 | 
					    free(furi_hal_serial_control);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_control_suspend(void) {
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial_control);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessage message;
 | 
				
			||||||
 | 
					    message.type = FuriHalSerialControlMessageTypeSuspend;
 | 
				
			||||||
 | 
					    message.api_lock = api_lock_alloc_locked();
 | 
				
			||||||
 | 
					    furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
 | 
				
			||||||
 | 
					    api_lock_wait_unlock_and_free(message.api_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_control_resume(void) {
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial_control);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessage message;
 | 
				
			||||||
 | 
					    message.type = FuriHalSerialControlMessageTypeResume;
 | 
				
			||||||
 | 
					    message.api_lock = api_lock_alloc_locked();
 | 
				
			||||||
 | 
					    furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
 | 
				
			||||||
 | 
					    api_lock_wait_unlock_and_free(message.api_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id) {
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial_control);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialHandle* output = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessage message;
 | 
				
			||||||
 | 
					    message.type = FuriHalSerialControlMessageTypeAcquire;
 | 
				
			||||||
 | 
					    message.api_lock = api_lock_alloc_locked();
 | 
				
			||||||
 | 
					    message.input = &serial_id;
 | 
				
			||||||
 | 
					    message.output = &output;
 | 
				
			||||||
 | 
					    furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
 | 
				
			||||||
 | 
					    api_lock_wait_unlock_and_free(message.api_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return output;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_control_release(FuriHalSerialHandle* handle) {
 | 
				
			||||||
 | 
					    furi_check(furi_hal_serial_control);
 | 
				
			||||||
 | 
					    furi_check(handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessage message;
 | 
				
			||||||
 | 
					    message.type = FuriHalSerialControlMessageTypeRelease;
 | 
				
			||||||
 | 
					    message.api_lock = api_lock_alloc_locked();
 | 
				
			||||||
 | 
					    message.input = &handle;
 | 
				
			||||||
 | 
					    furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
 | 
				
			||||||
 | 
					    api_lock_wait_unlock_and_free(message.api_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate) {
 | 
				
			||||||
 | 
					    furi_check(serial_id <= FuriHalSerialIdMax);
 | 
				
			||||||
 | 
					    furi_check(baud_rate >= 9600 && baud_rate <= 4000000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Very special case of updater, where RTC initialized before kernel start
 | 
				
			||||||
 | 
					    if(!furi_hal_serial_control) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessageInputLogging message_input = {
 | 
				
			||||||
 | 
					        .id = serial_id,
 | 
				
			||||||
 | 
					        .baud_rate = baud_rate,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    FuriHalSerialControlMessage message;
 | 
				
			||||||
 | 
					    message.type = FuriHalSerialControlMessageTypeLogging;
 | 
				
			||||||
 | 
					    message.api_lock = api_lock_alloc_locked();
 | 
				
			||||||
 | 
					    message.input = &message_input;
 | 
				
			||||||
 | 
					    furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
 | 
				
			||||||
 | 
					    api_lock_wait_unlock_and_free(message.api_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								targets/f7/furi_hal/furi_hal_serial_control.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								targets/f7/furi_hal/furi_hal_serial_control.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "furi_hal_serial_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Initialize Serial Control */
 | 
				
			||||||
 | 
					void furi_hal_serial_control_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** De-Initialize Serial Control */
 | 
				
			||||||
 | 
					void furi_hal_serial_control_deinit(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Suspend All Serial Interfaces */
 | 
				
			||||||
 | 
					void furi_hal_serial_control_suspend(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Resume All Serial Interfaces */
 | 
				
			||||||
 | 
					void furi_hal_serial_control_resume(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Acquire Serial Interface Handler
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  serial_id  The serial transceiver identifier
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     The Serial Interface Handle or null if interfaces is in use
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Release Serial Interface Handler
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param      handle  The handle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_control_release(FuriHalSerialHandle* handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Acquire Serial Interface Handler
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param[in]  serial_id  The serial transceiver identifier. Use FuriHalSerialIdMax to disable logging.
 | 
				
			||||||
 | 
					 * @param[in]  baud_rate  The baud rate
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return     The Serial Interface Handle or null if interfaces is in use
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										15
									
								
								targets/f7/furi_hal/furi_hal_serial_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								targets/f7/furi_hal/furi_hal_serial_types.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * UART channels
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    FuriHalSerialIdUsart,
 | 
				
			||||||
 | 
					    FuriHalSerialIdLpuart,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriHalSerialIdMax,
 | 
				
			||||||
 | 
					} FuriHalSerialId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct FuriHalSerialHandle FuriHalSerialHandle;
 | 
				
			||||||
							
								
								
									
										8
									
								
								targets/f7/furi_hal/furi_hal_serial_types_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								targets/f7/furi_hal/furi_hal_serial_types_i.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <furi_hal_serial_types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct FuriHalSerialHandle {
 | 
				
			||||||
 | 
					    FuriHalSerialId id;
 | 
				
			||||||
 | 
					    bool in_use;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,244 +0,0 @@
 | 
				
			|||||||
#include <furi_hal_uart.h>
 | 
					 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stm32wbxx_ll_lpuart.h>
 | 
					 | 
				
			||||||
#include <stm32wbxx_ll_usart.h>
 | 
					 | 
				
			||||||
#include <stm32wbxx_ll_rcc.h>
 | 
					 | 
				
			||||||
#include <furi_hal_resources.h>
 | 
					 | 
				
			||||||
#include <furi_hal_bus.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <furi.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool furi_hal_usart_prev_enabled[2];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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) {
 | 
					 | 
				
			||||||
    furi_hal_bus_enable(FuriHalBusUSART1);
 | 
					 | 
				
			||||||
    LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_gpio_init_ex(
 | 
					 | 
				
			||||||
        &gpio_usart_tx,
 | 
					 | 
				
			||||||
        GpioModeAltFunctionPushPull,
 | 
					 | 
				
			||||||
        GpioPullUp,
 | 
					 | 
				
			||||||
        GpioSpeedVeryHigh,
 | 
					 | 
				
			||||||
        GpioAltFn7USART1);
 | 
					 | 
				
			||||||
    furi_hal_gpio_init_ex(
 | 
					 | 
				
			||||||
        &gpio_usart_rx,
 | 
					 | 
				
			||||||
        GpioModeAltFunctionPushPull,
 | 
					 | 
				
			||||||
        GpioPullUp,
 | 
					 | 
				
			||||||
        GpioSpeedVeryHigh,
 | 
					 | 
				
			||||||
        GpioAltFn7USART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LL_USART_InitTypeDef USART_InitStruct;
 | 
					 | 
				
			||||||
    USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
 | 
					 | 
				
			||||||
    USART_InitStruct.BaudRate = baud;
 | 
					 | 
				
			||||||
    USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
 | 
					 | 
				
			||||||
    USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
 | 
					 | 
				
			||||||
    USART_InitStruct.Parity = LL_USART_PARITY_NONE;
 | 
					 | 
				
			||||||
    USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
 | 
					 | 
				
			||||||
    USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
 | 
					 | 
				
			||||||
    USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
 | 
					 | 
				
			||||||
    LL_USART_Init(USART1, &USART_InitStruct);
 | 
					 | 
				
			||||||
    LL_USART_EnableFIFO(USART1);
 | 
					 | 
				
			||||||
    LL_USART_ConfigAsyncMode(USART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LL_USART_Enable(USART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1))
 | 
					 | 
				
			||||||
        ;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LL_USART_DisableIT_ERROR(USART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void furi_hal_lpuart_init(uint32_t baud) {
 | 
					 | 
				
			||||||
    furi_hal_bus_enable(FuriHalBusLPUART1);
 | 
					 | 
				
			||||||
    LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_gpio_init_ex(
 | 
					 | 
				
			||||||
        &gpio_ext_pc0,
 | 
					 | 
				
			||||||
        GpioModeAltFunctionPushPull,
 | 
					 | 
				
			||||||
        GpioPullUp,
 | 
					 | 
				
			||||||
        GpioSpeedVeryHigh,
 | 
					 | 
				
			||||||
        GpioAltFn8LPUART1);
 | 
					 | 
				
			||||||
    furi_hal_gpio_init_ex(
 | 
					 | 
				
			||||||
        &gpio_ext_pc1,
 | 
					 | 
				
			||||||
        GpioModeAltFunctionPushPull,
 | 
					 | 
				
			||||||
        GpioPullUp,
 | 
					 | 
				
			||||||
        GpioSpeedVeryHigh,
 | 
					 | 
				
			||||||
        GpioAltFn8LPUART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LL_LPUART_InitTypeDef LPUART_InitStruct;
 | 
					 | 
				
			||||||
    LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
 | 
					 | 
				
			||||||
    LPUART_InitStruct.BaudRate = 115200;
 | 
					 | 
				
			||||||
    LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
 | 
					 | 
				
			||||||
    LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
 | 
					 | 
				
			||||||
    LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
 | 
					 | 
				
			||||||
    LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
 | 
					 | 
				
			||||||
    LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
 | 
					 | 
				
			||||||
    LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
 | 
					 | 
				
			||||||
    LL_LPUART_EnableFIFO(LPUART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LL_LPUART_Enable(LPUART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1))
 | 
					 | 
				
			||||||
        ;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud);
 | 
					 | 
				
			||||||
    LL_LPUART_DisableIT_ERROR(LPUART1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) {
 | 
					 | 
				
			||||||
    if(ch == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
        furi_hal_lpuart_init(baud);
 | 
					 | 
				
			||||||
    } else if(ch == FuriHalUartIdUSART1) {
 | 
					 | 
				
			||||||
        furi_hal_usart_init(baud);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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))
 | 
					 | 
				
			||||||
                ;
 | 
					 | 
				
			||||||
            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_Enable(USART1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else if(ch == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
        if(LL_LPUART_IsEnabled(LPUART1)) {
 | 
					 | 
				
			||||||
            // Wait for transfer complete flag
 | 
					 | 
				
			||||||
            while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
 | 
					 | 
				
			||||||
                ;
 | 
					 | 
				
			||||||
            LL_LPUART_Disable(LPUART1);
 | 
					 | 
				
			||||||
            uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE);
 | 
					 | 
				
			||||||
            if(uartclk / baud > 4095) {
 | 
					 | 
				
			||||||
                LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
 | 
					 | 
				
			||||||
                LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
 | 
					 | 
				
			||||||
                LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            LL_LPUART_Enable(LPUART1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_uart_deinit(FuriHalUartId ch) {
 | 
					 | 
				
			||||||
    furi_hal_uart_set_irq_cb(ch, NULL, NULL);
 | 
					 | 
				
			||||||
    if(ch == FuriHalUartIdUSART1) {
 | 
					 | 
				
			||||||
        if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) {
 | 
					 | 
				
			||||||
            furi_hal_bus_disable(FuriHalBusUSART1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
					 | 
				
			||||||
        furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
					 | 
				
			||||||
    } else if(ch == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
        if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) {
 | 
					 | 
				
			||||||
            furi_hal_bus_disable(FuriHalBusLPUART1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
					 | 
				
			||||||
        furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_uart_suspend(FuriHalUartId channel) {
 | 
					 | 
				
			||||||
    if(channel == FuriHalUartIdLPUART1 && LL_LPUART_IsEnabled(LPUART1)) {
 | 
					 | 
				
			||||||
        LL_LPUART_Disable(LPUART1);
 | 
					 | 
				
			||||||
        furi_hal_usart_prev_enabled[channel] = true;
 | 
					 | 
				
			||||||
    } else if(channel == FuriHalUartIdUSART1 && LL_USART_IsEnabled(USART1)) {
 | 
					 | 
				
			||||||
        LL_USART_Disable(USART1);
 | 
					 | 
				
			||||||
        furi_hal_usart_prev_enabled[channel] = true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_uart_resume(FuriHalUartId channel) {
 | 
					 | 
				
			||||||
    if(!furi_hal_usart_prev_enabled[channel]) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    } else if(channel == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
        LL_LPUART_Enable(LPUART1);
 | 
					 | 
				
			||||||
    } else if(channel == FuriHalUartIdUSART1) {
 | 
					 | 
				
			||||||
        LL_USART_Enable(USART1);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_usart_prev_enabled[channel] = false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) {
 | 
					 | 
				
			||||||
    if(ch == FuriHalUartIdUSART1) {
 | 
					 | 
				
			||||||
        if(LL_USART_IsEnabled(USART1) == 0) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while(buffer_size > 0) {
 | 
					 | 
				
			||||||
            while(!LL_USART_IsActiveFlag_TXE(USART1))
 | 
					 | 
				
			||||||
                ;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            LL_USART_TransmitData8(USART1, *buffer);
 | 
					 | 
				
			||||||
            buffer++;
 | 
					 | 
				
			||||||
            buffer_size--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    } else if(ch == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
        if(LL_LPUART_IsEnabled(LPUART1) == 0) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while(buffer_size > 0) {
 | 
					 | 
				
			||||||
            while(!LL_LPUART_IsActiveFlag_TXE(LPUART1))
 | 
					 | 
				
			||||||
                ;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            LL_LPUART_TransmitData8(LPUART1, *buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            buffer++;
 | 
					 | 
				
			||||||
            buffer_size--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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);
 | 
					 | 
				
			||||||
            LL_USART_DisableIT_RXNE_RXFNE(USART1);
 | 
					 | 
				
			||||||
        } else if(ch == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
            NVIC_DisableIRQ(LPUART1_IRQn);
 | 
					 | 
				
			||||||
            LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        irq_cb[ch] = cb;
 | 
					 | 
				
			||||||
        irq_ctx[ch] = ctx;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        irq_ctx[ch] = ctx;
 | 
					 | 
				
			||||||
        irq_cb[ch] = cb;
 | 
					 | 
				
			||||||
        if(ch == FuriHalUartIdUSART1) {
 | 
					 | 
				
			||||||
            NVIC_EnableIRQ(USART1_IRQn);
 | 
					 | 
				
			||||||
            LL_USART_EnableIT_RXNE_RXFNE(USART1);
 | 
					 | 
				
			||||||
        } else if(ch == FuriHalUartIdLPUART1) {
 | 
					 | 
				
			||||||
            NVIC_EnableIRQ(LPUART1_IRQn);
 | 
					 | 
				
			||||||
            LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void LPUART1_IRQHandler(void) {
 | 
					 | 
				
			||||||
    if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
 | 
					 | 
				
			||||||
        uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
 | 
					 | 
				
			||||||
        irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]);
 | 
					 | 
				
			||||||
    } else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) {
 | 
					 | 
				
			||||||
        LL_LPUART_ClearFlag_ORE(LPUART1);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void USART1_IRQHandler(void) {
 | 
					 | 
				
			||||||
    if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
 | 
					 | 
				
			||||||
        uint8_t data = LL_USART_ReceiveData8(USART1);
 | 
					 | 
				
			||||||
        irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]);
 | 
					 | 
				
			||||||
    } else if(LL_USART_IsActiveFlag_ORE(USART1)) {
 | 
					 | 
				
			||||||
        LL_USART_ClearFlag_ORE(USART1);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,89 +0,0 @@
 | 
				
			|||||||
/**
 | 
					 | 
				
			||||||
 * @file furi_hal_uart.h
 | 
					 | 
				
			||||||
 * @version 1.0
 | 
					 | 
				
			||||||
 * @date 2021-11-19
 | 
					 | 
				
			||||||
 * 
 | 
					 | 
				
			||||||
 * UART HAL api interface
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stddef.h>
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __cplusplus
 | 
					 | 
				
			||||||
extern "C" {
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * UART channels
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    FuriHalUartIdUSART1,
 | 
					 | 
				
			||||||
    FuriHalUartIdLPUART1,
 | 
					 | 
				
			||||||
} FuriHalUartId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * UART events
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    UartIrqEventRXNE,
 | 
					 | 
				
			||||||
} UartIrqEvent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 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);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 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);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Suspend UART operation
 | 
					 | 
				
			||||||
 * Disables UART hardware, settings and callbacks are preserved
 | 
					 | 
				
			||||||
 * @param channel UART channel
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void furi_hal_uart_suspend(FuriHalUartId channel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Resume UART operation
 | 
					 | 
				
			||||||
 * Resumes UART hardware from suspended state
 | 
					 | 
				
			||||||
 * @param channel UART channel
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void furi_hal_uart_resume(FuriHalUartId channel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Changes UART baudrate
 | 
					 | 
				
			||||||
 * @param channel UART channel
 | 
					 | 
				
			||||||
 * @param baud baudrate
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void furi_hal_uart_set_br(FuriHalUartId channel, uint32_t baud);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 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);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -14,7 +14,6 @@ struct STOP_EXTERNING_ME {};
 | 
				
			|||||||
#include <furi_hal_clock.h>
 | 
					#include <furi_hal_clock.h>
 | 
				
			||||||
#include <furi_hal_bus.h>
 | 
					#include <furi_hal_bus.h>
 | 
				
			||||||
#include <furi_hal_crypto.h>
 | 
					#include <furi_hal_crypto.h>
 | 
				
			||||||
#include <furi_hal_console.h>
 | 
					 | 
				
			||||||
#include <furi_hal_debug.h>
 | 
					#include <furi_hal_debug.h>
 | 
				
			||||||
#include <furi_hal_dma.h>
 | 
					#include <furi_hal_dma.h>
 | 
				
			||||||
#include <furi_hal_os.h>
 | 
					#include <furi_hal_os.h>
 | 
				
			||||||
@ -36,7 +35,8 @@ struct STOP_EXTERNING_ME {};
 | 
				
			|||||||
#include <furi_hal_usb.h>
 | 
					#include <furi_hal_usb.h>
 | 
				
			||||||
#include <furi_hal_usb_hid.h>
 | 
					#include <furi_hal_usb_hid.h>
 | 
				
			||||||
#include <furi_hal_usb_ccid.h>
 | 
					#include <furi_hal_usb_ccid.h>
 | 
				
			||||||
#include <furi_hal_uart.h>
 | 
					#include <furi_hal_serial_control.h>
 | 
				
			||||||
 | 
					#include <furi_hal_serial.h>
 | 
				
			||||||
#include <furi_hal_info.h>
 | 
					#include <furi_hal_info.h>
 | 
				
			||||||
#include <furi_hal_random.h>
 | 
					#include <furi_hal_random.h>
 | 
				
			||||||
#include <furi_hal_target_hw.h>
 | 
					#include <furi_hal_target_hw.h>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user