diff --git a/applications/examples/uart_write.c b/applications/examples/uart_write.c new file mode 100644 index 00000000..c6602123 --- /dev/null +++ b/applications/examples/uart_write.c @@ -0,0 +1,33 @@ +#include "flipper.h" +#include +#include "log.h" + +void application_uart_write(void* p) { + // Red led for showing progress + GpioPin led = {.pin = GPIO_PIN_8, .port = GPIOA}; + pinMode(led, GpioModeOpenDrain); + + // get_default_log open "tty" record + FuriRecordSubscriber* log = get_default_log(); + + // create buffer + const char test_string[] = "test\n"; + furi_write(log, test_string, strlen(test_string)); + + // for example, create counter and show its value + uint8_t counter = 0; + + while(1) { + // continously write it to UART + fuprintf(log, "counter: %d\n", counter); + counter++; + + // flash at every send + digitalWrite(led, LOW); + delay(50); + digitalWrite(led, HIGH); + + // delay with overall perion of 1s + delay(950); + } +} \ No newline at end of file diff --git a/applications/startup.h b/applications/startup.h index 31bac7d0..02685c98 100644 --- a/applications/startup.h +++ b/applications/startup.h @@ -12,6 +12,7 @@ void flipper_test_app(void* p); #endif void application_blink(void* p); +void application_uart_write(void* p); const FlipperStartupApp FLIPPER_STARTUP[] = { #ifdef TEST @@ -21,4 +22,7 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { #ifdef EXAMPLE_BLINK {.app = application_blink, .name = "blink"}, #endif + #ifdef EXAMPLE_UART_WRITE + {.app = application_uart_write, .name = "uart write"}, + #endif }; \ No newline at end of file diff --git a/applications/tests/furi_record_test.c b/applications/tests/furi_record_test.c index 4fb1f495..cf608d51 100644 --- a/applications/tests/furi_record_test.c +++ b/applications/tests/furi_record_test.c @@ -1,7 +1,7 @@ #include #include #include "flipper.h" -#include "debug.h" +#include "log.h" /* TEST: pipe record @@ -22,48 +22,48 @@ void pipe_record_cb(const void* value, size_t size) { pipe_record_value = *((uint8_t*)value); } -bool test_furi_pipe_record(FILE* debug_uart) { +bool test_furi_pipe_record(FuriRecordSubscriber* log) { // 1. create pipe record if(!furi_create("test/pipe", NULL, 0)) { - fprintf(debug_uart, "cannot create record\n"); + fuprintf(log, "cannot create record\n"); return false; } // 2. Open/subscribe to it - FuriRecordHandler pipe_record = furi_open( + FuriRecordSubscriber* pipe_record = furi_open( "test/pipe", false, false, pipe_record_cb, NULL ); - if(pipe_record.record == NULL) { - fprintf(debug_uart, "cannot open record\n"); + if(pipe_record == NULL) { + fuprintf(log, "cannot open record\n"); return false; } const uint8_t WRITE_VALUE = 1; // 3. write data - if(!furi_write(&pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { - fprintf(debug_uart, "cannot write to record\n"); + if(!furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { + fuprintf(log, "cannot write to record\n"); return false; } // 4. check that subscriber get data if(pipe_record_value != WRITE_VALUE) { - fprintf(debug_uart, "wrong value (get %d, write %d)\n", pipe_record_value, WRITE_VALUE); + fuprintf(log, "wrong value (get %d, write %d)\n", pipe_record_value, WRITE_VALUE); return false; } // 5. try to read, get error uint8_t read_value = 0; - if(furi_read(&pipe_record, &read_value, sizeof(uint8_t))) { - fprintf(debug_uart, "reading from pipe record not allowed\n"); + if(furi_read(pipe_record, &read_value, sizeof(uint8_t))) { + fuprintf(log, "reading from pipe record not allowed\n"); return false; } // 6. close record - furi_close(&pipe_record); + furi_close(pipe_record); // 7. try to write, get error - if(furi_write(&pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { - fprintf(debug_uart, "writing to closed record not allowed\n"); + if(furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { + fuprintf(log, "writing to closed record not allowed\n"); return false; } @@ -88,56 +88,56 @@ void holding_record_cb(const void* value, size_t size) { holding_record_value = *((uint8_t*)value); } -bool test_furi_holding_data(FILE* debug_uart) { +bool test_furi_holding_data(FuriRecordSubscriber* log) { // 1. Create holding record uint8_t holder = 0; if(!furi_create("test/holding", (void*)&holder, sizeof(holder))) { - fprintf(debug_uart, "cannot create record\n"); + fuprintf(log, "cannot create record\n"); return false; } // 2. Open/Subscribe on it - FuriRecordHandler holding_record = furi_open( + FuriRecordSubscriber* holding_record = furi_open( "test/holding", false, false, holding_record_cb, NULL ); - if(holding_record.record == NULL) { - fprintf(debug_uart, "cannot open record\n"); + if(holding_record == NULL) { + fuprintf(log, "cannot open record\n"); return false; } const uint8_t WRITE_VALUE = 1; // 3. write data - if(!furi_write(&holding_record, &WRITE_VALUE, sizeof(uint8_t))) { - fprintf(debug_uart, "cannot write to record\n"); + if(!furi_write(holding_record, &WRITE_VALUE, sizeof(uint8_t))) { + fuprintf(log, "cannot write to record\n"); return false; } // 4. check that subscriber get data if(holding_record_value != WRITE_VALUE) { - fprintf(debug_uart, "wrong sub value (get %d, write %d)\n", holding_record_value, WRITE_VALUE); + fuprintf(log, "wrong sub value (get %d, write %d)\n", holding_record_value, WRITE_VALUE); return false; } // 5. Read and check data uint8_t read_value = 0; - if(!furi_read(&holding_record, &read_value, sizeof(uint8_t))) { - fprintf(debug_uart, "cannot read from record\n"); + if(!furi_read(holding_record, &read_value, sizeof(uint8_t))) { + fuprintf(log, "cannot read from record\n"); return false; } if(read_value != WRITE_VALUE) { - fprintf(debug_uart, "wrong read value (get %d, write %d)\n", read_value, WRITE_VALUE); + fuprintf(log, "wrong read value (get %d, write %d)\n", read_value, WRITE_VALUE); return false; } // 6. Try to write/read wrong size of data - if(furi_write(&holding_record, &WRITE_VALUE, 100)) { - fprintf(debug_uart, "overflowed write not allowed\n"); + if(furi_write(holding_record, &WRITE_VALUE, 100)) { + fuprintf(log, "overflowed write not allowed\n"); return false; } - if(furi_read(&holding_record, &read_value, 100)) { - fprintf(debug_uart, "overflowed read not allowed\n"); + if(furi_read(holding_record, &read_value, 100)) { + fuprintf(log, "overflowed read not allowed\n"); return false; } @@ -161,21 +161,22 @@ typedef struct { } ConcurrentValue; void furi_concurent_app(void* p) { - FILE* debug_uart = (FILE*)p; + FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; - FuriRecordHandler holding_record = furi_open( + FuriRecordSubscriber* holding_record = furi_open( "test/concurrent", false, false, NULL, NULL ); - if(holding_record.record == NULL) { - fprintf(debug_uart, "cannot open record\n"); + if(holding_record == NULL) { + fuprintf(log, "cannot open record\n"); furiac_exit(NULL); } for(size_t i = 0; i < 10; i++) { - ConcurrentValue* value = (ConcurrentValue*)furi_take(&holding_record); + ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); if(value == NULL) { - fprintf(debug_uart, "cannot take record\n"); + fuprintf(log, "cannot take record\n"); + furi_give(holding_record); furiac_exit(NULL); } // emulate read-modify-write broken by context switching @@ -186,40 +187,41 @@ void furi_concurent_app(void* p) { delay(2); // this is only for test, do not add delay between take/give in prod! value->a = a; value->b = b; - furi_give(&holding_record); + furi_give(holding_record); } furiac_exit(NULL); } -bool test_furi_concurrent_access(FILE* debug_uart) { +bool test_furi_concurrent_access(FuriRecordSubscriber* log) { // 1. Create holding record ConcurrentValue holder = {.a = 0, .b = 0}; if(!furi_create("test/concurrent", (void*)&holder, sizeof(ConcurrentValue))) { - fprintf(debug_uart, "cannot create record\n"); + fuprintf(log, "cannot create record\n"); return false; } // 2. Open it - FuriRecordHandler holding_record = furi_open( + FuriRecordSubscriber* holding_record = furi_open( "test/concurrent", false, false, NULL, NULL ); - if(holding_record.record == NULL) { - fprintf(debug_uart, "cannot open record\n"); + if(holding_record == NULL) { + fuprintf(log, "cannot open record\n"); return false; } // 3. Create second app for interact with it FuriApp* second_app = furiac_start( - furi_concurent_app, "furi concurent app", (void*)debug_uart + furi_concurent_app, "furi concurent app", (void*)log ); // 4. multiply ConcurrentValue::a for(size_t i = 0; i < 4; i++) { - ConcurrentValue* value = (ConcurrentValue*)furi_take(&holding_record); + ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); if(value == NULL) { - fprintf(debug_uart, "cannot take record\n"); + fuprintf(log, "cannot take record\n"); + furi_give(holding_record); return false; } // emulate read-modify-write broken by context switching @@ -230,18 +232,18 @@ bool test_furi_concurrent_access(FILE* debug_uart) { value->a = a; delay(10); // this is only for test, do not add delay between take/give in prod! value->b = b; - furi_give(&holding_record); + furi_give(holding_record); } delay(20); if(second_app->handler != NULL) { - fprintf(debug_uart, "second app still alive\n"); + fuprintf(log, "second app still alive\n"); return false; } if(holder.a != holder.b) { - fprintf(debug_uart, "broken integrity: a=%d, b=%d\n", holder.a, holder.b); + fuprintf(log, "broken integrity: a=%d, b=%d\n", holder.a, holder.b); return false; } @@ -256,7 +258,7 @@ TEST: non-existent data TODO: implement this test */ -bool test_furi_nonexistent_data(FILE* debug_uart) { +bool test_furi_nonexistent_data(FuriRecordSubscriber* log) { return true; } @@ -315,20 +317,20 @@ void mute_record_state_cb(FlipperRecordState state) { } void furi_mute_parent_app(void* p) { - FILE* debug_uart = (FILE*)p; + FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; // 1. Create pipe record if(!furi_create("test/mute", NULL, 0)) { - fprintf(debug_uart, "cannot create record\n"); + fuprintf(log, "cannot create record\n"); furiac_exit(NULL); } // 2. Open watch handler: solo=false, no_mute=false, subscribe to data - FuriRecordHandler watch_handler = furi_open( + FuriRecordSubscriber* watch_handler = furi_open( "test/mute", false, false, mute_record_cb, NULL ); - if(watch_handler.record == NULL) { - fprintf(debug_uart, "cannot open watch handler\n"); + if(watch_handler == NULL) { + fuprintf(log, "cannot open watch handler\n"); furiac_exit(NULL); } @@ -338,61 +340,61 @@ void furi_mute_parent_app(void* p) { } } -bool test_furi_mute_algorithm(FILE* debug_uart) { +bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { // 1. Create "parent" application: FuriApp* parent_app = furiac_start( - furi_mute_parent_app, "parent app", (void*)debug_uart + furi_mute_parent_app, "parent app", (void*)log ); delay(2); // wait creating record // 2. Open handler A: solo=false, no_mute=false, NULL subscriber. Subscribe to state. - FuriRecordHandler handler_a = furi_open( + FuriRecordSubscriber* handler_a = furi_open( "test/mute", false, false, NULL, mute_record_state_cb ); - if(handler_a.record == NULL) { - fprintf(debug_uart, "cannot open handler A\n"); + if(handler_a == NULL) { + fuprintf(log, "cannot open handler A\n"); return false; } uint8_t test_counter = 1; // Try to write data to A and check subscriber - if(!furi_write(&handler_a, &test_counter, sizeof(uint8_t))) { - fprintf(debug_uart, "write to A failed\n"); + if(!furi_write(handler_a, &test_counter, sizeof(uint8_t))) { + fuprintf(log, "write to A failed\n"); return false; } if(mute_last_value != test_counter) { - fprintf(debug_uart, "value A mismatch: %d vs %d\n", mute_last_value, test_counter); + fuprintf(log, "value A mismatch: %d vs %d\n", mute_last_value, test_counter); return false; } // 3. Open handler B: solo=true, no_mute=true, NULL subscriber. - FuriRecordHandler handler_b = furi_open( + FuriRecordSubscriber* handler_b = furi_open( "test/mute", true, true, NULL, NULL ); - if(handler_b.record == NULL) { - fprintf(debug_uart, "cannot open handler B\n"); + if(handler_b == NULL) { + fuprintf(log, "cannot open handler B\n"); return false; } // Check A state cb get FlipperRecordStateMute. if(mute_last_state != FlipperRecordStateMute) { - fprintf(debug_uart, "A state is not FlipperRecordStateMute: %d\n", mute_last_state); + fuprintf(log, "A state is not FlipperRecordStateMute: %d\n", mute_last_state); return false; } test_counter = 2; // Try to write data to A and check that subscriber get no data. (muted) - if(furi_write(&handler_a, &test_counter, sizeof(uint8_t))) { - fprintf(debug_uart, "A not muted\n"); + if(furi_write(handler_a, &test_counter, sizeof(uint8_t))) { + fuprintf(log, "A not muted\n"); return false; } if(mute_last_value == test_counter) { - fprintf(debug_uart, "value A must be muted\n"); + fuprintf(log, "value A must be muted\n"); return false; } @@ -400,23 +402,23 @@ bool test_furi_mute_algorithm(FILE* debug_uart) { // Try to write data to B and check that subscriber get data. - if(!furi_write(&handler_b, &test_counter, sizeof(uint8_t))) { - fprintf(debug_uart, "write to B failed\n"); + if(!furi_write(handler_b, &test_counter, sizeof(uint8_t))) { + fuprintf(log, "write to B failed\n"); return false; } if(mute_last_value != test_counter) { - fprintf(debug_uart, "value B mismatch: %d vs %d\n", mute_last_value, test_counter); + fuprintf(log, "value B mismatch: %d vs %d\n", mute_last_value, test_counter); return false; } // 4. Open hadler C: solo=true, no_mute=false, NULL subscriber. - FuriRecordHandler handler_c = furi_open( + FuriRecordSubscriber* handler_c = furi_open( "test/mute", true, false, NULL, NULL ); - if(handler_c.record == NULL) { - fprintf(debug_uart, "cannot open handler C\n"); + if(handler_c == NULL) { + fuprintf(log, "cannot open handler C\n"); return false; } @@ -425,11 +427,11 @@ bool test_furi_mute_algorithm(FILE* debug_uart) { // TODO: Try to write data to C and check that subscriber get data. // 5. Open handler D: solo=false, no_mute=false, NULL subscriber. - FuriRecordHandler handler_d = furi_open( + FuriRecordSubscriber* handler_d = furi_open( "test/mute", false, false, NULL, NULL ); - if(handler_d.record == NULL) { - fprintf(debug_uart, "cannot open handler D\n"); + if(handler_d == NULL) { + fuprintf(log, "cannot open handler D\n"); return false; } @@ -445,7 +447,7 @@ bool test_furi_mute_algorithm(FILE* debug_uart) { // 7. Exit "parent application" if(!furiac_kill(parent_app)) { - fprintf(debug_uart, "kill parent_app fail\n"); + fuprintf(log, "kill parent_app fail\n"); return false; } diff --git a/applications/tests/furiac_test.c b/applications/tests/furiac_test.c index b1810a4d..2c0f30cb 100644 --- a/applications/tests/furiac_test.c +++ b/applications/tests/furiac_test.c @@ -1,7 +1,7 @@ #include #include #include "flipper.h" -#include "debug.h" +#include "log.h" /* Test: creating and killing task @@ -23,26 +23,26 @@ void create_kill_app(void* p) { } } -bool test_furi_ac_create_kill(FILE* debug_uart) { +bool test_furi_ac_create_kill(FuriRecordSubscriber* log) { uint8_t counter = 0; uint8_t value_a = counter; FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter); if(widget == NULL) { - fprintf(debug_uart, "create widget fail\n"); + fuprintf(log, "create widget fail\n"); return false; } delay(10); if(!furiac_kill(widget)) { - fprintf(debug_uart, "kill widget fail\n"); + fuprintf(log, "kill widget fail\n"); return false; } if(value_a == counter) { - fprintf(debug_uart, "counter unchanged\n"); + fuprintf(log, "counter unchanged\n"); return false; } @@ -51,7 +51,7 @@ bool test_furi_ac_create_kill(FILE* debug_uart) { delay(10); if(value_a != counter) { - fprintf(debug_uart, "counter changes after kill (counter = %d vs %d)\n", value_a, counter); + fuprintf(log, "counter changes after kill (counter = %d vs %d)\n", value_a, counter); return false; } @@ -111,7 +111,7 @@ void task_b(void* p) { furiac_exit(p); } -bool test_furi_ac_switch_exit(FILE* debug_uart) { +bool test_furi_ac_switch_exit(FuriRecordSubscriber* log) { // init sequence TestSwitchSequence seq; seq.count = 0; @@ -124,7 +124,7 @@ bool test_furi_ac_switch_exit(FILE* debug_uart) { seq.sequence[seq.count] = '\0'; if(strcmp(seq.sequence, "ABA/") != 0) { - fprintf(debug_uart, "wrong sequence: %s\n", seq.sequence); + fuprintf(log, "wrong sequence: %s\n", seq.sequence); return false; } diff --git a/applications/tests/test_index.c b/applications/tests/test_index.c index 4b07104e..7a9d6765 100644 --- a/applications/tests/test_index.c +++ b/applications/tests/test_index.c @@ -1,67 +1,67 @@ #include #include "flipper.h" -#include "debug.h" +#include "log.h" #include "flipper-core.h" -bool test_furi_ac_create_kill(FILE* debug_uart); -bool test_furi_ac_switch_exit(FILE* debug_uart); +bool test_furi_ac_create_kill(FuriRecordSubscriber* log); +bool test_furi_ac_switch_exit(FuriRecordSubscriber* log); -bool test_furi_pipe_record(FILE* debug_uart); -bool test_furi_holding_data(FILE* debug_uart); -bool test_furi_concurrent_access(FILE* debug_uart); -bool test_furi_nonexistent_data(FILE* debug_uart); -bool test_furi_mute_algorithm(FILE* debug_uart); +bool test_furi_pipe_record(FuriRecordSubscriber* log); +bool test_furi_holding_data(FuriRecordSubscriber* log); +bool test_furi_concurrent_access(FuriRecordSubscriber* log); +bool test_furi_nonexistent_data(FuriRecordSubscriber* log); +bool test_furi_mute_algorithm(FuriRecordSubscriber* log); void flipper_test_app(void* p) { - FILE* debug_uart = get_debug(); - - if(test_furi_ac_create_kill(debug_uart)) { - fprintf(debug_uart, "[TEST] test_furi_ac_create_kill PASSED\n"); + FuriRecordSubscriber* log = get_default_log(); + + if(test_furi_ac_create_kill(log)) { + fuprintf(log, "[TEST] test_furi_ac_create_kill PASSED\n"); } else { - fprintf(debug_uart, "[TEST] test_furi_ac_create_kill FAILED\n"); + fuprintf(log, "[TEST] test_furi_ac_create_kill FAILED\n"); } - if(test_furi_ac_switch_exit(debug_uart)) { - fprintf(debug_uart, "[TEST] test_furi_ac_switch_exit PASSED\n"); + if(test_furi_ac_switch_exit(log)) { + fuprintf(log, "[TEST] test_furi_ac_switch_exit PASSED\n"); } else { - fprintf(debug_uart, "[TEST] test_furi_ac_switch_exit FAILED\n"); + fuprintf(log, "[TEST] test_furi_ac_switch_exit FAILED\n"); } - if(test_furi_pipe_record(debug_uart)) { - fprintf(debug_uart, "[TEST] test_furi_pipe_record PASSED\n"); + if(test_furi_pipe_record(log)) { + fuprintf(log, "[TEST] test_furi_pipe_record PASSED\n"); } else { - fprintf(debug_uart, "[TEST] test_furi_pipe_record FAILED\n"); + fuprintf(log, "[TEST] test_furi_pipe_record FAILED\n"); } - if(test_furi_holding_data(debug_uart)) { - fprintf(debug_uart, "[TEST] test_furi_holding_data PASSED\n"); + if(test_furi_holding_data(log)) { + fuprintf(log, "[TEST] test_furi_holding_data PASSED\n"); } else { - fprintf(debug_uart, "[TEST] test_furi_holding_data FAILED\n"); + fuprintf(log, "[TEST] test_furi_holding_data FAILED\n"); } - if(test_furi_concurrent_access(debug_uart)) { - fprintf(debug_uart, "[TEST] test_furi_concurrent_access PASSED\n"); + if(test_furi_concurrent_access(log)) { + fuprintf(log, "[TEST] test_furi_concurrent_access PASSED\n"); } else { - fprintf(debug_uart, "[TEST] test_furi_concurrent_access FAILED\n"); + fuprintf(log, "[TEST] test_furi_concurrent_access FAILED\n"); } - if(test_furi_nonexistent_data(debug_uart)) { - fprintf(debug_uart, "[TEST] test_furi_nonexistent_data PASSED\n"); + if(test_furi_nonexistent_data(log)) { + fuprintf(log, "[TEST] test_furi_nonexistent_data PASSED\n"); } else { - fprintf(debug_uart, "[TEST] test_furi_nonexistent_data FAILED\n"); + fuprintf(log, "[TEST] test_furi_nonexistent_data FAILED\n"); } - if(test_furi_mute_algorithm(debug_uart)) { - fprintf(debug_uart, "[TEST] test_furi_mute_algorithm PASSED\n"); + if(test_furi_mute_algorithm(log)) { + fuprintf(log, "[TEST] test_furi_mute_algorithm PASSED\n"); } else { - fprintf(debug_uart, "[TEST] test_furi_mute_algorithm FAILED\n"); + fuprintf(log, "[TEST] test_furi_mute_algorithm FAILED\n"); } if(add(1, 2) == 3) { - fprintf(debug_uart, "[TEST] Rust add PASSED\n"); + fuprintf(log, "[TEST] Rust add PASSED\n"); } else { - fprintf(debug_uart, "[TEST] Rust add FAILED\n"); + fuprintf(log, "[TEST] Rust add FAILED\n"); } furiac_exit(NULL); diff --git a/core/app.cpp b/core/app.cpp index 3c666808..807b3474 100644 --- a/core/app.cpp +++ b/core/app.cpp @@ -4,10 +4,16 @@ extern "C" { #include "startup.h" #include "furi.h" - #include "debug.h" + #include "log.h" + #include "tty_uart.h" } extern "C" void app() { + register_tty_uart(); + + FuriRecordSubscriber* log = get_default_log(); + fuprintf(log, "\n=== Welcome to Flipper Zero! ===\n\n"); + // FURI startup FuriApp* handlers[sizeof(FLIPPER_STARTUP)/sizeof(FLIPPER_STARTUP[0])]; diff --git a/core/debug.c b/core/debug.c deleted file mode 100644 index ce482417..00000000 --- a/core/debug.c +++ /dev/null @@ -1,31 +0,0 @@ -#define _GNU_SOURCE -#include "main.h" -#include - -extern UART_HandleTypeDef DEBUG_UART; - -ssize_t uart_write(void* cookie, const char * buffer, size_t size) { - if (buffer == 0) { - /* - * This means that we should flush internal buffers. Since we - * don't we just return. (Remember, "handle" == -1 means that all - * handles should be flushed.) - */ - return 0; - } - - return (ssize_t)HAL_UART_Transmit(&DEBUG_UART, (uint8_t*)buffer, (uint16_t)size, HAL_MAX_DELAY); -} - -FILE* get_debug() { - FILE* fp = fopencookie(NULL, "w+", (cookie_io_functions_t){ - .read = NULL, - .write = uart_write, - .seek = NULL, - .close = NULL - }); - - setvbuf(fp, NULL, _IONBF, 0); - - return fp; -} \ No newline at end of file diff --git a/core/debug.h b/core/debug.h deleted file mode 100644 index c94181a2..00000000 --- a/core/debug.h +++ /dev/null @@ -1 +0,0 @@ -FILE* get_debug(); \ No newline at end of file diff --git a/core/furi.c b/core/furi.c index 1a5d3ff1..fc1e91ec 100644 --- a/core/furi.c +++ b/core/furi.c @@ -35,7 +35,7 @@ bool furi_create(const char* name, void* value, size_t size) { if(current_buffer_idx >= MAX_RECORD_COUNT) { // max record count exceed #ifdef FURI_DEBUG - printf("[FURI] max record count exceed\n"); + printf("[FURI] create: max record count exceed\n"); #endif return NULL; } @@ -52,10 +52,12 @@ bool furi_create(const char* name, void* value, size_t size) { records[current_buffer_idx].subscribers[i].allocated = false; } + current_buffer_idx++; + return true; } -FuriRecordHandler furi_open( +FuriRecordSubscriber* furi_open( const char* name, bool solo, bool no_mute, @@ -75,16 +77,15 @@ FuriRecordHandler furi_open( printf("[FURI] cannot find record %s\n", name); #endif - FuriRecordHandler res = {.record = NULL, .subscriber = NULL}; - return res; + return NULL; } // allocate subscriber FuriRecordSubscriber* subscriber = NULL; for(size_t i = 0; i < MAX_RECORD_SUBSCRIBERS; i++) { - if(!records[current_buffer_idx].subscribers[i].allocated) { - subscriber = &records[current_buffer_idx].subscribers[i]; + if(!record->subscribers[i].allocated) { + subscriber = &record->subscribers[i]; break; } } @@ -92,11 +93,10 @@ FuriRecordHandler furi_open( if(subscriber == NULL) { // cannot add subscriber (full) #ifdef FURI_DEBUG - printf("[FURI] cannot add subscriber (full)\n"); + printf("[FURI] open: cannot add subscriber (full)\n"); #endif - - FuriRecordHandler res = {.record = NULL, .subscriber = NULL}; - return res; + + return NULL; } // increase mute_counter @@ -110,25 +110,31 @@ FuriRecordHandler furi_open( subscriber->no_mute = no_mute; subscriber->cb = value_callback; subscriber->state_cb = state_callback; + subscriber->record = record; // register record in application FuriApp* current_task = find_task(xTaskGetCurrentTaskHandle()); - current_task->records[current_task->records_count] = record; - current_task->records_count++; + if(current_task != NULL) { + current_task->records[current_task->records_count] = record; + current_task->records_count++; + } else { + #ifdef FURI_DEBUG + printf("[FURI] open: no current task\n"); + #endif + } - FuriRecordHandler res = {.record = record, .subscriber = subscriber}; - return res; + return subscriber; } -void furi_close(FuriRecordHandler* handler) { +void furi_close(FuriRecordSubscriber* handler) { #ifdef FURI_DEBUG printf("[FURI] closing %s record\n", handler->record->name); #endif // deallocate subscriber - handler->subscriber->allocated = false; + handler->allocated = false; // set mute counter to next max value uint8_t max_mute_counter = 0; @@ -142,7 +148,7 @@ void furi_close(FuriRecordHandler* handler) { handler->record->mute_counter = max_mute_counter; } -static void furi_notify(FuriRecordHandler* handler, const void* value, size_t size) { +static void furi_notify(FuriRecordSubscriber* handler, const void* value, size_t size) { for(size_t i = 0; i < MAX_RECORD_SUBSCRIBERS; i++) { if(handler->record->subscribers[i].allocated) { if(handler->record->subscribers[i].cb != NULL) { @@ -152,17 +158,17 @@ static void furi_notify(FuriRecordHandler* handler, const void* value, size_t si } } -void* furi_take(FuriRecordHandler* handler) { +void* furi_take(FuriRecordSubscriber* handler) { // take mutex return handler->record->value; } -void furi_give(FuriRecordHandler* handler) { +void furi_give(FuriRecordSubscriber* handler) { // release mutex } -bool furi_read(FuriRecordHandler* handler, void* value, size_t size) { +bool furi_read(FuriRecordSubscriber* handler, void* value, size_t size) { #ifdef FURI_DEBUG printf("[FURI] read from %s\n", handler->record->name); #endif @@ -182,23 +188,44 @@ bool furi_read(FuriRecordHandler* handler, void* value, size_t size) { return true; } -bool furi_write(FuriRecordHandler* handler, const void* value, size_t size) { +bool furi_write(FuriRecordSubscriber* handler, const void* value, size_t size) { #ifdef FURI_DEBUG printf("[FURI] write to %s\n", handler->record->name); #endif - if(handler == NULL || handler->record == NULL || value == NULL) return false; + if(handler == NULL || handler->record == NULL || value == NULL) { + #ifdef FURI_DEBUG + printf("[FURI] write: null param %x %x\n", (uint32_t)(size_t)handler, (uint32_t)(size_t)value); + #endif + + return false; + } // check if closed - if(!handler->subscriber->allocated) return false; + if(!handler->allocated) { + #ifdef FURI_DEBUG + printf("[FURI] write: handler closed\n"); + #endif + return false; + } - if(handler->record->value != NULL && size > handler->record->size) return false; + if(handler->record->value != NULL && size > handler->record->size) { + #ifdef FURI_DEBUG + printf("[FURI] write: wrong size %d\n", (uint32_t)size); + #endif + return false; + } // check mute if( - handler->record->mute_counter != handler->subscriber->mute_counter - && !handler->subscriber->no_mute - ) return false; + handler->record->mute_counter != handler->mute_counter + && !handler->no_mute + ) { + #ifdef FURI_DEBUG + printf("[FURI] write: muted\n"); + #endif + return false; + } if(handler->record->value != NULL) { // real write to value diff --git a/core/furi.h b/core/furi.h index 53d029a4..00db3406 100644 --- a/core/furi.h +++ b/core/furi.h @@ -22,16 +22,19 @@ typedef enum { /// pointer to state callback function typedef void(*FlipperRecordStateCallback)(FlipperRecordState); +struct _FuriRecord; + typedef struct { bool allocated; FlipperRecordCallback cb; ///< value cb FlipperRecordStateCallback state_cb; ///< state cb uint8_t mute_counter; ///< see "wiki/FURI#mute-algorithm" bool no_mute; + struct _FuriRecord* record; ///< parent record } FuriRecordSubscriber; /// FURI record handler -typedef struct { +struct _FuriRecord { const char* name; void* value; size_t size; @@ -39,13 +42,9 @@ typedef struct { SemaphoreHandle_t mutex; uint8_t mute_counter; FuriRecordSubscriber subscribers[MAX_RECORD_SUBSCRIBERS]; -} FuriRecord; +}; -/// FURI record handler for use after open -typedef struct { - FuriRecord* record; ///< full record (for read/write/take/give value) - FuriRecordSubscriber* subscriber; ///< current handler info -} FuriRecordHandler; +typedef struct _FuriRecord FuriRecord; /// store info about active task typedef struct { @@ -110,7 +109,7 @@ When appication has exited or record has closed, all handlers is unmuted. It may be useful for concurrently acces to resources like framebuffer or beeper. \param[in] no_mute if true, another applications cannot mute this handler. */ -FuriRecordHandler furi_open( +FuriRecordSubscriber* furi_open( const char* name, bool solo, bool no_mute, @@ -121,7 +120,7 @@ FuriRecordHandler furi_open( /*! */ -void furi_close(FuriRecordHandler* handler); +void furi_close(FuriRecordSubscriber* handler); /*! read message from record. @@ -130,7 +129,7 @@ Also return false if you try to read from FURI pipe TODO: enum return value with execution status */ -bool furi_read(FuriRecordHandler* record, void* data, size_t size); +bool furi_read(FuriRecordSubscriber* record, void* data, size_t size); /*! write message to record. @@ -138,7 +137,7 @@ Returns true if success, false otherwise (closed/non-existent record or muted). TODO: enum return value with execution status */ -bool furi_write(FuriRecordHandler* record, const void* data, size_t size); +bool furi_write(FuriRecordSubscriber* record, const void* data, size_t size); /*! lock value mutex. @@ -150,9 +149,9 @@ Returns pointer to data, NULL if closed/non-existent record or muted TODO: enum return value with execution status */ -void* furi_take(FuriRecordHandler* record); +void* furi_take(FuriRecordSubscriber* record); /*! unlock value mutex. */ -void furi_give(FuriRecordHandler* record); +void furi_give(FuriRecordSubscriber* record); diff --git a/core/log.c b/core/log.c new file mode 100644 index 00000000..8e989652 --- /dev/null +++ b/core/log.c @@ -0,0 +1,25 @@ +#define _GNU_SOURCE + +#include +#include +#include + +#include "log.h" +#include "furi.h" + +#define PRINT_STR_SIZE 64 + +void fuprintf(FuriRecordSubscriber* f, const char * format, ...) { + char buffer[PRINT_STR_SIZE]; + + va_list args; + va_start(args, format); + vsprintf(buffer, format, args); + va_end(args); + + furi_write(f, buffer, strlen(buffer)); +} + +FuriRecordSubscriber* get_default_log() { + return furi_open("tty", false, false, NULL, NULL); +} \ No newline at end of file diff --git a/core/log.h b/core/log.h new file mode 100644 index 00000000..9417ddaf --- /dev/null +++ b/core/log.h @@ -0,0 +1,6 @@ +#pragma once + +#include "furi.h" + +FuriRecordSubscriber* get_default_log(); +void fuprintf(FuriRecordSubscriber* f, const char * format, ...); diff --git a/core/tty_uart.c b/core/tty_uart.c new file mode 100644 index 00000000..20ef2caf --- /dev/null +++ b/core/tty_uart.c @@ -0,0 +1,20 @@ +#include "furi.h" +#include "main.h" + +extern UART_HandleTypeDef DEBUG_UART; + +void handle_uart_write(const void* data, size_t size) { + HAL_UART_Transmit(&DEBUG_UART, (uint8_t*)data, (uint16_t)size, HAL_MAX_DELAY); +} + +bool register_tty_uart() { + if(!furi_create("tty", NULL, 0)) { + return false; + } + + if(furi_open("tty", false, false, handle_uart_write, NULL) == NULL) { + return false; + } + + return true; +} \ No newline at end of file diff --git a/core/tty_uart.h b/core/tty_uart.h new file mode 100644 index 00000000..21cdd676 --- /dev/null +++ b/core/tty_uart.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +bool register_tty_uart(); diff --git a/target_f1/Makefile b/target_f1/Makefile index 04261bd1..417ca185 100644 --- a/target_f1/Makefile +++ b/target_f1/Makefile @@ -111,7 +111,8 @@ startup_stm32l476xx.s CPP_SOURCES += ../core/app.cpp -C_SOURCES += ../core/debug.c +C_SOURCES += ../core/log.c +C_SOURCES += ../core/tty_uart.c C_SOURCES += ../core/furi.c C_SOURCES += ../core/furi_ac.c @@ -131,6 +132,11 @@ C_SOURCES += ../applications/examples/blink.c C_DEFS += -DEXAMPLE_BLINK endif +ifeq ($(EXAMPLE_UART_WRITE), 1) +C_SOURCES += ../applications/examples/uart_write.c +C_DEFS += -DEXAMPLE_UART_WRITE +endif + # User application # Add C_SOURCES +=, C_DEFS += or CPP_SOURCES += here @@ -253,6 +259,9 @@ all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET example_blink: EXAMPLE_BLINK=1 make +example_uart_write: + EXAMPLE_UART_WRITE=1 make + test: TEST=1 make diff --git a/target_lo/Makefile b/target_lo/Makefile index 79e98549..ee35e317 100644 --- a/target_lo/Makefile +++ b/target_lo/Makefile @@ -32,11 +32,13 @@ C_SOURCES += Src/lo_os.c C_SOURCES += Src/lo_hal.c C_DEFS += -DFURI_DEBUG + # Core CPP_SOURCES += ../core/app.cpp -C_SOURCES += ../core/debug.c +C_SOURCES += ../core/log.c +C_SOURCES += ../core/tty_uart.c C_SOURCES += ../core/furi.c C_SOURCES += ../core/furi_ac.c @@ -56,6 +58,11 @@ C_SOURCES += ../applications/examples/blink.c C_DEFS += -DEXAMPLE_BLINK endif +ifeq ($(EXAMPLE_UART_WRITE), 1) +C_SOURCES += ../applications/examples/uart_write.c +C_DEFS += -DEXAMPLE_UART_WRITE +endif + # User application # Add C_SOURCES +=, C_DEFS += or CPP_SOURCES += here @@ -130,10 +137,18 @@ all: $(BUILD_DIR)/$(TARGET) example_blink: EXAMPLE_BLINK=1 make + rm $(BUILD_DIR)/app.o + $(BUILD_DIR)/$(TARGET) + + +example_uart_write: + EXAMPLE_UART_WRITE=1 make + rm $(BUILD_DIR)/app.o $(BUILD_DIR)/$(TARGET) test: TEST=1 make + rm $(BUILD_DIR)/app.o $(BUILD_DIR)/$(TARGET) diff --git a/wiki/examples/UART-write.md b/wiki/examples/UART-write.md new file mode 100644 index 00000000..3c784637 --- /dev/null +++ b/wiki/examples/UART-write.md @@ -0,0 +1,71 @@ +In this example we try to use FURI for interacting between user application and core subsystem. + +First of all, we open FURI record by name "tty". This record is used for send some debug/logging info and interact with user by kind-of-TTY (like UART or USB CDC). By default on Flipper target all writes to tty record handled by debug UART (configured by `DEBUG_UART` define). On local target all writes simply prints to stdout. + +Open record: + +```C +FuriRecordSubscriber* log = get_default_log(); +``` + +This is just wrapper on common FURI method: + +```C +furi_open("tty", false, false, NULL, NULL); +``` + +"tty" is FURI pipe record. It means that there is no "data" hold in record, it only manage callbacks: when you call `furi_write`, all subscriber's callback is called. You can find default implementation in `core/tty_uart.c`. + +Let's get a look at full example code: + +```C +#include "flipper.h" +#include +#include "log.h" + +void application_uart_write(void* p) { + // Red led for showing progress + GpioPin led = {.pin = GPIO_PIN_8, .port = GPIOA}; + pinMode(led, GpioModeOpenDrain); + + // get_default_log open "tty" record + FuriRecordSubscriber* log = get_default_log(); + + // create buffer + const char test_string[] = "test\n"; + furi_write(log, test_string, strlen(test_string)); + + // for example, create counter and show its value + uint8_t counter = 0; + + while(1) { + // continously write it to UART + fuprintf(log, "counter: %d\n", counter); + counter++; + + // flash at every send + digitalWrite(led, LOW); + delay(50); + digitalWrite(led, HIGH); + + // delay with overall perion of 1s + delay(950); + } +} +``` + +This code demonstrates two way to work with record: + +1. Directly writes some data by `furi_write` +2. Uses `fuprintf` wrapper on `printf`. + +For creating application and set it to autorun, read [Blink example](Blink-app). + +_You can also find source of this example in `applications/examples/uart_write.c` and run it by `docker-compose exec dev make -C target_lo example_uart_write`_ + +![](https://github.com/Flipper-Zero/flipperzero-firmware-community/raw/master/wiki_static/application_examples/example_uart_write.gif) + +_Code for target F1 can be compiled by `docker-compose exec dev make -C target_f1 example_uart_write`_ + +![](https://github.com/Flipper-Zero/flipperzero-firmware-community/raw/master/wiki_static/application_examples/example_uart_write_hw.gif) + diff --git a/wiki/fw/Application-examples.md b/wiki/fw/Application-examples.md index 3eff9592..2e907691 100644 --- a/wiki/fw/Application-examples.md +++ b/wiki/fw/Application-examples.md @@ -25,4 +25,5 @@ void application_name(void* p) { # Application examples -* **[Blink](Blink-app)** +* **[Blink](Blink-app)** show how to create app and control GPIO +* **[UART write](UART-write)** operate with FURI pipe and print some messages diff --git a/wiki_static/application_examples/example_uart_write.gif b/wiki_static/application_examples/example_uart_write.gif new file mode 100644 index 00000000..e40d39f4 --- /dev/null +++ b/wiki_static/application_examples/example_uart_write.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:818b3ef2a3b10fdade1be9459904f8295f75efd16fb532927d5ef6ff187e60e6 +size 265769 diff --git a/wiki_static/application_examples/example_uart_write_hw.gif b/wiki_static/application_examples/example_uart_write_hw.gif new file mode 100644 index 00000000..90514517 --- /dev/null +++ b/wiki_static/application_examples/example_uart_write_hw.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76e5e8205a6cec14f5cc34f9b48a12133e0a8d79347f3286eb4bb28aacde4337 +size 772608