Skip to content

Commit

Permalink
FuriHal: UART refactoring (#3211)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: hedger <[email protected]>
Co-authored-by: SkorP <[email protected]>
Co-authored-by: Skorpionm <[email protected]>
  • Loading branch information
4 people authored Jan 15, 2024
1 parent d73d007 commit fc043da
Show file tree
Hide file tree
Showing 37 changed files with 1,946 additions and 739 deletions.
85 changes: 69 additions & 16 deletions applications/debug/uart_echo/uart_echo.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#include <furi.h>
#include <furi_hal.h>

#include <gui/gui.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <gui/elements.h>
#include <furi_hal_uart.h>
#include <furi_hal_console.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/dialog_ex.h>

#include <notification/notification.h>
#include <notification/notification_messages.h>

#define LINES_ON_SCREEN 6
#define COLUMNS_ON_SCREEN 21
#define TAG "UartEcho"
Expand All @@ -22,6 +23,7 @@ typedef struct {
View* view;
FuriThread* worker_thread;
FuriStreamBuffer* rx_stream;
FuriHalSerialHandle* serial_handle;
} UartEchoApp;

typedef struct {
Expand All @@ -39,10 +41,16 @@ struct UartDumpModel {
typedef enum {
WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
WorkerEventStop = (1 << 1),
WorkerEventRx = (1 << 2),
WorkerEventRxData = (1 << 2),
WorkerEventRxIdle = (1 << 3),
WorkerEventRxOverrunError = (1 << 4),
WorkerEventRxFramingError = (1 << 5),
WorkerEventRxNoiseError = (1 << 6),
} WorkerEventFlags;

#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
#define WORKER_EVENTS_MASK \
(WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \
WorkerEventRxFramingError | WorkerEventRxNoiseError)

const NotificationSequence sequence_notification = {
&message_display_backlight_on,
Expand Down Expand Up @@ -91,14 +99,39 @@ static uint32_t uart_echo_exit(void* context) {
return VIEW_NONE;
}

static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
static void
uart_echo_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) {
furi_assert(context);
UNUSED(handle);
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_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) {
Expand Down Expand Up @@ -153,13 +186,13 @@ static int32_t uart_echo_worker(void* context) {
furi_check((events & FuriFlagError) == 0);

if(events & WorkerEventStop) break;
if(events & WorkerEventRx) {
if(events & WorkerEventRxData) {
size_t length = 0;
do {
uint8_t data[64];
length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0);
if(length > 0) {
furi_hal_uart_tx(FuriHalUartIdUSART1, data, length);
furi_hal_serial_tx(app->serial_handle, data, length);
with_view_model(
app->view,
UartDumpModel * model,
Expand All @@ -176,6 +209,23 @@ static int32_t uart_echo_worker(void* context) {
with_view_model(
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;
Expand Down Expand Up @@ -221,22 +271,25 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
furi_thread_start(app->worker_thread);

// Enable uart listener
furi_hal_console_disable();
furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate);
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart);
furi_check(app->serial_handle);
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;
}

static void uart_echo_app_free(UartEchoApp* 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_join(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
view_dispatcher_remove_view(app->view_dispatcher, 0);

Expand Down
2 changes: 1 addition & 1 deletion applications/main/gpio/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ App(
name="GPIO",
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="gpio_app",
stack_size=1 * 1024,
stack_size=2 * 1024,
icon="A_GPIO_14",
order=50,
fap_libs=["assets"],
Expand Down
6 changes: 3 additions & 3 deletions applications/main/gpio/scenes/gpio_scene_usb_uart_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void line_ensure_flow_invariant(GpioApp* app) {
// selected. This function enforces that invariant by resetting flow_pins
// 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;
variable_item_set_values_count(item, available_flow_pins);

Expand Down Expand Up @@ -77,9 +77,9 @@ static void line_port_cb(VariableItem* item) {
variable_item_set_current_value_text(item, uart_ch[index]);

if(index == 0)
app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
app->usb_uart_cfg->uart_ch = FuriHalSerialIdUsart;
else if(index == 1)
app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
app->usb_uart_cfg->uart_ch = FuriHalSerialIdLpuart;

line_ensure_flow_invariant(app);
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
Expand Down
78 changes: 43 additions & 35 deletions applications/main/gpio/usb_uart_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,18 @@ typedef enum {

WorkerEvtTxStop = (1 << 2),
WorkerEvtCdcRx = (1 << 3),
WorkerEvtCdcTxComplete = (1 << 4),

WorkerEvtCfgChange = (1 << 4),
WorkerEvtCfgChange = (1 << 5),

WorkerEvtLineCfgSet = (1 << 5),
WorkerEvtCtrlLineSet = (1 << 6),
WorkerEvtLineCfgSet = (1 << 6),
WorkerEvtCtrlLineSet = (1 << 7),

} WorkerEvtFlags;

#define WORKER_ALL_RX_EVENTS \
(WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \
WorkerEvtCtrlLineSet)
WorkerEvtCtrlLineSet | WorkerEvtCdcTxComplete)
#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx)

struct UsbUartBridge {
Expand All @@ -50,6 +51,7 @@ struct UsbUartBridge {
FuriThread* tx_thread;

FuriStreamBuffer* rx_stream;
FuriHalSerialHandle* serial_handle;

FuriMutex* usb_mutex;

Expand Down Expand Up @@ -80,11 +82,23 @@ static const CdcCallbacks cdc_cb = {

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;

if(ev == UartIrqEventRXNE) {
furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0);
if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) {
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);
}
}
Expand Down Expand Up @@ -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) {
if(uart_ch == FuriHalUartIdUSART1) {
furi_hal_console_disable();
} else if(uart_ch == FuriHalUartIdLPUART1) {
furi_hal_uart_init(uart_ch, 115200);
}
furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart);
furi_assert(!usb_uart->serial_handle);

usb_uart->serial_handle = furi_hal_serial_control_acquire(uart_ch);
furi_assert(usb_uart->serial_handle);

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) {
UNUSED(usb_uart);
furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL);
if(uart_ch == FuriHalUartIdUSART1)
furi_hal_console_enable();
else if(uart_ch == FuriHalUartIdLPUART1)
furi_hal_uart_deinit(uart_ch);
static void usb_uart_serial_deinit(UsbUartBridge* usb_uart) {
furi_assert(usb_uart->serial_handle);

furi_hal_serial_deinit(usb_uart->serial_handle);
furi_hal_serial_control_release(usb_uart->serial_handle);
usb_uart->serial_handle = NULL;
}

static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) {
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;
} else {
struct usb_cdc_line_coding* line_cfg =
furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch);
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;
}
}
Expand Down Expand Up @@ -191,7 +206,7 @@ static int32_t usb_uart_worker(void* context) {
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
furi_check(!(events & FuriFlagError));
if(events & WorkerEvtStop) break;
if(events & WorkerEvtRxDone) {
if(events & (WorkerEvtRxDone | WorkerEvtCdcTxComplete)) {
size_t len = furi_stream_buffer_receive(
usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0);
if(len > 0) {
Expand Down Expand Up @@ -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_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->cfg.uart_ch = usb_uart->cfg_new.uart_ch;
Expand Down Expand Up @@ -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_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);

Expand Down Expand Up @@ -320,18 +335,10 @@ static int32_t usb_uart_tx_thread(void* context) {
if(usb_uart->cfg.software_de_re != 0)
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) {
//TODO: FL-3276 port to new USART API
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_serial_tx_wait_complete(usb_uart->serial_handle);
furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);
}
}
Expand All @@ -345,6 +352,7 @@ static int32_t usb_uart_tx_thread(void* context) {
static void vcp_on_cdc_tx_complete(void* context) {
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
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) {
Expand Down
2 changes: 0 additions & 2 deletions applications/main/u2f/u2f_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#include <furi_hal_usb_hid_u2f.h>
#include <storage/storage.h>

#include <furi_hal_console.h>

#define TAG "U2fHid"
#define WORKER_TAG TAG "Worker"

Expand Down
9 changes: 7 additions & 2 deletions applications/services/cli/cli_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,12 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) {
furi_log_level_to_string(furi_log_get_level(), &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("Press CTRL+C to stop...\r\n");
Expand All @@ -220,7 +225,7 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) {
cli_write(cli, buffer, ret);
}

furi_hal_console_set_tx_callback(NULL, NULL);
furi_log_remove_handler(log_handler);

if(restore_log_level) {
// There will be strange behaviour if log level is set from settings while log command is running
Expand Down
Loading

0 comments on commit fc043da

Please sign in to comment.