вівторок, 20 серпня 2013 р.

Продовження експерименту 5. Практичне використання динамічної індикації.

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

ТЕХНІЧНЕ ЗАВДАННЯ

     Отже спочатку слід зробити технічне завдання. Спершу зробимо варіант, який буде відраховувати час в секундах. Так як наш індикатор трьохрозрядний, то ми можемо відображати час від 0 до 999с. Такий і буде діапазон нашого таймера. Так як ми робимо програму без використання переривань, то інтервали часу не будуть відрізнятися високою точністю, але пізніше можна буде переробити програму на роботу по перериванням апаратного таймера і досягти нормальної точності роботи.
      Визначимося з керуванням нашим таймером. В нас є на даний момент 4 вільних кнопки, (порт на якому знаходиться 5-та кнопка зайнятий катодом індикатора).  Зробимо порозрядне встановлення числа, так як послідовно проклацувати від 0 до наприклад 500 це нездорово і для кнопок і для пальців :-).
         Отже: 
 1. Кнопками S1, S2 (вверх вниз) змінюється значення розряду.
 2. Кнопка S3 (->), буде використовуватись як кнопка встановлення (SET). при натисканні  хід таймера зупиняється, починає блимати перший розряд, тоді можна змінювати його значення, при повторному натисканні блимає наступний розряд і так далі. Після третього розряду натискання S3 вимикає режим установки значень і таймер готовий до запуску.
  3. Кнопка S4 (<-), використовується як старт/стоп. Запуск таймера можливий зразу з режиму установки, тоді режим установки вимикається.
  4. Таймер здійснює зворотній відлік від встановленого початкового значення. При досягненні нульового значення вмикається світлодіод, що під'єднаний до 7-го каналу порта А. Погасити світлодіод можна або повторним встановленням таймера на певний проміжок часу, або натисканням на кнопку S4.

АРХІТЕКТУРА ПРОГРАМИ

     Програму будемо будувати по архітектурі флагвого автомата.  За відрахунок часових відрізків відповідатимуть програмні таймери. Основний період програмних таймерів відповідає періоду динамічної індикації.  Для виключення застосування двохбайтного програмного таймера вибиремо період динамічної індикації таким, щоб можна було сформувати найбільший інтервал, який нам потрібний (1с) не більше ніж за 255 основних періодів. Виберемо його рівним 1/250 с. Тоді частота блимання розрядів індикатора становитиме 1/(1/250)*3 = 83,3 Гц. В принцмпі частота достатня, блимання не повинно бути сильно помітно.
 
     Тепер визначимося з кількістю програмних таймерів, які нам необхідні.  Один буде відраховувати секундні інтервали. Другий потрібен для створення затримки після натискання кнопки (час нечутливості, для уникнення ефекту "дрижання контактів"). Але ми використаємо 2 таких таймера - один для кнопок установки значень (S1, S2), другий - для SET і старт/стоп.  І ще один таймер буде відповідати за блимання індикатора в режимі установки. Отже нам потрібно реалізувати 4 програмних таймера.

     Спробуємо спланувати керуючу структуру флагового автомата.
Спершу відзначимо, що наша програма повинна мати 2 основних режима роботи.
     Перший - це робочий режим, в якому кожну секунду відбувається декрементування значення виставленого інтервалу і перевіряється чи не досягли ми нульового значення. При досягненні подається сигнал на 7-ий біт порта А і декрементування припиняється. При натисканні кнопки пуск/стоп під час роботи  декрементування припиняється, при повторному натисканні - продовжується.  На індикаторі в цьому режимі відображається значення робочих регістрів (їх буде 2 так як число 999 не поміщається у один 8-ми бітний регістр).
    Другий режим - це режим встановлення. В цьому режимі ми можемо змінювати значення розрядів індикатора кнопками S1, S2 (вверх вниз), міняючи розряди кнопкою S3. Вхід в цей режим відбувається при першому натисканні кнопки S3 (SET), при цьому встановлюється старший розряд числа, при другому натисканні середній розряд, при третьому - молодший, при 4-ому відбувається вихід у робочий режим. У цьому режимі відповідні розряди індикатора блимають, і їх значення інкрементується при натисканні кнопки S1, декрементується при натисканні кнопки S2. При досягеннні нульового значення декремент забороняється, при досягенні 9 - забороняєтья інкремент.
В режимі установки ми встановлюємо значення порозрядно, отже потрібно виконувати перетворення з порозрядної форми у значення робочих регістрів.

        З цього випливає, що нам необхідно ввести флаг режиму (MODEFLAG), коли він рівний 1 то маємо робочий режим, коли 0 - режим встановлення.
     Наступні флаги відповідають за стан установки (номер розряду що встановлюється). Ми бачимо, що натискання кнопки S3 повинне оброблятися скінченим автоматом, що має 4 стани:

1. Робочий режим
2. Встановлення старшого розряду
3. Встановлення середнього розряду
4. Встановлення молодшого розряду

