[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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 coreglitch
						coreglitch