PineTime Watch Face Simulator с портом LVGL для WebAssembly
Симулятор PineTime Watch Face в веб-браузере (с использованием WebAssembly) для упрощения разработки пользовательских циферблатов.
Онлайн-демонстрация: https://appkaki.github.io/lvgl-wasm/lvgl.html
Исходный код циферблата на C++: clock/Clock.cpp
Прочитайте статью...
«Предварительный просмотр циферблатов PineTime в вашем веб-браузёре с помощью WebAssembly» https://lupyuen.github.io/pinetime-rust-mynewt/articles/simulator
Для циферблатов на основе Rust см. ветку rust
https://github.com/AppKaki/lvgl-wasm/blob/rust/README.md
Компилирует реальный циферблат PineTime из C++ в WebAssembly: Clock.cpp
был преобразован в WebAssembly clock
.
Автоматически преобразует любой циферблат PineTime из C++ в WebAssembly с помощью sed
и GitHub Actions / GitLab CD. Демонстрация пользовательского циферблата https://appkaki.github.io/lvgl-wasm/lvgl2.html / Исходный код clock/Clock2.cpp.
Использует рабочий процесс GitHub Actions для сборки любого форка [InfiniTime Watch Face] (https://github.com/JF002/Pinetime) в WebAssembly.
Отображает LVGL на HTML Canvas напрямую через WebAssembly, без использования SDL2. См. lvgl.html#L1296-L1357.
Включает шрифты и символы PineTime из LittleVgl.cpp (https://github.com/JF002/Pinetime/blob/master/src/DisplayApp/LittleVgl.cpp).
Поддерживает формат фреймбуфера RGB565, используемый контроллером дисплея PineTime, чтобы растровые изображения отображались правильно. Демонстрация растрового изображения https://appkaki.github.io/lvgl-wasm/lvgl2.html / Исходный код clock/Clock2.cpp.
Показывает текущую дату и время.
Мы создаём форк репозитория PineTime InfiniTime Firmware на GitHub (или GitLab): https://github.com/JF002/Pinetime.
Включаем публикацию GitHub Pages (или GitLab Pages) для ветки master
, папки docs
.
Добавляем __рабочий процесс GitHub Actions (или GitLab CD): .github/workflows/simulate.yml* (https://github.com/lupyuen/pinetime-lab/blob/master/.github/workflows/simulate.yml).
Активируем рабочий процесс.
Мы редактируем DisplayApp/Screens/Clock.cpp в веб-браузёре через GitHub (или веб-IDE GitLab).
Это запускает сборку PineTime Firmware в GitHub Actions (или GitLab CD), при условии, что установлен .github/workflows/main.yml
(https://github.com/lupyuen/pinetime-lab/blob/master/.github/workflows/main.yml).
Также собирается симулятор PineTime Watch Face в WebAssembly.
Затем файлы WebAssembly отправляются в GitHub Pages (или GitLab Pages).
Мы предварительно просматриваем циферблат PineTime через симулятор в веб-браузёре: https://YOUR_ACCOUNT.github.io/Pinetime (см. Онлайн-демонстрацию).
Если нас устраивает циферблат, мы загружаем собранную прошивку на PineTime через Bluetooth. См. «Тестируем нашу прошивку PineTime» (https://lupyuen.github.io/pinetime-rust-mynewt/articles/cloud#download-and-test-our-pinetime-firmware).
Обработка сенсорного ввода для LVGL.
Преобразование Clock.cpp из C++ в Rust с помощью lvgl-rs (https://github.com/rafaelcaricio/lvgl-rs).
См. ветку rust проекта lvgl-asm (https://github.com/AppKaki/lvgl-wasm/blob/rust/README.md).
Позволить создавать циферблаты PineTime онлайн на Rust с предварительным просмотром.
«Предварительный просмотр циферблатов PineTime в вашем веб-браузёре с помощью WebAssembly».
Программирование с PineTime
Сборка прошивки PineTime в облаке с помощью GitHub Actions
Чтобы собрать симулятор циферблата часов PineTime на Linux x64 или Arm64, выполните следующие шаги:
Установите emscripten и wabt. Следуйте инструкциям ниже.
Введите...
git clone https://github.com/AppKaki/lvgl-wasm
cd lvgl-wasm
Необходимо предотвратить параллельное выполнение команд make, так как это может привести к зависанию машины из-за высокой нагрузки на ввод-вывод.
Отредактируйте файл wasm/lvgl.sh
и измените...
make -j
На...
make
DisplayApp/Screens/Clock.cpp
из нашего форка репозитория InfiniTime в папку clock/Clock.cpp
:# Предположим, что наш форк InfiniTime находится в ~/Pinetime
cp ~/Pinetime/src/DisplayApp/Screens/Clock.cpp clock/Clock.cpp
Этот файл будет встроен в симулятор.
# Сборка приложения LVGL: wasm/lvgl.html, lvgl.js, lvgl.wasm
wasm/lvgl.sh
Вы должны увидеть...
...
clock/ClockTmp.cpp:172:32: warning: format specifies type 'unsigned long' but the argument has type 'unsigned int' [-Wformat]
sprintf(stepBuffer, "%lu", stepCount.Get());
~~~ ^~~~~~~~~~~~~~~
%u
17 warnings generated.
+ wasm-objdump -x wasm/lvgl.wasm
+ mv wasm/lvgl.html wasm/lvgl.old.html
Это создаст файлы wasm/lvgl.html
, wasm/lvgl.js
и wasm/lvgl.wasm
.
cp wasm/lvgl.js wasm/lvgl.wasm docs
Нам не нужен lvgl.html
, потому что папка docs
уже содержит версию lvgl.html
с пользовательским JavaScript.
Для Arm64: Используйте darkhttpd
...
darkhttpd docs
Для x64: используйте расширение Chrome Web Server for Chrome и установите папку в docs
.
darkhttpd
или Web Server for Chrome.Введите lvgl.html
в адресной строке, чтобы просмотреть симулятор циферблата PineTime.
В случае возникновения проблем сравните с журналом сборки GitHub Actions (https://github.com/AppKaki/lvgl-wasm/actions?query=workflow%3A%22C%2FC%2B%2B+CI%22).
Симулятор циферблата часов PineTime был скомпилирован из C и C++ в WebAssembly с использованием emscripten.
Исходный файл приложения LVGL (wasm/lvgl.c)
Рабочий процесс GitHub Actions (.github/workflows/ccpp.yml)
Скрипт сборки (wasm/lvgl.sh)
Makefile
Рассмотрим скрипт сборки: wasm/lvgl.sh
. Toolchain для emscripten
rustup default nightly
rustup target add wasm32-unknown-emscripten
cargo build --target=wasm32-unknown-emscripten
emcc * -g* * wasm/test_rust.c* * -s WASM=1* * -s "EXPORTED_FUNCTIONS=[ '_main', '_get_display_buffer', '_get_display_width', '_get_display_height', '_test_display', '_test_c', '_test_c_set_buffer', '_test_c_get_buffer', '_test_c_buffer_address', '_test_rust', '_test_rust2', '_test_rust3', '_test_rust_set_buffer', '_test_rust_get_buffer' ]"* * -o wasm/test_rust.html* * -I src/lv_core* * target/wasm32-unknown-emscripten/debug/liblvgl_wasm_rust.a*
wasm-objdump -x wasm/test_rust.wasm >wasm/test_rust.txt
mv wasm/test_rust.html wasm/test_rust.old.html
InfiniTime Sandbox
PineTime Web Simulator работает в веб-браузере на основе WebAssembly (что-то вроде Java Applets).
Clock.cpp
— это наш класс C++, который содержит код Watch Face. Clock.cpp
вызывает функции из двух провайдеров...
InfiniTime Operating System, основанная на FreeRTOS
lvgl-wasm
имитирует минимальный набор функций InfiniTime, необходимых для рендеринга Watch Faces. (FreeRTOS не поддерживается симулятором).
Следовательно, lvgl-wasm
работает как Sandbox. Вот как работает песочница InfiniTime...
Песочница экспортирует следующие функции WebAssembly из C в JavaScript...
Эти функции создают класс часов из Clock.cpp
, отображают виджеты LVGL на циферблате и обновляют время...
create_clock()
Создаёт экземпляр часов.
refresh_clock()
Перерисовывает часы.
update_clock(year, month, day, hour, minute, second)
Устанавливает текущую дату и время в DateTimeController
. Время необходимо скорректировать для текущего часового пояса, см. ниже вызов JavaScript для update_clock()
.
Эти функции инициализируют библиотеку LVGL и отображают виджеты LVGL в буфере отображения WebAssembly...
init_display()
Инициализирует дисплей LVGL.
Из wasm/lvgl.c
render_display()
Отображает дисплей LVGL в 16-битном формате RGB565. Из wasm/lvgl.c
Вызывает драйвер дисплея WebAssembly, определённый в wasm/lv_port_disp.c
, который вызывает put_display_px()
, чтобы нарисовать отдельные пиксели в буфер отображения WebAssembly: wasm/lvgl.c
.
Драйвер дисплея WebAssembly поддерживает буфер отображения: массив пикселей 240 x 240, 4 байта на пиксель, в формате цвета RGBA: wasm/lvgl.c
:
/// RGBA WebAssembly Display Buffer that will be rendered to HTML Canvas
#define LV_HOR_RES_MAX 240
#define LV_VER_RES_MAX 240
#define DISPLAY_BYTES_PER_PIXEL 4
uint8_t display_buffer[LV_HOR_RES_MAX * LV_VER_RES_MAX * DISPLAY_BYTES_PER_PIXEL];
Наш код JavaScript копирует буфер отображения из памяти WebAssembly и отображает его на холсте HTML, вызывая следующие функции...
get_display_width()
Возвращает 240 — ширину буфера отображения WebAssembly.
Из wasm/lvgl.c
get_display_height()
Возвращает 240 — высоту буфера отображения WebAssembly.
Из wasm/lvgl.c
get_display_buffer()
Возвращает адрес буфера отображения WebAssembly.
Из wasm/lvgl.c
Символы.h
Символы циферблата часов.
Основан на DisplayApp/Screens/Symbols.h.
date.h
Функции даты.
Основан на libs/date/includes/date/date.h.
InfiniTime Sandbox предоставляет два стиля LVGL...
Стиль по умолчанию, определённый в lv_conf.h, со шрифтом jetbrains_mono_bold_20.
Примечание: Используйте базовую тему, определённую в LittleVgl.cpp. Она не работает с LVGL версии 7, поскольку для LVGL 7 нужны функции обратного вызова стилей.
LabelBigStyle, определённый в LittleVgl.cpp, со шрифтом jetbrains_mono_extrabold_compressed.
Здесь функции JavaScript вызывают экспортированные функции WebAssembly для отображения циферблата часов. Из docs/lvgl.html.
Мы регистрируем обратный вызов в API emscripten, чтобы получать уведомления о загрузке модуля WebAssembly lvgl.wasm.
// В JavaScript: дождитесь инициализации emscripten
Module.onRuntimeInitialized = function() {
// Рендеринг LVGL на HTML Canvas
render_canvas();
};
После загрузки модуля WebAssembly lvgl.wasm мы вызываем функцию WebAssembly init_display(), чтобы инициализировать дисплей LVGL.
/// В JavaScript: создайте циферблат часов в WebAssembly
function render_canvas() {
// Инициализируйте дисплей LVGL
Module._init_display();
Затем мы создаём класс циферблата часов LVGL из Clock.cpp.
// Создайте циферблат часов в WebAssembly
Module._create_clock();
Каждую минуту мы обновляем время циферблата часов в DateTimeController.
/// В JavaScript: обновите время циферблата часов в WebAssembly и отобразите буфер отображения WebAssembly на холсте HTML
function updateCanvas() {
// Обновите дату и время WebAssembly: год, месяц, день, час, минута, секунда
const localTime = new Date();
const timezoneOffset = localTime.getTimezoneOffset(); // В минутах
// Компенсируйте часовой пояс
const now = new Date(
localTime.valueOf() // Преобразуйте время в миллисекунды
- (timezoneOffset * 60 * 1000) // Преобразуем минуты в миллисекунды
);
Module._update_clock(
now.getFullYear(),
now.getMonth() - 1, // getMonth() возвращает от 1 до 12
now.getDay(),
now.getHours(),
now.getMinutes(),
now.getSeconds()
);
Обратите внимание, что нам нужно компенсировать часовой пояс.
И перерисовываем циферблат часов в Clock.cpp.
// Обновите время циферблата часов в WebAssembly
Module._refresh_clock();
Вызываем LVGL для рендеринга виджетов в буфер отображения WebAssembly.
// Отрендерите виджеты LVGL в буфер отображения WebAssembly
Module._render_display();
Изменяем размер холста HTML до разрешения PineTime 240 x 240, масштабированного в 2 раза.
const DISPLAY_SCALE = 2; // Масштабируйте ширину и высоту холста
// Получите размеры PineTime из буфера отображения WebAssembly
var width = Module._get_display_width();
var height = Module._get_get_display_height();
// Измените размер холста до размеров PineTime (240 x 240)
if (
Module.canvas.width != width * DISPLAY_SCALE ||
Module.canvas.height != height * DISPLAY_SCALE
) {
Module.canvas.width = width * DISPLAY_SCALE;
Module.canvas.height = height * DISPAY_SCALE;
}
Получаем холст HTML.
// Получаем пиксели холста
var ctx = Module.canvas.getContext('2d');
var... ## Копирование буфера отображения WebAssembly на холст HTML
Мы копируем пиксели из буфера отображения WebAssembly на HTML-холст (который использует 24-битный формат RGBA)...
```javascript
const DISPLAY_SCALE = 2; // Масштабируем ширину и высоту холста
const DISPLAY_BYTES_PER_PIXEL = 4; // 4 байта на пиксель: RGBA
// Копируем пиксели из буфера отображения WebAssembly на канвас
var addr = Module._get_display_buffer();
Module.print(`В JavaScript: get_display_buffer() вернул ${toHex(addr)}`);
for (var y = 0; y < height; y++) {
// Масштабируем пиксели по вертикали, чтобы заполнить холст
for (var ys = 0; ys < DISPLAY_SCALE; ys++) {
for (var x = 0; x < width; x++) {
// Копируем из источника в место назначения с масштабированием
const src = ((y * width) + x) * DISPLAY_BYTES_PER_PIXEL;
const dest = ((((y * DISPLAY_SCALE + ys) * width) + x) * DISPLAY_BYTES_PER_PIXEL)
* DISPLAY_SCALE;
// Масштабируем пиксели по горизонтали, чтобы заполнить холст
for (var xs = 0; xs < DISPLAY_SCALE; xs++) {
const dest2 = dest + xs * DISPLAY_BYTES_PER_PIXEL;
// Копируем 4 байта: RGBA
for (var b = 0; b < DISPLAY_BYTES_PER_PIXEL; b++) {
data[dest2 + b] = Module.HEAPU8[addr + src + b];
}
}
}
}
}
Обратите внимание, что JavaScript может читать и записывать в память WebAssembly (рассматривая её как массив байтов JavaScript в Module.HEAPU8[]
). Но WebAssembly не может получить доступ к любой памяти JavaScript.
Вот почему мы разработали функции буфера отображения для управления памятью WebAssembly.
Наконец, мы обновляем холст HTML...
// Рисуем холст
ctx.putImageData(imageData, 0, 0);
}
См. GitHub Actions Workflow...
Ищите шаги...
«Установить emscripten».
«Установите wabt».
Измените /tmp
на постоянный путь, например ~
.
Затем добавьте emscripten и wabt в PATH...
# Добавляем emscripten и wabt в PATH
source ~/emsdk/emsdk_env.sh
export PATH=$PATH:~/wabt/build
Работает на Pinebook Pro с Manjaro...
sudo pacman -S emscripten
sudo pacman -S wabt
source /etc/profile.d/emscripten.sh
emcc --version
# Показывает версию emscripten 1.39.20
wasm-as --version
# Показывает binaryen версии 95
emscripten и binaryen, вероятно, будут работать, пропустите остальную часть этого раздела.
Это не удастся во время сборки, потому что emscripten 1.39 нужен binaryen версии 93, а не 95.
Можно установить binaryen версии 93... Но emcc завершится ошибкой с сообщением «stackSave уже существует». Это связано с тем, что binaryen 93 генерирует «stackSave», который конфликтует с emscripten 1.39.20. Подробнее здесь.
Чтобы это исправить, устанавливаем binaryen версии 94, но переименовываем его в версию 93...
# Скачиваем binaryen 94
git clone --branch version_94 https://github.com/WebAssembly/binaryen
cd binaryen
nano CMakeLists.txt
Изменить
project(binaryen LANGUAGES C CXX VERSION 94)
На
project(binaryen LANGUAGES C CXX VERSION 93)
Затем соберите и установите binaryen...
cmake .
make -j 5
sudo cp bin/* /usr/bin
sudo cp lib/* /usr/lib
wasm-as --version
# Показывает двоичный файл «версия 93 (версия_94)»
Теперь binaryen имеет версию 93, которая является правильной. Перейдите к сборке приложения...
cd lvgl-wasm
rm -rf ~/.emscripten_cache
make clean
make -j 5
Сборка приложения должна завершиться успешно.
Если мы увидим эту ошибку...
emcc: error: unexpected binaryen version: 95 (expected 93) [-Wversion-check] [-Werror]
FAIL: Compilation failed!: ['/usr/lib/emscripten/emcc', '-D_GNU_SOURCE', '-o', '/tmp/tmpbe4ik5na.js', '/tmp/tmpzu5jusdg.c', '-O0', '--js-opts',
*В этом тексте есть фрагменты кода, которые не удалось перевести. Вероятно, они содержат синтаксические ошибки или специальные символы.* 0, --memory-init-file, 0, -Werror, -Wno-format, -s, BOOTSTRAPPING_STRUCT_INFO=1, -s, WARN_ON_UNDEFINED_SYMBOLS=0, -s, STRICT=1, -s, SINGLE_FILE=1]
emcc Error: stackSave уже существует
Затем нам нужно установить правильную версию binaryen (см. выше).
Если мы видим эту ошибку...
Fatal: Module::addExport: stackSave already exists
emcc: error: '/usr/bin/wasm-emscripten-finalize --detect-features --global-base=1024 --check-stack-overflow /tmp/emscripten_temp_84xtyzya/tmpzet09r88.wasm -o /tmp/emscripten_temp_84xtyzya/tmpzet09r88.wasm.o.wasm' failed (1)
FAIL: Compilation failed!: ['/usr/lib/emscripten/emcc', '-D_GNU_SOURCE', '-o', '/tmp/tmpzet09r88.js', '/tmp/tmpxk8zxvza.c', '-O0', '--js-opts', '0', '--memory-init-file', '0', '-Werror', '-Wno-format', '-s', 'BOOTSTRAPPING_STRUCT_INFO=1', '-s', 'WARN_ON_UNDEFINED_SYMBOLS=0', '-s', 'STRICT=1', '-s', 'SINGLE_FILE=1']
Это означает, что binaryen 93 генерирует «stackSave», который конфликтует с emscripten 1.39.20. Подробнее здесь.
Нам нужно установить ветку version_94 binaryen, изменить версию в CMakeLists.txt на версию 93 (см. выше).
Введите следующие команды...
brew install emscripten
brew install binaryen
# Обновить llvm до 10.0.0
brew install llvm
brew upgrade llvm
nano /usr/local/Cellar/emscripten/1.40.1/libexec/.emscripten
Измените BINARYEN_ROOT и LLVM_ROOT на
BINARYEN_ROOT = os.path.expanduser(os.getenv('BINARYEN', '/usr/local')) # каталог
LLVM_ROOT = os.path.expanduser(os.getenv('LLVM', '/usr/local/opt/llvm/bin')) # каталог
Выходит ошибка:
emcc: warning: LLVM version appears incorrect (seeing "10.0", expected "12.0") [-Wversion-check]
shared:INFO: (Emscripten: Running sanity checks)
clang-10: error: unknown argument: '-fignore-exceptions'
emcc: error: '/usr/local/opt/llvm/bin/clang -target wasm32-unknown-emscripten -D__EMSCRIPTEN_major__=1 -D__EMSCRIPTEN_minor__=40 -D__EMSCRIPTEN_tiny__=1 -D_LIBCPP_ABI_VERSION=2 -Dunix -D__unix -D__unix__ -Werror=implicit-function-declaration -Xclang -nostdsysteminc -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/include/compat -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/include -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/include/libc -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/lib/libc/musl/arch/emscripten -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/local/include -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/include/SSE -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/lib/compiler-rt/include -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/lib/libunwind/include -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/cache/wasm/include -DEMSCRIPTEN -fignore-exceptions -Isrc/lv_core -D LV_USE_DEMO_WIDGETS ././src/lv_core/lv_group.c -Xclang -isystem/usr/local/Cellar/emscripten/1.40.1/libexec/system/include/SDL -c -o /var/folders/gp/jb0b68fn3b187mgyyrjml3km0000gn/T/emscripten_temp_caxv1fls/lv_group_0.o -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr' failed (1)
Вот пример трассировки стека WebAssembly, которая появляется в веб-браузере. Это происходит, когда мы не инициализируем стиль LVGL LabelBigStyle, используемый Clock.cpp
.
lvgl.js:1839 Fetch finished loading: GET "http://127.0.0.1:8887/lvgl.wasm".
instantiateAsync @ lvgl.js:1839
createWasm @ lvgl.js:1866
(anonymous) @ lvgl.js:2113
lvgl2.html:1237 In JavaScript: render_canvas()
lvgl2.html:1237 In C: Init display...
lvgl2.html:1237 Init display...
Uncaught RuntimeError: memory access out of bounds
at _lv_style_get_int (http://127.0.0.1:8887/lvgl.wasm:wasm-function[229]:0x21bfb)
at _lv_style_list_get_int (http://127.0.0.1:8887/lvgl.wasm:wasm-function[234]:0x22bf7)
at _lv_obj_get_style_int
``` **Миграция с LVGL версии 6 на версию 7**
PineTime работает на LVGL версии 6, в то время как наш порт WebAssembly работает на LVGL версии 7. Программы, созданные для LVGL версии 6, не будут компилироваться с версией 7.
Вот как мы перенесли код с LVGL версии 6...
* Оригинальный PineTime Clock.cpp (https://github.com/JF002/Pinetime/blob/master/src/DisplayApp/Screens/Clock.cpp).
* Оригинальный PineTime LittleVgl.cpp (https://github.com/JF002/Pinetime/blob/master/src/DisplayApp/LittleVgl.cpp).
На LVGL версии 7...
* Преобразованный WebAssembly Clock.cpp (clock/Clock.cpp).
* Преобразованный WebAssembly LittleVgl.cpp (clock/LittleVgl.cpp).
Сравните оригинальные и преобразованные файлы...
* Clock.cpp: LVGL версия 6 против версии 7 (https://github.com/AppKaki/lvgl-wasm/compare/clock_before...master#diff-9a3204013cda108f0edc5647e908ea82). Нажмите «Файлы изменены», затем «Изменённые файлы» и найдите Clock/Clock.cpp.
* LittleVgl.cpp: LVGL версия 6 против версии 7 (https://github.com/AppKaki/lvgl-wasm/compare/AppKaki:littlevgl_before...master#diff-c2a76b9cd8a6d2fd824f1441a1e2ed34). Нажмите «Файлы изменены», затем «Изменённые файлы» и найдите Clock/LittleVgl.cpp.
**Миграция lv_label_set_style**
Код, использующий lv_label_set_style...
```c++
lv_label_set_style(label_time, LV_LABEL_STYLE_MAIN, LabelBigStyle);
Следует изменить на lv_obj_reset_style_list и lv_obj_add_style...
// Удалить стили, поступающие из темы
lv_obj_reset_style_list(label_time, LV_LABEL_PART_MAIN);
// Затем добавить стиль
lv_obj_add_style(label_time, LV_LABEL_PART_MAIN, LabelBigStyle);
Или определите макрос следующим образом...
/// Изменить LVGL v6 lv_label_set_style() на LVGL v7 lv_obj_reset_style_list() и lv_obj_add_style()
#define lv_label_set_style(label, style_type, style) \
{ \
lv_obj_reset_style_list(label, LV_LABEL_PART_MAIN); \
lv_obj_add_style(label, LV_LABEL_PART_MAIN, style); \
}
lv_label_set_style(label_time, LV_LABEL_STYLE_MAIN, LabelBigStyle);
Миграция LVGL lv_style_plain
lv_style_plain был удалён в LVGL 7. Код вроде этого...
lv_style_copy(&def, &lv_style_plain);
Следует заменить на...
lv_style_init(&def);
Перенос тем LVGL
В LVL 6 было легко установить шрифт по умолчанию для темы...
lv_style_init(&def);
lv_style_set_text_font(&def, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
...
lv_theme_set_current(&theme);
Но в LVL 7 нам нужно использовать Theme Callback. Функции](https://docs.lvgl.io/latest/en/html/overview/style.html?highlight=theme#themes) для применения стиля.
Более простое решение — задать шрифт по умолчанию в lv_conf.h
...
#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(jetbrains_mono_bold_20)
#define LV_THEME_DEFAULT_FONT_SMALL &jetbrains_mono_bold_20
#define LV_THEME_DEFAULT_FONT_NORMAL &jetbrains_mono_bold_20
#define LV_THEME_DEFAULT_FONT_SUBTITLE &jetbrains_mono_bold_20
#define LV_THEME_DEFAULT_FONT_TITLE &jetbrains_mono_bold_20
Изменение стиля LVL 6...
lv_style_copy(&bg, &lv_style_plain);
bg.body.main_color = LV_COLOR_BLACK;
bg.body.grad_color = LV_COLOR_BLACK;
bg.text.color = LV_COLOR_WHITE;
bg.text.font = font;
bg.image.color = LV_COLOR_WHITE;
На стиль LVL 7...
lv_style_init(&bg);
lv_style_set_bg_color(&bg, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_style_set_bg_grad_color(&bg, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_style_set_text_color(&bg, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_text_font(&bg, LV_STATE_DEFAULT, font);
lv_style_set_image_recolor(&bg, LV_STATE_DEFAULT, LV_COLOR_WHITE);
Изменение стиля LVL 6...
lv_style_copy(&panel, &bg);
panel.body.main_color = lv_color_hsv_to_rgb(hue, 11, 18);
panel.body.grad_color = lv_color_hsv_to_rgb(hue, 11, 18);
panel.body.radius = LV_DPI / 20;
panel.body.border.color = lv_color_hsv_to_rgb(hue, 10, 25);
panel.body.border.width = 1;
panel.body.border.opa = LV_OPA_COVER;
panel.body.padding.left = LV_DPI / 10;
panel.body.padding.right = LV_DPI / 10;
panel.body.padding.top = LV_DPI / 10;
panel.body.padding.bottom = LV_DPI / 10;
panel.line.color = lv_color_hsv_to_rgb(hue, 20, 40);
panel.line.width = 1;
На стиль LVL 7...
lv_style_copy(&panel, &bg);
lv_style_set_bg_color(&panel, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(hue, 11, 18));
lv_style_set_bg_grad_color(&panel, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(hue, 11, 18));
lv_style_set_radius(&panel, LV_STATE_DEFAULT, LV_DPI / 20);
lv_style_set_border_color(&panel, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(hue, 10, 25));
lv_style_set_border_width(&panel, LV_STATE_DEFAULT, 1);
lv_style_set_border_opa(&panel, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_set_pad_left(&panel, LV_STATE_DEFAULT, LV_DPI / 10);
lv_style_set_pad_right(&panel, LV_STATE_DEFAULT, LV_DPI / 10);
lv_style_set_pad_top(&panel, LV_STATE_DEFAULT, LV_DPI / 10);
lv_style_set_pad_bottom(&panel, LV_STATE_DEFAULT, LV_DPI / 10);
lv_style_set_line_color(&panel, LV_STATE_DEFAULT, lv_color_hsv_to_rgb(hue, 20, 40));
lv_style_set_line_width(&panel, LV_STATE_DEFAULT, 1);
Дополнительные примеры миграции стилей LVL см. в...
LittleVgl.cpp: LVGL Version 6 vs Version 7
Нажмите «Файлы изменены», затем «Изменённые файлы» и найдите «Clock/LittleVgl.cpp».
LVGL предоставляет всё необходимое для создания встроенного графического интерфейса пользователя с удобными в использовании графическими элементами, красивыми визуальными эффектами и низким потреблением памяти.
Независимый для использования с любым микроконтроллером или дисплеем
Масштабируемый для работы с небольшим объёмом памяти (64 КБ Flash, 10 КБ RAM)
Многоязыковая поддержка с обработкой UTF-8, поддержкой двунаправленного текста и арабского письма
Полностью настраиваемые графические элементы через CSS-подобные стили
Поддержка ОС, внешней памяти и GPU, но не обязательно
Плавный рендеринг даже с одним буфером кадра
Написан на C для максимальной совместимости (совместим с C++)
Micropython Binding предоставляет доступ к LVGL API в Micropython
Симулятор для разработки на ПК без встроенного оборудования
Примеры и учебные пособия для быстрой разработки
Документация и справочные материалы по API
Требования
В целом, подходит любой современный контроллер, способный управлять дисплеем. Минимальные требования:
Имя | Минимальное | Рекомендуемое |
---|---|---|
Архитектура | 16, 32 или 64 битный микроконтроллер или процессор | |
Тактовая частота | > 16 МГц | > 48 МГц |
Flash/ROM | > 64 КБ | > 180 КБ |
Статическая RAM | > 2 КБ | > 4 КБ |
Стек | > 2 КБ | > 8 КБ |
Куча | > 2 КБ | > 8 КБ |
Буфер дисплея | > 1 * hor. res. пикселей | > 10 * hor. res. пикселей |
Компилятор | C99 или новее |
Обратите внимание, что использование памяти может варьироваться в зависимости от архитектуры, компилятора и параметров сборки.
Вот некоторые платформы, которые можно использовать:
— STM32F1, STM32F3, STM32F4, STM32F7, STM32L4, STM32L5, STM32H7;
— Microchip dsPIC33, PIC24, PIC32MX, PIC32MZ;
— NXP: Kinetis, LPC, iMX, iMX RT;
— Linux frame buffer (/dev/fb);
— Raspberry Pi;
— Espressif ESP32;
— Infineon Aurix;
— Nordic NRF52 Bluetooth модули;
— Quectel модемы.
Начало работы
Этот список показывает рекомендуемый способ изучения библиотеки:
Проверьте онлайн-демонстрации, чтобы увидеть LVGL в действии (3 минуты).
Прочитайте страницу «Введение» документации (5 минут).
Ознакомьтесь с основами на странице «Краткий обзор» (15 минут).
Настройте симулятор (10 минут).
Попробуйте примеры.
Перенесите LVGL на плату. См. руководство по переносу или готовые проекты.
Прочитайте страницу обзора, чтобы лучше понять библиотеку (2–3 часа).
Изучите документацию виджетов, чтобы узнать об их функциях и использовании.
Если у вас есть вопросы, перейдите на форум.
Прочитайте руководство по внесению вклада, чтобы узнать, как вы можете помочь улучшить LVGL (15 минут). ### Создание кнопки с надписью
lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL); /*Добавить кнопку на текущий экран*/
lv_obj_set_pos(btn, 10, 10); /*Установить её положение*/
lv_obj_set_size(btn, 100, 50); /*Установить её размер*/
lv_obj_set_event_cb(btn, btn_event_cb); /*Присвоить кнопке обратный вызов*/
lv_obj_t * label = lv_label_create(btn, NULL); /*Добавить надпись к кнопке*/
lv_label_set_text(label, "Button"); /*Задать текст надписи*/
...
void btn_event_cb(lv_obj_t * btn, lv_event_t event)
{
if (event == LV_EVENT_CLICKED) {
printf("Clicked\n");
}
}
Подробнее о Micropython.
# Создать кнопку и надпись
scr = lv.obj()
btn = lv.btn(scr)
btn.align(lv.scr_act(), lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)
label.set_text("Button")
# Загрузить экран
lv.scr_load(scr)
LVGL — это открытый проект, и мы приветствуем вклад от всех желающих. Есть много способов внести свой вклад: от простого рассказа о своём проекте до написания примеров, улучшения документации, исправления ошибок или создания собственного проекта на основе LVGL.
Подробное описание возможностей для вклада можно найти в разделе Вклад документации.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )