Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UART echo app #831

Merged
merged 4 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions applications/applications.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ extern int32_t desktop_srv(void* p);
extern int32_t accessor_app(void* p);
extern int32_t archive_app(void* p);
extern int32_t bad_usb_app(void* p);
extern int32_t uart_echo_app(void* p);
extern int32_t blink_test_app(void* p);
extern int32_t bt_debug_app(void* p);
extern int32_t delay_test_app(void* p);
Expand Down Expand Up @@ -236,6 +237,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
{.app = bad_usb_app, .name = "Bad USB test", .stack_size = 2048, .icon = NULL},
#endif

#ifdef APP_UART_ECHO
{.app = uart_echo_app, .name = "Uart Echo", .stack_size = 2048, .icon = NULL},
#endif

#ifdef APP_IRDA_MONITOR
{.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = NULL},
#endif
Expand Down
6 changes: 6 additions & 0 deletions applications/applications.mk
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ APP_DISPLAY_TEST = 1

APP_USB_MOUSE = 1
APP_BAD_USB = 1
APP_UART_ECHO = 1
endif


Expand Down Expand Up @@ -132,6 +133,11 @@ CFLAGS += -DAPP_USB_TEST
SRV_GUI = 1
endif

APP_UART_ECHO ?= 0
ifeq ($(APP_UART_ECHO), 1)
CFLAGS += -DAPP_UART_ECHO
SRV_GUI = 1
endif

APP_DISPLAY_TEST ?= 0
ifeq ($(APP_DISPLAY_TEST), 1)
Expand Down
268 changes: 268 additions & 0 deletions applications/debug_tools/uart_echo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
#include <furi.h>
#include <m-string.h>
#include <gui/gui.h>
#include <notification/notification.h>
#include <notification/notification-messages.h>
#include <gui/elements.h>
#include <stream_buffer.h>
#include <furi-hal-uart.h>
#include <furi-hal-console.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/dialog_ex.h>

#define LINES_ON_SCREEN 6
#define COLUMNS_ON_SCREEN 21

typedef struct UartDumpModel UartDumpModel;

typedef struct {
Gui* gui;
NotificationApp* notification;
ViewDispatcher* view_dispatcher;
View* view;
FuriThread* worker_thread;
StreamBufferHandle_t rx_stream;
} UartEchoApp;

typedef struct {
string_t text;
} ListElement;

struct UartDumpModel {
ListElement* list[LINES_ON_SCREEN];
uint8_t line;

char last_char;
bool escape;
};

typedef enum {
WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
WorkerEventStop = (1 << 1),
WorkerEventRx = (1 << 2),
} WorkerEventFlags;

#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)

const NotificationSequence sequence_notification = {
&message_display_on,
&message_green_255,
&message_delay_10,
NULL,
};

static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) {
UartDumpModel* model = _model;

// Prepare canvas
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontKeyboard);

for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
canvas_draw_str(
canvas,
0,
(i + 1) * (canvas_current_font_height(canvas) - 1),
string_get_cstr(model->list[i]->text));

if(i == model->line) {
uint8_t width = canvas_string_width(canvas, string_get_cstr(model->list[i]->text));

canvas_draw_box(
canvas,
width,
(i) * (canvas_current_font_height(canvas) - 1) + 2,
2,
canvas_current_font_height(canvas) - 2);
}
}
}

static bool uart_echo_view_input_callback(InputEvent* event, void* context) {
bool consumed = false;
return consumed;
}

static uint32_t uart_echo_exit(void* context) {
return VIEW_NONE;
}

static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
furi_assert(context);
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
UartEchoApp* app = context;

if(ev == UartIrqEventRXNE) {
xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
osThreadFlagsSet(furi_thread_get_thread_id(app->worker_thread), WorkerEventRx);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}

static void uart_echo_push_to_list(UartDumpModel* model, const char data) {
if(model->escape) {
// escape code end with letter
if((data >= 'a' && data <= 'z') || (data >= 'A' && data <= 'Z')) {
model->escape = false;
}
} else if(data == '[' && model->last_char == '\e') {
// "Esc[" is a escape code
model->escape = true;
} else if((data >= ' ' && data <= '~') || (data == '\n' || data == '\r')) {
bool new_string_needed = false;
if(string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) {
new_string_needed = true;
} else if((data == '\n' || data == '\r')) {
// pack line breaks
if(model->last_char != '\n' && model->last_char != '\r') {
new_string_needed = true;
}
}

if(new_string_needed) {
if((model->line + 1) < LINES_ON_SCREEN) {
model->line += 1;
} else {
ListElement* first = model->list[0];

for(size_t i = 1; i < LINES_ON_SCREEN; i++) {
model->list[i - 1] = model->list[i];
}

string_reset(first->text);
model->list[model->line] = first;
}
}

if(data != '\n' && data != '\r') {
string_push_back(model->list[model->line]->text, data);
}
}
model->last_char = data;
}

