Я вирішив перервати експеримент №5, зупинившися на програмі виводу значення регістра на індикатор, так як наступні програми складніші і потребують організації у вигляді флагового автомата. Тому я вирішив дати трошки теорії і показати декілька своїх макросів, що спрощують роботу з такою архітектурою.
Отже, для планування флагового автомата спочатку треба визначитись з тим, що саме нам потрібно реалізувати з керуванням від флагів, на що саме мають впливати значення флагів. Краще робити на листочку графічні зображення зв'язків у програмі чи текстовий опис.
Нехай нам потрібно реалізувати програму, яка буде зчитувати натискання 4-ох кнопок (Кн1...Кн4). Також у нас є 4 світлодіода (ЛЕД1...ЛЕД4). В початковому положенні світлодіоди погашені. При натисканні кнопки Кн1 світлодіод ЛЕД1 засвічується і світиться поки кнопка натиснута. Світлодіод ЛЕД2 починає блимати з частотою 2Гц (період 0,5с).
При натисканні Кн2 світлодіод ЛЕД2 перестає блимати.
Кнопки Кн3, Кн4 працюють аналогічно зі світлодіодами ЛЕД3, ЛЕД4, але період блимання ЛЕД4 становитиме 0,2с
Як бачимо, тут варто використати програмний таймер для відрахунку періодів.
Тепер починаємо прикидати флаги, що необхідні для керування
Спершу - флаги, що встановлюватимуться при натисканні кнопок. Назвемо їх
keyflag1, keyflag2, keyflag3, keyflag4.
Флаги програмних таймерів timflag1, timflag2
Флаги, що дозволяють чи забороняють блимання ЛЕД2 і ЛЕД4 blnkflag1, blnkflag2
Як бачимо, флагів 8, що дозволить помістити їх в 1 регістр(назвемо flagreg). Обовязково зразу розподілити флаги по бітам регістра, можна намалювати графічно
Тепер можна прикинути сам алгоритм програми:
1. Обнулюємо усі флаги, задаємо початкові значення змінни програмних таймерів рт1, рт2 (рівні половині періода блимання відповідних світлодіодів, виражених у кількості циклів довжиною з базову затримку)
2. Зчитуємо значення порта з кнопками, встановлюємо флаги що відповідають натиснутим кнопкам keyflag1, keyflag2, keyflag3, keyflag4.
3. Перевіряємо keyflag1, якщо встановлено то вмикаємо ЛЕД1, встановлюємо blnkflag1
4. Перевіряємо keyflag1, якщо не встановлено то вимикаємо ЛЕД1.
5. Перевіряємо keyflag2, якщо встановлено то скидаємо blnkflag1, гасимо ЛЕД2
6. Перевіряємо keyflag3, якщо встановлено то вмикаємо ЛЕД3, встановлюємо blnkflag2
7. Перевіряємо keyflag3, якщо не встановлено то вимикаємо ЛЕД3.
8. Перевіряємо keyflag4, якщо встановлено то скидаємо blnkflag2, гасимо ЛЕД4
10. Перевіряємо blnkflag1, якщо встановлено, то виконуємо підпрограму блимання ЛЕД2
11. Перевіряємо blnkflag2, якщо встановлено, то виконуємо підпрограму блимання ЛЕД4
12. Базова затримка для програмного таймера (наприклад 0,01с)
13. Підпрограма обробки програмних таймерів (при досягненні рт1 нульового значення встановлюємо флаг timflag1, рт2 - timflag2 )
14 зациклюємось, перехід до п2
15. Підпрограма блимання ЛЕД2
15.1 перевіряємо timflag1, якщо не встановлено то виходимо з підпрограми
15.2 якщо встановлено, отже пройшло півперіода блимання, змінюємо значення ЛЕД2, обнулюємо timflag1, задаємо початкове значення рт1
15.3 Вихід з підпрограми
16. Підпрограма блимання ЛЕД4
16.1 перевіряємо timflag1, якщо не встановлено то виходимо з підпрограми
16.2 якщо встановлено, отже пройшло півперіода блимання, змінюємо значення ЛЕД2, обнулюємо timflag2, задаємо початкове значення рт2
16.3 Вихід з підпрограми
Таким чином, ми створили керуючу структуру флагового автомата. Як бачимо, найбільш часто зустрічаються такі операції як встановити флаг, скинути (обнулити) флаг і змінити значення флагу (якщо було 0 то стане 1 і навпаки). Для спрощення програмування варто використати макроси для цих операцій.
По перше, зручно описати флаговий регістр (регістри)
.def FlagReg=r20
І номера бітів згідно нашого плану
.equ keyflag1=0;
.equ keyflag2=1;
.equ keyflag3=2;
.equ keyflag4=3;
.equ timflag1=4;
.equ timflag2=5;
.equ blnkflag1=6;
.equ blnkflag2=7;
Макрос встановлення флагу має наступний вигляд:
.macro SetFlag
sbr @0,1<<@1
.endmacro
Команда SBR [РРЗП], [Константа] аналогічна до ORI, фактично встановлює біти в регістрі загального призначення відповідно до маски, що задана константою.
Але в якості константи тут використовується незвичний вираз - 1<<K. Цей вираз означає логічний зсув вліво, тобто одиниця просувається вліво на К бітів. Такі вирази часто використовують для подібних цілей. Адже, якщо К=0 то ми поставимо 1 в нульовий біт, якщо К=1 то в перший і так далі. Це дає змогу звертатися до макроса дуже просто, наприклад
SetFlag FlagReg, timflag2 буде замінене компілятором на
sbr FlagReg,1<<timflag2, тобто sbr r20,1<<5, а далі - sbr r20,0b00100000, тобто встановить 5-ий біт регістра рівним 1!
Макрос скидання (онулення флагу)
.macro СlearFlag
cbr @0,1<<@1
.endmacro
Команда SBR [РРЗП], [Константа] аналогічна до ANDI [РРЗП], [побітово інвертована константа], фактично обнулює біти в регістрі загального призначення відповідно до маски, що задана константою, але обнулюються ті біти, де в константі 1, там де 0 - залишаються без змін.
Макрос зміни значення флагу дещо складніший
.macro СhangeFlag
com @0; інвертуємо всі біти регістра флагів
bst @0,@1 ; зберігаємо проінвертований флаг у користувацький флаг Т
com @0; інвертуємо всі біти регістра флагів (повертаючи регістр до початкового значення)
bld @0,@1 ; замінюємо потрібний флаг на той, що знаходиться у користувацькому флагу Т (а там інверсне значення флагу!!!)
. endmacro
Команди BST і BLD розглядалися нами раніше.
Проблема двох перших макросів полягає в тому, що вони працюють тільки з "верхніми" РРЗП у якості флагового регістра. Якщо ж потрібно застосувати молодший РРЗП то макроси необхідно використати інші:
Макрос встановлення флагу:
.macro SetFlag
set
bld @0,@1
.endmacro
Макрос скидання (онулення флагу)
.macro ClearFlag
clt
bld @0,@1
.endmacro
Ці макроси працюють через користувацький флаг Т, і можуть використовувати усі РРЗП.
В загальному випадку, структура флагового автомата може бути складнішою, і вимагати велику кількість флагів. Тобто ми матимемо не один флаговий регістр, а декілька. В такому випадку просто добавимо опис інших регістрів і і нших бітів. Єдине що - варто відсортувати флаги по регістрам для кращого розуміння програми, наприклад флаги програмних таймерів скласти в один регістр, флаги що стосуються кнопок - в інший, а не мішати їх в безпорядку.
Наприклад, часто застосовується такий метод усунення "дрижання контакту" кнопки - як внесення затримки після першого спрацювання. Тобто саме опитування кнопки має бути дозволене, при першому спрацюванні флаг дозволу знімається і запускається таймер затримки, при завершенні якого знову встановлюється флаг дозволу. В такій структурі для кожної кнопки будуть потрібні два флаги - флаг дозволу і флаг спрацювання. Плюс ще і флаг програмного таймера затримки для кожної кнопки (якщо не передбачається комбіноване натискання декількох кнопок, то звичайно, можна обійтися одним таймером). Тут і знадобиться логічний і зрозумілий розподіл флагів по регістрам.
Немає коментарів:
Дописати коментар