[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
This commit is contained in:
parent
06ee165ab6
commit
870fa8c7cd
82
wiki/fw/Core-API.md
Normal file
82
wiki/fw/Core-API.md
Normal file
@ -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
wiki/fw/api/API:Display.md
Normal file
61
wiki/fw/api/API:Display.md
Normal file
@ -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
wiki/fw/api/API:Input.md
Normal file
107
wiki/fw/api/API:Input.md
Normal file
@ -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
wiki/fw/api/API:LED.md
Normal file
124
wiki/fw/api/API:LED.md
Normal file
@ -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, ¤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);
|
||||||
|
}
|
||||||
|
```
|
122
wiki/fw/api/API:Sound.md
Normal file
122
wiki/fw/api/API:Sound.md
Normal file
@ -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, ¤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);
|
||||||
|
}
|
||||||
|
```
|
Loading…
Reference in New Issue
Block a user