Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЦП та МП лр6 Бег огни (таймер с прерыванием).doc
Скачиваний:
19
Добавлен:
29.08.2019
Размер:
1.93 Mб
Скачать

Опис програми (лістинг 6.1)

Початок програми (рядки 1-5) у вас викликати труднощів не повинен. Тут виконується приєднання бібліотечного файлу, опис двох змінних (temp і rab) і опис константи kdel. Подібні операції ми вже виконували в попередній програмі. Відмінності починаються у рядку 6.

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

У нашій програмі весь процес резервування займає всього три рядки (рядки 6-8):

■ у рядку 6 вибирається потрібний нам сегмент пам'яті (сегмент пам'яті даних);

■ у рядку 7 вибирається нове значення для вказівника в цьому сегменті;

■ у рядку 8 власне відбувається резервування.

Так як в рядку 7 вказівнику присвоюється значення 0x60, то саме за цією адресою буде розташовуватися комірка пам'яті, що резервується у рядку 8. Чому ж ми вибрали таку адресу?

Згадайте схему розподілу пам'яті мікроконтролера AVR [3].

Комірки ОЗП з адресами від 0 до 0x1F суміщені з файлом регістрів загального призначення, комірки з адресами 0x20-0x5F суміщені з регістрами вводу-виводу. Комірка з адресою 0x60 - це перша комірка ОЗП, призначена виключно для зберігання даних.

Зарезервована нами комірка далі в програмі буде використовуватися як буфер для зберігання вмісту робочого регістру rab в проміжку між двома викликами переривання. Саме з цих міркувань для неї вибрано ім'я buf.

Резервуванням пам'яті закінчується модуль визначень. Далі починається безпосередньо програмний код, тобто код, що поміщається в програмну пам'ять. Тому ми вибираємо програмний сегмент пам'яті (рядок 9).

У рядку 10 встановлюється початкове значення вказівника для цього сегмента. Далі починається код самої нашої програми. Але починається код програми зовсім не так, як ми вже звикли в усіх попередніх прикладах. Рядки 11-29 займає блок команд перевизначення векторів переривань. До цих пір в наших програмах ми не мали подібного блоку команд, тому що до цих пір ми не використовували переривань.

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

Мікроконтролер ATiny2313 має таблицю векторів переривань, що складається з 19 адрес (з адреси 0x0000 по адресу 0x0012). Кожна з цих адрес, по суті, є адресою початку процедури обробки одного з видів переривань. Перевизначення векторів полягає в тому, що в кожну таку комірку ми можемо помістити команду безумовного переходу, що передає управління на адресу в програмній пам'яті, де вже дійсно починається відповідна процедура.

Зазвичай програма не використовує відразу всі закладені в мікропроцесор переривання. Наприклад, у нашому випадку використовується лише одне переривання – переривання по збігу таймера. Тому перевизначення виконують тільки для тих векторів, які використовуються в даній програмі. Однак і всі інші вектори прийнято не залишати без уваги. За всіма іншими адресами таблиці прийнято ставити команди-заглушки.

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

Які ж вектори переривань перевизначаються в нашій програмі? По-перше, вектор нульової адреси. За адресою 0x0000 (рядок 11 програми) розміщується команда безумовного переходу по мітці init. У рядку з цією міткою починається основна процедура нашої програми. Як відомо, нульова адреса - це вектор початкового скидання мікроконтролера. Саме з цієї адреси починається виконання програми після системного скидання. Безумовний перехід з нульової адреси дозволяє «перестрибнути» таблицю векторів переривань і розмістити основну програму за межами цієї таблиці.

Другий вектор, що перевизначається - це вектор переривань за збігом таймера/лічильника Т1. Його адреса дорівнює 0x0004. Сюди ми поміщаємо команду безумовного переходу на мітку prtim1 (рядок 15 програми). Саме з цієї мітки починається процедура обробки даного переривання.

За всіма іншими адресами таблиці розміщені команди reti.

Відразу за таблицею векторів переривань починається модуль ініціалізації. Подібний модуль для нас не новий. Модуль ініціалізації обов'язково входить в будь-яку програму. Наша програма – це лише новий варіант програми для вже знайомої нам схеми біжучих вогнів. Режими роботи більшості систем мікроконтролера не змінюються. Тому модуль ініціалізації нової програми майже повністю повторює відповідний модуль з попереднього прикладу. У попередній програмі (лістинг 1.11) подібний модуль займав рядки 8-19.

Але є й відмінності. У новій програмі трохи по-іншому відбувається ініціалізація таймера/лічильника. Тепер таймер повинен бути переведений в режим скидання при співпадінні. Можливі два варіанти реалізації такого режиму:

■ скидання при співпадінні в каналі А;

■ скидання при співпадінні в каналі В.

Для кожного з каналів є свій власний регістр співпадіння. Не буду вдаватися в подробиці. Просто скажу, що ми виберемо канал А. Для того, щоб перевести наш таймер/лічильник в обраний нами режим, достатньо в регістр конфігурації таймера TCCR1B записати код 0x0D (рядки 38, 39). Цей код не тільки переводить таймер в обраний нами режим, але й встановлює коефіцієнт попереднього ділення, рівний 1/1024. Докладніше про конфігурації таймера/лічильника дивіться в розділі 6.

