середа, 29 травня 2013 р.

Трохи про архітектуру програм для МК

     Отже, як я і обіцяв, розповім трохи про архітектуру, тобто принципи побудови програм для мікроконтролерів. В загальному, програма складається з вже показаних мною основних частин:



0. Вказання файла-опису для вибраного МК. (в Атмел Студіо, починаючи з версії 5 цей пункт не є обов'язковим, в іншик компіляторах потрібне застосування директиви
.include "ім'я файла опису")
1. Опис змінних (регістрів, портів)
2. Опис констант (ці два пункта можуть бути і поміняні місцями).
3. Початок програми (мітка Reset або таблиця векторів переривань)
4. Ініціалізація стека
5. Ініціалізація периферії (портів, таймерів, інших пристроїв, налаштування переривань)
6. Основний цикл програми.

7. Підпрограми

Коротко про переривання

     Для подальшої розмови про архітектуру важливим є поняття переривання.
Що ж це таке?

ПЕРЕРИВАННЯ - це сигнал, що повідомляє процесору про те, що відбулася деяка подія. При цьому виконання текучої програми переривається, керування передається спеціальній підпрограмі - ОБРОБНИКУ ПЕРЕРИВАННЯ, після виконання якої повертається у початкову програму в те місце, де її виконання було перерване.

     Таким чином, переривання може виникнути у будь-якому місці програми (якщо там дозволені переривання), і після виконання коду обробника керування повернеться в нашу програму.
     Викликати переривання можуть різні периферійні пристрої - наприклад таймери, АЦП, аналоговий компаратор та ін. Також в МК існують так звані зовнішні переривання - вони виникають при певних сигналах на ножках портів.
Ні один серйозний проект не обходиться без використання переривань.

Найпростіші архітектури програм МК

     Вказані вище пункти будови прогами існують практично в кожному проекті, А далі ми будемо розглядати принципи побудови основної частини програми.

1. Першим варіантом архітектури є ЦИКЛОВА (ще називають суперцикл). Це найпростіший варіант, він використовувався нами у перших двох експериментах. Структура програми виглядає так:

Тобто ми бачимо, що усі задачі виконуються строго послдідовно в циклі. Звичайно, задачі можуть містити в собі інші цикли чи розгалуження (умовні переходи), але принцип загальної побудови лишається.  Така архітектура проста і зрозуміла, поки проект простий. А при його ускладненні починаються проблеми. Так, різко падає швидкодія, адже всі задачі мусять пройти в кожному циклі основної програми, а якщо в якісь із них вставлена, скажімо часова затримка.то всі решта будуть чекати поки вона виконається. Також виникає потреба виконувати всілякі переходи через частину команд, і в результаті можна самому заплутатись у своєму проекті.

2. Трошки кращим варіантом є ЦИКЛОВА АРХІТЕКТУРА З ПЕРЕРИВАННЯМИ. Тут в основному циклі можна виконувати задачі, які потребують виконання весь час, а тимчасові виносяться в обробник переривань. Наприклад, опитування кнопок можна виконувати по зовнішньому перериванню - при зміні стану виводів порта. Блимання світлодіодами можна виконувати по перериванню таймера, настроєного на потрібний проміжок часу.
В загальному структура виглядає таким чином:

     Така побудова зустрічається досить часто, вона має кращу швидкодію (бо фактично реалізується "псевдобагатозадачність") Але є і недоліки - так, обробники переривань варто робити якомога коротшими, адже виконуючи обробник одного перивання можна пропустити інше (при виконанні обробника переривання блокуються), а якщо дозволити переривання під час виконання обробника, може виникнути ситуація, що ми і не будемо з них виходити і "забудемо" про виконання основної програми. Крім того, припустимо виконання декількох задач через різні проміжки часу (скажімо, задачу1 треба повторювати через 0,5с, задачу2 - через 1,2с, задачу3 -через 0,8с) легко реалізувати, поки вистачає незалежних таймерів, а їхх у нашому МК, припу3стимо всього 2. Потім починаються дикі  "навороти" в коді. Отже така архітектура може використовуватись, у проектах, де не ставиться вимога до отримання високої швидкодії та інші специфічні задачі.

     Ще одним недоліком цих архітектур є важкість додавання нових функцій в існуючу програму, адже усі залежності прописані безпосередньо між існуючими елементами і вставити туди щось додаткове може потребувати змін у купі різних частин програми.

Архітектури з керуючую структурою

     По мірі подальшого ускладнення проектів постає необхідність у такій собі "керуючій структурі". Тобто в програмі з'являється код, який не виконує "корисної роботи", а керує виконанням задач. Тобто він може вирішувати, чи потрібно запускати ту чи іншу задачу і в який час.

3. Найпростіша реалізація керуючої структури називається ФЛАГОВИМ АВТОМАТОМ. Суть її полягає в тому, що структура основного циклу будується таким чином:



При цьому вибирається один чи декілька регістрів, які служать для керування - їх біти (флаги) і перевіряються в циклі. А встановлюються/обнуляються вони або в самих задачах, або в обробниках переривань. Така побудова дозволяє зменшити обробники переривань, виконавши у них всього декілька команд по встановленню потрібних флагів. При цьому основний цикл буде виконувати тількі ті задачі, які потребують виконання (згідно встановлених флагів). 

Перевагою такої побудови є простота додавання нових функцій - просто додаємо новий флаг і ще одну операцію вибору. Крім того, флаговий автомат дозволяє легко реалізувати так званий "програмний таймер", для створення великої кількості різних по величині часових затримок (хоча він не забезпечує точності їх значень). Про програмний таймер ми поговоримо в іншій статті, там же покажу і реалізацію.
Проте ця архітектура має і недоліки:
     -  Все ж таки задачі виконуються в певному порядку, згідно процедур перевірки стану флагів
     -  При довготривалому виконанні однієї задачі інші простоюють, переривання лише виставляють керуючі флаги, а саме виконання не відбувається
     -  При великій кількості задач розростається керуюча структура, кількість флагів і команд їх перевірок.

