Added Lilygo T-Deck support & more (#4)
* added lilygo t-deck restructured boards implemented HardwareConfig implemented lilygo t-deck lcd and touch drivers added sdkconfig defaults for supported boards * cleanup * added esp32s3 job * build job names updated * wip * partial revert * update readme and build.yml * updated build.yaml with fix for quotes * use esp-idf 5.1.2 * improvements and fixes * fixes for display code * made config const * various improvements
This commit is contained in:
parent
eed990217f
commit
8336316133
23
.github/workflows/build.yml
vendored
23
.github/workflows/build.yml
vendored
@ -1,16 +1,33 @@
|
||||
name: Build
|
||||
on: [push]
|
||||
jobs:
|
||||
build:
|
||||
Build-Yellow-Board:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: esp-idf build
|
||||
- name: Board select
|
||||
run: cp sdkconfig.board.yellow_board sdkconfig
|
||||
- name: esp32 build
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
esp_idf_version: v5.1
|
||||
esp_idf_version: v5.1.2
|
||||
target: esp32
|
||||
path: './'
|
||||
Build-Lilygo-T-Deck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Board select
|
||||
run: cp sdkconfig.board.lilygo_tdeck sdkconfig
|
||||
- name: esp32s3 build
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
esp_idf_version: v5.1.2
|
||||
target: esp32s3
|
||||
path: './'
|
||||
|
||||
@ -3,6 +3,17 @@ cmake_minimum_required(VERSION 3.16)
|
||||
add_definitions(-DFURI_DEBUG)
|
||||
|
||||
set(COMPONENTS main)
|
||||
set(EXTRA_COMPONENT_DIRS "boards" "components")
|
||||
|
||||
# Yellow Board only runs on ESP32
|
||||
if(NOT "${IDF_TARGET}" STREQUAL "esp32")
|
||||
set(EXCLUDE_COMPONENTS "yellow_board_2432s024")
|
||||
endif()
|
||||
|
||||
# T-Deck is an S3 platform
|
||||
if(NOT "${IDF_TARGET}" STREQUAL "esp32s3")
|
||||
set(EXCLUDE_COMPONENTS "lilygo_tdeck")
|
||||
endif()
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(NanoBake)
|
||||
|
||||
45
README.md
45
README.md
@ -1,6 +1,7 @@
|
||||
## Overview
|
||||
|
||||
NanoBake is a front-end application platform for ESP32. It provides an application framework that is based on code from the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware/) project.
|
||||
NanoBake is a front-end application platform for ESP32.
|
||||
It provides an application framework that is based on code from the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware/) project.
|
||||
|
||||
Nanobake provides:
|
||||
- A hardware abstraction layer
|
||||
@ -16,11 +17,11 @@ Requirements:
|
||||
|
||||
## Technologies
|
||||
|
||||
UI is created with [lvgl](https://github.com/lvgl/lvgl) via [esp_lvgl_port](https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port).
|
||||
|
||||
LCD and input drivers are based on [esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html)
|
||||
and [esp_lcd_touch](https://components.espressif.com/components/espressif/esp_lcd_touch).
|
||||
|
||||
UI is created with [lvgl](https://github.com/lvgl/lvgl) via [esp_lvgl_port](https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port).
|
||||
|
||||
## Supported Hardware
|
||||
|
||||
**NOTE**: `sdkconfig.defaults` currently contains `CONFIG_LV_COLOR_16_SWAP=y`.
|
||||
@ -28,33 +29,15 @@ You might have to remove this setting if you're not using the Yellow Board descr
|
||||
|
||||
### Devices
|
||||
|
||||
See below for the supported hardware.
|
||||
Predefined configurations are available for:
|
||||
- Yellow Board / 2432S024 (capacitive touch variant)
|
||||
- Yellow Board: 2.4" with capacitive touch (2432S024) (sources: AliExpress [1](https://www.aliexpress.com/item/1005005902429049.html), [2](https://www.aliexpress.com/item/1005005865107357.html))
|
||||
- LilyGo T-Deck
|
||||
- (more will follow)
|
||||
|
||||
### Drivers
|
||||
Other configurations can be supported, but they require you to set up the drivers yourself:
|
||||
|
||||
**Displays** (see [esp-bsp](https://github.com/espressif/esp-bsp/blob/master/LCD.md) and [Espressif Registry](https://components.espressif.com/components?q=esp_lcd)):
|
||||
- GC9503
|
||||
- GC9A01
|
||||
- ILI9341
|
||||
- RA8875
|
||||
- RM68120
|
||||
- SH1107
|
||||
- SSD1306
|
||||
- SSD1963
|
||||
- ST7262E43
|
||||
- ST7789
|
||||
- ST7796
|
||||
|
||||
**Touch** (see [Espressif Registry](https://components.espressif.com/components?q=esp_lcd_touch)):
|
||||
- CST8xx
|
||||
- FT5X06
|
||||
- GT1151
|
||||
- GT911
|
||||
- STMPE610
|
||||
- TT2100
|
||||
- Display drivers: [esp-bsp](https://github.com/espressif/esp-bsp/blob/master/LCD.md) and [Espressif Registry](https://components.espressif.com/components?q=esp_lcd).
|
||||
- Touch drivers: [Espressif Registry](https://components.espressif.com/components?q=esp_lcd_touch).
|
||||
|
||||
## Guide
|
||||
|
||||
@ -63,6 +46,16 @@ Until there is proper documentation, here are some pointers:
|
||||
- [NanoBake](./components/nanobake/inc)
|
||||
- [Furi](./components/furi/src)
|
||||
|
||||
## Building Firmware
|
||||
|
||||
First we have to select the correct device:
|
||||
|
||||
1. If you use CLion, close it and delete the `cmake-build-debug` folder.
|
||||
2. If you have a `build` folder, then delete it or run `idf.py fullclean`
|
||||
3. Copy the `sdkconfig.board.YOUR_BOARD` into `sdkconfig`
|
||||
|
||||
Now you can run `idf.py flash monitor`
|
||||
|
||||
## License
|
||||
|
||||
[GNU General Public License Version 3](LICENSE.md)
|
||||
|
||||
5
boards/lilygo_tdeck/CMakeLists.txt
Normal file
5
boards/lilygo_tdeck/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES nanobake esp_lcd esp_lcd_touch_gt911
|
||||
)
|
||||
26
boards/lilygo_tdeck/bootstrap.c
Normal file
26
boards/lilygo_tdeck/bootstrap.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "kernel.h"
|
||||
|
||||
#define TAG "lilygo_tdeck_bootstrap"
|
||||
#define TDECK_PERI_POWERON GPIO_NUM_10
|
||||
|
||||
static void tdeck_power_on() {
|
||||
ESP_LOGI(TAG, "power on");
|
||||
gpio_config_t device_power_signal_config = {
|
||||
.pin_bit_mask = BIT64(TDECK_PERI_POWERON),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&device_power_signal_config);
|
||||
gpio_set_level(TDECK_PERI_POWERON, 1);
|
||||
}
|
||||
|
||||
void lilygo_tdeck_bootstrap() {
|
||||
tdeck_power_on();
|
||||
// Give keyboard's ESP time to boot
|
||||
// It uses I2C and seems to interfere with the touch driver
|
||||
furi_delay_ms(500);
|
||||
}
|
||||
168
boards/lilygo_tdeck/display.c
Normal file
168
boards/lilygo_tdeck/display.c
Normal file
@ -0,0 +1,168 @@
|
||||
#include "nanobake.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "driver/ledc.h"
|
||||
|
||||
#define TAG "lilygo_tdeck_display"
|
||||
|
||||
#define LCD_SPI_HOST SPI2_HOST
|
||||
#define LCD_PIN_SCLK GPIO_NUM_40
|
||||
#define LCD_PIN_MOSI GPIO_NUM_41
|
||||
#define LCD_PIN_MISO GPIO_NUM_38
|
||||
#define LCD_PIN_CS GPIO_NUM_12
|
||||
#define LCD_PIN_DC GPIO_NUM_11 // RS
|
||||
#define LCD_PIN_BACKLIGHT GPIO_NUM_42
|
||||
|
||||
#define LCD_SPI_FREQUENCY 40000000
|
||||
#define LCD_HORIZONTAL_RESOLUTION 320
|
||||
#define LCD_VERTICAL_RESOLUTION 240
|
||||
#define LCD_BITS_PER_PIXEL 16
|
||||
#define LCD_DRAW_BUFFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10)
|
||||
|
||||
// Backlight PWM
|
||||
#define LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0
|
||||
#define LCD_BACKLIGHT_LEDC_MODE LEDC_LOW_SPEED_MODE
|
||||
#define LCD_BACKLIGHT_LEDC_OUTPUT_IO LCD_PIN_BACKLIGHT
|
||||
#define LCD_BACKLIGHT_LEDC_CHANNEL LEDC_CHANNEL_0
|
||||
#define LCD_BACKLIGHT_LEDC_DUTY_RES LEDC_TIMER_8_BIT
|
||||
#define LCD_BACKLIGHT_LEDC_DUTY (191)
|
||||
#define LCD_BACKLIGHT_LEDC_FREQUENCY (1000)
|
||||
|
||||
static void tdeck_backlight() {
|
||||
ESP_LOGI(TAG, "enable backlight");
|
||||
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.speed_mode = LCD_BACKLIGHT_LEDC_MODE,
|
||||
.timer_num = LCD_BACKLIGHT_LEDC_TIMER,
|
||||
.duty_resolution = LCD_BACKLIGHT_LEDC_DUTY_RES,
|
||||
.freq_hz = LCD_BACKLIGHT_LEDC_FREQUENCY,
|
||||
.clk_cfg = LEDC_AUTO_CLK
|
||||
};
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
ledc_channel_config_t ledc_channel = {
|
||||
.speed_mode = LCD_BACKLIGHT_LEDC_MODE,
|
||||
.channel = LCD_BACKLIGHT_LEDC_CHANNEL,
|
||||
.timer_sel = LCD_BACKLIGHT_LEDC_TIMER,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.gpio_num = LCD_BACKLIGHT_LEDC_OUTPUT_IO,
|
||||
.duty = 0, // Set duty to 0%
|
||||
.hpoint = 0
|
||||
};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LCD_BACKLIGHT_LEDC_MODE, LCD_BACKLIGHT_LEDC_CHANNEL, LCD_BACKLIGHT_LEDC_DUTY));
|
||||
}
|
||||
|
||||
static bool create_display_device(DisplayDevice* display) {
|
||||
ESP_LOGI(TAG, "creating display");
|
||||
|
||||
int draw_buffer_size = LCD_HORIZONTAL_RESOLUTION * LCD_DRAW_BUFFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8);
|
||||
|
||||
spi_bus_config_t bus_config = {
|
||||
.sclk_io_num = LCD_PIN_SCLK,
|
||||
.mosi_io_num = LCD_PIN_MOSI,
|
||||
.miso_io_num = LCD_PIN_MISO,
|
||||
.quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported
|
||||
.quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported
|
||||
.max_transfer_sz = draw_buffer_size,
|
||||
};
|
||||
|
||||
if (spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "spi bus init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const esp_lcd_panel_io_spi_config_t panel_io_config = {
|
||||
.cs_gpio_num = LCD_PIN_CS,
|
||||
.dc_gpio_num = LCD_PIN_DC,
|
||||
.spi_mode = 0,
|
||||
.pclk_hz = LCD_SPI_FREQUENCY,
|
||||
.trans_queue_depth = 10,
|
||||
.on_color_trans_done = NULL,
|
||||
.user_ctx = NULL,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
.flags = {
|
||||
.dc_low_on_data = 0,
|
||||
.octal_mode = 0,
|
||||
.quad_mode = 0,
|
||||
.sio_mode = 1,
|
||||
.lsb_first = 0,
|
||||
.cs_high_active = 0,
|
||||
}
|
||||
};
|
||||
|
||||
if (esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &panel_io_config, &display->io_handle) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to create panel IO");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "install driver");
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = GPIO_NUM_NC,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
|
||||
.data_endian = LCD_RGB_DATA_ENDIAN_BIG,
|
||||
.bits_per_pixel = LCD_BITS_PER_PIXEL,
|
||||
.flags = {
|
||||
.reset_active_high = 0
|
||||
},
|
||||
.vendor_config = NULL
|
||||
};
|
||||
|
||||
if (esp_lcd_new_panel_st7789(display->io_handle, &panel_config, &display->display_handle) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to create panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_reset(display->display_handle) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to reset panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_init(display->display_handle) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to init panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_invert_color(display->display_handle, true) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to init panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_swap_xy(display->display_handle, true) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to init panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_mirror(display->display_handle, true, false) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to init panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_disp_on_off(display->display_handle, true) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "failed to turn display on");
|
||||
return false;
|
||||
}
|
||||
|
||||
display->horizontal_resolution = LCD_HORIZONTAL_RESOLUTION;
|
||||
display->vertical_resolution = LCD_VERTICAL_RESOLUTION;
|
||||
display->draw_buffer_height = LCD_DRAW_BUFFER_HEIGHT;
|
||||
display->bits_per_pixel = LCD_BITS_PER_PIXEL;
|
||||
display->monochrome = false;
|
||||
display->double_buffering = false;
|
||||
|
||||
tdeck_backlight();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DisplayDriver lilygo_tdeck_display_driver() {
|
||||
return (DisplayDriver) {
|
||||
.name = "lilygo_tdeck_display",
|
||||
.create_display_device = &create_display_device
|
||||
};
|
||||
}
|
||||
11
boards/lilygo_tdeck/lilygo_tdeck.c
Normal file
11
boards/lilygo_tdeck/lilygo_tdeck.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "lilygo_tdeck.h"
|
||||
|
||||
void lilygo_tdeck_bootstrap();
|
||||
DisplayDriver lilygo_tdeck_display_driver();
|
||||
TouchDriver lilygo_tdeck_touch_driver();
|
||||
|
||||
const HardwareConfig lilygo_tdeck = {
|
||||
.bootstrap = &lilygo_tdeck_bootstrap,
|
||||
.display_driver = &lilygo_tdeck_display_driver,
|
||||
.touch_driver = &lilygo_tdeck_touch_driver
|
||||
};
|
||||
5
boards/lilygo_tdeck/lilygo_tdeck.h
Normal file
5
boards/lilygo_tdeck/lilygo_tdeck.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "nanobake.h"
|
||||
|
||||
extern const HardwareConfig lilygo_tdeck;
|
||||
74
boards/lilygo_tdeck/touch.c
Normal file
74
boards/lilygo_tdeck/touch.c
Normal file
@ -0,0 +1,74 @@
|
||||
#include "nanobake.h"
|
||||
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#define TOUCH_I2C_PORT 0
|
||||
|
||||
#define TAG "lilygo_tdeck_touch"
|
||||
|
||||
static bool create_touch_device(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) {
|
||||
ESP_LOGI(TAG, "creating touch");
|
||||
|
||||
const i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = GPIO_NUM_18,
|
||||
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.scl_io_num = GPIO_NUM_8,
|
||||
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.master.clk_speed = 400000
|
||||
};
|
||||
|
||||
if (i2c_param_config(TOUCH_I2C_PORT, &i2c_conf) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c config failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i2c_driver_install(TOUCH_I2C_PORT, i2c_conf.mode, 0, 0, 0) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c driver install failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
|
||||
if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TOUCH_I2C_PORT, &touch_io_config, io_handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "touch io i2c creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "create_touch");
|
||||
esp_lcd_touch_config_t config = {
|
||||
.x_max = 240,
|
||||
.y_max = 320,
|
||||
.rst_gpio_num = GPIO_NUM_NC,
|
||||
.int_gpio_num = GPIO_NUM_16,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 1,
|
||||
.mirror_x = 1,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
.process_coordinates = NULL,
|
||||
.interrupt_callback = NULL,
|
||||
.user_data = NULL
|
||||
};
|
||||
|
||||
if (esp_lcd_touch_new_i2c_gt911(*io_handle, &config, touch_handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "gt911 driver creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TouchDriver lilygo_tdeck_touch_driver() {
|
||||
return (TouchDriver) {
|
||||
.name = "lilygo_tdeck_touch",
|
||||
.create_touch_device = &create_touch_device
|
||||
};
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
#include "board_2432s024_display.h"
|
||||
|
||||
#include "nanobake.h"
|
||||
#include "esp_lcd_ili9341.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
@ -23,13 +22,7 @@ static SemaphoreHandle_t refresh_finish = NULL;
|
||||
#define LCD_HORIZONTAL_RESOLUTION 240
|
||||
#define LCD_VERTICAL_RESOLUTION 320
|
||||
#define LCD_BITS_PER_PIXEL 16
|
||||
#define LCD_DRAW_BUFFER_HEIGHT 80
|
||||
|
||||
IRAM_ATTR static bool prv_on_color_trans_done(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_io_event_data_t* edata, void* user_ctx) {
|
||||
BaseType_t need_yield = pdFALSE;
|
||||
xSemaphoreGiveFromISR(refresh_finish, &need_yield);
|
||||
return (need_yield == pdTRUE);
|
||||
}
|
||||
#define LCD_DRAW_BUFFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10)
|
||||
|
||||
static bool create_display_device(DisplayDevice* display) {
|
||||
ESP_LOGI(TAG, "creating display");
|
||||
@ -58,7 +51,7 @@ static bool create_display_device(DisplayDevice* display) {
|
||||
const esp_lcd_panel_io_spi_config_t panel_io_config = ILI9341_PANEL_IO_SPI_CONFIG(
|
||||
LCD_PIN_CS,
|
||||
LCD_PIN_DC,
|
||||
prv_on_color_trans_done,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
@ -118,6 +111,7 @@ static bool create_display_device(DisplayDevice* display) {
|
||||
display->draw_buffer_height = LCD_DRAW_BUFFER_HEIGHT;
|
||||
display->bits_per_pixel = LCD_BITS_PER_PIXEL;
|
||||
display->monochrome = false;
|
||||
display->double_buffering = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,11 +1,10 @@
|
||||
#include "board_2432s024_touch.h"
|
||||
|
||||
#include "nanobake.h"
|
||||
#include "esp_lcd_touch_cst816s.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#define CST816_I2C_PORT (0)
|
||||
#define TOUCH_I2C_PORT 0
|
||||
|
||||
#define TAG "2432s024_cst816"
|
||||
|
||||
@ -21,20 +20,20 @@ static bool create_touch_device(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_to
|
||||
.master.clk_speed = 400000
|
||||
};
|
||||
|
||||
if (i2c_param_config(CST816_I2C_PORT, &i2c_conf) != ESP_OK) {
|
||||
if (i2c_param_config(TOUCH_I2C_PORT, &i2c_conf) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c config failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i2c_driver_install(CST816_I2C_PORT, i2c_conf.mode, 0, 0, 0) != ESP_OK) {
|
||||
if (i2c_driver_install(TOUCH_I2C_PORT, i2c_conf.mode, 0, 0, 0) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c driver install failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
|
||||
|
||||
if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)CST816_I2C_PORT, &touch_io_config, io_handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_lcd_panel creation failed");
|
||||
if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TOUCH_I2C_PORT, &touch_io_config, io_handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "touch I2C IO init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -53,7 +52,9 @@ static bool create_touch_device(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_to
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
.process_coordinates = NULL,
|
||||
.interrupt_callback = NULL,
|
||||
.user_data = NULL
|
||||
};
|
||||
|
||||
if (esp_lcd_touch_new_i2c_cst816s(*io_handle, &config, touch_handle) != ESP_OK) {
|
||||
10
boards/yellow_board/yellow_board.c
Normal file
10
boards/yellow_board/yellow_board.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include "yellow_board.h"
|
||||
|
||||
DisplayDriver board_2432s024_create_display_driver();
|
||||
TouchDriver board_2432s024_create_touch_driver();
|
||||
|
||||
const HardwareConfig yellow_board_24inch_cap = {
|
||||
.bootstrap = NULL,
|
||||
.display_driver = &board_2432s024_create_display_driver,
|
||||
.touch_driver = &board_2432s024_create_touch_driver
|
||||
};
|
||||
6
boards/yellow_board/yellow_board.h
Normal file
6
boards/yellow_board/yellow_board.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "nanobake.h"
|
||||
|
||||
// Capacitive touch version of the 2.4" yellow board
|
||||
extern const HardwareConfig yellow_board_24inch_cap;
|
||||
@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "board_2432s024_display.h"
|
||||
#include "board_2432s024_touch.h"
|
||||
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern DisplayDriver board_2432s024_create_display_driver();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "touch.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern TouchDriver board_2432s024_create_touch_driver();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -63,11 +63,6 @@ typedef struct {
|
||||
* Non-blocking method to create the GUI
|
||||
*/
|
||||
const AppOnShow _Nullable on_show;
|
||||
|
||||
/**
|
||||
* Callstack size. If you get a stackoverflow, then consider increasing this value.
|
||||
*/
|
||||
const AppStackSize stack_size;
|
||||
} AppManifest;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -65,7 +65,7 @@ FURI_NORETURN void __furi_halt_implementation();
|
||||
do { \
|
||||
if (!(__e)) { \
|
||||
ESP_LOGE("check", "%s", #__e); \
|
||||
__furi_crash(__m); \
|
||||
__furi_crash(#__m); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -83,7 +83,7 @@ FURI_NORETURN void __furi_halt_implementation();
|
||||
do { \
|
||||
if (!(__e)) { \
|
||||
ESP_LOGE("assert", "%s", #__e); \
|
||||
__furi_crash(__m); \
|
||||
__furi_crash(#__m); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
|
||||
@ -116,7 +116,11 @@ uint32_t furi_event_flag_wait(
|
||||
}
|
||||
|
||||
rflags = xEventGroupWaitBits(
|
||||
hEventGroup, (EventBits_t)flags, exit_clr, wait_all, (TickType_t)timeout
|
||||
hEventGroup,
|
||||
(EventBits_t)flags,
|
||||
exit_clr,
|
||||
wait_all,
|
||||
(TickType_t)timeout
|
||||
);
|
||||
|
||||
if (options & FuriFlagWaitAll) {
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
/**
|
||||
* @file kernel.h
|
||||
* Furi Kernel primitives
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "furi_core_types.h"
|
||||
|
||||
#define configTICK_RATE_HZ_RAW 1000
|
||||
#define configTICK_RATE_HZ_RAW CONFIG_FREERTOS_HZ
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@ -13,13 +13,14 @@ LIST_DEF(FuriPubSubSubscriptionList, FuriPubSubSubscription, M_POD_OPLIST);
|
||||
|
||||
struct FuriPubSub {
|
||||
FuriPubSubSubscriptionList_t items;
|
||||
// TODO: replace recursive mutex with semaphore
|
||||
FuriMutex* mutex;
|
||||
};
|
||||
|
||||
FuriPubSub* furi_pubsub_alloc() {
|
||||
FuriPubSub* pubsub = malloc(sizeof(FuriPubSub));
|
||||
|
||||
pubsub->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
pubsub->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
||||
furi_assert(pubsub->mutex);
|
||||
|
||||
FuriPubSubSubscriptionList_init(pubsub->items);
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
#include "mutex.h"
|
||||
#include "m-dict.h"
|
||||
#include "m_cstr_dup.h"
|
||||
#include "log.h"
|
||||
|
||||
#define TAG "record"
|
||||
|
||||
#define FURI_RECORD_FLAG_READY (0x1)
|
||||
|
||||
@ -37,7 +40,7 @@ static void furi_record_erase(const char* name, FuriRecordData* record_data) {
|
||||
|
||||
void furi_record_init() {
|
||||
furi_record = malloc(sizeof(FuriRecord));
|
||||
furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
furi_record->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
||||
furi_check(furi_record->mutex);
|
||||
FuriRecordDataDict_init(furi_record->records);
|
||||
}
|
||||
@ -111,6 +114,7 @@ bool furi_record_destroy(const char* name) {
|
||||
}
|
||||
|
||||
void* furi_record_open(const char* name) {
|
||||
furi_assert(name);
|
||||
furi_assert(furi_record);
|
||||
|
||||
furi_record_lock();
|
||||
@ -134,6 +138,7 @@ void* furi_record_open(const char* name) {
|
||||
}
|
||||
|
||||
void furi_record_close(const char* name) {
|
||||
furi_assert(name);
|
||||
furi_assert(furi_record);
|
||||
|
||||
furi_record_lock();
|
||||
|
||||
@ -51,7 +51,7 @@ struct FuriThread {
|
||||
|
||||
// Keep all non-alignable byte types in one place,
|
||||
// this ensures that the size of this structure is minimal
|
||||
bool is_service;
|
||||
bool is_static;
|
||||
bool heap_trace_enabled;
|
||||
|
||||
configSTACK_DEPTH_TYPE stack_size;
|
||||
@ -113,7 +113,7 @@ static void furi_thread_body(void* context) {
|
||||
|
||||
furi_assert(thread->state == FuriThreadStateRunning);
|
||||
|
||||
if (thread->is_service) {
|
||||
if (thread->is_static) {
|
||||
ESP_LOGI(
|
||||
TAG,
|
||||
"%s service thread TCB memory will not be reclaimed",
|
||||
@ -135,7 +135,7 @@ FuriThread* furi_thread_alloc() {
|
||||
// TODO: create default struct instead of using memset()
|
||||
memset(thread, 0, sizeof(FuriThread));
|
||||
thread->output.buffer = furi_string_alloc();
|
||||
thread->is_service = false;
|
||||
thread->is_static = false;
|
||||
|
||||
FuriThread* parent = NULL;
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
||||
@ -207,8 +207,8 @@ void furi_thread_set_appid(FuriThread* thread, const char* appid) {
|
||||
thread->appid = appid ? strdup(appid) : NULL;
|
||||
}
|
||||
|
||||
void furi_thread_mark_as_service(FuriThread* thread) {
|
||||
thread->is_service = true;
|
||||
void furi_thread_mark_as_static(FuriThread* thread) {
|
||||
thread->is_static = true;
|
||||
}
|
||||
|
||||
bool furi_thread_mark_is_service(FuriThreadId thread_id) {
|
||||
@ -216,7 +216,7 @@ bool furi_thread_mark_is_service(FuriThreadId thread_id) {
|
||||
assert(!FURI_IS_IRQ_MODE() && (hTask != NULL));
|
||||
FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0);
|
||||
assert(thread != NULL);
|
||||
return thread->is_service;
|
||||
return thread->is_static;
|
||||
}
|
||||
|
||||
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
|
||||
@ -281,7 +281,7 @@ void furi_thread_start(FuriThread* thread) {
|
||||
|
||||
uint32_t stack = thread->stack_size / sizeof(StackType_t);
|
||||
UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal;
|
||||
if (thread->is_service) {
|
||||
if (thread->is_static) {
|
||||
thread->task_handle = xTaskCreateStatic(
|
||||
furi_thread_body,
|
||||
thread->name,
|
||||
|
||||
@ -109,7 +109,7 @@ void furi_thread_set_appid(FuriThread* thread, const char* appid);
|
||||
*
|
||||
* @param thread
|
||||
*/
|
||||
void furi_thread_mark_as_service(FuriThread* thread);
|
||||
void furi_thread_mark_as_static(FuriThread* thread);
|
||||
|
||||
/** Set FuriThread stack size
|
||||
*
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "desktop.h"
|
||||
#include "lvgl.h"
|
||||
#include "check.h"
|
||||
#include "record.h"
|
||||
#include "apps/services/loader/loader.h"
|
||||
#include "apps/services/gui/gui.h"
|
||||
#include "apps/services/gui/view_port.h"
|
||||
@ -11,9 +10,7 @@ static void on_open_app(lv_event_t* e) {
|
||||
lv_event_code_t code = lv_event_get_code(e);
|
||||
if (code == LV_EVENT_CLICKED) {
|
||||
const AppManifest* manifest = lv_event_get_user_data(e);
|
||||
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
loader_start_app_nonblocking(loader, manifest->id, NULL);
|
||||
})
|
||||
loader_start_app_nonblocking(manifest->id, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,9 +35,7 @@ static void desktop_show(lv_obj_t* parent, void* context) {
|
||||
static void desktop_start() {
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, &desktop_show, NULL);
|
||||
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
|
||||
gui_add_view_port(gui, view_port, GuiLayerDesktop);
|
||||
})
|
||||
gui_add_view_port(view_port, GuiLayerDesktop);
|
||||
}
|
||||
|
||||
static void desktop_stop() {
|
||||
@ -54,6 +49,5 @@ const AppManifest desktop_app = {
|
||||
.type = AppTypeDesktop,
|
||||
.on_start = &desktop_start,
|
||||
.on_stop = &desktop_stop,
|
||||
.on_show = NULL,
|
||||
.stack_size = AppStackSizeNormal
|
||||
.on_show = NULL
|
||||
};
|
||||
|
||||
@ -4,192 +4,36 @@
|
||||
#include "gui_i.h"
|
||||
#include "log.h"
|
||||
#include "record.h"
|
||||
#include "kernel.h"
|
||||
|
||||
#define TAG "gui"
|
||||
|
||||
// Forward declarations from gui_draw.c
|
||||
// Forward declarations
|
||||
bool gui_redraw_fs(Gui*);
|
||||
void gui_redraw(Gui*);
|
||||
static int32_t gui_main(void*);
|
||||
|
||||
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
|
||||
// Iterating backward
|
||||
ViewPortArray_it_t it;
|
||||
ViewPortArray_it_last(it, array);
|
||||
while (!ViewPortArray_end_p(it)) {
|
||||
ViewPort* view_port = *ViewPortArray_ref(it);
|
||||
if (view_port_is_enabled(view_port)) {
|
||||
ViewPort* view_port = *ViewPortArray_ref(it);
|
||||
return view_port;
|
||||
}
|
||||
ViewPortArray_previous(it);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) {
|
||||
furi_assert(gui);
|
||||
furi_check(layer < GuiLayerMAX);
|
||||
size_t ret = 0;
|
||||
|
||||
gui_lock(gui);
|
||||
ViewPortArray_it_t it;
|
||||
ViewPortArray_it_last(it, gui->layers[layer]);
|
||||
while (!ViewPortArray_end_p(it)) {
|
||||
ViewPort* view_port = *ViewPortArray_ref(it);
|
||||
if (view_port_is_enabled(view_port)) {
|
||||
ret++;
|
||||
}
|
||||
ViewPortArray_previous(it);
|
||||
}
|
||||
gui_unlock(gui);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gui_update(Gui* gui) {
|
||||
furi_assert(gui);
|
||||
|
||||
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
|
||||
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
|
||||
}
|
||||
|
||||
void gui_lock(Gui* gui) {
|
||||
furi_assert(gui);
|
||||
furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void gui_unlock(Gui* gui) {
|
||||
furi_assert(gui);
|
||||
furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {
|
||||
furi_assert(gui);
|
||||
furi_assert(view_port);
|
||||
furi_check(layer < GuiLayerMAX);
|
||||
|
||||
gui_lock(gui);
|
||||
// Verify that view port is not yet added
|
||||
ViewPortArray_it_t it;
|
||||
for (size_t i = 0; i < GuiLayerMAX; i++) {
|
||||
ViewPortArray_it(it, gui->layers[i]);
|
||||
while (!ViewPortArray_end_p(it)) {
|
||||
furi_assert(*ViewPortArray_ref(it) != view_port);
|
||||
ViewPortArray_next(it);
|
||||
}
|
||||
}
|
||||
// Add view port and link with gui
|
||||
ViewPortArray_push_back(gui->layers[layer], view_port);
|
||||
view_port_gui_set(view_port, gui);
|
||||
gui_unlock(gui);
|
||||
|
||||
// Request redraw
|
||||
gui_update(gui);
|
||||
}
|
||||
|
||||
void gui_remove_view_port(Gui* gui, ViewPort* view_port) {
|
||||
furi_assert(gui);
|
||||
furi_assert(view_port);
|
||||
|
||||
gui_lock(gui);
|
||||
view_port_gui_set(view_port, NULL);
|
||||
ViewPortArray_it_t it;
|
||||
for (size_t i = 0; i < GuiLayerMAX; i++) {
|
||||
ViewPortArray_it(it, gui->layers[i]);
|
||||
while (!ViewPortArray_end_p(it)) {
|
||||
if (*ViewPortArray_ref(it) == view_port) {
|
||||
ViewPortArray_remove(gui->layers[i], it);
|
||||
} else {
|
||||
ViewPortArray_next(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
if(gui->ongoing_input_view_port == view_port) {
|
||||
gui->ongoing_input_view_port = NULL;
|
||||
}
|
||||
*/
|
||||
gui_unlock(gui);
|
||||
|
||||
// Request redraw
|
||||
gui_update(gui);
|
||||
}
|
||||
|
||||
void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) {
|
||||
furi_assert(gui);
|
||||
furi_assert(view_port);
|
||||
|
||||
gui_lock(gui);
|
||||
// Remove
|
||||
GuiLayer layer = GuiLayerMAX;
|
||||
ViewPortArray_it_t it;
|
||||
for (size_t i = 0; i < GuiLayerMAX; i++) {
|
||||
ViewPortArray_it(it, gui->layers[i]);
|
||||
while (!ViewPortArray_end_p(it)) {
|
||||
if (*ViewPortArray_ref(it) == view_port) {
|
||||
ViewPortArray_remove(gui->layers[i], it);
|
||||
furi_assert(layer == GuiLayerMAX);
|
||||
layer = i;
|
||||
} else {
|
||||
ViewPortArray_next(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
furi_assert(layer != GuiLayerMAX);
|
||||
// Return to the top
|
||||
ViewPortArray_push_back(gui->layers[layer], view_port);
|
||||
gui_unlock(gui);
|
||||
|
||||
// Request redraw
|
||||
gui_update(gui);
|
||||
}
|
||||
|
||||
void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) {
|
||||
furi_assert(gui);
|
||||
furi_assert(view_port);
|
||||
|
||||
gui_lock(gui);
|
||||
// Remove
|
||||
GuiLayer layer = GuiLayerMAX;
|
||||
ViewPortArray_it_t it;
|
||||
for (size_t i = 0; i < GuiLayerMAX; i++) {
|
||||
ViewPortArray_it(it, gui->layers[i]);
|
||||
while (!ViewPortArray_end_p(it)) {
|
||||
if (*ViewPortArray_ref(it) == view_port) {
|
||||
ViewPortArray_remove(gui->layers[i], it);
|
||||
furi_assert(layer == GuiLayerMAX);
|
||||
layer = i;
|
||||
} else {
|
||||
ViewPortArray_next(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
furi_assert(layer != GuiLayerMAX);
|
||||
// Return to the top
|
||||
ViewPortArray_push_at(gui->layers[layer], 0, view_port);
|
||||
gui_unlock(gui);
|
||||
|
||||
// Request redraw
|
||||
gui_update(gui);
|
||||
}
|
||||
static Gui* gui = NULL;
|
||||
|
||||
Gui* gui_alloc() {
|
||||
Gui* gui = malloc(sizeof(Gui));
|
||||
gui->thread = furi_thread_alloc_ex(
|
||||
Gui* instance = malloc(sizeof(Gui));
|
||||
memset(instance, 0, sizeof(Gui));
|
||||
furi_check(instance != NULL);
|
||||
instance->thread = furi_thread_alloc_ex(
|
||||
"gui",
|
||||
2048,
|
||||
AppStackSizeLarge, // Last known minimum was 2800 for launching desktop
|
||||
&gui_main,
|
||||
NULL
|
||||
);
|
||||
gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
instance->mutex = xSemaphoreCreateRecursiveMutex();
|
||||
|
||||
furi_check(lvgl_port_lock(100));
|
||||
gui->lvgl_parent = lv_scr_act();
|
||||
instance->lvgl_parent = lv_scr_act();
|
||||
lvgl_port_unlock();
|
||||
|
||||
for (size_t i = 0; i < GuiLayerMAX; i++) {
|
||||
ViewPortArray_init(gui->layers[i]);
|
||||
instance->layers[i] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -200,25 +44,72 @@ Gui* gui_alloc() {
|
||||
furi_check(gui->input_events);
|
||||
furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui);
|
||||
*/
|
||||
return gui;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void gui_free(Gui* gui) {
|
||||
furi_thread_free(gui->thread);
|
||||
void gui_free(Gui* instance) {
|
||||
furi_assert(instance != NULL);
|
||||
furi_thread_free(instance->thread);
|
||||
furi_mutex_free(instance->mutex);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
if (gui->mutex) {
|
||||
furi_mutex_free(gui->mutex);
|
||||
}
|
||||
void gui_lock() {
|
||||
furi_assert(gui);
|
||||
furi_assert(gui->mutex);
|
||||
furi_check(xSemaphoreTakeRecursive(gui->mutex, portMAX_DELAY) == pdPASS);
|
||||
}
|
||||
|
||||
void gui_unlock() {
|
||||
furi_assert(gui);
|
||||
furi_assert(gui->mutex);
|
||||
furi_check(xSemaphoreGiveRecursive(gui->mutex) == pdPASS);
|
||||
}
|
||||
|
||||
void gui_request_draw() {
|
||||
furi_assert(gui);
|
||||
|
||||
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
|
||||
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
|
||||
}
|
||||
|
||||
void gui_add_view_port(ViewPort* view_port, GuiLayer layer) {
|
||||
furi_assert(gui);
|
||||
furi_assert(view_port);
|
||||
furi_check(layer < GuiLayerMAX);
|
||||
|
||||
gui_lock();
|
||||
furi_check(gui->layers[layer] == NULL, "layer in use");
|
||||
gui->layers[layer] = view_port;
|
||||
view_port_gui_set(view_port, gui);
|
||||
gui_unlock();
|
||||
|
||||
gui_request_draw();
|
||||
}
|
||||
|
||||
void gui_remove_view_port(ViewPort* view_port) {
|
||||
furi_assert(gui);
|
||||
furi_assert(view_port);
|
||||
|
||||
gui_lock();
|
||||
|
||||
view_port_gui_set(view_port, NULL);
|
||||
for (size_t i = 0; i < GuiLayerMAX; i++) {
|
||||
ViewPortArray_clear(gui->layers[i]);
|
||||
if (gui->layers[i] == view_port) {
|
||||
gui->layers[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(gui);
|
||||
gui_unlock();
|
||||
|
||||
gui_request_draw();
|
||||
}
|
||||
|
||||
static int32_t gui_main(void* parameter) {
|
||||
UNUSED(parameter);
|
||||
static int32_t gui_main(void* p) {
|
||||
UNUSED(p);
|
||||
furi_check(gui);
|
||||
Gui* local_gui = gui;
|
||||
|
||||
while (1) {
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
@ -236,11 +127,10 @@ static int32_t gui_main(void* parameter) {
|
||||
}*/
|
||||
// Process and dispatch draw call
|
||||
if (flags & GUI_THREAD_FLAG_DRAW) {
|
||||
FURI_LOG_D(TAG, "redraw requested");
|
||||
furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW);
|
||||
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
|
||||
gui_redraw(gui);
|
||||
})
|
||||
gui_lock();
|
||||
gui_redraw(local_gui);
|
||||
gui_unlock();
|
||||
}
|
||||
|
||||
if (flags & GUI_THREAD_FLAG_EXIT) {
|
||||
@ -252,29 +142,27 @@ static int32_t gui_main(void* parameter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// region AppManifest
|
||||
|
||||
static void gui_start(void* parameter) {
|
||||
UNUSED(parameter);
|
||||
|
||||
Gui* gui = gui_alloc();
|
||||
furi_record_create(RECORD_GUI, gui);
|
||||
furi_thread_set_priority(gui->thread, FuriThreadPriorityHigh);
|
||||
gui = gui_alloc();
|
||||
|
||||
furi_thread_set_priority(gui->thread, FuriThreadPriorityNormal);
|
||||
furi_thread_start(gui->thread);
|
||||
}
|
||||
|
||||
static void gui_stop() {
|
||||
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
|
||||
gui_lock(gui);
|
||||
gui_lock();
|
||||
|
||||
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
|
||||
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
|
||||
furi_thread_join(gui->thread);
|
||||
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
|
||||
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
|
||||
furi_thread_join(gui->thread);
|
||||
|
||||
gui_unlock(gui);
|
||||
gui_unlock();
|
||||
|
||||
gui_free(gui);
|
||||
})
|
||||
|
||||
furi_record_destroy(RECORD_GUI);
|
||||
gui_free(gui);
|
||||
}
|
||||
|
||||
const AppManifest gui_app = {
|
||||
@ -284,6 +172,7 @@ const AppManifest gui_app = {
|
||||
.type = AppTypeService,
|
||||
.on_start = &gui_start,
|
||||
.on_stop = &gui_stop,
|
||||
.on_show = NULL,
|
||||
.stack_size = AppStackSizeNormal
|
||||
.on_show = NULL
|
||||
};
|
||||
|
||||
// endregion
|
||||
|
||||
@ -12,26 +12,11 @@ extern const AppManifest gui_app;
|
||||
/** Gui layers */
|
||||
typedef enum {
|
||||
GuiLayerDesktop, /**< Desktop layer for internal use. Like fullscreen but with status bar */
|
||||
|
||||
GuiLayerWindow, /**< Window layer, status bar is shown */
|
||||
|
||||
GuiLayerStatusBarLeft, /**< Status bar left-side layer, auto-layout */
|
||||
GuiLayerStatusBarRight, /**< Status bar right-side layer, auto-layout */
|
||||
|
||||
GuiLayerFullscreen, /**< Fullscreen layer, no status bar */
|
||||
|
||||
GuiLayerMAX /**< Don't use or move, special value */
|
||||
} GuiLayer;
|
||||
|
||||
/** Gui Canvas Commit Callback */
|
||||
typedef void (*GuiCanvasCommitCallback)(
|
||||
uint8_t* data,
|
||||
size_t size,
|
||||
void* context
|
||||
);
|
||||
|
||||
#define RECORD_GUI "gui"
|
||||
|
||||
typedef struct Gui Gui;
|
||||
|
||||
/** Add view_port to view_port tree
|
||||
@ -42,7 +27,7 @@ typedef struct Gui Gui;
|
||||
* @param view_port ViewPort instance
|
||||
* @param[in] layer GuiLayer where to place view_port
|
||||
*/
|
||||
void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer);
|
||||
void gui_add_view_port(ViewPort* view_port, GuiLayer layer);
|
||||
|
||||
/** Remove view_port from rendering tree
|
||||
*
|
||||
@ -51,25 +36,7 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer);
|
||||
* @param gui Gui instance
|
||||
* @param view_port ViewPort instance
|
||||
*/
|
||||
void gui_remove_view_port(Gui* gui, ViewPort* view_port);
|
||||
|
||||
/** Send ViewPort to the front
|
||||
*
|
||||
* Places selected ViewPort to the top of the drawing stack
|
||||
*
|
||||
* @param gui Gui instance
|
||||
* @param view_port ViewPort instance
|
||||
*/
|
||||
void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port);
|
||||
|
||||
/** Send ViewPort to the back
|
||||
*
|
||||
* Places selected ViewPort to the bottom of the drawing stack
|
||||
*
|
||||
* @param gui Gui instance
|
||||
* @param view_port ViewPort instance
|
||||
*/
|
||||
void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port);
|
||||
void gui_remove_view_port(ViewPort* view_port);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -39,12 +39,10 @@ static lv_obj_t* screen_with_top_bar_and_toolbar(lv_obj_t* parent) {
|
||||
|
||||
top_bar(vertical_container);
|
||||
|
||||
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
const AppManifest* manifest = loader_get_current_app(loader);
|
||||
if (manifest != NULL) {
|
||||
toolbar(vertical_container, TOP_BAR_HEIGHT, manifest);
|
||||
}
|
||||
})
|
||||
const AppManifest* manifest = loader_get_current_app();
|
||||
if (manifest != NULL) {
|
||||
toolbar(vertical_container, TOP_BAR_HEIGHT, manifest);
|
||||
}
|
||||
|
||||
lv_obj_t* spacer = lv_obj_create(vertical_container);
|
||||
lv_obj_set_size(spacer, 2, 2);
|
||||
@ -60,7 +58,7 @@ static lv_obj_t* screen_with_top_bar_and_toolbar(lv_obj_t* parent) {
|
||||
}
|
||||
|
||||
static bool gui_redraw_window(Gui* gui) {
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
|
||||
ViewPort* view_port = gui->layers[GuiLayerWindow];
|
||||
if (view_port) {
|
||||
lv_obj_t* container = screen_with_top_bar_and_toolbar(gui->lvgl_parent);
|
||||
view_port_draw(view_port, container);
|
||||
@ -71,7 +69,7 @@ static bool gui_redraw_window(Gui* gui) {
|
||||
}
|
||||
|
||||
static bool gui_redraw_desktop(Gui* gui) {
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
|
||||
ViewPort* view_port = gui->layers[GuiLayerDesktop];
|
||||
if (view_port) {
|
||||
lv_obj_t* container = screen_with_top_bar(gui->lvgl_parent);
|
||||
view_port_draw(view_port, container);
|
||||
@ -84,7 +82,7 @@ static bool gui_redraw_desktop(Gui* gui) {
|
||||
}
|
||||
|
||||
bool gui_redraw_fs(Gui* gui) {
|
||||
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
|
||||
ViewPort* view_port = gui->layers[GuiLayerFullscreen];
|
||||
if (view_port) {
|
||||
view_port_draw(view_port, gui->lvgl_parent);
|
||||
return true;
|
||||
@ -95,7 +93,6 @@ bool gui_redraw_fs(Gui* gui) {
|
||||
|
||||
void gui_redraw(Gui* gui) {
|
||||
furi_assert(gui);
|
||||
gui_lock(gui);
|
||||
|
||||
furi_check(lvgl_port_lock(100));
|
||||
lv_obj_clean(gui->lvgl_parent);
|
||||
@ -107,6 +104,4 @@ void gui_redraw(Gui* gui) {
|
||||
}
|
||||
|
||||
lvgl_port_unlock();
|
||||
|
||||
gui_unlock(gui);
|
||||
}
|
||||
|
||||
@ -1,57 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "gui.h"
|
||||
|
||||
#include <m-algo.h>
|
||||
#include <m-array.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "message_queue.h"
|
||||
#include "mutex.h"
|
||||
#include "pubsub.h"
|
||||
#include "view_port.h"
|
||||
#include "view_port_i.h"
|
||||
|
||||
#define GUI_DISPLAY_WIDTH 128
|
||||
#define GUI_DISPLAY_HEIGHT 64
|
||||
|
||||
#define GUI_STATUS_BAR_X 0
|
||||
#define GUI_STATUS_BAR_Y 0
|
||||
#define GUI_STATUS_BAR_WIDTH GUI_DISPLAY_WIDTH
|
||||
/* 0-1 pixels for upper thin frame
|
||||
* 2-9 pixels for icons (battery, sd card, etc)
|
||||
* 10-12 pixels for lower bold line */
|
||||
#define GUI_STATUS_BAR_HEIGHT 13
|
||||
/* icon itself area (battery, sd card, etc) excluding frame.
|
||||
* painted 2 pixels below GUI_STATUS_BAR_X.
|
||||
*/
|
||||
#define GUI_STATUS_BAR_WORKAREA_HEIGHT 8
|
||||
|
||||
#define GUI_WINDOW_X 0
|
||||
#define GUI_WINDOW_Y GUI_STATUS_BAR_HEIGHT
|
||||
#define GUI_WINDOW_WIDTH GUI_DISPLAY_WIDTH
|
||||
#define GUI_WINDOW_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_WINDOW_Y)
|
||||
#include <stdio.h>
|
||||
|
||||
#define GUI_THREAD_FLAG_DRAW (1 << 0)
|
||||
#define GUI_THREAD_FLAG_INPUT (1 << 1)
|
||||
#define GUI_THREAD_FLAG_EXIT (1 << 2)
|
||||
#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT)
|
||||
|
||||
ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST);
|
||||
|
||||
typedef struct {
|
||||
GuiCanvasCommitCallback callback;
|
||||
void* context;
|
||||
} CanvasCallbackPair;
|
||||
|
||||
/** Gui structure */
|
||||
struct Gui {
|
||||
// Thread and lock
|
||||
FuriThread* thread;
|
||||
FuriMutex* mutex;
|
||||
SemaphoreHandle_t mutex;
|
||||
|
||||
// Layers and Canvas
|
||||
ViewPortArray_t layers[GuiLayerMAX];
|
||||
ViewPort* layers[GuiLayerMAX];
|
||||
lv_obj_t* lvgl_parent;
|
||||
|
||||
// Input
|
||||
@ -63,19 +34,11 @@ struct Gui {
|
||||
*/
|
||||
};
|
||||
|
||||
/** Find enabled ViewPort in ViewPortArray
|
||||
*
|
||||
* @param[in] array The ViewPortArray instance
|
||||
*
|
||||
* @return ViewPort instance or NULL
|
||||
*/
|
||||
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array);
|
||||
|
||||
/** Update GUI, request redraw
|
||||
*
|
||||
* @param gui Gui instance
|
||||
*/
|
||||
void gui_update(Gui* gui);
|
||||
void gui_request_draw();
|
||||
|
||||
///** Input event callback
|
||||
// *
|
||||
@ -86,21 +49,14 @@ void gui_update(Gui* gui);
|
||||
// */
|
||||
//void gui_input_events_callback(const void* value, void* ctx);
|
||||
|
||||
/** Get count of view ports in layer
|
||||
*
|
||||
* @param gui The Gui instance
|
||||
* @param[in] layer GuiLayer that we want to get count of view ports
|
||||
*/
|
||||
size_t gui_active_view_port_count(Gui* gui, GuiLayer layer);
|
||||
|
||||
/** Lock GUI
|
||||
*
|
||||
* @param gui The Gui instance
|
||||
*/
|
||||
void gui_lock(Gui* gui);
|
||||
void gui_lock();
|
||||
|
||||
/** Unlock GUI
|
||||
*
|
||||
* @param gui The Gui instance
|
||||
*/
|
||||
void gui_unlock(Gui* gui);
|
||||
void gui_unlock();
|
||||
|
||||
@ -36,7 +36,7 @@ void view_port_enabled_set(ViewPort* view_port, bool enabled) {
|
||||
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
if (view_port->is_enabled != enabled) {
|
||||
view_port->is_enabled = enabled;
|
||||
if (view_port->gui) gui_update(view_port->gui);
|
||||
if (view_port->gui) gui_request_draw();
|
||||
}
|
||||
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
}
|
||||
@ -66,7 +66,7 @@ void view_port_update(ViewPort* view_port) {
|
||||
ESP_LOGW(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
|
||||
}
|
||||
|
||||
if (view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
|
||||
if (view_port->gui && view_port->is_enabled) gui_request_draw();
|
||||
furi_mutex_release(view_port->mutex);
|
||||
}
|
||||
|
||||
|
||||
@ -4,9 +4,7 @@
|
||||
#include "apps/services/loader/loader.h"
|
||||
|
||||
static void app_toolbar_close(lv_event_t* event) {
|
||||
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
loader_stop_app(loader);
|
||||
})
|
||||
loader_stop_app();
|
||||
}
|
||||
|
||||
void toolbar(lv_obj_t* parent, lv_coord_t offset_y, const AppManifest* manifest) {
|
||||
|
||||
@ -6,18 +6,55 @@
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "apps/services/gui/gui.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#define TAG "Loader"
|
||||
|
||||
// Forward declarations
|
||||
static int32_t loader_main(void* p);
|
||||
static LoaderStatus loader_do_start_by_id(
|
||||
Loader* loader,
|
||||
const char* id,
|
||||
const char* args,
|
||||
FuriString* error_message
|
||||
);
|
||||
|
||||
LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args, FuriString* error_message) {
|
||||
static Loader* loader = NULL;
|
||||
|
||||
static Loader* loader_alloc() {
|
||||
furi_check(loader == NULL);
|
||||
loader = malloc(sizeof(Loader));
|
||||
loader->pubsub = furi_pubsub_alloc();
|
||||
loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
|
||||
loader->thread = furi_thread_alloc_ex(
|
||||
"loader",
|
||||
AppStackSizeLarge, // Last known minimum was 2400 for starting Hello World app
|
||||
&loader_main,
|
||||
NULL
|
||||
);
|
||||
loader->app_data.args = NULL;
|
||||
loader->app_data.app = NULL;
|
||||
loader->app_data.view_port = NULL;
|
||||
loader->mutex = xSemaphoreCreateRecursiveMutex();
|
||||
return loader;
|
||||
}
|
||||
|
||||
static void loader_free() {
|
||||
furi_check(loader != NULL);
|
||||
furi_thread_free(loader->thread);
|
||||
furi_pubsub_free(loader->pubsub);
|
||||
furi_message_queue_free(loader->queue);
|
||||
furi_mutex_free(loader->mutex);
|
||||
free(loader);
|
||||
}
|
||||
|
||||
void loader_lock() {
|
||||
furi_assert(loader);
|
||||
furi_assert(loader->mutex);
|
||||
furi_check(xSemaphoreTakeRecursive(loader->mutex, portMAX_DELAY) == pdPASS);
|
||||
}
|
||||
|
||||
void loader_unlock() {
|
||||
furi_assert(loader);
|
||||
furi_assert(loader->mutex);
|
||||
furi_check(xSemaphoreGiveRecursive(loader->mutex) == pdPASS);
|
||||
}
|
||||
|
||||
LoaderStatus loader_start_app(const char* id, const char* args, FuriString* error_message) {
|
||||
LoaderMessage message;
|
||||
LoaderMessageLoaderStatusResult result;
|
||||
|
||||
@ -32,7 +69,7 @@ LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args,
|
||||
return result.value;
|
||||
}
|
||||
|
||||
void loader_start_app_nonblocking(Loader* loader, const char* id, const char* args) {
|
||||
void loader_start_app_nonblocking(const char* id, const char* args) {
|
||||
LoaderMessage message;
|
||||
LoaderMessageLoaderStatusResult result;
|
||||
|
||||
@ -45,22 +82,22 @@ void loader_start_app_nonblocking(Loader* loader, const char* id, const char* ar
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
}
|
||||
|
||||
void loader_stop_app(Loader* loader) {
|
||||
void loader_stop_app() {
|
||||
LoaderMessage message;
|
||||
message.type = LoaderMessageTypeAppStop;
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
}
|
||||
|
||||
const AppManifest* _Nullable loader_get_current_app(Loader* loader) {
|
||||
App* app = loader->app_data.app;
|
||||
if (app != NULL) {
|
||||
return app->manifest;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
const AppManifest* _Nullable loader_get_current_app() {
|
||||
loader_lock();
|
||||
const App* app = loader->app_data.app;
|
||||
const AppManifest* manifest = app ? app->manifest : NULL;
|
||||
loader_unlock();
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
FuriPubSub* loader_get_pubsub(Loader* loader) {
|
||||
FuriPubSub* loader_get_pubsub() {
|
||||
furi_assert(loader);
|
||||
// it's safe to return pubsub without locking
|
||||
// because it's never freed and loader is never exited
|
||||
@ -68,30 +105,6 @@ FuriPubSub* loader_get_pubsub(Loader* loader) {
|
||||
return loader->pubsub;
|
||||
}
|
||||
|
||||
// implementation
|
||||
|
||||
static Loader* loader_alloc() {
|
||||
Loader* loader = malloc(sizeof(Loader));
|
||||
loader->pubsub = furi_pubsub_alloc();
|
||||
loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
|
||||
loader->thread = furi_thread_alloc_ex(
|
||||
"loader",
|
||||
2048,
|
||||
&loader_main,
|
||||
NULL
|
||||
);
|
||||
loader->app_data.args = NULL;
|
||||
loader->app_data.app = NULL;
|
||||
return loader;
|
||||
}
|
||||
|
||||
static void loader_free(Loader* loader) {
|
||||
furi_pubsub_free(loader->pubsub);
|
||||
furi_message_queue_free(loader->queue);
|
||||
furi_thread_free(loader->thread);
|
||||
free(loader);
|
||||
}
|
||||
|
||||
static void loader_log_status_error(
|
||||
LoaderStatus status,
|
||||
FuriString* error_message,
|
||||
@ -128,15 +141,19 @@ static LoaderStatus loader_make_success_status(FuriString* error_message) {
|
||||
}
|
||||
|
||||
static void loader_start_app_with_manifest(
|
||||
Loader* loader,
|
||||
const AppManifest* _Nonnull manifest,
|
||||
const char* args
|
||||
) {
|
||||
FURI_LOG_I(TAG, "start with manifest %s", manifest->id);
|
||||
|
||||
if (manifest->type != AppTypeUser && manifest->type != AppTypeSystem) {
|
||||
furi_crash("app type not supported by loader");
|
||||
furi_crash("App type not supported by loader");
|
||||
}
|
||||
|
||||
App* _Nonnull app = furi_app_alloc(manifest);
|
||||
|
||||
loader_lock();
|
||||
|
||||
loader->app_data.app = app;
|
||||
loader->app_data.args = (void*)args;
|
||||
|
||||
@ -149,23 +166,20 @@ static void loader_start_app_with_manifest(
|
||||
loader->app_data.view_port = view_port;
|
||||
view_port_draw_callback_set(view_port, manifest->on_show, NULL);
|
||||
|
||||
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
|
||||
gui_add_view_port(gui, view_port, GuiLayerWindow);
|
||||
})
|
||||
gui_add_view_port(view_port, GuiLayerWindow);
|
||||
} else {
|
||||
loader->app_data.view_port = NULL;
|
||||
}
|
||||
|
||||
loader_unlock();
|
||||
}
|
||||
|
||||
// process messages
|
||||
|
||||
static LoaderStatus loader_do_start_by_id(
|
||||
Loader* loader,
|
||||
const char* id,
|
||||
const char* args,
|
||||
FuriString* _Nullable error_message
|
||||
) {
|
||||
FURI_LOG_I(TAG, "loader start by id %s", id);
|
||||
FURI_LOG_I(TAG, "Start by id %s", id);
|
||||
|
||||
const AppManifest* manifest = app_manifest_registry_find_by_id(id);
|
||||
if (manifest == NULL) {
|
||||
@ -177,11 +191,13 @@ static LoaderStatus loader_do_start_by_id(
|
||||
);
|
||||
}
|
||||
|
||||
loader_start_app_with_manifest(loader, manifest, args);
|
||||
loader_start_app_with_manifest(manifest, args);
|
||||
return loader_make_success_status(error_message);
|
||||
}
|
||||
|
||||
static void loader_do_stop_app(Loader* loader) {
|
||||
static void loader_do_stop_app() {
|
||||
loader_lock();
|
||||
|
||||
App* app = loader->app_data.app;
|
||||
if (app == NULL) {
|
||||
FURI_LOG_W(TAG, "Stop app: no app running");
|
||||
@ -192,9 +208,7 @@ static void loader_do_stop_app(Loader* loader) {
|
||||
|
||||
ViewPort* view_port = loader->app_data.view_port;
|
||||
if (view_port) {
|
||||
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
|
||||
gui_remove_view_port(gui, view_port);
|
||||
})
|
||||
gui_remove_view_port(view_port);
|
||||
view_port_free(view_port);
|
||||
loader->app_data.view_port = NULL;
|
||||
}
|
||||
@ -211,6 +225,8 @@ static void loader_do_stop_app(Loader* loader) {
|
||||
furi_app_free(loader->app_data.app);
|
||||
loader->app_data.app = NULL;
|
||||
|
||||
loader_unlock();
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Application stopped. Free heap: %zu",
|
||||
@ -222,43 +238,38 @@ static void loader_do_stop_app(Loader* loader) {
|
||||
furi_pubsub_publish(loader->pubsub, &event);
|
||||
}
|
||||
|
||||
// app
|
||||
bool loader_is_app_running() {
|
||||
loader_lock();
|
||||
bool is_running = loader->app_data.app != NULL;
|
||||
loader_unlock();
|
||||
return is_running;
|
||||
}
|
||||
|
||||
static int32_t loader_main(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FuriMessageQueue* queue = NULL;
|
||||
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
queue = loader->queue;
|
||||
})
|
||||
furi_check(queue != NULL);
|
||||
|
||||
LoaderMessage message;
|
||||
bool exit_requested = false;
|
||||
while (!exit_requested) {
|
||||
if (furi_message_queue_get(queue, &message, FuriWaitForever) == FuriStatusOk) {
|
||||
FURI_LOG_I(TAG, "processing message of type %d", message.type);
|
||||
furi_check(loader != NULL);
|
||||
if (furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
|
||||
FURI_LOG_I(TAG, "Processing message of type %d", message.type);
|
||||
switch (message.type) {
|
||||
case LoaderMessageTypeStartByName:
|
||||
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
if (loader->app_data.app) {
|
||||
loader_do_stop_app(loader);
|
||||
}
|
||||
message.status_value->value = loader_do_start_by_id(
|
||||
loader,
|
||||
message.start.id,
|
||||
message.start.args,
|
||||
message.start.error_message
|
||||
);
|
||||
if (message.api_lock) {
|
||||
api_lock_unlock(message.api_lock);
|
||||
}
|
||||
})
|
||||
if (loader_is_app_running()) {
|
||||
loader_do_stop_app();
|
||||
}
|
||||
message.status_value->value = loader_do_start_by_id(
|
||||
message.start.id,
|
||||
message.start.args,
|
||||
message.start.error_message
|
||||
);
|
||||
if (message.api_lock) {
|
||||
api_lock_unlock(message.api_lock);
|
||||
}
|
||||
break;
|
||||
case LoaderMessageTypeAppStop:
|
||||
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
loader_do_stop_app(loader);
|
||||
})
|
||||
loader_do_stop_app();
|
||||
break;
|
||||
case LoaderMessageTypeExit:
|
||||
exit_requested = true;
|
||||
@ -270,30 +281,30 @@ static int32_t loader_main(void* p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void loader_start(void* p) {
|
||||
Loader* loader = loader_alloc();
|
||||
furi_record_create(RECORD_LOADER, loader);
|
||||
furi_thread_set_priority(loader->thread, FuriThreadPriorityHigh);
|
||||
// region AppManifest
|
||||
|
||||
static void loader_start(void* parameter) {
|
||||
UNUSED(parameter);
|
||||
furi_check(loader == NULL);
|
||||
loader = loader_alloc();
|
||||
|
||||
furi_thread_set_priority(loader->thread, FuriThreadPriorityNormal);
|
||||
furi_thread_start(loader->thread);
|
||||
}
|
||||
|
||||
static void loader_stop() {
|
||||
furi_check(loader != NULL);
|
||||
LoaderMessage message = {
|
||||
.api_lock = NULL,
|
||||
.type = LoaderMessageTypeExit
|
||||
};
|
||||
|
||||
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
// Send stop signal to thread and wait for thread to finish
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
furi_thread_join(loader->thread);
|
||||
|
||||
furi_thread_join(loader->thread);
|
||||
furi_thread_free(loader->thread);
|
||||
loader->thread = NULL;
|
||||
|
||||
loader_free(loader);
|
||||
})
|
||||
|
||||
furi_record_destroy(RECORD_LOADER);
|
||||
loader_free();
|
||||
loader = NULL;
|
||||
}
|
||||
|
||||
const AppManifest loader_app = {
|
||||
@ -303,6 +314,7 @@ const AppManifest loader_app = {
|
||||
.type = AppTypeService,
|
||||
.on_start = &loader_start,
|
||||
.on_stop = &loader_stop,
|
||||
.on_show = NULL,
|
||||
.stack_size = AppStackSizeNormal
|
||||
.on_show = NULL
|
||||
};
|
||||
|
||||
// endregion
|
||||
@ -8,8 +8,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RECORD_LOADER "loader"
|
||||
|
||||
typedef struct Loader Loader;
|
||||
|
||||
typedef enum {
|
||||
@ -36,7 +34,7 @@ typedef struct {
|
||||
* @param[out] error_message detailed error message, can be NULL
|
||||
* @return LoaderStatus
|
||||
*/
|
||||
LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args, FuriString* error_message);
|
||||
LoaderStatus loader_start_app(const char* id, const char* args, FuriString* error_message);
|
||||
|
||||
/**
|
||||
* @brief Close any running app, then start new one. Non-blocking.
|
||||
@ -44,11 +42,13 @@ LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args,
|
||||
* @param[in] id application name or id
|
||||
* @param[in] args application arguments
|
||||
*/
|
||||
void loader_start_app_nonblocking(Loader* loader, const char* id, const char* args);
|
||||
void loader_start_app_nonblocking(const char* id, const char* args);
|
||||
|
||||
void loader_stop_app(Loader* loader);
|
||||
void loader_stop_app();
|
||||
|
||||
const AppManifest* _Nullable loader_get_current_app(Loader* loader);
|
||||
bool loader_is_app_running();
|
||||
|
||||
const AppManifest* _Nullable loader_get_current_app();
|
||||
/**
|
||||
* @brief Start application with GUI error message
|
||||
* @param[in] instance loader instance
|
||||
@ -56,20 +56,20 @@ const AppManifest* _Nullable loader_get_current_app(Loader* loader);
|
||||
* @param[in] args application arguments
|
||||
* @return LoaderStatus
|
||||
*/
|
||||
//LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);
|
||||
//LoaderStatus loader_start_with_gui_error(const char* name, const char* args);
|
||||
|
||||
/**
|
||||
* @brief Show loader menu
|
||||
* @param[in] instance loader instance
|
||||
*/
|
||||
void loader_show_menu(Loader* instance);
|
||||
void loader_show_menu();
|
||||
|
||||
/**
|
||||
* @brief Get loader pubsub
|
||||
* @param[in] instance loader instance
|
||||
* @return FuriPubSub*
|
||||
*/
|
||||
FuriPubSub* loader_get_pubsub(Loader* instance);
|
||||
FuriPubSub* loader_get_pubsub();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
#include "api_lock.h"
|
||||
#include "app_manifest.h"
|
||||
#include "apps/services/gui/view_port.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "loader.h"
|
||||
#include "message_queue.h"
|
||||
#include "pubsub.h"
|
||||
#include "thread.h"
|
||||
#include "apps/services/gui/view_port.h"
|
||||
|
||||
typedef struct {
|
||||
char* args;
|
||||
@ -18,6 +20,7 @@ struct Loader {
|
||||
FuriPubSub* pubsub;
|
||||
FuriMessageQueue* queue;
|
||||
LoaderAppData app_data;
|
||||
SemaphoreHandle_t mutex;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
||||
@ -38,6 +38,5 @@ AppManifest system_info_app = {
|
||||
.type = AppTypeSystem,
|
||||
.on_start = NULL,
|
||||
.on_stop = NULL,
|
||||
.on_show = app_show,
|
||||
.stack_size = AppStackSizeNormal
|
||||
.on_show = app_show
|
||||
};
|
||||
|
||||
@ -5,7 +5,12 @@
|
||||
|
||||
#define TAG "hardware"
|
||||
|
||||
Devices nb_devices_create(Config _Nonnull* config) {
|
||||
Hardware nb_hardware_init(const HardwareConfig _Nonnull* config) {
|
||||
if (config->bootstrap != NULL) {
|
||||
ESP_LOGI(TAG, "Bootstrapping");
|
||||
config->bootstrap();
|
||||
}
|
||||
|
||||
furi_check(config->display_driver != NULL, "no display driver configured");
|
||||
DisplayDriver display_driver = config->display_driver();
|
||||
ESP_LOGI(TAG, "display with driver %s", display_driver.name);
|
||||
@ -21,7 +26,7 @@ Devices nb_devices_create(Config _Nonnull* config) {
|
||||
touch = NULL;
|
||||
}
|
||||
|
||||
return (Devices) {
|
||||
return (Hardware) {
|
||||
.display = display,
|
||||
.touch = touch
|
||||
};
|
||||
|
||||
@ -10,7 +10,7 @@ extern "C" {
|
||||
typedef struct {
|
||||
DisplayDevice* _Nonnull display;
|
||||
TouchDevice* _Nullable touch;
|
||||
} Devices;
|
||||
} Hardware;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Devices nb_devices_create(Config _Nonnull* config);
|
||||
Hardware nb_hardware_init(const HardwareConfig _Nonnull* config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ typedef struct {
|
||||
uint16_t vertical_resolution;
|
||||
uint16_t draw_buffer_height;
|
||||
uint16_t bits_per_pixel;
|
||||
bool double_buffering;
|
||||
bool mirror_x;
|
||||
bool mirror_y;
|
||||
bool swap_xy;
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
#define TAG "lvgl"
|
||||
|
||||
Lvgl nb_graphics_init(Devices _Nonnull* hardware) {
|
||||
Lvgl nb_graphics_init(Hardware _Nonnull* hardware) {
|
||||
const lvgl_port_cfg_t lvgl_cfg = {
|
||||
.task_priority = 4,
|
||||
.task_stack = 4096,
|
||||
@ -23,7 +23,7 @@ Lvgl nb_graphics_init(Devices _Nonnull* hardware) {
|
||||
.io_handle = display->io_handle,
|
||||
.panel_handle = display->display_handle,
|
||||
.buffer_size = display->horizontal_resolution * display->draw_buffer_height * (display->bits_per_pixel / 8),
|
||||
.double_buffer = 0,
|
||||
.double_buffer = display->double_buffering,
|
||||
.hres = display->horizontal_resolution,
|
||||
.vres = display->vertical_resolution,
|
||||
.monochrome = display->monochrome,
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Lvgl nb_graphics_init(Devices _Nonnull* hardware);
|
||||
Lvgl nb_graphics_init(Hardware _Nonnull* hardware);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -4,9 +4,11 @@
|
||||
#include "furi.h"
|
||||
#include "graphics_i.h"
|
||||
#include "partitions.h"
|
||||
|
||||
#include "apps/services/gui/gui.h"
|
||||
#define TAG "nanobake"
|
||||
|
||||
Gui* gui_alloc();
|
||||
|
||||
// System services
|
||||
extern const AppManifest gui_app;
|
||||
extern const AppManifest loader_app;
|
||||
@ -50,12 +52,12 @@ static void start_desktop() {
|
||||
FURI_LOG_I(TAG, "Startup complete");
|
||||
}
|
||||
|
||||
__attribute__((unused)) extern void nanobake_start(Config* _Nonnull config) {
|
||||
__attribute__((unused)) extern void nanobake_start(const Config* _Nonnull config) {
|
||||
furi_init();
|
||||
|
||||
nb_partitions_init();
|
||||
|
||||
Devices hardware = nb_devices_create(config);
|
||||
Hardware hardware = nb_hardware_init(config->hardware);
|
||||
/*NbLvgl lvgl =*/nb_graphics_init(&hardware);
|
||||
|
||||
register_apps(config);
|
||||
|
||||
@ -10,20 +10,27 @@ extern "C" {
|
||||
|
||||
// Forward declarations
|
||||
typedef void* FuriThreadId;
|
||||
typedef void (*Bootstrap)();
|
||||
typedef TouchDriver (*CreateTouchDriver)();
|
||||
typedef DisplayDriver (*CreateDisplayDriver)();
|
||||
|
||||
typedef struct {
|
||||
// Optional bootstrapping method
|
||||
const Bootstrap _Nullable bootstrap;
|
||||
// Required driver for display
|
||||
const CreateDisplayDriver _Nonnull display_driver;
|
||||
// Optional driver for touch input
|
||||
const CreateTouchDriver _Nullable touch_driver;
|
||||
} HardwareConfig;
|
||||
|
||||
typedef struct {
|
||||
const HardwareConfig* hardware;
|
||||
// List of user applications
|
||||
const size_t apps_count;
|
||||
const AppManifest* const apps[];
|
||||
} Config;
|
||||
|
||||
__attribute__((unused)) extern void nanobake_start(Config _Nonnull* config);
|
||||
__attribute__((unused)) extern void nanobake_start(const Config _Nonnull* config);
|
||||
|
||||
FuriThreadId nanobake_get_app_thread_id(size_t index);
|
||||
size_t nanobake_get_app_thread_count();
|
||||
|
||||
@ -1,5 +1,17 @@
|
||||
# Yellow Board only runs on ESP32
|
||||
set(project_components nanobake)
|
||||
|
||||
if("${IDF_TARGET}" STREQUAL "esp32")
|
||||
list(APPEND project_components yellow_board)
|
||||
endif()
|
||||
|
||||
# T-Deck is an S3 platform
|
||||
if("${IDF_TARGET}" STREQUAL "esp32s3")
|
||||
list(APPEND project_components lilygo_tdeck)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRC_DIRS "src"
|
||||
"src/hello_world"
|
||||
REQUIRES nanobake board_2432s024
|
||||
REQUIRES ${project_components}
|
||||
)
|
||||
|
||||
14
main/Kconfig
Normal file
14
main/Kconfig
Normal file
@ -0,0 +1,14 @@
|
||||
# Kconfig file for NanoBake example app
|
||||
menu "NanoBake App"
|
||||
choice
|
||||
prompt "Board"
|
||||
default NB_BOARD_CUSTOM
|
||||
|
||||
config NB_BOARD_CUSTOM
|
||||
bool "Custom"
|
||||
config NB_BOARD_YELLOW_BOARD_24_CAP
|
||||
bool "Yellow Board (2.4\" capacitive)"
|
||||
config NB_BOARD_LILYGO_TDECK
|
||||
bool "LilyGo T-Deck"
|
||||
endchoice
|
||||
endmenu
|
||||
@ -1,6 +1,7 @@
|
||||
dependencies:
|
||||
espressif/esp_lcd_ili9341: "^2.0.0"
|
||||
espressif/esp_lcd_touch_cst816s: "^1.0.3"
|
||||
espressif/esp_lcd_touch_gt911: "^1.0.0"
|
||||
espressif/esp_lcd_touch: "1.1.1"
|
||||
esp_lvgl_port: '1.4.0'
|
||||
idf: '>=5.1'
|
||||
|
||||
14
main/src/board_config.h
Normal file
14
main/src/board_config.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Supported hardware:
|
||||
#if defined(CONFIG_NB_BOARD_LILYGO_TDECK)
|
||||
#include "lilygo_tdeck.h"
|
||||
#define NB_BOARD_HARDWARE &lilygo_tdeck
|
||||
#elif defined(CONFIG_NB_BOARD_YELLOW_BOARD_24_CAP)
|
||||
#include "yellow_board.h"
|
||||
#define NB_BOARD_HARDWARE &yellow_board_24inch_cap
|
||||
#elif defined(CONFIG_NB_BOARD_CUSTOM)
|
||||
#define NB_BOARD_HARDWARE furi_crash( \
|
||||
"Replace NB_BOARD_HARDWARE in main.c with your own, or use \"idf.py menuconfig\" to select a supported board." \
|
||||
)
|
||||
#endif
|
||||
@ -1,5 +1,4 @@
|
||||
#include "hello_world.h"
|
||||
#include "furi.h"
|
||||
#include "apps/services/gui/gui.h"
|
||||
#include "apps/services/loader/loader.h"
|
||||
|
||||
@ -21,6 +20,5 @@ const AppManifest hello_world_app = {
|
||||
.type = AppTypeUser,
|
||||
.on_start = NULL,
|
||||
.on_stop = NULL,
|
||||
.on_show = &app_show,
|
||||
.stack_size = AppStackSizeNormal,
|
||||
.on_show = &app_show
|
||||
};
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
#include "nanobake.h"
|
||||
#include "record.h"
|
||||
#include "apps/services/loader/loader.h"
|
||||
|
||||
// Hardware
|
||||
#include "board_2432s024.h"
|
||||
#include "board_config.h"
|
||||
|
||||
// Apps
|
||||
#include "hello_world/hello_world.h"
|
||||
|
||||
__attribute__((unused)) void app_main(void) {
|
||||
static Config config = {
|
||||
.display_driver = &board_2432s024_create_display_driver,
|
||||
.touch_driver = &board_2432s024_create_touch_driver,
|
||||
static const Config config = {
|
||||
.hardware = NB_BOARD_HARDWARE,
|
||||
.apps = {
|
||||
&hello_world_app
|
||||
},
|
||||
@ -19,12 +14,4 @@ __attribute__((unused)) void app_main(void) {
|
||||
};
|
||||
|
||||
nanobake_start(&config);
|
||||
|
||||
// FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
|
||||
// FuriString* error_message = furi_string_alloc();
|
||||
// if (loader_start_app(loader, hello_world_app.id, NULL, error_message) != LoaderStatusOk) {
|
||||
// FURI_LOG_E(hello_world_app.id, "%s\r\n", furi_string_get_cstr(error_message));
|
||||
// }
|
||||
// furi_string_free(error_message);
|
||||
// });
|
||||
}
|
||||
|
||||
23
sdkconfig.board.lilygo_tdeck
Normal file
23
sdkconfig.board.lilygo_tdeck
Normal file
@ -0,0 +1,23 @@
|
||||
# Software defaults
|
||||
CONFIG_LV_FONT_MONTSERRAT_14=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_18=y
|
||||
CONFIG_LV_USE_USER_DATA=y
|
||||
CONFIG_LV_USE_FS_STDIO=y
|
||||
CONFIG_LV_FS_STDIO_LETTER=65
|
||||
CONFIG_LV_FS_STDIO_PATH=""
|
||||
CONFIG_LV_FS_STDIO_CACHE_SIZE=4096
|
||||
CONFIG_LV_USE_PNG=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
|
||||
# Hardware defaults
|
||||
CONFIG_NB_BOARD_LILYGO_TDECK=y
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_LV_COLOR_16_SWAP=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
CONFIG_FLASHMODE_QIO=y
|
||||
23
sdkconfig.board.yellow_board
Normal file
23
sdkconfig.board.yellow_board
Normal file
@ -0,0 +1,23 @@
|
||||
# Software defaults
|
||||
CONFIG_LV_FONT_MONTSERRAT_14=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_18=y
|
||||
CONFIG_LV_USE_USER_DATA=y
|
||||
CONFIG_LV_USE_FS_STDIO=y
|
||||
CONFIG_LV_FS_STDIO_LETTER=65
|
||||
CONFIG_LV_FS_STDIO_PATH=""
|
||||
CONFIG_LV_FS_STDIO_CACHE_SIZE=4096
|
||||
CONFIG_LV_USE_PNG=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
|
||||
# Hardware defaults
|
||||
CONFIG_NB_BOARD_YELLOW_BOARD_24_CAP=y
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_LV_COLOR_16_SWAP=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_FLASHMODE_QIO=y
|
||||
@ -1,5 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_LV_COLOR_16_SWAP=y
|
||||
# Software defaults
|
||||
CONFIG_LV_FONT_MONTSERRAT_14=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_18=y
|
||||
CONFIG_LV_USE_USER_DATA=y
|
||||
@ -8,8 +7,13 @@ CONFIG_LV_FS_STDIO_LETTER=65
|
||||
CONFIG_LV_FS_STDIO_PATH=""
|
||||
CONFIG_LV_FS_STDIO_CACHE_SIZE=4096
|
||||
CONFIG_LV_USE_PNG=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
# Work-around for Furi issue
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
|
||||
# Hardware defaults
|
||||
CONFIG_NB_BOARD_CUSTOM=y
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user