В попередніх експериментах я показував окремо різні функціональні можливості простого програмування МК. Тепер настав час об'єднати отримані знання. Отже я вирішив написати декілька програмок, де будуть об'єднані вивід у порти, зчитування даних з порта, часові затримки. Також спробую реалізувати відмінну від простого циклу архітектуру програми.
Отже ставимо завдання:
1. Програма що суміщає 1 і 2 - тобто вмикає світлодіод, що відповідає натиснутій кнопці, але світлодіод повинен блимати.
2. Програма "бігучої крапки", кнопки 3 і 4 повинні міняти напрям руху крапки, крапка повинна рухатись поки кнопка натиснута, при відпусканні кнопки зупиняється. Кнопка 5 міняє "бігучу крапку" на "бігучу тінь", повторне натискання повертає "бігучу крапку".
Спочатку спробуємо теоретично скласти послідовність головного циклу для першої задачі. Приблизно це має виглядати так:
1. Зчитуємо значення з вхідного порта (порт Б), до якого підєднані кнопки у РРЗП
2. Інвертуємо значеення бітів РРЗП (адже натиснута кнопка дає 0 на вході порта, ненатиснута - 1)
3. Накладаємо бітову маску щоб відсіяти біти, що відповідають непідключеним входам порта.
4. Виводимо в порт А значення РРЗП, засвічуючи світлодіоди
5. Викликаємо підпрограму часової затримки
6. Виводимо в порт А нульове значення, гасячи світлодіоди
7. Викликаємо підпрограму затримки
8. Зациклюємось, переходячи до п.1
1. Блимаючі світлодіоди
Спочатку спробуємо теоретично скласти послідовність головного циклу для першої задачі. Приблизно це має виглядати так:
1. Зчитуємо значення з вхідного порта (порт Б), до якого підєднані кнопки у РРЗП
2. Інвертуємо значеення бітів РРЗП (адже натиснута кнопка дає 0 на вході порта, ненатиснута - 1)
3. Накладаємо бітову маску щоб відсіяти біти, що відповідають непідключеним входам порта.
4. Виводимо в порт А значення РРЗП, засвічуючи світлодіоди
5. Викликаємо підпрограму часової затримки
6. Виводимо в порт А нульове значення, гасячи світлодіоди
7. Викликаємо підпрограму затримки
8. Зациклюємось, переходячи до п.1
Проте така організація програми не є зовсім правильною, точніше в цьому випадку вона нормальна, але при ускладненні програми вона буде сильно впливати на швидкодію. Припустимо, нам треба не просто засвітити і погасити світлодіоди через деякий проміжок часу, а припустимо послідовно висвітити декілька різних значень аналогічним способом. Тоді виходить, що всі решта команд (в нашому випадку - зчитування натиснутих кнопок) повинні чекати поки пройдуть усі часові затримки. Не дуже добре? Тоді на поміч приходить так званий принцип скінченного автомата.
Серед операцій зсуву є логічний зсув і зсув через перенос. (LSL, LSR і ROL, ROR відповідно). я пробував робити зсув через перенос і зтикнувся з некоректною роботою програми в деяких випадках. Проаналізувавши ситуацію я зрозумів, що це цілком логічно, адже після операції зсуву останній біт зберігається у флазі переносу, а в наступних операціях цей флаг може змінюватись і при виконанні наступного зсуву з переносу вертається зовсім не те, що туда клалося. Отже потрібно використовувати команду логічного зсуву. Але тоді крайній біт заміниться на 0. Щоб цього не виникало (нам потрібний циклічний зсув, щоб останні біт ставав першим наприклад), перед зсувом збережемо останній біт, а після зсуву поставимо його на місце першого (при зсуві вліво, при зсуві вправо - навпаки). Для цього зручно скористатися користувацьким флагом Т у регістрі SREG і командами BST і BLD (див. Операції з розрядами)
СКІНЧЕННИЙ АВТОМАТ (STATE MACHINE) - це об'єкт що має певне скінченне число станів і один чи декілька входів. Особливістю є те, що при поступленні даних на входи стан автомата змінюється не лишень в залежності від вхідних даних, а і від його стану в момент надходження даних.
Найпростішим варіантом скінченного автомата є кулькова ручка з кнопкою. Вона має два стани - стержень схований і стержень висунутий. Вхід один - кнопка. Зміна стану при натисканні кнопки залежить від попереднього стану - якщо стержень був висунутий, то він сховається а якщо був схований - висунеться.
Бачимо аналогію? Можна реалізувати наше блимання світлодіодом на тому ж принципі, просто викликаючи в циклі скінченний автомат, станом якого буде світіння діода. Тобто при одному проході цикла діод ввімкнеться, при другому - вимкнеться.
Тоді головний цикл виглядатиме таким чином:
1. Зчитуємо значення з вхідного порта (порт Б), до якого підєднані кнопки у РРЗП
2. Інвертуємо значеення бітів РРЗП (адже натиснута кнопка дає 0 на вході порта, ненатиснута - 1)
3. Накладаємо бітову маску щоб відсіяти біти, що відповідають непідключеним входам порта.
4. Викликаємо скінченний автомат BLINK
5. Викликаємо підпрограму затримки
6. Зациклюємось, переходячи до п.1
Сам скінченний автомат можна реалізувати таким чином:
BLINK:
1. Якщо стан = 0 то:
1.1 Виводимо в порт А нульове значення, гасячи світлодіоди,
1.2 Змінюємо стан на 1,
1.3 Виходимо;
2. Інакше (стан не =0):
2.1 Виводимо в порт А значення РРЗП, засвічуючи світлодіоди,
1.2 Змінюємо стан на 0,
1.3 Виходимо;
Програмний скінченний автомат зазвичай використовує певну змінну для зберігання стану. адже автомат повинен пам'ятати значення стану до наступного отримання вхідних даних. Ми використаємо ще один РРЗП, надавши йому символічне ім'я blinkstate.
Текст програми (ASM файл) ТУТ
В принципі програма цілком зрозуміла і відповідає написаному вище теоретичному представленню.
2. Інвертуємо значеення бітів РРЗП (адже натиснута кнопка дає 0 на вході порта, ненатиснута - 1)
3. Накладаємо бітову маску щоб відсіяти біти, що відповідають непідключеним входам порта.
4. Викликаємо скінченний автомат BLINK
5. Викликаємо підпрограму затримки
6. Зациклюємось, переходячи до п.1
Сам скінченний автомат можна реалізувати таким чином:
BLINK:
1. Якщо стан = 0 то:
1.1 Виводимо в порт А нульове значення, гасячи світлодіоди,
1.2 Змінюємо стан на 1,
1.3 Виходимо;
2. Інакше (стан не =0):
2.1 Виводимо в порт А значення РРЗП, засвічуючи світлодіоди,
1.2 Змінюємо стан на 0,
1.3 Виходимо;
Програмний скінченний автомат зазвичай використовує певну змінну для зберігання стану. адже автомат повинен пам'ятати значення стану до наступного отримання вхідних даних. Ми використаємо ще один РРЗП, надавши йому символічне ім'я blinkstate.
Текст програми (ASM файл) ТУТ
В принципі програма цілком зрозуміла і відповідає написаному вище теоретичному представленню.
Створюємо проект, проганяємо відладку (змінивши кількість циклів у затримці), прошиваємо контролер.
Відео створення і відладки проекту (дивитись на повний екран в HD)
Відео роботи макетної плати з цією прошивкою
Отже ми використали для побудови програми принцип скінченого автомата (СА). Тут він найпростіший, з двома станами і одним входом (хоча, залежно як розглядати, якщо повністю оцінити його роботу, то входів 2 - один це безпосередньо запуск автомата, другий - це змінна temp, яка надає інформацію про то, які світлодіоди повинні блимати, тоді і станів є не 2, а досить багато, скільки комбінацій з 5 світлодіодів :-)). Цей принцип досить часто використовується для побудови програм МК, в одній з наступних статей я покажу більш складний СА.
2. Біжучі вогні
І знову почнемо з теоретичного опису принципу роботи програми. Спочатку розглянемо безпосередню реалізацію "біжучого вогню". Біжуча крапка реалізується, якщо внести одиницю в один з бітів РРЗП, а потім виконувати операцію зсуву вліво чи вправо з затримкоюю наприклад:
1. запишемо в РРЗП 00100000
2. виведемо значення РРЗП в порт
3. зсув РРЗП вліво (стане 01000000)
4. затримка
5 зациклюємось, повертаючись до п.2
Команда BST [РРЗП] , [номер біта] копіює значення даного біта РРЗП в користувацький флаг Т в регістрі SREG.
Команда BLD [РРЗП] , [номер біта] замінює значення даного біта РРЗП на поточне значення користувацького флага Т в регістрі SREG.
Тобто циклічний зсув вліво регістра light виглядатиме наступним чином:
BST light,7
LSL light
BLD light,0
циклічний зсув вправо регістра light:
BST light,0
LSR light
BLD light,7
Біжуча тінь - це такий самий циклічний зсув тільки з іншим початковим значенням регістру, наприклад 11101111. Тобто будуть світитися всі світлодіоди крім одного. В принципі за допомогою зсуву можна примусити "бігти" любу комбінацію включених і виключених світлодіодів.
Тепер розглянемо програму в загальному. У відповідності до поставленої задачі, натискання кнопки 3 повинне викликати зсув вправо, натискання кнопки 4 - зсув вліво, Кнопка 5 переключає між біжучою крапкою і біжучою тінню шляхом інвертування початкового значення регістру light. Приблизно так:
1. задаємо початкове значення light=00000001
2. зчитуємо значення вхідного порта (кнопки)
3. перевіряємо чи натиснута кнопка 3, якщо так - то виконуємо зсув РРЗП light вправо
4. перевіряємо чи натиснута кнопка 4, якщо так - то виконуємо зсув РРЗП light вліво
5. перевіряємо чи натиснута кнопка 5, якщо так - то виконуємо інвертування бітів РРЗП light
6. виводимо значення РРЗП light в вихідний порт (світлодіоди)
7. викликаємо підпрограму затримки
8. зациклюємось, повертаючись до п.2
В даному випадку можна реалізувати архітектуру найпростішого "Флагового автомата". Натискання кнопок буде виставляти певні флаги у регістрі, назвемо його keystate. І в залежності від цих флагів будуть виконуватись ті чи інші дії. Просто у нас тут зчитування кнопок виконується в основному циклі і тому реалізація нагадує звичайну циклову структуру. І якщо не використовувати змінну keystate, а перевіряти натискання кнопок напряму то вона і стане звичайною.
Для виконання перевірок флагів дуже зручно використовувати саме ті, невказані в таблиці-довіднику команди:
Тепер розглянемо програму в загальному. У відповідності до поставленої задачі, натискання кнопки 3 повинне викликати зсув вправо, натискання кнопки 4 - зсув вліво, Кнопка 5 переключає між біжучою крапкою і біжучою тінню шляхом інвертування початкового значення регістру light. Приблизно так:
1. задаємо початкове значення light=00000001
2. зчитуємо значення вхідного порта (кнопки)
3. перевіряємо чи натиснута кнопка 3, якщо так - то виконуємо зсув РРЗП light вправо
4. перевіряємо чи натиснута кнопка 4, якщо так - то виконуємо зсув РРЗП light вліво
5. перевіряємо чи натиснута кнопка 5, якщо так - то виконуємо інвертування бітів РРЗП light
6. виводимо значення РРЗП light в вихідний порт (світлодіоди)
7. викликаємо підпрограму затримки
8. зациклюємось, повертаючись до п.2
В даному випадку можна реалізувати архітектуру найпростішого "Флагового автомата". Натискання кнопок буде виставляти певні флаги у регістрі, назвемо його keystate. І в залежності від цих флагів будуть виконуватись ті чи інші дії. Просто у нас тут зчитування кнопок виконується в основному циклі і тому реалізація нагадує звичайну циклову структуру. І якщо не використовувати змінну keystate, а перевіряти натискання кнопок напряму то вона і стане звичайною.
Для виконання перевірок флагів дуже зручно використовувати саме ті, невказані в таблиці-довіднику команди:
SBRC[РРЗП],[Номер біта] - Пропускає наступну команду, якщо даний біт в РРЗП рівний 0.
SBRS[РРЗП],[Номер біта] - Пропускає наступну команду, якщо даний біт в РРЗП рівний 1.
Код програми (ASM).
Така програма також повинна бути зрозуміла. Виконуємо компіляцію і відладку, бачимо що все виконується.
Відео відладки (дивитись на повний екран в HD)
Прошиваємо МК і тестуємо на макетній платі.
Відео.
Таким чином, ми реалізували поставлені задачі. В наступній статті я опишу ще одну річ, яка часто використовується в реальних проектах - так звану динамічну індикацію. Після цього треба починати роботу з перериваннями, а то я і так щось довго топчуся з простими програмами :-)
Така програма також повинна бути зрозуміла. Виконуємо компіляцію і відладку, бачимо що все виконується.
Відео відладки (дивитись на повний екран в HD)
Прошиваємо МК і тестуємо на макетній платі.
Відео.
Таким чином, ми реалізували поставлені задачі. В наступній статті я опишу ще одну річ, яка часто використовується в реальних проектах - так звану динамічну індикацію. Після цього треба починати роботу з перериваннями, а то я і так щось довго топчуся з простими програмами :-)
Немає коментарів:
Дописати коментар