Bare Metal vs FreeRTOS
Ітак, ви маєте ідею, маєте плату, потрібно створити прототип пристрою. Ви знаєте схемотехніку, знаєте програмування ( зазвичай С), створили макет пристрою і потрібно вдихнути в нього життя — написати програму.
У вас є два шляхи — створити програму самостійно (Bare Metal ), чи використати доступну операційну систему ( зазвичай FreeRTOS). Вибір варіанту залежить в першу чергу від пристрою — якщо процеси в пристрої потрібно обробляти швидше 1 мілісекунди і цей процес не один, а декілька — RTOS вам, мабуть, уже не підійде. Наприклад, ядро FreeRTOS має об’єм біля 6 KB і квантується ( виконується) кожну мілісекунду ( в default конфігурації).
Embedded розробнику в такій ситуації потрібно знати схемотехніку, мову програмування і також вчитися користуватися RTOS, але неможливо знати все. При цьому в кожній програмі є помилки, в більшості випадків це недописані макроси, і без належного досвіду з цим важко розібратися.
В Bare Metal програмі присутній лише код виконавця, він тут цар і бог в одній особі. Йому лише потрібно правильно організувати програму — основне визначити пріоритети, розбити програму на задачі і максимально запаралелити процеси, по максимуму використовуючи існуючу в мікроконтролері периферію.
Задачею цієї статті є можливість показати алгоритм організаціїї Bare
Metal програми таким чином, щоб вона виконувалася подібно RTOS, але повністю під вашим контролем.
Допустим, ви розбили програму на 4 задачі:
Task1 — самий високий пріоритет
Task2 — високий пріоритет
Task3 — середній пріоритет
Task4 — низький пріоритет
Cтворюємо глобальні змінні uint32_t Stat_Main і Stat_Work. Бітові поля
Stat_Main мають наступне значення:
Біт 3
— Запит на виконання Task1
Біт 2-1-0
— Стан (кроки) виконання Task1
/**/
Біт 7
— Запит на виконання Task2
Біт 6-5-4
— Стан виконання Task2
/**/
Біт 11
— Запит на виконання Task3
Біт 10-9-8
— Стан виконання Task3
/**/
Біт 15
— Запит на виконання Task4
Біт
Біти 31..27 використовуються для тимчасової зміни пріоритету ( поки не виконається задача, яка запросила екстренне виконання).
Для контролю за часом виконання задач створюємо програмний або
апаратний тімер і для кожної задачі ставимо в таймері контрольний час
виконання. Якщо в якійсь задачі час очікування вийшов, таймер ставить біт
зміни пріоритету для цієї задачі і вона виконується в першу чергу.
Створюємо допоміжні константи для установлення та скиду біта запиту.
const uint32_t Set_Q1 = 0×00000008; const uint32_t Set_Q2 = 0×00000080;const uint32_t Set_Q3 = 0×00000800; const uint32_t Set_Q4 = 0×00008000; const uint32_t RSet_Q1 = 0xfffffff7; const uint32_t RSet_Q2 = 0xffffff7f; const uint32_t RSet_Q3 = 0xffffff7ff; const uint32_t RSet_Q4 = 0xfffff7fff; // const uint32_t Mask_OverTime = 0xf0000000;
Task1 найбільш пріоритетна і коротка задача (обробка даних АЦП, аварійні ситуації), вона зазвичай готовить дані для менш пріоритетних задач.
Зміни для запиту виконання вносятся в Status; Stat_Work є копією Stat_Main і використовується оператором switch(); Формуємо змінну Stat_Main для оператора switch();
if((Stat_Main & Mask_OverTime) == 0) { //Stat_Work = Stat_Main; if((Stat_Main & Set_Q3) > 0×00U) { Stat_Work = (Stat_Main & RSet_Q4); } if((Stat_Main & Set_Q2) > 0×00U) { Stat_Work = (Stat_Main & RSet_Q3 & RSet_Q4); } if((Stat_Main & Set_Q1) > 0×00U) { Stat_Work = (Stat_Main & RSet_Q2 & RSet_Q3 & RSet_Q4); /* High } } else { Stat_Work = (Stat_Main & 0×30000000); } switch(Stat_Work) { case 0×00U: // задача з найбільшим пріоритетом {} break; case 0×01U: {} break; default: {} }
Алгоритм такий — більше пріоритетна задача маскує виконання менше пріоритетної і так до максимального пріоритету. Задача (або частина задачі) виконується і знімає свій біт вибору в глобальній змінній Stat_Main і (можливо) запускає іншу задачу, обумовлену логікою роботи пристрою.Зазвичай переривання модифікують Stat_Main, обумовлюючи роботу пристрою. Таким чином можна реалізувати алгоритм роботи пристрою без чужого коду (RTOS) швидше і надійніше і з контроле
4 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів