UART write example (#53)
* Rename test functions * rewrite furi API, segfault * make fixes in FURI, log through FURI * add uart write example blank * implement fuprintf instead of fopencookie * add gif, blank page * UART write example description Co-authored-by: Vadim Kaushan <admin@disasm.info>
This commit is contained in:
parent
5094623d04
commit
4dc82b68d1
33
applications/examples/uart_write.c
Normal file
33
applications/examples/uart_write.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include "flipper.h"
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
||||
}
|
@ -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
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -1,67 +1,67 @@
|
||||
#include <stdio.h>
|
||||
#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);
|
||||
|
@ -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])];
|
||||
|
||||
|
31
core/debug.c
31
core/debug.c
@ -1,31 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "main.h"
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
@ -1 +0,0 @@
|
||||
FILE* get_debug();
|
81
core/furi.c
81
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
|
||||
|
25
core/furi.h
25
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);
|
||||
|
25
core/log.c
Normal file
25
core/log.c
Normal file
@ -0,0 +1,25 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
6
core/log.h
Normal file
6
core/log.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "furi.h"
|
||||
|
||||
FuriRecordSubscriber* get_default_log();
|
||||
void fuprintf(FuriRecordSubscriber* f, const char * format, ...);
|
20
core/tty_uart.c
Normal file
20
core/tty_uart.c
Normal file
@ -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;
|
||||
}
|
5
core/tty_uart.h
Normal file
5
core/tty_uart.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool register_tty_uart();
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
71
wiki/examples/UART-write.md
Normal file
71
wiki/examples/UART-write.md
Normal file
@ -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 <string.h>
|
||||
#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)
|
||||
|
@ -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
|
||||
|
3
wiki_static/application_examples/example_uart_write.gif
Normal file
3
wiki_static/application_examples/example_uart_write.gif
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:818b3ef2a3b10fdade1be9459904f8295f75efd16fb532927d5ef6ff187e60e6
|
||||
size 265769
|
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:76e5e8205a6cec14f5cc34f9b48a12133e0a8d79347f3286eb4bb28aacde4337
|
||||
size 772608
|
Loading…
Reference in New Issue
Block a user