static int32_t uart_echo_worker(void* context) {
furi_assert(context);
UartEchoApp* app = context;

while(1) {
uint32_t events = osThreadFlagsWait(WORKER_EVENTS_MASK, osFlagsWaitAny, osWaitForever);
furi_check((events & osFlagsError) == 0);

if(events & WorkerEventStop) break;
if(events & WorkerEventRx) {
size_t length = 0;
do {
uint8_t data[64];
length = xStreamBufferReceive(app->rx_stream, data, 64, 0);
if(length > 0) {
furi_hal_uart_tx(FuriHalUartIdUSART1, data, length);
with_view_model(
app->view, (UartDumpModel * model) {
for(size_t i = 0; i < length; i++) {
uart_echo_push_to_list(model, data[i]);
}
return false;
});
}
} while(length > 0);

notification_message(app->notification, &sequence_notification);
with_view_model(
app->view, (UartDumpModel * model) { return true; });
}
}

return 0;
}

static UartEchoApp* uart_echo_app_alloc() {
UartEchoApp* app = furi_alloc(sizeof(UartEchoApp));

app->rx_stream = xStreamBufferCreate(2048, 1);

// Gui
app->gui = furi_record_open("gui");
app->notification = furi_record_open("notification");

// View dispatcher
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);

// Views
app->view = view_alloc();
view_set_draw_callback(app->view, uart_echo_view_draw_callback);
view_set_input_callback(app->view, uart_echo_view_input_callback);
view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel));
with_view_model(
app->view, (UartDumpModel * model) {
for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
model->line = 0;
model->escape = false;
model->list[i] = furi_alloc(sizeof(ListElement));
string_init(model->list[i]->text);
}
return true;
});

view_set_previous_callback(app->view, uart_echo_exit);
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
view_dispatcher_switch_to_view(app->view_dispatcher, 0);

// Enable uart listener
furi_hal_console_disable();
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);

app->worker_thread = furi_thread_alloc();
furi_thread_set_name(app->worker_thread, "UsbUartWorker");
furi_thread_set_stack_size(app->worker_thread, 1024);
furi_thread_set_context(app->worker_thread, app);
furi_thread_set_callback(app->worker_thread, uart_echo_worker);
furi_thread_start(app->worker_thread);

return app;
}

static void uart_echo_app_free(UartEchoApp* app) {
furi_assert(app);

osThreadFlagsSet(furi_thread_get_thread_id(app->worker_thread), WorkerEventStop);
furi_thread_join(app->worker_thread);
furi_thread_free(app->worker_thread);

furi_hal_console_enable();

// Free views
view_dispatcher_remove_view(app->view_dispatcher, 0);

with_view_model(
app->view, (UartDumpModel * model) {
for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
string_clear(model->list[i]->text);
free(model->list[i]);
}
return true;
});
view_free(app->view);
view_dispatcher_free(app->view_dispatcher);

// Close gui record
furi_record_close("gui");
furi_record_close("notification");
app->gui = NULL;

vStreamBufferDelete(app->rx_stream);

// Free rest
free(app);
}

int32_t uart_echo_app(void* p) {
UartEchoApp* app = uart_echo_app_alloc();
view_dispatcher_run(app->view_dispatcher);
uart_echo_app_free(app);
return 0;
}
6 changes: 3 additions & 3 deletions applications/gpio/usb_uart_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static CdcCallbacks cdc_cb = {

static int32_t usb_uart_tx_thread(void* context);

static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;

if(ev == UartIrqEventRXNE) {
Expand Down Expand Up @@ -94,10 +94,10 @@ static int32_t usb_uart_worker(void* context) {
furi_hal_console_disable();
} else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) {
furi_hal_uart_init(usb_uart->cfg.uart_ch, 115200);
furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb, NULL);
}

furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb, NULL);
if(usb_uart->cfg.baudrate != 0)
furi_hal_uart_set_br(usb_uart->cfg.uart_ch, usb_uart->cfg.baudrate);
else
Expand Down
2 changes: 1 addition & 1 deletion firmware/targets/f6/furi-hal/furi-hal-console.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ void furi_hal_console_init() {
}

void furi_hal_console_enable() {
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL);
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
while (!LL_USART_IsActiveFlag_TC(USART1));
furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
furi_hal_console_alive = true;
Expand Down
Loading