diff --git a/wiki/fw/Core-API.md b/wiki/fw/Core-API.md new file mode 100644 index 00000000..d1a35092 --- /dev/null +++ b/wiki/fw/Core-API.md @@ -0,0 +1,82 @@ +# Basic concepts: + +* ValueMutex +* PubSub, Publisher, Subscriber +* ValueManager +* LayeredReducer + +# HAL + +We use [Zephyr HAL](https://docs.zephyrproject.org/latest/reference/peripherals/index.html). + +# OS + +We use [CMSIS OS v2](https://www.keil.com/pack/doc/CMSIS_Dev/RTOS2/html/group__CMSIS__RTOS.html) for thread management and IPC. + +# UI + +* **[Input](API:Input)** + +* **[Display](API:Display)** + +* **[LED](API:LED)** + +## vibro + +* **[Sound](API:Sound)** + +## backlight + +# System + +## batt voltage + +## batt charge + +# CC1101 + +## SPI + +## IRQ + +# SD Card + +## SPI + +# NFC + +## SPI + +## IRQ + +# IR + +## TX LED + +## RX ADC + +# RFID 125 kHz + +## Carrier + +## Pull + +## Comparator RX (shared with touch key) + +# Touch key + +## Pull + +## Comparator RX (shared with RFID 125 kHz) + +# External GPIO + +# External SPI + +# External I2C + +# UART + +# USB + +# BLE diff --git a/wiki/fw/api/API:Display.md b/wiki/fw/api/API:Display.md new file mode 100644 index 00000000..ab737162 --- /dev/null +++ b/wiki/fw/api/API:Display.md @@ -0,0 +1,61 @@ +All display operations based on [u8g2](https://github.com/olikraus/u8g2) library. + +API available as struct, contains u8g2 functions, instance and fonts: + +```C +typedef struct { + ValueManager* display; /// ValueManager + void (*u8g2_SetFont)(u8g2_t *u8g2, const uint8_t *font); + void (*u8g2_SetDrawColor)(u8g2_t *u8g2, uint8_t color); + void (*u8g2_SetFontMode)(u8g2_t *u8g2, uint8_t is_transparent); + u8g2_uint_t (*u8g2_DrawStr)(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *str); + + Fonts fonts; +} Display; + +typedef struct { + const uint8_t* u8g2_font_6x10_mf; +} Fonts; +``` + +First of all you can open display API instance by calling `open_display` + +```C +/// Get display instance and API +inline Display* open_display(const char* name) { + return (Display*)furi_open(name); +} +``` + +Default display name is `/dev/display`. + +For draw something to display you can get display instance pointer by calling `take_display`, do something and commit your changes by calling `commit_display`: + +```C +/// return pointer in case off success, NULL otherwise +inline u8g2_t* take_display(Display* api, uint32_t timeout) { + return (u8g2_t*)take_mutex(api->display->value, timeout); +} + +inline void commit_display(Display* api, u8g2_t* display) { + commit_valuemanager(api->display, display); +} +``` + +## Usage example + +```C +void u8g2_example(void* p) { + Display* display_api = open_display("/dev/display"); + if(display_api == NULL) return; // display not available, critical error + + u8g2_t* display = take_display(display_api); + if(display != NULL) { + display_api->u8g2_SetFont(display, display_api->fonts.u8g2_font_6x10_mf); + display_api->u8g2_SetDrawColor(display, 1); + display_api->u8g2_SetFontMode(display, 1); + display_api->u8g2_DrawStr(display, 2, 12, "hello world!"); + } + commit_display(display_api, display); +} +``` \ No newline at end of file diff --git a/wiki/fw/api/API:Input.md b/wiki/fw/api/API:Input.md new file mode 100644 index 00000000..0fcea327 --- /dev/null +++ b/wiki/fw/api/API:Input.md @@ -0,0 +1,107 @@ +All input API available by struct: + +```C +typedef struct { + Subscriber* events; /// debounced keyboards events: press/release, Subscriber + Subscriber* raw_events; /// raw keyboards events: press/release, Subscriber + ValueMutex* state; /// current keyboard state, ValueMutex +} Input; +``` + +You can get API instance by calling `open_input`: + +```C +/// Get input struct +inline Input* open_input(const char* name) { + return furi_open(name); +} +``` + +Default (system) input name is `/dev/kb`. + +Buttons state store as struct: + +```C +/// Current state of buttons +typedef struct { + bool up; + bool down; + bool right; + bool left; + bool ok; + bool back; +} InputState; +``` + +To read buttons state you should use `read_state` function: + +```C +/// read current state of all buttons. Return true if success, false otherwise +inline bool read_state(ValueMutex* state, InputState* value, uint32_t timeout) { + return read_mutex(state, (void*)value, sizeof(InputState), timeout); +} +``` + +Also you can subscribe to input events: + +```C +/// used to pass button press/release evens +typedef struct { + Inputs input; /// what button + bool state; /// true = press, false = release +} InputEvent; + +/// List of buttons +typedef enum { + InputsUp = 0, + InputsDown, + InputsRight, + InputsLeft, + InputsOk, + InputsBack, + InputsSize +} Inputs; +``` + +Use `subscribe_input_events` to register your callback: + +```C +/// subscribe on button press/release events. Return true if success, false otherwise +inline bool subscribe_input_events(Subscriber* events, void(*cb)(InputEvent*, void*), void* ctx) { + return subscribe_pubsub(events, void(*)(void*, void*)(cb), ctx); +} +``` + +## Usage example + +```C +// function used to handle keyboard events +void handle_keyboard(InputEvent* event, void* _ctx) { + if(event->state) { + printf("you press %d", event->input); + } else { + printf("you release %d", event->input); + } +} + +void input_example(void* p) { + Input* input = open_input("/dev/kb"); + if(input == NULL) return; // keyboard not available, critical error + + // async way + subscribe_input_events(input->events, handle_keyboard, NULL); + + // blocking way + InputState state; + while(1) { + if(read_state(input->state, &state, OsWaitForever)) { + if(state.up) { + printf("up is pressed"); + delay(1000); + } + } + + delay(10); + } +} +``` diff --git a/wiki/fw/api/API:LED.md b/wiki/fw/api/API:LED.md new file mode 100644 index 00000000..e0ddac87 --- /dev/null +++ b/wiki/fw/api/API:LED.md @@ -0,0 +1,124 @@ +LED state describes by struct: + +```C +typedef struct { + uint8_t red; + uint8_t green; + uint8_t blue; +} Rgb; +``` + +LED API provided by struct: + +```C +typedef struct { + LayeredReducer* source; /// every app add its layer to set value, LayeredReducer + Subscriber* updates; /// LED value changes Supscriber + ValueMutex* state; /// LED state, ValueMutex +} LedApi; +``` + +You can get API instance by calling `open_led`: + +```C +/// Add new layer to LED: +inline LedApi* open_led(const char* name) { + return (LedApi*)furi_open(name); +} +``` + +Default system led is `/dev/led`. + +Then add new layer to control LED by calling `add_led_layer`: + +```C +inline ValueManager* add_led_layer(Rgb* layer, uint8_t priority) { + ValueManager* manager = register_valuemanager((void*)layer); + if(manager == NULL) return NULL; + + if(!add_layered_reducer(manager, priority, layer_compose_default)) { + unregister_valuemanager(manager); + return NULL; + } + + return manager; +} +``` + +For change led you can get display instance pointer by calling `take_led`, do something and commit your changes by calling `commit_led`. Or you can call `write_led`: + +```C +/// return pointer in case off success, NULL otherwise +inline Rgb* take_led(ValueManager* led, uint32_t timeout) { + return (Rgb*)take_mutex(led->value, timeout); +} + +inline void commit_led(ValueManager* led, Rgb* value) { + commit_valuemanager(led, value); +} + +/// return true if success, false otherwise +inline bool write_led(ValueManager* led, Rgb* value, uint32_t timeout) { + return write_valuemanager(state, (void*)value, sizeof(Rgb), timeout); +} +``` + +To read current led state you should use `read_led` function: + +```C +/// return true if success, false otherwise +inline bool read_led(ValueManager* led, Rgb* value, uint32_t timeout) { + return read_mutex(led->value, (void*)value, sizeof(Rgb), timeout); +} +``` + +Also you can subscribe to led state changes: + +Use `subscribe_led_changes` to register your callback: + +```C +/// return true if success, false otherwise +inline bool subscribe_led_changes(Subscriber* updates, void(*cb)(Rgb*, void*), void* ctx) { + return subscribe_pubsub(events, void(*)(void*, void*)(cb), ctx); +} +``` + +## Usage example + +```C + +void handle_led_state(Rgb* rgb, void* _ctx) { + printf("led: #%02X%02X%02X\n", rgb->red, rgb->green, rgb->blue); +} + +void led_example(void* p) { + LedApi* led_api = open_display("/dev/led"); + if(led_api == NULL) return; // led not available, critical error + + // subscribe to led state updates + subscribe_led_changes(led_api->updates, handle_led_state, NULL); + + Rgb current_state; + if(read_led(led_api->state, ¤t_state, OsWaitForever)) { + printf( + "initial led: #%02X%02X%02X\n", + current_state->red, + current_state->green, + current_state->blue + ); + } + + // add layer to control led + ValueManager* led_manager = add_led_layer(¤t_state, UI_LAYER_APP); + + // write only blue by getting pointer + Rgb* rgb = take_led(led_manager, OsWaitForever); + if(rgb != NULL) { + rgb->blue = 0; + } + commit_led(led_manager, rgb); + + // write RGB value + write_led(led_manager, &(Rgb{.red = 0xFA, green = 0xCE, .blue = 0x8D}), OsWaitForever); +} +``` diff --git a/wiki/fw/api/API:Sound.md b/wiki/fw/api/API:Sound.md new file mode 100644 index 00000000..4cd528bb --- /dev/null +++ b/wiki/fw/api/API:Sound.md @@ -0,0 +1,122 @@ +sound state describes by struct: + +```C +typedef struct { + float freq; /// frequency in Hz + float width; /// pulse witdh 0...1 +} Tone; +``` + +sound API provided by struct: + +```C +typedef struct { + LayeredReducer* source; /// every app add its layer to set value, LayeredReducer + Subscriber* updates; /// sound value changes Supscriber + ValueMutex* state; /// sound state, ValueMutex +} SoundApi; +``` + +You can get API instance by calling `open_sound`: + +```C +/// Add new layer to sound: +inline SoundApi* open_sound(const char* name) { + return (SoundApi*)furi_open(name); +} +``` + +Default system sound is `/dev/sound`. + +Then add new layer to control sound by calling `add_sound_layer`: + +```C +inline ValueManager* add_sound_layer(Tone* layer, uint8_t priority) { + ValueManager* manager = register_valuemanager((void*)layer); + if(manager == NULL) return NULL; + + if(!add_layered_reducer(manager, priority, layer_compose_default)) { + unregister_valuemanager(manager); + return NULL; + } + + return manager; +} +``` + +For change sound you can get display instance pointer by calling `take_sound`, do something and commit your changes by calling `commit_sound`. Or you can call `write_sound`: + +```C +/// return pointer in case off success, NULL otherwise +inline Tone* take_sound(ValueManager* sound, uint32_t timeout) { + return (Tone*)take_mutex(sound->value, timeout); +} + +inline void commit_sound(ValueManager* sound, Tone* value) { + commit_valuemanager(sound, value); +} + +/// return true if success, false otherwise +inline bool write_sound(ValueManager* sound, Tone* value, uint32_t timeout) { + return write_valuemanager(state, (void*)value, sizeof(Tone), timeout); +} +``` + +To read current sound state you should use `read_sound` function: + +```C +/// return true if success, false otherwise +inline bool read_sound(ValueManager* sound, Tone* value, uint32_t timeout) { + return read_mutex(sound->value, (void*)value, sizeof(Tone), timeout); +} +``` + +Also you can subscribe to sound state changes: + +Use `subscribe_sound_changes` to register your callback: + +```C +/// return true if success, false otherwise +inline bool subscribe_sound_changes(Subscriber* updates, void(*cb)(Tone*, void*), void* ctx) { + return subscribe_pubsub(events, void(*)(void*, void*)(cb), ctx); +} +``` + +## Usage example + +```C + +void handle_sound_state(Tone* tone, void* _ctx) { + printf("sound: %d Hz, %d %%\n", (uint16_t)tone->freq, (uint8_t)(tone->witdh * 100)); +} + +void sound_example(void* p) { + soundApi* sound_api = open_display("/dev/sound"); + if(sound_api == NULL) return; // sound not available, critical error + + // subscribe to sound state updates + subscribe_sound_changes(sound_api->updates, handle_sound_state, NULL); + + Tone current_state; + if(read_sound(sound_api->state, ¤t_state, OsWaitForever)) { + printf( + "sound: %d Hz, %d %%\n", + (uint16_t)current_state->freq, + (uint8_t)(current_state->witdh * 100) + ); + } + + // add layer to control sound + ValueManager* sound_manager = add_sound_layer(¤t_state, UI_LAYER_APP); + + // write only freq by getting pointer + Tone* tone = take_sound(sound_manager, OsWaitForever); + if(tone != NULL) { + tone->freq = 440; + } + commit_sound(sound_manager, tone); + + // write tone value + write_sound(sound_manager, &(Tone{.freq = 110., witdh = 0.5}), OsWaitForever); +} +```