Существует множество библиотек графического интерфейса для микроконтроллеров и встраиваемых систем, но многие из них имеют ограниченный функционал, сложность в использовании и интеграции, необходимость обязательного использования внешней оперативной памяти, а некоторые стоят больше, чем ваш проект целиком. Из-за этих причин и многих других было решено писать свою библиотеку.
Назвал я её MakiseGui.
Перед началом разработки я поставил себе цели:
- Простота конечной разработки. Писать интерфейс не должно быть сложнее, чем используя WindowsForms и тп
- Простота интеграции. Встроить и запустить интерфейс в приложении должно быть максимально просто на любом железе или ПО.
- Чистый Си. Был использован только gnu-c99 и из библиотек только stdlib
- Минимальное потребление RAM. Возможность использования на средних микроконтроллерах без внешней памяти(примерно 40kb с цветным дисплеем 320х240).
- Достаточное количество графических элементов для комфортной разработки. Простое добавление новых.
- opensource лицензия и бесплатное использование даже в коммерческих проектах
Пример без объяснений.
В качестве демонстрации возможностей библиотеки и примеров использования может быть использован проект созданный специально для этих целей: https://github.com/SL-RU/MakiseSDLTest
Он использует SDL2 для отрисовки и ввода и имеет примеры использования всех элементов и почти всех функций системы. Может быть скомпиллирован и запущен на любом linux дистрибутиве. На windows тоже, но лишь теоретически — сам не пробовал.
Видео работы:
Структура
Библиотека состоит из трёх чётко разделённых частей:
1) Ядро. Ядро состоит из интерфейса к драйверу, функций отрисовки в драйвер и функций отрисовки примитивов в буфер.
2) Драйвер. Драйвер обеспечивает всё общение с железом и с ПО, поэтому под каждую задачу придётся писать обычно свой, чтобы учесть все моменты(DMA, прерывания и тд). Драйвер лишь обеспечивает передачу изображения из буфера на железо и очищает буфер изображения. Как примеры, в проекте есть драйверы для дисплея на ili9340, а так же SDL2 для отладки библиотеки на компьютере. Ядро и драйвер могут работать отдельно, без GUI.
3) Сам GUI. Занимает бОльшую часть системы, тут воплощены все необходимые функции для работы интерфейса: контейнеры, элементы, системы отрисовки, фокуса, ввода, обработки событий и прочего.
GUI
Разработка графического интерфейса максимально приближена к объектно-ориентированному для максимальной простоты конечного программирования. Благодаря этому она имеет некоторые приятные особенности
Простейший пример, создающий кнопку на экране:
MHost host; //базовая структура системы, root-контейнер, содержащий все другие контейнеры и элементы.
//метод будет вызыван при нажатии на кнопку
void click(MButton *b)
{
printf("Button was clicked"); //выводим сообщение в стандартный поток
b->text = "Clicked!"; //меняем текст кнопки
}
MButton button; //структура, содержащая всю информацию о кнопке
void create_gui()
{
//создаём кнопку
m_create_button(&button, //указатель на структуру кнопки
host->host, //контейнер, в который будет добавлена кнопка после создания. В данном случае это контейнер MHost'a
mp_rel(20, 20, //координаты элемента относительно левого верхнего угла
90, 30), //ширина, высота
"Click me", //текст кнопки
//События
&click, //Вызывается при нажатии на кнопку
0, //Вызывается до обработки нажатия, может прервать обработку нажатия
0, //Вызывается при действиях с фокусом кнопки
&ts_button //стиль кнопки
);
}
void main()
{
//тут была инициализация MakiseGui, драйвера, MakiseBuffer и MHost. Запуск драйвера.
create_gui();
while(1)
{
//драйвер вызывает функции рисовки
//совершается ввод
//и логика
}
}
Итого, этот пример создаёт на экране кнопку при нажатии на которую в стандартном потоке вывода появится надпись "Button was clicked" и текст кнопки изменится.
Инициализация
Инициализация предполагает только лишь запуск драйвера, задание размеров и выделение памяти для структур и буфферов элементов. Чисто формальная операция. Как инициализировать систему можно поглядеть тут: https://github.com/SL-RU/MakiseSDLTest/blob/master/src/main.c в методе start_m();
Для начала использования GUI нужно создать makise_config.h и сконфигурировать его. В этом файле задаются системные дефайны и выбираются нужные драйверы дисплея. https://github.com/SL-RU/MakiseSDLTest/blob/master/makise_config.h
Ввод
Ввод приспособлен для работы в мультипоточных приложениях — он имеет очередь событий, которые посылаются интерфейсу при вызове makise_gui_input_perform(host);
Любое событие ввода представлено структурой MInputData.
Возможен ввод кнопок(список стандартных в makise_gui_input.h MInputKeyEnum), символов(пока нигде не используется) и ввод курсора(сенсорный экран или мышь). В примере с SDL используется ввод с клавиатуры и ввод мышью.
Контейнеры.
MContainer — структура контейнера.
Контейнеры содержат связанный список элементов. Из контейнеров можно удалять или добавлять элементы, перемещать их и совершать другие операции.
Позиция элемента в контейнере прямо влияет на очередь отрисовки и ввода.
Линкованый список осуществляется при помощи указателей на первый и последний элемент списка MElement и в структуре MElement имеются указатели на следующий и предыдущий элемент.
Элементы.
Любой элемент представлен ввиде структуры MElement, которая содержит в себе информацию о элементе, указатели на функции отрисовки, ввода, фокуса и тд элемента и указатель на его содержимое.
На данный момент существуют следующие элементы:
- MButton — кнопка. Которая отображает текст посылает события при нажатии
- MCanvas — простейший контейнер, который просто содержит элементы.
- MLable — простейшее текстовое поле
- MTextField — текстовое поле, поддерживающее перенос слов и переносы
- MSlider — слайдер
- MToggle — кнопка имеющая два состояния.
- MSlist — список. Может быть как просто списком, так и radioкнопками, так и чекбосками. Поддерживает обычные списки и динамические линкованные.
- MTabs — вкладки. Несколько переключаемых контейнеров.
Лучшей документацией являются примеры, поэтому для каждого элемента есть свои примеры использования. Как сложные, так и простые.
Количество элементов будет пополняться со временем. Да, тут нет многих необходимых функций — графики, изображения и тд. Но для моих целей они пока не нужны, но если вскоре понадобятся, то буду добавлять и публиковать в библиотеку. Так же не стесняйтесь добавлять свои или править существующие! Пулл-реквесты приветствуются.
Стили
Стиль элемента определяет его внешний вид. Cтиль задаёт цвета элемента в определённом состоянии. За это отвечают структуры MakiseStyle и MakiseStyleTheme. MakiseStyle содержит несколько MakiseStyleTheme для определённых состояний, а так же параметры шрифта.
Для кнопки стиль может выглядеть так:
MakiseStyle ts_button =
{
MC_White, //основной цвет. Не несёт никакого значения
&F_Arial24,//Шрифт стиля
0, //межстрочное расстояние
//цвет заднего фона | шрифта бортик есть лидвойной бортик
{MC_Black, MC_Gray, MC_Gray, 0 }, //когда кнопка не активна
{MC_Black, MC_White, MC_White, 0 }, //нормальное состояние
{MC_White, MC_Green, MC_White, 0 }, //в фокусе
{MC_Green, MC_White, MC_White, 0 }, //когда была кликнута
};
Фокус
Фокус определяет к какому элементу пойдёт ввод. Для управления фокусом существуют следующие функции:
MFocusEnum makise_g_focus(MElement *el, MFocusEnum event); //фокусирует или расфокусирует нужный элемент
MFocusEnum makise_g_host_focus_next(MHost *host);//переведёт фокус на следующий по очереди элемент
MFocusEnum makise_g_host_focus_prev(MHost *host);//на предыдущий
Пример работы на микроконтроллере
Так же был написан пример библиотеки для STM32 микроконтроллеров. Был использован МК STM32f437VGT6 с тактовой частотой 180МГц и 2.2" дисплей 230х320 пикселей на контроллере ILI9341. Управления с компьютерной клавиатуры по UART.
Код примера: https://github.com/SL-RU/MakiseILI9341Test
Видео примера:
Немножко документации есть в репозитории. Но вся основная документация находиться в комментариях к функциям и в примерах. Задавайте вопросы! На основе них я буду дописывать документацию. Много моментов не было затронуто в статье или затронуто мимоходом. Если статья найдёт популярность, то с удовольствием напишу ещё несколько, например про создание драйвера для STM32 + tft дисплей, подключенный по FSMC для данного GUI.
Есть много моментов которые нужно совершенствовать в библиотеки и есть много путей развития. Но на данный момент библиотека является совершенно рабочей и стабильной.
Лицензия проекта — MIT. Вы можете использовать библиотеку и исходный код как хотите и где хотите, даже использовать без проблем в коммерческих проектах, но в то же время я не даю никаких гарантий по работе библиотеки — всё как есть.
Если вы что-то хотите поменять в коде, исправить найденный баг или ошибку, то пишите в issue в репозитории или даже кидайте пуллреквесты.
Буду рад вопросам и пожеланиям!
Автор: Александр Луцай