Просмотр исходного кода

[WIP] Core api (#134)

* add input debounce code from old fw

* exampl of input api

* change input API to get/release

* revert input API to read

* pointer instead of instance

* add input API description

* add display API

* rewrite display names

* migrate to valuemanager

* add links

* little changes

* add LED API

* add closing brakets

* add sound api

* change format

* Delete input.c

* Delete input.h

* change format
coreglitch 5 лет назад
Родитель
Сommit
870fa8c7cd
5 измененных файлов с 496 добавлено и 0 удалено
  1. 82 0
      wiki/fw/Core-API.md
  2. 61 0
      wiki/fw/api/API:Display.md
  3. 107 0
      wiki/fw/api/API:Input.md
  4. 124 0
      wiki/fw/api/API:LED.md
  5. 122 0
      wiki/fw/api/API:Sound.md

+ 82 - 0
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

+ 61 - 0
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<u8g2_t*>
+    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);
+}
+```

+ 107 - 0
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<InputEvent*>
+    Subscriber* raw_events; /// raw keyboards events: press/release, Subscriber<InputEvent*>
+    ValueMutex* state; /// current keyboard state, ValueMutex<InputState*>
+} 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);
+    }
+}
+```

+ 124 - 0
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<Rgb*>
+    Subscriber* updates; /// LED value changes Supscriber<Rgb*>
+    ValueMutex* state; /// LED state, ValueMutex<Rgb*>
+} 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, &current_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(&current_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);
+}
+```

+ 122 - 0
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<Tone*>
+    Subscriber* updates; /// sound value changes Supscriber<Tone*>
+    ValueMutex* state; /// sound state, ValueMutex<Tone*>
+} 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, &current_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(&current_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);
+}
+```