Після того, як режим таймер обраний, потрібно записати код співпадіння у відповідний регістр. Для каналу А цей регістр називається OCR1A. Він має шістнадцять розрядів і фізично складається з двох окремих регістрів OCR1AH ​​і OCR1AL. У кожну з цих половинок регістра записується своя частина коду співпадіння. У регістр OCR1AH ​​записується старший байт (рядки 40, 41), а в регістр OCR1AL - молодший байт (рядки 42,43) коду. Даний регістр співпадіння має властивість подвійної буферизації. Тому й тут важливий порядок запису двох його половинок. Спочатку потрібно записувати старший байт коду, а потім молодший.

Після ініціалізації таймера необхідно ініціалізувати систему переривань. Ініціалізація системи переривань зводиться до вибору нового значення маски переривань по таймеру. Значення маски записується в регістр TIMSK. У даному випадку нам потрібно дозволити лише один вид переривань: переривання по співпадінню в каналі А. Для цього відповідний обраному перериванню біт у байті маски повинен бути встановлений в одиницю.

Інші біти повинні залишатися рівними нулю. Запис маски проводиться в рядках 44 і 45. У всьому іншому новий модуль ініціалізації повністю відповідає аналогічного модулю в програмі з попереднього прикладу (лістинг 1.11).

За модулем ініціалізації починається основна програма. У нашому випадку вона займає всього чотири рядки (рядки 48-51). У рядках 48,49 відбувається присвоєння початкового значення робочого регістру rab і збереження цього значення в буфері buf. Як і в попередніх прикладах, робочий регістр буде використовуватися для операцій зсуву, що імітують рух нашого «вогню».

Початкове значення має представляти собою двійкове число, один двійковий розряд якого дорівнює одиниці, а всі інші - нулю. Потім процедура обробки переривання буде рухати цей розряд вправо і вліво. Тому буде логічно, якщо спочатку нашу одиничку ми розташуємо десь посередині. Тобто виберемо в якості початкового значення, наприклад, число 0b00010000. Що і зроблено в рядку 48.

Так як ми домовилися, що в проміжках між двома перериваннями ця величина буде зберігатися в буфері buf, у рядку 49 вміст rab розміщується в цей буфер. Тепер все готово до запуску системи переривань. У рядку 50 знаходиться команда, що дозволяє всі переривання. Зверніть увагу, що до цього моменту наш таймер/лічильник вже знаходиться в режимі лічби. Він почав працювати відразу після запису значення в регістр TCCR1B. Проте всі переривання до цих пір були заборонені. Тепер, коли переривання ми дозволили, система біжучих вогнів відразу починає працювати.

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

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

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

Тепер перейдемо до самої процедури обробки переривання. Текст цієї процедури займає рядки 52-71. Починається процедура з збереження всіх регістрів, які вона надалі буде використовувати (рядки 52,53). Як бачите, ми зберігаємо навіть регістр rab. Тепер, у разі необхідності, наша основна програма зможе використовувати цей регістр для своїх цілей. Так як у проміжку між двома перериваннями вміст rab зберігається в буфері ОЗП, то в рядку 54 ми витягаємо це значення з буфера і поміщаємо в rab.

Тепер все готово до операції здвигу. Але спочатку нам потрібно визначити напрямок цього здвигу. Для цього достатньо перевірити стан контактів перемикача. Перевірка проводиться в рядках 55-57. У рядку 55 читається вміст порту PD і записується в регістр temp. У рядку 56 перевіряється значення молодшого розряду зчитаного значення. Якщо значення цього розряду дорівнює одиниці (контакти перемикача розімкнуті), то рядок 57 пропускається, і програма переходить до процедури здвигу вправо, яка починається в рядку 58. Якщо контакти замкнені, то виконується безумовний перехід в рядку 57, і управління передається по мітці р2, де починається процедура зсуву вліво.

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

Перевірка прапора переносу виконується в рядку 59. Якщо значення прапору дорівнює нулю, рядок 60 програми пропускається. Якщо ж значення прапору виявиться рівним одиниці, то команда в рядку 60 записує в регістр rab нове значення. Після запису цього значення одиниця опиниться в самому старшому розряді. Таким чином організовується рух одиниці по колу (дійшовши до крайньої правої позиції, одиниця з'являється зліва).

У рядку 61 процедура зсуву вправо завершується. Управління передається по мітці рЗ. Тобто до процедури виводу здвинутого значення в порт.

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

У рядку 65 починається процедура виведення вмісту rab в порт РВ. Процедура займає рядки 65-67. Точно така ж процедура застосовувалася і в двох попередніх версіях програми біжучих вогнів.

У рядках 68-70 відбувається підготовка до завершення процедури обробки переривання. Спочатку вміст rab зберігається в буфері ОЗУ (рядок 68). Потім у рядках 69, 70 відновлюються значення регістрів temp і rab. І, нарешті, у рядку 71 процедура обробки переривання завершується.

Схема біжучих вогнів зі світлодіодами наведена на рис. 6.1.

Рис. 6.1. схема автомата біжучих вогнів