При натисканні кнопки здійснюється зміна станів по схемі 1->2->3->4->1....

Отже матимемо ще 3 флага для основної структури, назвемо їх SETHI SETMI SETLO.

    Кнопка старт/стоп теж діє на свій кінцевий автомат з двома станами - Стоп і Робота. Результатом є флаг COUNT, встановлення якого в лог.1 дозволяє відрахунок, а обнулення - забороняє. Також натискання цієї кнопки в любому випадку змінює MODEFLAG на лог.1 (перехід в робочий режим).
        Також використаємо флаг, який буде відповідати за ввімкнення і вимкнення світлодіода (результат роботи таймера) Назвемо його OUTFLAG. Він встановлюватиметься в робочому режимі при досягненні таймером нульового значення.

Також використаємо флаги для  кнопок (KEYUPFLAG, KEYDNFLAG, KEYSTFLAG, KEYSSFLAG для кнопок S1...S4 відповідно). І опишемо флаги, що генеруються при відрахунку інтервалів програмними таймерами.

1. SECFLAG - встановлюється кожну секунду, відповідну змінну програмного таймера назвемо ТІМSEC.

2. BLINKFLAG - встановлюється кожні 0,2с. Визначає період блимання індикаторів в режимі встановлення. Змінна таймера - ТIMBLINK

3. TKEY1FLAG - Визначає дозвіл обробки повторного натискання кнопок 1,2. Період таймера - 0,3с. Змінна таймера - TIMKEY1

4.  TKEY2FLAG - Визначає дозвіл обробки повторного натискання кнопок 3,3. Період таймера - 0,5с. Змінна таймера - TIMKEY2

Розподілимо флаги по регістрам. В один регістр помістимо флаги  MODEFLAG, SETHI SETMI SETLO, COUNT, OUTFLAG.  В другий - флаги кнопок і таймерів.

Загальна структура керуючих регістрів показана на малюнку
Я розмістив флаги SETHI SETMI SETLO. у відповідності з бітами порта де підєднані катоди індикатора, щоб спростити організацію блимання потрібного розряду під час режиму установки. Адже в асемблері символічне імя флага це просто номер біта і тому можна використати звертання по цьому імені і до інших регістрів.

     Загальна архітектура програми показана на рисунку нижче:

КОД ПРОГРАМИ

    Загальну програму на асемблері можна скачати ТУТ.
В коді програми застосовані модулі, які ми розглядали раніше. Деякі з них містять невеликі зміни.
     Частина присвоєння імен регістрам і константам стандартна, також ми вводимо константи що називаються іменами флагів і містять номера відповідних бітів.
     В описі макросів ми бачимо вже відомі макроси завантаження констант в молодші РРЗП, макрос виклику часової затримки, макроси для роботи з флагами з попередньої статті. Також є макрос обробки змінної програмного таймера, але трохи змінений для можливості використання молодших РРЗП в якості флагових регістрів (для них недоступна команда SBR) тому здійснюємо встановлення флага через регістр temp.

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

     Керуюча структура побудована на перевірці стану флагів командами SBRC  та SBRS, Процедура динамічної індикації аналогічна до наведеної у попередніх статтях про ДІ, зміни стосуються лишень забезпечення роботи частини виводів порта Б як входів, використання 7-го біта порта А для підключення світлодіода та організації блимання розрядів (бітова маска Blinkreg).

       Підпрограма обробки натискання кнопок побудована на перевірці стану бітів порта командами SBIS і не являє собою ніяких складностей. В ній також реалізований запуск програмного таймера затримки і обнулення флагу дозволу обробки кнопок. Таким чином, при натисканні кнопки встановиться її флаг, буде запущений таймер, а наступне зчитування кнопок буде призупинене, поки таймер не відрахує період затримки і не виставить флаг дозволу. Таким чином забезпечується уникнення "дрижання контактів" і створюється затримка для усунення багаторазового виставлення флага кнопки при її натисканні (адже програма працює досить швидко, довжина одного циклу 1/250с, так що навіть утримання кнопки на протязі 0,1с викличе 25 "спрацювань").
   
     Скінчений автомат SET здійснює циклічне переключення режимів MODEFLAG - SETHI - SETMI - SETLO - і побудований на простих перевірках флагів командами SBRC.
        Скінчений автомат старт/стоп ще простіший так як має всього два стани.

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

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

Наприклад,  порівняння двохбайтного числа з константою 100:
clr temp
cpi tempLO,100; Порівнюємо значення  з константою (число 100)
cpc tempHI,temp

Команда cpi tempLO,100 порівнює молодший регістр з числом 100, не змінюючи його значення, проте, якщо tempLO менше 100 то встановлюється флаг С
Наступна команда cpc tempHI,temp (порівняння з врахуванням переносу) фактично порівнює старший регістр з регістром temp+С. Так як temp містить 0, то ми порівнюємо старший розряд з числом 0b00000001 у випадку якщо   tempLO менше 100. 

