Написання програм на C++
Що таке програма в Keira?
Keira написана на C++, і вона містить ряд вбудованих програм. Програма в Keira - це клас, який наслідує клас App
та визначає метод App::run()
. Цей метод викликається при запуску програми.
Всі програми є вбудованими, тобто вони мають бути частиною Keira. Це означає, що ви не можете додати свою програму до Keira, якщо не зміните код Keira і не перепрошиєте Лілку новим кодом.
Ви можете використовувати будь-які функції з бібліотеки Lilka.
Примітка
Чи можу я написати свою програму на C++ окремо від Keira, якось скомпілювати її і запустити в Keira з SD-карти?
Ні. Динамічне завантаження програм у вбудованих системах - це дуже складний процес. Keira наразі не підтримує цю функцію. Але це може змінитись в майбутньому.
Якщо ви хочете писати програми, не перепрошиваючи Лілку, ми радимо спробувати вам Lua: Написання програм на Lua.
Клас App
Для створення власної програми вам потрібно наслідувати клас App
та визначити метод App::run()
. Цей метод буде викликатися при запуску програми.
Ось перелік важливих методів та властивостей класу App
:
-
class App
Клас, що представляє додаток для Кіри.
Додатки запускаються за допомогою синглтону AppManager.
Додаток має визначити принаймні метод run(), який буде викликатися в окремій задачі FreeRTOS.
При завершенні додатку, AppManager зупиняє задачу та видаляє об’єкт додатку.
Приклад запуску додатку:
#include <appmanager.h> #include <myapp.h> // ... AppManager::getInstance()->runApp(new MyApp());
Subclassed by AbstractLuaRunnerApp, BallApp, CallBackTestApp, ComboApp, CubeApp, DemoLines, DiskApp, EpilepsyApp, FileManagerApp, GPIOManagerApp, KeyboardApp, LauncherApp, LetrisApp, LilTrackerApp, MJSApp, ModPlayerApp, NesApp, PetPetApp, ScanI2CApp, SoundConfigApp, StatusBarApp, TamagotchiApp, TransformApp, UserSPIApp, WeatherApp, WiFiConfigApp, pastebinApp
Public Functions
-
void queueDraw()
Повідомити ОС, що додаток завершив малювання кадру.
Цей метод слід викликати після малювання кожного кадру.
Технічно, цей метод міняє місцями передній буфер (canvas) та задній буфер (backCanvas).
Якщо цей метод викликається в той час, коли ОС вже малює попередній кадр, то він призведе до нетривалого блокування додатку. Іншими словами, якщо ваш додаток малює кадри швидше, ніж ОС здатна їх відображати, то цей метод буде час від часу заповільнювати ваш додаток. Це не проблема, але варто про це пам’ятати.
Public Members
-
lilka::Canvas *canvas
Вказівник на передній буфер для малювання.
Додаток повинен використовувати цей буфер для малювання всієї графіки.
Після малювання, буфер потрібно відобразити на екрані за допомогою методу queueDraw().
Protected Functions
-
void setFlags(AppFlags flags)
Встановити прапорці додатку.
Наприклад, якщо додаток має відображатися на весь екран, то слід викликати setFlags(APP_FLAG_FULLSCREEN).
- Параметри:
flags –
-
void setStackSize(uint32_t stackSize)
Встановити розмір стеку задачі додатку.
За замовчуванням, розмір стеку задачі дорівнює 8192 байт. Проте деякі додатки можуть вимагати більший розмір стеку.
Private Functions
-
virtual void run() = 0
Основний код додатку.
Цей метод викликається в окремій задачі FreeRTOS.
Додаток завершується, коли цей метод завершується або робить return.
-
inline virtual void onSuspend()
Цей метод викликається операційною системою, коли вона збирається зупинити ваш додаток.
-
inline virtual void onResume()
Цей метод викликається операційною системою, коли вона збирається відновити роботу вашого додатку.
-
inline virtual void onStop()
Цей метод викликається операційною системою, коли вона збирається зупинити ваш додаток.
-
void queueDraw()
Приклад програми
Давайте створимо просту програму, яка буде малювати круг на екрані, який можна переміщувати за допомогою кнопок.
Для цього створіть два нові файли в директорії firmware/keira/src/apps
:
#include <lilka.h>
#include "app.h"
class MyApp : public App {
public:
MyApp();
private:
void run() override;
};
#include "myapp.h"
MyApp::MyApp() : App("Моя програма") {
}
void MyApp::run() {
int16_t x = canvas->width() / 2;
int16_t y = canvas->height() / 2;
while (true) {
// читаємо стан кнопок
lilka::State state = lilka::controller.getState();
if (state.up.pressed) {
y--;
} else if (state.down.pressed) {
y++;
}
if (state.left.pressed) {
x--;
} else if (state.right.pressed) {
x++;
}
if (state.a.pressed) {
// Завершуємо програму
return;
}
// заповнюємо екран чорним кольором
canvas->fillScreen(canvas->color565(0, 0, 0));
// малюємо білий круг
canvas->fillCircle(x, y, 10, canvas->color565(255, 255, 255));
// повідомляємо Keira, що буфер змінився і його потрібно перемалювати
queueDraw();
}
}
Давайте розберемося з кодом.
Ми створили клас
MyApp
, який наслідує класApp
.App
містить в собі віртуальний методrun
, який викликається при запуску програми.Також
App
автоматично створює об’єктcanvas
, який представляє собою буфер для малювання. Ви повинні малювати саме на ньому, а не на екрані. Детальніше про це - згодом.Весь код нашої програми знаходиться в методі
run
. Він автоматично викликається при запуску програми.Програма виконується в циклі
while (true)
. Це означає, що вона буде виконуватися постійно, поки ви не викличетеreturn
.Ми читаємо стан кнопок за допомогою
lilka::controller.getState()
. Це повертає об’єктlilka::State
, який містить в собі стан кожної кнопки.Наприклад,
state.up.pressed
- цеtrue
, якщо кнопкаup
натиснута.Ми щоразу заповнюємо екран чорним кольором, малюємо білий круг, а потім викликаємо
queueDraw()
.Цей метод повідомляє Keira, що буфер змінився і його потрібно перемалювати.
Примітка
Чому ми не малюємо безпосередньо на екрані, і чому щоразу заповнюємо його чорним кольором? І що таке*
queueDraw()
?Це все пов’язано з тим, що Keira - це мультизадачна операційна система, і різні програми можуть намагатись одночасно малювати щось на екрані.
Щоб уникнути конфліктів, Keira використовує подвійну буферизацію. Це означає, що кожна програма має два власні буфери: один («передній») для малювання, а інший («задній») - для відображення на екрані.
canvas
- це передній буфер. Саме на ньому ваша програма малює все, що ви хочете побачити на екрані.backCanvas
- це задній буфер. Вам не потрібно ним керувати.
Коли ви викликаєте метод
queueDraw()
, Keira міняє місцями передній і задній буфери і через деякий час починає малювати задній буфер на екрані в фоновому режимі. Таким чином ваша програма ніколи не малює безпосередньо на екрані: це робить Keira, а конкретніше - класAppManager
.canvas
завжди вказує на передній буфер, тому ви повинні малювати саме на ньому. Але оскільки ці буфери постійно міняються місцями, ваша програма не повинна робити жодних припущень про те, що було намальовано в попередній ітерації.Тому після кожного виклику
queueDraw()
кожна програма повинна знову малювати все, що ви хочете побачити на екрані, оскількиcanvas
буде містити «сміття», а не те, що ви малювали в попередній ітерації, і завжди відставатиме на одну ітерацію від того, що відображається на екрані.Це дає можливість не лише здійснювати конкурентне малювання на екрані з декількох програм, але й використовувати для цього обидва ядра процесора: одне ядро виконує вашу програму, а інше - перемальовує екран. Це збільшує FPS (кількість кадрів в секунду) і дозволяє досягнути максимальної утилізації процесора.
Майте на увазі, що виклик
queueDraw()
може заблокувати вашу програму на деякий час. Це ставатиметься в ситуаціях, коли Кіра ще не завершила малювати на екрані попередній буфер, а ви вже викликаєтеqueueDraw()
знову. Це - не проблема, але варто про це пам’ятати.В середньому, малювання займає близько 1/30 секунди. Це означає, що ви можете викликати
queueDraw()
близько 30 разів в секунду без блокування вашої програми.
Реєстрація програми в меню програм
Основна програма, що запускається при завантаженні Кіри, називається Launcher
. Вона відповідає за відображення меню програм, налаштувань, інформації, а такоє запуск програм.
Щоб програма з’явилася в меню програм, вам потрібно зареєструвати її в одному з меню Launcher
. Найпростіший спосіб - це додати вашу програму в меню додатків.
Для цього знайдіть наступний код у файлі launcher.cpp
та додайте вашу програму в список програм:
1#include "myapp.h" // <--- підключаємо вашу програму
2
3// ...
4
5ITEM_LIST app_items = {
6 ITEM_SUBMENU(
7 "Демо",
8 {
9 ITEM_APP("Лінії", DemoLines),
10 ITEM_APP("Диск", DiskApp),
11 ITEM_APP("Перетворення", TransformApp),
12 ITEM_APP("М'ячик", BallApp),
13 ITEM_APP("Куб", CubeApp),
14 ITEM_APP("Епілепсія", EpilepsyApp),
15 }
16 ),
17 ITEM_SUBMENU(
18 "Тести",
19 {
20 ITEM_APP("Клавіатура", KeyboardApp),
21 ITEM_APP("Тест SPI", UserSPIApp),
22 ITEM_APP("I2C-сканер", ScanI2CApp),
23 },
24 ),
25 ITEM_APP("Летріс", LetrisApp),
26 ITEM_APP("Тамагочі", TamagotchiApp),
27 ITEM_APP("Моя програма", MyApp), // <--- додаємо вашу програму
28};
Після цього перепрошийте Лілку, і ваша програма з’явиться в меню програм.