Подготовка к велосипедостроению.
Valentyn Korniienko
Все живет тут:
https://github.com/ValentiWorkLearning/GradWork
P.S. Буду рад любым вопросам, пожеланиям, идеям по проекту. Все описать в одной статье не получится. Закрыл необходимый минимум вопросов.
Идея, которая крутилась крайне давно в голове и ждала своей реализации до момента, пока не увидел Samsung Gear и момента приближения защиты диплома. Так и появилась идея. Что если начать творить что-то похожее, но своими руками? Как раз, давно хотел разобраться с процессорами от Nordic. Слышал много, интересно было попробовать.
Задача была поставлена ясно( как оказалось в последствии, нет ). Сделать что-то около умных часов своими руками, на доступных компонентах. И сразу поставлена хотелка- круглый дисплей. Графика- LittlevGL. Языки разработки - С/С++. Система сборки- CMake. САПР- KiCAD.
Работа началась с запуска сборки под VSCode для NRF52832. Далее-трассировка и заказ печатной платы. После- написание драйверов под дисплей и разработка UI( с которым любезно помогла natali.kolesnichenko.01@gmail.com ).
Первый прототип платы выглядел как:

И были спешно проведены первые тесты запуска библиотеки LittlevGL и BLE-стека.


Компоненты выбрал достаточно типовые, а именно: модуль от EByte E73NRF52832, пульсометр - MAX30102, магнитометр/акселерометр/гироскоп -MPU9250. Память - W25Q16BVS. Из интересного - круглые дисплеи на Aliexpress продаются с разными интерфейсами SPI(8-9bit). 9-bit-ный попробую запустить в ближайшее время. После сборки спешно запустил BLE стек, добавил отображение заряда батареи.
Изначально понимая, что разработку и протипирование интерфейса прийдется вести на ПК ( во избежание прошивки контроллера каждый раз при изменении координат виджетов на несколько пикселей), архитектура проекта выстраивалась с учетом данного требования. Примерную диаграмму взаимодействия компонентов рисовал в draw.io. Подробнее с ней можно ознакомиться в репозитории проекта.
К текущему моменту рассказа появились первые прототипы нормального интерфейса.

Вкратце о том, что представляет из себя задача по портированию LVGL для выбранного проекта и дисплея.
1) Выбрать политику работы с буфером. LVGL поддерживает двойную буферизацию с буферами меньше, чем дисплей. Или же честная двойная буферизация, два фреймбуфера. Или же один фреймбуфер размером, соответствующим дисплею, либо меньше.[1]
2) В драйвере дисплея реализовать функцию заполнения прямоугольника заданных размеров заданным буфером цвета.
3) По окончанию заполнения дисплея вызывать lv_disp_flush_ready
4) С заданным периодом вызывать lv_tick_inc, необходимо для нужд библиотеки, таких как анимации[2]
5) Периодически вызывать lv_task_handler, период порядка 5мс[3]
Подробнее можно ознакомиться тут:
[1]https://docs.littlevgl.com/en/html/porting/display.html
[2]https://docs.littlevgl.com/en/html/porting/tick.html
[3]https://docs.littlevgl.com/en/html/porting/task-handler.html
В моем случае была необходимость запуска прошивки как на PC, для разработки UI, так и на железке. LVGL предоставляет возможность запуска на ПК, можно посмотреть в примерах.
Реализация, использованная в проекте представляет собой идею PIMPL(Pointer to Implementation), которая инкапсулирует работу с платформозависимой графикой. В случае PC графическая подсистема использует библиотеку SDL. В случае Nordic - реализацию драйвера для ST7789V дисплея.


Подробнее ознакомиться можно тут:
Для хранения виджетов по умолчанию Lvgl использует пул памяти, размер которого выставляется в lv_conf.h
/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/# define LV_MEM_SIZE (8U * 1024U)
Таким образом, все объекты, аллоцируемые библиотекой, будут помещены в него.
Для рационального использования пула памяти, а именно- выгрузки виджетов, если они не видны пользователю, была выдумана простая обертка поверх std::unique_ptr, а именно:

Использование крайне простое. Достаточно указать тип и функцию, необходимую для освобождения ресурсов за объектом. Таким образом, при скрытии страницы все ресурсы, занятые страницой, освобождаются автоматически.

Немного о модели сервисов, использованной в проекте. В Nordic SDK есть модуль-симулятор, позволяющий эмулировать данные с периферии ( на деле это таймер + функция, отдающая значения с увеличивающимся/уменьшающимся шагом). В случае с PC- это std::thread + std::condition_variable.
Все платформо-зависимые части спрятаны за сервисами, которые могут быть либо реальными, либо заглушками/симуляторами.

UI и сервисы, в свою очередь общаются с помощью сообщений. Компоненты UI могут осуществить подписку на сообщения от сервисов, например:


В свою очередь, на стороне отправки сообщений механизм будет следующий:

Говоря о сервисах. Все доступные сервисы создаются через ServiceCreator. интерфейс приведен ниже:

В свою очередь, интерфейс сервиса выглядит как:

Где конкретный наследник реализует его методы + инициирует вызовы сигналов. Например, как это делает FakeBatteryService:

</Закончить листинги кода>
Что получилось в итоге:



Технологии, использованные в проекте:
-Nordic SDK
-C++17
-CMake
-LittlevGL
-Embedded Template Library
-SimpleSignal [https://github.com/larspensjo/SimpleSignal/blob/master/SimpleSignal.h]
-Callback Connector - wrapper for C-callbacks
[https://github.com/Toxa-man/CallbackConnector]
-SDL Library