Аналогічні набори команд використовуються для порівняння з 10 і з 1 для формування десяткових розрядів числа. Також використовуються арифметичні команди  для двохбайтних чисел

subi tempLO,100; віднімаємо від значення РРЗП tempLO 100
sbci tempHI,0;      віднімаємо від tempHI число 0 і флаг переносу С

     Після переведення значення робочих регістрів таймера в порозрядну форму відбувається зміна розрядів при натисканні кнопок ВВЕРХ (інкремент) і ВНИЗ (декремент) При цьому контролюється, щоб значення розряду не стало менше 0 або більше 9.

     Потім ми робимо зворотнє перетворення з порозрядної форми у двохбайтне число.

; Здійснюємо зворотнє перетворення з порозрядної форми у значення регістрів таймера
clr TIMLO;  Очищуємо регістр TIMLO
clr TIMHI;  Очищуємо регістр TIMHI
hundreds:   ; Старший розряд
ldi tempLO,100;  Заносимо в регістр tempLO число 100
ldi tempHI,0;    Заносимо в регістр tempHI число 0
mov temp, rozrad3; Заносимо в регістр temp значення rozrad3 (старший розряд) - це потрібно тому що rozrad3 є молодшим РРЗП і з ним не працюють операції з константою
cpi temp,0;        Порівнюємо чи не 0
breq decims;       Якщо 0 то переходимо до мітки decims
dec rozrad3;       Якщо не 0 то зменшуємо значення rozrad3 на 1
add timLO,tempLO;  Додаємо до двохбайтного числа таймера 100
adc TIMHI,tempHI;
rjmp hundreds;     Переходимо до мітки hundreds (зациклюємо)
        ; Аналогічно виконуємо для середнього розряду (десятки)
decims:
ldi tempLO,10
ldi tempHI,0
mov temp, rozrad2
cpi temp,0;
breq ones
dec rozrad2;
add timLO,tempLO;
adc TIMHI,tempHI;
rjmp decims
      ; І для молодшого (одиниці)
ones:
ldi tempLO,1
ldi tempHI,0
mov temp, rozrad1
cpi temp,0;
breq outthis
dec rozrad1;
add timLO,tempLO;
adc TIMHI,tempHI;
rjmp ones
outthis:

     Принцип перетворення полягає в циклічному додаванні до регістрів таймера спочатку числа 100 (кількість циклів рівна значенню старшого розряду), потім 10 (середній розряд), і одиниць (молодший розряд). Фактично реалізується функція TIM = (Rozrad3*100)+(Rozrad2*10)+(Rozrad1*1).
     Інтерес становить процедура додавання до двохбайтного числа:
ldi tempLO, 100
ldi tempHI, 0
add timLO,tempLO;
adc TIMHI,tempHI;

    Команд додавання до числа константи АТМЕЛ не передбачив (а віднімання є, що досить дивно), тому константи запишемо у регістри tempLO і  tempHI.
Команда add timLO,tempLO додає до молодшого розряду таймера число що міститься у регістрі tempLO (100 в нашому випадку), і встановлює флаг С при переповненні регістру (результат більший наж 255).
Команда adc TIMHI,tempHI додає до старшого розряду таймера число 0, що міститься у tempHI і флаг переносу (додавання з врахуванням переносу), таким чином ця пара забезпечує додавання до двохбайтного числа.

      Решта програми розглядалася в попередніх статтях про ДІ, підпрограма блимання фактично являє собою скінчений автомат що міняє флаги в регістрі Blinkreg по сигналу з програмного таймера перыоду блимання ы залежно выд флагів SETLO SETHI SETMI.
Чиста логіка, ніякої магії :-).

ВІДЕО ДЕМОНСТРАЦІЇ РОБОТИ



     Отже ми зробили перший пристрій на МК, який можна реально застосувати. Адже можна вивести вивід порта, шо керує світлодіодом і застосувати його для керування іншим навантаженням через підсилюючий транзистор і реле чи опторозвязку (є оптосимістори, що дозволяють керувати навантаженням з живленням від мережі 220В). Звичайно, точність такого таймера невисока, проте можна спробувати підігнати величину часової затримки в циклі для більш точного відрахування часу. Набагато кращої точності можна досягти при використанні апаратних таймерів і переривань, які будуть розглянуті в наступних статтях. 
      Також можна зробити відображення на індикаторі у звичному вигляді - хвилини-секунди, але це потребує більшої кількості коду. Крім того, бажано розділити крапкою на індикаторі хвилини і секунди, але вільних портів для цього анода не лишилося. Та і трьохрозрядний індикатор забезпечить відображення лише 9 хв 59с, що становить 599с, а в нашому випадку діапазон досягає 999с. У наступних статтях я розгляну практичні методи, що дозволяють економити кількість виводів, що використовуються для зовнішніх підєднань до МК. Хоча вони потребують додаткових елементів, проте часто використовуються коли недоцільна купівля дорожчого МК з великою кількістю портів вводу/виводу.

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

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