Для уникнення цих недоліків використовуються наступні архітектури:

4. ДИСПЕТЧЕР.  Тут керуюча структура - диспетчер обслуговує так звану "ЧЕРГУ ЗАДАЧ". Це певна виділена область памяті, куди записуються вказівники на задачі. Сам диспетчер бере перший вказівник з черги, посуває чергу і запускає дану задачу на виконання, після її завершення - бере наступний вказівник і так далі. Підставляти задачі в кінець черги можуть або переривання, або задачі, що виконувались.

     Тобто порядок виконання задач уже не визначається конструкцією циклу, все залежить від того, яка задача поставлена в чергу раніше. Розміри керуючої структури тут не залежать від кількості задач, її справа просто брати з черги вказівник і переходити до виконання задачі.  Крім того, сама структура не залежить від виконуваних задач, тому її можна оформити у вигляді бібліотеки і використовувати у різних проектах. 
     Проте проблема простоювання при довготиривалому виконанні однієї задачі залишається, тут не бажано робити такі задачі, слід намагатися якомога швидше передавати виконання диспетчеру (наприклад не рекомендується робити прямі переходи між задачами, краще реалізувати задачі так, щоб керування передавалося через диспетчер).  
     Ще один недолік диспетчера (не кажучи вже про попередні варіанти архітектур) - вони не дозволяють отримати різний пріоритет задач. Тобто всі задачі будуть виконуватись по черзі (фіксованій у флаговому автоматі чи динамічній у диспетчері), але якщо треба негайно виконати якусь задачу - вибачайте, дочекайтеся своєї черги. Для запобігання цьому є наступна реалвізація.

5. ПРІОРИТЕТНИЙ ДИСПЕТЧЕР. Тут кожна задача крім безпосередньо вказівника має ще певне значення пріоритету. В найпростішому випадку можна зробити 2 пріоритета - термінові задачі і звичайні. Тоді якщо задача термінова, вона підставляється в початок черги, а звичайна - в кінець. Тут слід лишень слідкувати щоб термінові задачі не забивали чергу, не даючи виконуватися звичайним. Реально захист не реалізується, просто треба думати при побудові програми і розставлянні пріоритетів. 
     Також можлива реалізація і багаторівневого пріоритету, тоді диспетчер повинен пройти по черзі, знайти задачу з максимальним пріоритетом і виконати її. Це звичайно сповільнює швидкодію, але іноді потрібно. 

Операційні системи

     Проте ці архітектури так і не подолали останній недолік - якщо задача виконується довго, то вона "тормозить" виконання решти задач. Для уникнення цього існують найскладніші архітектури. Вони називаються операційними системами. Є два різновида - кооперативна ОС (Існує термін Кооперативна РТОС , РТОС - операційна система реального часу, хоча насправді кооперативна ОС не є повноцінною системою реального часу.) і витіснююча РТОС.

Кооперативна ОС - це система, де кожна задача є окремим завершеним блоком. Але команди часових затримок у них замінені на команди, що передають на час затримки керування диспетчеру, і той виконує наступні задачі. Звичайно, що диспетчер може бути і пріоритетним. При закінченні визначеного проміжку часу виконання текучої задачі переривається і виконується повернення керування в задачу де була затримка. Отже тут реалізована багатозадачність - час, що витрачався б на пусті цикли затримки використовується для виконання інших задач. Також задача може просто очікувати якихось значень від інших задач, на цей час її виконання також переривається і передається іншим. Така побудова є складною і може використовуватись у серйозних проектах, якщо немає вимог до повноцінного реалтайму - тобто коли певна задача має виконатись не пізніше поставленого "терміну". Тут швидкодія збільшена, проте, якшо якась із задач припустимо не передасть керування диспетчеру із-за якоїсь помилки чи інших причин, вся система зупиниться.  

Витіснююча РТОС - це повноцінна РТОС. Задачі є закінченими блоками, притому що ніяких команд передачі керування диспетчеру вони не потребують.Однією із задач може бути хоч безкінечний цикл. Справа в тому, що тут диспетчер має можливість переривати виконання текучої задачі в любий момент і переходити до виконання наступної. Фактично, диспетчер виконується по перериванню таймера через певні проміжки часу, які задані умовами роботи пристрою (наприклад 10мс), відповідна тут реалізується практично повноцінна багатозадачність - кожна задача виконується частинами, ривками, перекидаючись до виконання інших задач (це типу як пити каву під час написання статті - набираєш, набираєш, взяв чашку,сьорбнув ковток, поставив, набираєш далі :-) ) 
      Структура цікава, але має один серйозний недолік - так як виконання задачі переривається диспетчером у будь-який момент, то треба зберегти стан цієї задачі. Тобто усі регістри, що в ній використовуються і не тільки. Адже кожна задача може мати і свій стек (як повноцінна програма) і всілякі змінні в ОЗП. Отже на кожну задачу потрібно досить великий об'єм пам'яті, щоб був запас (по тому ж стеку наприклад) а зі збільшенням кількості задач загальна необхідність у пам'яті серйозно росте.  Тому на мікроконтролерах АВР і подібних такі архітектури практично не використовують. На 32 розрядних МК типу ARM, ще й з можливістю підключення зовнішнього ОЗП, такі системи значно поширеніші.

     В наступній статті перейдемо до практики, треба ж продовжувати наші експерименти :-).

Стаття написана по мотивам статей DI HALT: 
   




  

Немає коментарів:

Дописати коментар