Контекст
Уся логіка прошивки розміщується в спеціальних класах, які повинні наслідуватися від IContext. Ці класи називаються контекстом. Контекст можна уявляти окремим повноцінним модулем прошивки. Для розробника кожен такий модуль це main.cpp на максималках. Кожен контекст має свій власний набір змінних, свій setup, loop, а також update, в якому відбувається обробка вводу та формування кадру для виводу на дисплей. В кожен момент часу активним може бути тільки один модуль. Якщо потрібно виконувати паралельно декілька завдань, рекомендується використання задач FreeRTOS зі зворотніми викликами. Також можливим є використання псевдопаралелізму з мітками часу.
Як це працює
Коли починає виконуватися програма прошивки, створюється задача менеджера контекстів. Його єдина функція це контроль та перемикання контекстів першого рівня. Модулі одного рівня не повинні знати про існування один одного, все що їм потрібно для перемикання це числовий ідентифікатор наступного контексту. Теоретично на одному рівні може одночасно знаходитися до 255 контекстів. Проте додавати всі модулі на один рівень не рекомендується. Краще за все буде об’єднання їх в умовні логічні групи. Це досягається за рахунок того, що кожен модуль може сам виступати менеджером контекстів. Проте тільки розробник вирішує на якому рівні повинен знаходитися кожен із модулів.
Розглянемо простий приклад. Маємо контекст домашнього екрану, контекст головного меню та налаштувань. Усі вони знаходяться на першому рівні й керуються менеджером контекстів. Кожен доступний тік, який було виділено UI-задачі, менеджер контекстів викликає тік у поточного активного модуля. Інших модулів на цьому ж рівні не існує фізично в цей момент.
Тепер, якщо потрібно додати декілька вікон в налаштування, наприклад, вікно з налаштуванням годинника, вікно з налаштуванням точки доступу, тощо, можна обрати один зі шляхів:
- Додати перемикання шаблонів GUI та їх стану в середину контексту налаштувань (не рекомендується через заплутування логіки).
- Додати окремо контексти для кожної вкладки налаштувань, та помістити їх на перший рівень глибини (не бажано).
- Створити окремо контексти для кожної вкладки, та організувати їх перемикання в межах контексту головного вікна налаштувань.
У третьому випадку відбуватиметься наступне:
- Менеджер контекстів викликає tick у головного вікна модуля налаштувань.
- Поки користувач не натиснув вибір якогось певного пункту налаштувань, головний модуль налаштувань утримує контекст, оброблює натискання і тд.
- Якщо користувач обирає певну вкладку налаштувань зі списку, головний модуль налаштувань повинен створити відповідний вкладений контекст і передати йому керування.
- Тепер менеджер контекстів продовжує викликати tick у основного модуля налаштувань, а той віддає увесь свій час одному із вкладених активних контекстів, поки його не буде звільнено.
Важливо
Якщо поточний контекст передає свій час підмодулю, він мусить повернути false із перевизначеного метода loop. Це означатиме, що він не повинен додатково оновлювати стан вводу чи відмальовувати UI тому що цим займеться субмодуль.
Завдяки такому механізму може досягатися “нескінченна” вкладеність глибини контексту без значних додаткових витрат ресурсів МК.
Зміна контексту відбувається дуже просто. Під час виклику спеціального методу, до нього передається числовий ідентифікатор модуля, який повинен бути створений наступним. Після цього поточний контекст підніме прапор, який означатиме, що його потрібно звільнити. Якщо контекст не відкриває інші модулі, достатньо просто підняти прапор, який сигналізує про завершення роботи поточного контексту. Далі все відбувається, як описано вище. Об’єкт, який керує поточним контекстом, прочитає цей прапор, видалить активний модуль і виконає інші дії в залежності від логіки програми.