Text
                    Юрий Ревич
AVR
Программирование
микроконтроллеров
oArduino
к ассемблеру
Санкт-Петербург
«БХВ-Петербург»
2020


УДК 004.431.4 ББК 32.973.26-018.1 Р32 Ревич Ю. В. Р32 Программирование микроконтроллеров AVR: от Arduino к ассемблеру. — СПб.: БХВ-Петербург, 2020. — 448 с: ил. — (Электроника) ISBN 978-5-9775-4076-6 Рассмотрено практическое программирование микроконтроллеров AVR, в том числе популярной платформы Arduino. Рассказано, как выйти за рамки ограни- чений Arduino, когда следует применять прямое программирование на ассемблере, а когда использовать языки высокого уровня. Изложены общие принципы устройства микроконтроллеров AVR и их про- граммирования, система команд, программирование таймеров, арифметические операции, память, интерфейсы, режимы энергосбережения и сторожевой таймер, программы реального времени, обмен данными с персональным компьютером. Особое внимание уделено переносу типичных Arduino-проектов на ассемблер. Даны готовые рецепты для программирования большинства основных функций современной микроэлектронной аппаратуры. Для учащихся, инженерно-технических работников и радиолюбителей УДК 004.431.4 ББК 32.973.26-018.1 Группа подготовки издания: Руководитель проекта Евгений Рыбаков Зав. редакцией Екатерина Сависте Компьютерная верстка Ольги Сергиенко Дизайн серии Марины Дамбиевой Оформление обложки Карины Соловьевой Подписано в печать 07 04 20 Формат 70хЮ01/16 Печать офсетная. Усл. печ. л 36,12 Тираж 1000 экз. Заказ № 5615. "БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20. Отпечатано в ОАО «Можайский полиграфический комбинат» 143200, Россия, г Можайск, ул Мира, 93 www oaompk ru, тел (495) 745-84-28, (49638) 20-685 ISBN 978-5-9775-4076-6 © Ревич Ю В , 2020 © Оформление ООО "БХВ-Петербург", ООО "БХВ", 2020
Оглавление Введение. Почему ассемблер? 8 ЧАСТЬ I. ОБЩИЕ ПРИНЦИПЫ УСТРОЙСТВА И ФУНКЦИОНИРОВАНИЯ ATMEL AVR 13 Глава 1. Обзор микроконтроллеров AVR 15 AVR и другие 16 Почему AVR? 18 Краткий обзор возможностей AVR 21 Семейства и модификации AVR 23 Основные принципы маркировки AVR 25 Глава 2. Общее устройство, организация памяти, тактирование, сброс 28 Память программ , 29 Память данных (ОЗУ, SRAM) 31 Энергонезависимая память данных (EEPROM) 33 Способы тактирования 35 Сброс 39 Глава 3. Периферийные устройства и прерывания 43 Порты ввода/вывода 44 Таймеры-счетчики 46 Аналого-цифровой преобразователь 48 Последовательный порт 51 Интерфейс UART (USART) 52 Интерфейс SPI 57 Интерфейс TWI (12С) 61 Универсальный последовательный интерфейс USI 62 Прерывания 62 Порядок выполнения прерываний 64 Разновидности прерываний 65 Об общих принципах использования прерываний 67 Глава 4. Микроконтроллеры AVR на практике 69 Особенности практического использования МК AVR 69 Корпуса МК и их установка на плату 71
Оглавление Необходимое оборудование и приспособления 73 Панельки 73 Макетные платы 75 Адаптер для UART 76 Светодиоды-пробники 79 Мультиметр 80 Осциллограф 81 Генератор 82 Источники питания .;84 Потребление МК AVR 87 Примеры AVR-контроллеров 91 Глава 5. Подготовка к программированию МК AVR 93 Ассемблер без излишних сложностей 94 Редактор ASM Editor 95 Ассемблер Avrasm 97 Обустройство ассемблера 98 Об AVR Studio 100 Способы загрузки программ в контроллер 101 ISP-программаторы 102 Arduino как ISP-программатор 107 Конфигурационные ячейки (fuse-биты) 109 ЧАСТЬ II. ПРОГРАММИРОВАНИЕ МИКРОКОНТРОЛЛЕРОВ AVR НА АССЕМБЛЕРЕ 117 Глава 6. Основы программирования МК AVR 119 Общая структура ассемблерной программы и ее выполнение 120 Инструкции и нотация AVR-ассемблера 121 Числа и выражения 123 Директивы 125 Оформление вызова подпрограмм 129 Обработка прерываний 131 Процедура RESET 136 Использование макросов 138 НЕХ-файлы и их загрузка в контроллер 140 OBootloader 145 Простейшая программа 146 Таймер без прерываний 149 Задержка 150 Программа счетчика 152 Использование прерываний 158 Программа счетчика с использованием прерываний 158 Сравнение ассемблерной программы с программами Arduino и другими языками высокого уровня 161 Глава 7. Система команд AVR 163 Обзор команд 164 Команды передачи управления и регистр SREG 164 Команды проверки-пропуска 170
Оглавление Команды логических операций 174 Команды сдвига и операции с битами 175 Команды арифметических операций 177 Команды пересылки данных 180 Команды управления системой 187 Выполнение на ассемблере типовых процедур 188 О стеке, локальных и глобальных переменных 191 Ассемблерное представление символов и строк 194 Глава 8. Арифметические операции и операции в двоично-десятичном формате 197 Стандартные арифметические операции 199 Умножение многоразрядных чисел 200 Деление многоразрядных чисел 203 Операции с вещественными числами 206 Генератор случайных чисел 208 Операции с числами в двоично-десятичном формате (BCD) 210 Отрицательные и вещественные числа в МК 215 Представление отрицательных чисел 215 Представление вещественных чисел 218 ЧАСТЬ III. ПРАКТИЧЕСКОЕ ПРОГРАММИРОВАНИЕ МИКРОКОНТРОЛЛЕРОВ AVR 221 Глава 9. Программирование таймеров 223 8-и 16-разрядные таймеры 223 Формирование заданного значения частоты 227 Отсчет времени 230 Точная коррекция времени 237 Частотомер и периодомер 238 Частотомер 239 Периодомер 242 Управление динамической индикацией 246 LED-индикаторы и их подключение 246 Программирование динамической индикации 252 Таймеры в режиме ШИМ 254 Расчет режима ШИМ для инвертора 256 Программная реализация ШИМ 259 О схемотехнике инвертора 264 Другие применения ШИМ 267 Глава 10. Использование EEPROM 271 Еще раз о сохранности данных в EEPROM 271 Запись и чтение EEPROM 275 Регулируемый светильник с запоминанием состояния 278 Хранение констант в EEPROM 283 Глава 11. Аналоговый компаратор и АЦП 286 Аналоговые операции: понятие погрешности и построение градуировочных уравнений 286 Среднее значение и градуировочные уравнения 288
Оглавление Аналого-цифровые операции и их погрешности 291 Работа с аналоговым компаратором 294 Устройство компаратора 295 Система контроля батарейки 296 Встроенный АЦП 300 Питание и опорное напряжение » 301 Задание режима работы 303 Простейшее использование АЦП 306 Схема измерений с помощью АЦП 310 Перевод результатов в физические величины 318 Глава 12. Интерфейс SPI 323 Основные операции через SPI 324 Аппаратный вариант 324 Программный вариант 327 О разновидностях энергонезависимой памяти 328 Запись и чтение flash-памяти через SPI 330 Операции с микросхемой памяти 45DB011В 330 Программа обмена с памятью 45DB01 IB no SPI 333 Запись и чтение flash-карт 334 Подключение картММС 335 Подача команд и инициализация ММС 337 Запись и чтение ММС 341 Глава 13. Интерфейс TWI (12С) и его применение 343 Базовый протокол 12С , 344 Программная эмуляция протокола 12С 347 Часы с интерфейсом 12С 349 Особенности записи и чтения внешней памяти с 12С-интерфейсом 358 Дисплей МТ-1 ОТ И 361 Глава 14. Режимы энергосбережения и сторожевой таймер 365 В каком случае нужен режим энергосбережения? 367 Программирование режима энергосбережения 368 Выход по внешнему прерыванию 369 Применение сторожевого таймера 374 Инициализация, запуск и сброс WDT 376 Примеры использования WDT 379 О правильном построении малопотребляющих схем 383 Экономичный термометр на батарейках 384 Глава 15. Программирование UART и обмен данными с персональным компьютером 387 Способы обмена данными с ПК 389 Правила техники безопасности при подключении к ПК 389 Программы для связи ПК с контроллером 391 Дистанционная связь через UART 392 Программирование UART 394 Примеры использования UART в разных режимах 395
Оглавление Вывод и ввод символов через UART 400 Программа установки часов DS13 07 401 Как с помощью UART организовать выход из режима энергосбережения? 401 Глава 16. Некоторые Arduino-задачи на ассемблере 403 Дисплеи 403 4-разрядный цифровой дисплей на основе ТМ1637 404 Часы на дисплее ТМ1637 406 Ультразвуковой дальномер на дисплее ТМ1637 407 Термометр на дисплее ТМ163 7 409 Знакосинтезирующие дисплеи на базе HD44780 и его аналогов 410 Инициализация и вывод символов 415 Пример управления ЖК-дисплеями конфигурации 16x2 418 Дисплей МТ-10S1 фирмы МЭЛТ 419 OLED-дисплеи фирмы Winstar 420 Часы с календарем на OLED-дисплее 421 ИК-приемник 422 Управление серводвигателем 425 Приложение 1. Ликбез 429 Десятичные, двоичные и шестнадцатеричные числа 429 Запись чисел в различных форматах 431 Двоично-десятичный формат BCD 432 Перевод из одной системы счисления в другую 433 Булевы операции 434 Об обозначениях на принципиальных схемах 436 Приложение 2. Основные параметры некоторых микроконтроллеров AtmelAVR 438 Литература 444 Предметный указатель 446
ВВЕДЕНИЕ Почему ассемблер? По статистике на 2018 год 8-разрядные чипы, в том числе семейство AVR, все еще занимают около 12-13% рынка универсальных контроллеров, что совсем немало, учитывая его миллиардные объемы в количественном исчислении. Дешевые, не- прихотливые, несложные в программировании и схемотехнике, 8-разрядные кон- троллеры с большим количеством задач справляются собственными силами, без дорогих дополнительных компонентов. Вопрос в том, как проще всего приспосо- бить эти контроллеры под свои все расширяющиеся нужды? Вы уже познакомились с Arduino, имеете представление о возможностях AVR- контроллеров. Изначально рассчитанная на людей, не имеющих инженерного обра- зования, но склонных мастерить нужные вещи своими руками, платформа Arduino в несколько лет завоевала заслуженную популярность любителей по всему миру, породив целую отрасль индустрии. Платы и среда программирования, распростра- няющиеся под свободной лицензией, породили огромное количество подражаний и ответвлений, развивающих платформу в ту или иную сторону. Одновременно неис- числимое количество фирм по всему миру наладили выпуск самой разнообразной периферии: датчиков, исполнительных устройств, устройств сопряжения с комму- никационными сетями. Наверное, не осталось такой любительской задачи в облас- ти конструирования электронных устройств, которую нельзя было бы решить в рамках Arduino, причем чаще всего несколькими способами. Вероятно, важнейшая особенность экосистемы Arduino, которая и позволила завое- вать ей такую популярность — то, что порог вхождения (т. е. сумма необходимых априорных знаний и умений для начала работы) здесь сведен к достижимому ми- нимуму. Можно не разбираться ни в науке об электричестве, ни в программирова- нии — и тем не менее создавать своими руками вполне работоспособные схемы. Но даже непрофессионалу ясно, что создавая столь удобный инструмент, пришлось чем-то серьезно пожертвовать. Ограничения Arduino начинают чувствоваться сра- зу, как только вы выходите за рамки макета и пытаетесь создать удобный, эконо- мичный и эстетично выглядящий прибор. Закрадывается мысль — а зачем мне в нем столько всего лишнего, которое потребляет ток, занимает кучу места и после закачки отлаженной программы в контроллер уже никак не используется?
Почему ассемблер? Эта книга для тех, кому надоело раз за разом повторять готовые рецепты из Интер- нета, закачивая в громоздкую «этажерку» плат готовые решения. Конечно, вам тут же предлагают перейти на нормальный С и работать напрямую с AVR Studio и дру- гими подобными монстрами. И не подумайте, что я вас от этого пути буду отгова- ривать — смотря, какие задачи вы перед собой ставите. Конечно, на «чистом» С программировать контроллеры удобнее, никто не спорит, и стать профессионалом можно только на этом пути. И переносится такой код при смене модели контролле- ра даже в рамках одного только семейства AVR лучше, и значительную часть опе- раций проводить проще, текст программы получается компактнее. Заметки на полях Многие любители соблазняются средами программирования microPascal для AVR и BASCom, в которых они могут перейти на знакомые еще по школьным урокам инфор- матики языки Pascal или Basic. Вот этого действительно делать не следует — и не по- тому, что код получается гораздо хуже, чем в случае С, а потому что так вы еще дальше уходите от сути происходящего внутри контроллера, ничего не приобретая взамен, кроме сиюминутного удобства. Значительная часть преимуществ языка С кроется в доступности готовых библиотек на все случаи жизни, а в случае BASCom и тем более microPascal подобной развитой экосистемы даже близко не сложилось. Из этих сред вам почти некуда развиваться, а в случае С вы, как минимум, приобретаете базу для дальнейшего роста. Но даже профессионалы вряд ли будут спорить с тем, что имея за плечами только Arduino, с полпинка вы на С ничего хорошего не напишете. Чтобы писать эффек- тивные программы в условиях тотального дефицита ресурсов, надо знать устра- шающее количество различных тонкостей. Знания того, что означают, например, термины static и volatile, недостаточно — надо отчетливо понимать детали меха- низма действия этих и других спецификаторов в различных случаях, и еще много чего сверх того. А вы твердо уверены, что именно этого хотели— углубиться в изучение тонкостей программирования? Все споры о преимуществах того или иного подхода обычно ведутся во вполне виртуальном пространстве абстракций, игнорирующих собственно поставленную задачу. Как выразился один знаток программирования, «одна из сторон доказыва- ет, что дерево плавает, а другая, что кирпич тонет. Естественно, что при такой постановке каждая из сторон уверена в своей правоте и будет вечно ее отстаи- вать». Мы постарается не ввязываться в беспочвенные холивары, а сразу сформу- лируем задачу: мы собираемся создать экономичный, эстетично выглядящий и удобный в применении прибор. Надо ли для этого преодолевать многочисленные нюансы реализации языка С для 8-разрядных контроллеров? Да, если вы хотите посвятить этому всю жизнь. Но и в этом случае профессионалы (см., например, [2]) рекомендуют начинать с ассемблера, потому что только так можно полностью освоить возможности контроллера. Тем, кто готовится програм- мировать контроллеры профессионально, тоже будет небезынтересно узнать, как в 150 байт кода вмещается программка генерации вполне качественного синуса частотой 50 Гц, способная управлять инвертором напряжения киловаттной мощ- ности (см. главу 9).
tO Введение Заметки на полях Тут следует еще упомянуть исторический факт, что вообще-то языки высокого уровня, включая С, создавались не для контроллеров гарвардской архитектуры, к которым от- носятся в том числе и AVR. Для архитектуры фон Неймана, на которую они рассчита- ны, принципы составления программ существенно другие (иная парадигма, как гово- рят программисты). Да, с помощью С можно получить код, почти столь же эффектив- ный, как ассемблерный. Но даже в самом идеальном случае эффективность кода на С будет все-таки только «почти» такая же: живой пример этого — практически все про- граммы этой книги, которые при переводе на С дадут код, заведомо медленнее вы- полняющийся и большего объема. Специальный пример сравнения программ на Arduino, «чистом» С и ассемблере мы продемонстрируем в главе 6. Изучение собственно AVR-ассемблера, в отличие от С, занимает минимум време- ни. Краткое описание от Atmel когда-то занимало ровно 4 странички (сейчас оно существенно раздулось [4], но в основном из-за пояснений в виде примеров кода). Его вариант, переведенный на русский, также дополненный примерами и таблицей основных команд, укладывается в 17 страниц [3]. Не сравнить с многостраничными фолиантами руководств по языку С, правда? А почему так? А потому что в элемен- тарном ассемблере специфических для программирования деталей — раз, два и об- челся. Все остальное — структура контроллера, т. е. чистая электроника. И изучать возможности контроллера, когда все время идет речь об установках тех или иных битов в различных регистрах, в терминах команд ассемблера оказывается намного проще и нагляднее. Мы в этой книге обсудим многие существенные моменты, которые обычно обхо- дятся не только в отношении любительского Arduino, но и часто упускаются из ви- ду профессионалами: как сделать так, чтобы контроллер тратил на операцию ми- нимальное время? Как по максимуму использовать все, что может дать 10-разряд- ный АЦП? Как снизить потребление схемы до оптимального уровня и правильно настроить режим sleep'? И постараемся не забывать, что программирование — это всего лишь такой универсальный способ заставить работать схему по заданному алгоритму, а хорошо работающий прибор — это в первую очередь его схемотех- ника. Подробности При изучении материала этой книги читатель, оценивший компактность и предсказуе- мость ассемблерных программ, но не желающий отказываться от удобств языка С, рано или поздно задаст вопрос: а нельзя ли объединить то и другое в одном проекте? Компилятор AVR-GCC это позволяет делать, как минимум, со стороны проекта на С — вставлять в него ассемблерные фрагменты. Причем вызывать их можно даже двумя способами — см. [9], где описан один из них (а внутри статьи есть ссылка на второй, более распространенный способ). Способы применимы в любой среде программиро- вания, основанной на AVR-GCC, в том числе и в Arduino (правда, лаконичность чисто ассемблерных программ и возможность их составления в любом текстовом редакторе вы при этом теряете). Обратной возможности— использования С-библиотек в ас- семблерном тексте, как это позволяют «большие» ассемблеры для ПК (см. [13]), AVR- ассемблер, к сожалению, не предоставляет. За базовый контроллер мы возьмем традиционный ATmega8 — упрощенную вер- сию ардуиновского ATmega328, которая, однако, умеет почти все то же самое. Две другие базовые модели, программы для которых приведены в этой книге:
Почему ассемблер? 1J_ ATmega8535 (с большим числом выводов) и ATtiny2313 (с меньшим). Интересно, что ATtiny2313, по идее относящийся к младшему семейству, обладает некоторыми функциями более современных моделей. Эти три модели мы будем считать «наши- ми». Почти без изменений программы, которые вы здесь встретите, пригодны и для ATmegal6, который, если не считать большего объема памяти (совершенно для нас не существенного), в общем, отличается от ATmega8 только увеличенным числом выводов. Особенности переноса программ и на более «продвинутые», и на более простые модели AVR мы уточним по ходу дела. В первую очередь мы узнаем, что гонять контроллер на частоте 16 МГц совершен- но необязательно— для огромного большинства задач достаточно куда более скромной частоты с одновременным снижением потребления в разы. Научимся устанавливать разные режимы контроллера, в том числе от встроенного тактового генератора, когда ему вовсе не нужны никакие дополнительные компоненты. Не обойдем мы и ограничения ассемблера — вы сами сможете нащупать порог, когда более высокое качество ассемблерных программ уже перестает окупаться затрата- ми времени на их создание. Схемы у нас будут только принципиальные. Картинки монтажной платы с распо- ложением деталей, которые можно так красиво делать в пакете Frizing, только за- путывают, потому что самое главное на них отсутствует, и никакой реально необ- ходимой информации о схеме эти картинки не дают. Только принципиальная схема с указанием названий, номиналов, типов и полярностей всех компонентов может дать исчерпывающее представление о том, что вы делаете. Но ничто не мешает сделать ее более наглядной — даже ГОСТ не запрещает располагать компоненты в соответствии с реальной разводкой выводов, делая схему как бы частично мон- тажной. Именно в таком стиле исполнены все схемы в этой книге. Некоторые эле- ментарные правила, касающиеся обозначений на принципиальных схемах, приве- дены в приложении 7, а интересующихся подробностями отсылаем к моей кни- ге [10]. Автор предполагает, что читатель худо-бедно знаком с двоичными и шестнадцате- ричными цифрами и основными логическими операциями, умеет читать принципи- альные схемы и знает, как связаны напряжение и ток, и зачем светодиоду нужен резистор. Тем, кто в таких вопросах плавает, адресован краткий «Ликбез» (см. при- ложение 7), а также совет сначала ознакомиться с книжкой [10], где все эти вопро- сы разъясняются детально. Таблицы с основными характеристиками некоторых моделей микроконтроллеров Atmel AVR из числа самых ходовых приведены в при- ложении 2. Обсуждений радиолюбительских технологий в этой книге вы также почти не встре- тите— предполагается, что все примеры и макеты конкретных конструкций выполняются на беспаечной макетной плате, а особенности переноса их в закон- ченные устройства и нюансы подбора компонентов обговариваются только в слу- чаях, когда это критично с точки зрения функциональности. Книга построена по традиционному принципу «от общего к частному»: в части I рассматриваются общие вопросы устройства контроллеров, определяются необхо-
f2 Введение димые приспособления и инструменты, описаны программные средства и их на- стройка. К практическому программированию мь| переходим в части //, которая начинается с простейших примеров ассемблерных программ и краткого обзора сис- темы команд. В части III изучение продолжается примерами использования кон- кретных узлов контроллеров. В архиве, который вы можете скачать по адресу http://revich.lib.ru/AVR/AVR- asm.zip, вы найдете тексты и примеры большинства программ, размещенных в этой книге, распределенные по папкам с названием главы. В тексте глав указываются имена файлов, соответствующих этим программам. Все включенные в книгу схе- мы, программы и отдельные алгоритмы проверены на макетах, и их работоспособ- ность гарантируется при соблюдении указанных для них условий. Связаться с автором можно по электронной почте revich@lib.ru, можно также оставить личное сообщение на сайте habr.com пользователю YRevich.
ЧАСТЬ I Общие принципы устройства и функционирования Atmel AVR Глава 1. Обзор микроконтроллеров AVR Глава 2. Общее устройство, организация памяти, тактирование, сброс Глава 3. Периферийные устройства и прерывания Глава 4. Микроконтроллеры AVR на практике Глава 5. Подготовка к программированию МК AVR
ГЛАВА 1 Обзор микроконтроллеров AVR Говорят, что в 1960-е годы, наблюдая за участниками студенческих демонстраций протеста, Гордон Мур заметил: «Истинные революционеры — это мы». Ученик и сотрудник одного из изобретателей транзистора У. Шокли, в числе прочего счи- тающегося основателем знаменитой Кремниевой долины, в свою очередь основа- тель и лидер компаний, которым суждено было сыграть ведущую роль в развитии микроэлектроники, Мур знал, что говорил. Парадоксальным образом именно изо- бретениям Мура и его сотрудников было суждено стать основой того мира, в кото- ром впоследствии сконцентрировалась деятельность «бунтующей молодежи» 1960-х. Современные хакеры (не компьютерные хулиганы из новостей, а настоящие увле- ченные своим делом компьютерщики) — прямые идеологические наследники сор- боннских студентов и американских демонстрантов, сменившие девиз «Make love not war»1 на «Не пишите лозунги — пишите код». Неслучайно многие известные деятели электронно-компьютерной индустрии, авторы изобретений, сформировав- ших лицо современного мира, — выходцы из среды, близкой той самой «бунтую- щей молодежи». Наша история о микроконтроллерах началась с того, что в 1957 году Гордон Мур совместно с Робертом Нойсом и еще шестью сотрудниками Shockley Semiconductor Labs (Шокли назвал их «предательской восьмеркой») основали компанию Fairchild Semiconductor. Ей мы обязаны не только развитием полупроводникового рынка и внедрением микросхем в инженерную практику, но и тем, что она стала своеобраз- ной кузницей кадров и генератором идей для молодой отрасли. Из изобретений сотрудников Fairchild берет свои истоки большая часть ключевых инноваций, сформировавших лицо современного мира, начиная прямо с самой основы — изо- бретения микросхемы Робертом Нойсом. Из всего урагана событий, источником которых стала Fairchild, для нашего повест- вования важно то, что в числе прочих инноваций сотрудники Fairchild первыми стали продвигать полупроводниковую память. Сейчас, в век CD и DVD, твердо- тельных жестких дисков и flash-карточек, нам трудно представить себе, что в нача- 1 «Занимайтесь любовью, а не войной» — лозунг хиппи 1960-х, протестующих против войны во Вьет- наме.
_/б Часть I. Общие принципы устройства и функционирования AtmelAVR ле 1960-х годов программы для компьютеров хранились в основном на картонных листочках (перфокартах), конструкторы ломали голову над дорогущими модулями ОЗУ на ртутных линиях задержки, осциллографических трубках и ферритовых колечках в полмиллиметра диаметром, где каждый бит «прошивался» вручную. Самое компактное в те годы электронное устройство для хранения данных на маг- нитных дисках под названием RAM АС 305 емкостью 5 Мбайт было размером с промышленный холодильник и сдавалось в аренду за 5 тыс. долларов в месяц. Компактная полупроводниковая память была нужна абсолютно всем — от военных и NASA до изготовителей бытовых приборов. Почуяв, откуда дует ветер, в 1968 году Мур с Нойсом оставили Fairchild и основали Intel, как специализиро- ванную компанию по разработке и производству памяти. Они еще не ведали, что самым популярным детищем Intel станет вовсе не память, а микропроцессор, раз- работка которого первоначально затевалась, как вспомогательный этап в проекти- ровании обычного калькулятора. С набора из четырех небольших микросхем Intel под названием «семейство 4000» началось победное шествие микропроцессоров по всему миру. Они весьма быстро разделились на несколько разновидностей, в основном относящихся к двум глав- ным группам: собственно микропроцессорам (МП) и микроконтроллерам (МК). Первые предназначены для использования в составе вычислительных систем, са- мые распространенные из которых — персональные компьютеры (ПК), поэтому их еще часто называют «процессорами для ПК» (к этой же группе обычно относят также и производительные МП для серверов и некоторые другие). МК отличаются от МП тем, что они в первую очередь предназначены для управления различными системами, поэтому при относительно более слабом вычислительном ядре они включают в себя много дополнительных узлов. То, что для обычного МП предпо- лагается размещать во внешних «чипсетах» или дополнительных модулях (память, порты ввода/вывода, таймеры, контроллеры прерываний, узлы для обработки ана- логовых сигналов и пр.), в МК располагается прямо на кристалле, отчего их часто называют «computer-on-chip» («однокристальный компьютер»). И действительно, в простейшем случае для построения полностью функциони- рующего компьютера достаточно единственной микросхемы МК с подсоединен- ными к ней устройствами ввода/вывода. Современные модели рядовых однокри- стальных МК превышают вычислительные возможности IBM PC AT на 286-м про- цессоре образца второй половины 1980-х. Есть быстроразвивающиеся области, где границу между МП и МК провести трудно — таковы, например, процессоры для мобильных устройств, от обычных телефонов до смартфонов и планшетов, в кото- рых процессорный узел должен обладать развитыми вычислительными функциями и управлять многочисленными внешними компонентами. AVR и другие Собственно история AVR-контроллеров началась с того, что 1962 году в Калифор- нии появилась семья Перлегос, греческих эмигрантов, уроженцев города Триполис. Родители занялись, как и на родине, виноградарством, а сыновья Джордж и Гюст
Глава 1. Обзор микроконтроллеров AVR 17_ Перлегос выбрали модную специальность инженера-электронщика — оба окончи- ли вначале университет Сан-Хозе, а затем Стэнфордский университет. В 1974 году в возрасте 24 лет младший из братьев Джордж Перлегос начал работать в компании Intel, где попал на одно из самых передовых направлений — разработку электриче- ски стираемой памяти для замены «прожигаемой» ОТР ROM. При его участии, а затем и под его непосредственным руководством были созданы две технологии, ставшие точкой роста для всей отрасли по производству flash-памяти — одного из главных столпов современной «цифровой революции». В начале 1980-х Джордж Перлегос увольняется из Intel. С братом Гюстом и еще несколькими сотрудниками он в 1984 году создает вскладчину на личные средства компанию, полное название которой звучит как Advanced Technology MEmory and Logic или сокращенно — Atmel. Сначала продукцией Atmel были микросхемы энергонезависимой памяти всех раз- новидностей: как «однократно программируемых» ОТР EPROM и «перезаписы- ваемых» EEPROM с последовательным и параллельным доступом, так и Flash. В 1985 году Atmel выпустила первую в мире EEPROM по доминирующей ныне КМОП-технологии, а в 1989 году— первую flash-память с питанием от одного на- пряжения +5 В. В конце 1980-х Intel вознамерилась наказать ряд компаний- производителей EPROM, в том числе и Atmel, якобы за нарушение патентов, но в конце концов им удалось договориться об обмене лицензиями. Причем в конеч- ном итоге Atmel перепала лицензия на производство классического микроконтрол- лера 8051, от поддержки которого Intel уже в то время постепенно отходила, сосре- доточившись на процессорах для ПК. Подробности Напомним, что EEPROM отличается от flash-памяти тем, что первая допускает раз- дельный доступ к любой произвольной ячейке, а вторая — лишь к целым блокам. По- этому EEPROM меньше по объему (характерный объем специализированных микро- схем EEPROM — от единиц килобит до единиц мегабит) и дороже, в настоящее время ее используют в основном для хранения данных, в том числе в составе микроконтрол- леров. Flash-память проще и дешевле, и к тому же дает значительный выигрыш в ско- рости при больших объемах информации, особенно при потоковом чтении/записи, ха- рактерном для медиаустройств (вроде цифровых камер или МРЗ-плееров). В составе микроконтроллеров flash-память служит для хранения программ. Некоторые подроб- ности о различных типах памяти и их функционировании приведены также в главе 10. Так Atmel оказалась «втянута» в число производителей микроконтроллеров, в ко- тором очень быстро оказалась на первых позициях, — в 1993 году началось произ- водство первых в отрасли МК АТ89С51 со встроенной flash-памятью программ. Это означало начало переворота во всей инженерной практике, потому что сущест- вовавшие ранее МК обладали либо однократно программируемой ОТР-памятью, либо УФ-стираемой, которая значительно дороже в производстве, и работа с ней приводит к большим потерям времени разработчиков. Число циклов перезаписи для УФ ППЗУ не превышает нескольких десятков, а прямой дневной свет, попав- ший на такой кристалл, может привести к стиранию информации. Поэтому даже мелкосерийные устройства приходилось изготавливать преимущественно с исполь-
j[8 Часть I. Общие принципы устройства и функционирования AtmelAVR зованием ОТР ROM, что значительно рискованнее, — изменить в случае даже ма- лейшей ошибки записанную программу уже было невозможно. Появление flash- памяти изменило весь «ландшафт» в этой области — именно в результате ее вне- дрения стали возможными такие операции, как программное обновление BIOS компьютера или «перепрошивка» управляющих программ для бытовых электрон- ных устройств. В 1995 году два студента Норвежского университета естественных наук и техноло- гии из города Тронхейма, Альф Боген и Вегард Воллен, выдвинули идею 8-разрядного RISC-ядра, которую предложили руководству Atmel. Имена разработ- чиков вошли в название архитектуры AVR: Alf + Vergard + RISC. Идея настолько понравилась, что в 1996 году в Тронхейме был основан исследовательский центр Atmel, и уже в конце того же года выпущен первый опытный микроконтроллер но- вой серии AVR под названием AT90S1200. Во второй половине 1997 года корпора- ция Atmel приступила к серийному производству семейства AVR. Почему AVR? У AVR-контроллеров «с рождения» есть две особенности, которые отличают это семейство от остальных 8-разрядных МК. Во-первых, это наличие конвейера, бла- годаря чему для AVR не существует понятия машинного цикла: большинство команд выполняются за один такт. Для сравнения отметим, что пользующиеся большой популярностью МК семейства PIC выполняют команду за 4 такта, а клас- сические 8051 — вообще за 12 или даже 24 такта. Вторая особенность — наличие 32 оперативных регистров, не совсем равноправ- ных, но позволяющих в ряде случаев вообще не обращаться к оперативной памяти и не использовать в явном виде стек (что в принципе невозможно в том же семей- стве лг51). Более того, в младших моделях Tiny AVR стек вообще недоступен для программиста. Потому структура ассемблерных программ для AVR стала подозри- тельно напоминать программы на языке высокого уровня, где операторы взаимо- действуют не с ячейками памяти и регистрами, а с абстрактными переменными и константами. Программы при этом работают много быстрее и с точно предсказуе- мым временем выполнения отдельных операций. Суммировав мнения из различных источников и опираясь на собственный опыт, автор пришел примерно к такому подразделению областей применения самых рас- пространенных семейств 8-разрядных контроллеров: □ контроллеры классической архитектуры jc51 (первые микросхемы семейства 8051 были выпущены еще в начале 1980-х) уже давно не применяются на прак- тике и лучше всего подходят для общего изучения предмета— по аналогии с языками Pascal или Basic при обучении программированию. Intel-ассемблер замечательно продуман, программы на нем хорошо читаются, число различных команд для jc51 весьма велико (*51, в отличие от многих других, имеет не RISC-, а CISC-архитектуру, для которой характерно наличие большого числа весьма функциональных команд). Авторитетный Di Halt [2], отмечал: «Что касается лично меня, то я обожаю архитектуру 51 за ее чертовски приятный ассемблер,
Глава 1. Обзор микроконтроллеров AVR 19_ на котором просто кайфово писать. Под эту архитектуру уже написаны гига- байты кода, созданы все мыслимые и немыслимые алгоритмы»', П семейство AVR рекомендуется для начинающих электронщиков-практиков — в силу универсальности устройства, легкости загрузки программ, преемственно- сти структуры для различных типов контроллеров, разнообразия типов корпу- сов, простоты схемотехники, практически лишенной каких-либо специфических особенностей, затрудняющих освоение новичками. Отдельно следует отметить наличие отличной базы для начала работы с этими МК в виде платформы Arduino и всего с ней связанного (подробнее о платформе Arduino рассказано чуть далее); □ контроллеры PIC фирмы Microchip не имеют столь удобной системы команд (которых всего около трех десятков), отдельные представители семейства не универсальны, и требуют тщательного выбора «под задачу». Зато у них низкое энергопотребление и быстрый старт. PIC идеально подходят для проектирова- ния несложных устройств, особенно предназначенных для тиражирования. Тра- диционно они используются в «умных» узлах автомобилей, а также в устройст- вах бытовой сигнализации; □ последние годы набирает обороты семейство STM8, впервые выпущенное фир- мой STMicroelectronics в 2008 году. STM8 в целом похожи на AVR и PIC, но от- личаются как от них, так и от своих старших собратьев — STM32, в сторону не- которых удобств: большего диапазона питания, большей скорости выполнения, команд за счет большей глубины конвейера, большей тактовой частоты при меньшем потреблении, мелкими усовершенствованиями периферии ядра и т. п. Но главное то, что наученные горьким опытом своих коллег из других фирм разработчики архитектуры STM8 сосредоточились на максимальной совмести- мости всех контроллеров семейства между собой. AVR в этом смысле далеко не идеальны (хотя, заметим, все-таки получше других семейств), но STM8 пре- взошли всех, заявив совместимость по выводам — одни и те же узлы в разных контроллерах выводятся на одни и те же выводы корпуса, что позволяет менять один контроллер на другой без переделки кода и даже без переделки платы. Свихнувшимся на простейших инструментах адресована ST Visual Develop — фирменная среда разработки, позволяющая в том числе программировать на ас- семблере STM8, куда более простая и в установке, и в освоении, чем монструоз- ная Atmel Studio. Правда, архитектура STM8 намного лучше AVR приспособле- на к языку С, и там использование ассемблера почти теряет смысл. Возможно, единственное препятствие для распространения STM8 в широких кругах люби- телей — то, что STM8 выпускаются исключительно в совместимых друг с дру- гом планарных корпусах с мельчайшим шагом 0,5-0,6 мм и потому трудно под- даются макетированию и пайке «на коленке». Как ни странно, но 8-разрядные контроллеры вопреки предсказаниям десятилетней давности не стали исчезать под натиском 32-разрядных чипов: в конце предыдуще- го десятилетия они составляли более 50% всех продаваемых микропроцессорных изделий, а сейчас их доля сократилась до 12-13%, но вот уже несколько лет остает-
20 Часть I. Общие принципы устройства и функционирования Atmel AVR ся постоянной. Это понятно — зачем нужен высокопроизводительный 32-разряд- ный процессор для управления стиральной машиной? Несмотря на сопоставимую цену 32-разрядных моделей, вследствие более простой схемотехники, а также эко- номии времени на программировании и отладке, популярность 8-разрядных кон- троллеров не падает. Об Arduino Популярность как любительского конструирования электронных приборов вообще, так и конкретно AVR-микроконтроллеров в среде любителей существенно возросла с по- явлением платформы Arduino. Платформа возникла в среде сотрудников Interaction Design Institute (что можно перевести как «Институт конструирования взаимодейст- вий») из итальянского городка Ивреа и получила свое почти толкиеновское название по имени реально существовавшего короля Ардуина, правившего этой местностью в начале прошлого тысячелетия. Arduino выросла из задачи научить студентов не- профильных специальностей создавать электронные устройства, причем быстро и, желательно, без опоры на углубленное изучение электроники, электротехники и про- граммирования. В конце концов группа, руководимая программистом Массимо Банци, создала универ- сальную аппаратную платформу на основе дешевых, удобных и доступных микрокон- троллеров Atmel AVR и решила ее распространять на принципах open source. Такие свободные лицензии, как знаменитая GPL, разработанная применительно к софту, для «железа» напрямую не годится, потому создатели взяли за основу пакет лицензий Creative Commons для творческих продуктов. Лицензия Arduino запрещает использо- вание этой торговой марки для каких бы то ни было сторонних продуктов, кроме рас- ширений основного проекта. Это привело к тому, что от Arduino стали отпочковывать- ся аналогичные проекты, совместимые с ним, но желающие иметь иные названия — например, такие, как Freeduino, Craftduino, Carduino и многие другие. Пик роста возможностей Arduino приходится на конец нулевых — начало десятых го- дов XXI века, потом темпы развития платформы снизились. Разработчики поневоле загнали себя в тупик— ограничив выбор контроллеров двумя-тремя моделями и стандартизировав дизайн плат, они сильно облегчили порог вхождения пользовате- лям-непрофессионалам и производителям совместимого оборудования, но своими руками закрыли себе пути совершенствования. Отсюда и интерес к расширенному ис- пользованию контроллеров AVR, которое в том числе призвана утолить эта книга. Но скорее всего Arduino еще долго будет доминировать в любительском секторе — пока кто-нибудь не придумает что-нибудь столь же простое и удобное, только уже, навер- ное, на 32-разрядной платформе. Примерно то же самое, что и с Arduino, происходит с самими AVR-контроллерами. В руководстве Atmel «отец flash-памяти» и лауреат многих отраслевых наград Джордж Перлегос пребывал до 2006 года, когда оставил свой пост. Через десять лет, в 2016-м, компания была поглощена фирмой Mirochip. За это десятилетие в руководстве не нашлось инициативного человека, способного предложить идею кардинального обновления модельного ряда так, чтобы и преемственность сохра- нить, и обеспечить рост на долгий период (подобно тому, как это сделали в STMicroelectronics, предложив STM8 вместо мало кому интересных STM7). Но это, конечно, не значит, что AVR вымирают — достаточно посмотреть на обшир- ный список моделей на сайте Microchip, напротив которых стоит «In Production», причем не такая уж маленькая часть этого списка входит в категорию «New product». Для AVR еще на долгие годы обеспечена своя ниша, и уж в любительском секторе даже признаков увядания семейства пока не наблюдается.
Глава 1. Обзор микроконтроллеров AVR 21_ Краткий обзор возможностей AVR Atmel AVR представляет собой семейство универсальных 8-разрядных микрокон- троллеров на основе общего ядра с различными встроенными периферийными уст- ройствами. Возможности МК AVR позволяют решить множество типовых задач, возникающих перед разработчиками радиоэлектронной аппаратуры. Особенности микроконтроллеров Atmel AVR: □ Производительность порядка 1 MEPS/МГц, где MIPS (Millions of Instructions Per Second, миллион команд в секунду) — одна из самых старых и во многом формальная характеристика производительности процессоров, т. к. наборы команд для различных процессоров различаются, и, соответственно, одно и то же число инструкций на различных системах даст разную полезную работу. Тем не менее для простых 8-разрядных вычислительных систем, не содержащих ко- манд, оперирующих с большими числами, числами с плавающей точкой и мас- сивами данных, это неплохой показатель для сравнения их производительности. Вычислительное ядро AVR на ряде задач по производительности превосходит 16-разрядный процессор 80286. □ Усовершенствованная RISC-архитектура — концепция RISC (Reduced Instruc- tion Set Computing, вычисления с сокращенным набором команд) предполагает наличие набора команд, состоящего из минимума компактных и быстро выпол- няющихся инструкций. При этом такие более громоздкие операции, как вычис- ления с плавающей точкой или арифметические действия с многоразрядными числами, предполагается реализовать на уровне подпрограмм. Концепция RISC упрощает устройство ядра (в типовом ядре AVR содержится лишь 32 тыс. тран- зисторов, в отличие от десятков миллионов в процессорах для ПК) и ускоряет его работу — типовая инструкция выполняется за один такт, кроме команд вет- вления программы, обращения к памяти и некоторых других, оперирующих с данными большой длины. В AVR имеется простейший двухступенчатый кон- вейер, когда команда выполняется в одном такте с выборкой следующей. В отличие от Intel-архитектур, в «классическом» AVR не было аппаратного ум- ножения/деления, однако в подсемействе Mega появились операции умножения. □ Раздельные шины памяти команд и данных — AVR (как и большинство дру- гих микроконтроллеров) имеет т. н. гарвардскую архитектуру, где области па- мяти программ и данных разделены (в отличие от классической архитектуры фон Неймана в обычных компьютерах, где память общая). Раздельные шины для этих областей памяти значительно ускоряют выполнение программы — данные и команды могут выбираться одновременно. □ 32 регистра общего назначения (РОН) — Atmel была первой компанией, дале- ко отошедшей от классической модели вычислительного ядра, в которой выпол- нение команд предусматривает обмен данными между АЛУ и запоминающими ячейками в общей памяти. Введение РОН в таком количестве (напомним, что в архитектуре л:86 всего четыре таких регистра, а в х51 понятие РОН, как тако- вое, отсутствует) в ряде случаев позволяет вообще отказаться от расположения
22_ Часть I. Общие принципы устройства и функционирования Atmel AVR глобальных и локальных переменных в ОЗУ и от явного использования стека, операции с которым усложняют и загромождают программу. Подробности В ранних архитектурах обращение к памяти занимало много процессорного времени, потому в целях ускорения выполнения арифметических операций их стали произво- дить в специально выделенных регистрах (регистрах общего назначения, РОН), к ко- торым часто добавлялся специальный регистр-аккумулятор. В AVR от единого акку- мулятора отказались в пользу большого количества равноправных РОН, что облегча- ет создание программ непосредственно в инструкциях процессора (на ассемблере), но усложняет и запутывает задачу компилятора с языков высокого уровня. Наконец, в архитектуре STM (и 8-ми и 32-битовых) опять отказались от концепции РОН в пользу единственного аккумулятора — все операции АЛУ производит непосредственно с пе- ременными в оперативной памяти, — но на этот раз они выполняются за один такт. Иными словами, в архитектуре STM вы можете насоздавать сколько угодно аналогов регистров общего назначения — ограничивает только объем памяти. Платой за это в STM8 служит, во-первых, весьма запутанная система распределения памяти, за чем приходится внимательно следить (помните первую компьютерную аксиому: «Заклады- вая что-то в память компьютера, помните, куда вы это положили»?), во-вторых, неко- торая неопределенность с временем выполнения команд перехода из-за наличия трехуровневого конвейера. Зато и по части совместимости с языком С — никаких про- блем. □ Большое количество разнообразных команд (инструкций), номенклатура кото- рых (примерно от 90 до 130, в зависимости от модели контроллера) для AVR больше, чем в других RISC-семействах. Противоречия с концепцией RISC тут нет, поскольку значительная часть этих инструкций — псевдонимы, и введены они исключительно для удобства программирования. Так что изучать все инст- рукции сразу не потребуется. □ Flash-память программ (10 000 циклов стирание/запись)— с возможностью внутрисистемного перепрограммирования, т. е. загрузки программ прямо в го- товой схеме (In System Programming, ISP). ISP не следует путать с программиро- ванием через последовательный (Serial) порт с помощью отдельной программы- загрузчика (Bootloader), как это делается в Arduino (подобно об этом рассказано в главах 5 и 6). □ Отдельная область энергонезависимой памяти (EEPROM, 100 000 циклов стирание/запись) — для хранения данных с возможностью записи программным путем или внешней загрузки через ISP, подобно программам (см. главу 10). П Встроенные устройства для обработки аналоговых сигналов: аналоговый компаратор и многоканальный 10-разрядный АЦП (см. главу 77). □ Сторожевой таймер, позволяющий осуществлять автоматическую перезагрузку контроллера через определенные промежутки времени (например, для выхода из «спящего» режима — см. главу 14). □ Последовательные интерфейсы SPI, TWI (12С) и UART (USART), позволяю- щие осуществлять обмен данными с большинством стандартных датчиков и других внешних устройств аппаратными средствами (см. главы 12,13 и 75).
Глава 1. Обзор микроконтроллеров AVR 23^ □ Таймеры-счетчики с предустановкой и возможностью выбора источника счет- ных импульсов: как правило, один-два 8-разрядных и как минимум один 16-разрядный, в том числе могущие работать в режиме PWM (ШИМ) — много- канальной 8-, 9-, 10-, 16-битовой широтно-импульсной модуляции (см. главу 9). □ Возможность работы при тактовой частоте от 0 Гц до 16-20 МГц. □ Диапазон напряжений питания от 2,7 до 5,5 В (в некоторых случаях от 1,8 или даже 0,7 В). □ Многочисленные режимы энергосбережения, отличающиеся числом узлов, остающихся подключенными. Выход из «спящих» режимов осуществляется по сторожевому таймеру или по внешним прерываниям (см. главу 14). П Встроенный монитор питания — детектор падения напряжения (Brown-out Detector). Здесь упомянуты далеко не все особенности, характерные для различных моделей AVR. С некоторыми другими мы познакомимся в дальнейшем, а также на практике рассмотрим упомянутые подробнее. Но сначала дадим общую характеристику раз- личных семейств AVR с точки зрения их преимущественного назначения. Семейства и модификации AVR В 2002 году фирма Atmel начала выпуск новых подсемейств 8-разрядных МК на базе AVR-ядра. С тех пор МК этого семейства стали делиться на три основные группы (подсемейства): Classic, Tiny и Mega. В 2008 году к ним добавилось семей- ство Xmega, которое популярности не снискало, и потому в этой книге мы его не рассматриваем. МК семейства Classic (AT90Sjarar) уже давно не выпускаются — дольше всего в производстве «задержалась» очень удачная (простая, компактная и быстродействующая модель) AT90S2313, но и она была еще в 2005 году заменена HaATtiny2313. Все «классические» AVR с первыми цифрами 2, 4 и 8 в наименовании модели (что означает количество килобайт памяти программ) первоначально имели полные аналоги в семействах Tiny и Mega, и большая часть таких совместимых моделей выпускается до сих пор. Для Mega-аналогов при программировании возможна ус- тановка специального бита совместимости, который позволяет без каких-либо из- менений использовать программы, созданные для семейства Classic. Поэтому, если вы найдете на интернет-ресурсах примеры программирования AVR семейства Classic, то их можно будет задействовать в современных моделях без каких-либо доработок. Тем не менее в книге все примеры адаптированы для современных мо- делей Tiny и Mega. Микросхемы Tiny в основном имеют Flash-ПЗУ программ объемом 1-8 кбайт (в некоторых современных моделях расширенное до 16 кбайт) и размещаются в корпусах с 6-32 выводами, т. е. они в целом предназначены для более простых и дешевых устройств. Это не значит, что их возможности во всех случаях более ог-
24 Часть /. Общие принципы устройства и функционирования AtmelAVR раниченны, чем у семейства Mega. Так, например, ATtiny26 содержит таймер с вы- сокоскоростным ШИМ-режимом, а также 11-канальный АЦП с возможностью ра- боты в дифференциальном режиме с регулируемым входным усилителем и встро- енным источником опорного напряжения, что характерно для семейства Mega. Микросхема ATtiny2313, как уже говорилось, представляет собой улучшенную вер- сию одного из наиболее универсальных и удобных «классических» AVR AT90S2313, дополненную возможностями новых семейств. Старшие модели семейства Tiny в настоящий момент по некоторым параметрам даже обходят младшие Mega — на- пример, у ATtiny2313 есть расширенное внешнее прерывание PCINT на почти всех цифровых выводах портов (что дает возможность без излишних сложностей выво- дить его из режима энергосбережения), а у ATmega8/16 такая возможность отсут- ствует. Подсемейство Mega оснащено Flash-ПЗУ программ объемом 4-256 кбайт и имеет корпуса с 28-100 выводами. В целом МК этой группы более «навороченные», чем Tiny, имеют более разветвленную систему встроенных устройств с более развитой функциональностью. Таблицы с основными характеристиками некоторых моделей Tiny и Mega из числа самых ходовых приведены в приложении 2. Там же даны некоторые общие техни- ческие характеристики семейства AVR. Таблицы ориентированы на основные нуж- ды радиолюбителей, в основном не выходящие за рамки сведений из последующих глав этой книги, и представляют собой небольшую выборку из фирменной пара- метрической таблицы 8-разрядных AVR, которую можно найти на сайте www.microchip.com (не путать с таблицей Quick Reference Guide, доступной прак- тически с главной страницы сайта). Надо отметить, что пользоваться этой таблицей не слишком удобно — там все модели помещены внавал по непонятному принципу сортировки, а некоторые важные параметры отсутствуют. Поэтому, помимо при- ложения 2, рекомендую еще две более полные (хотя и частично устаревшие) вы- держки из фирменной таблицы на русском языке отдельно для Mega и Tiny [5], где ориентироваться гораздо проще. Более подробные сведения можно почерпнуть из фирменной технической докумен- тации, которая доступна на сайте www.microchip.com для всех без исключения мо- делей, включая снятые с производства. Документация размещается в англоязычных файлах в формате PDF, традиционно называемых Datasheets (мы их будем по- свойски именовать «даташитами»), и только там имеется полная официальная информация по каждому контроллеру. Нельзя сказать, что в «даташитах» вовсе не бывает ошибок и недоработок (например, таковые найдены автором в свежей ре- дакции файла по контроллеру ATmega8a, выпущенной уже под лейблом Microchip, а не Atmel), и поэтому в конце каждого такого файла ведется учет «ревизий» пре- дыдущих версий. Но в целом информации из этих документов можно и нужно доверять, и всегда следует сверяться именно с ними в случаях, когда сведения из какого-то постороннего источника вызывают сомнения. Наиболее полный и скрупулезно выполненный перевод официальных «даташитов» для ряда моделей AVR представляют книги А. В. Евстифеева [6,7]. Книги эти не-
Глава 1. Обзор микроконтроллеров AVR 25_ сколько устарели с точки зрения представленного модельного ряда, но все нужные нам модели в них охвачены, и их очень полезно иметь под рукой с другой точки зрения — в этих книгах приведен грамотный и в литературном, и в техническом плане перевод на русский язык не всегда внятно написанной фирменной докумен- тации, американский технический язык которой может быть весьма далек от анг- лийского литературного, который мы все изучали в школе. Обратите внимание: первые издания этих книг (под маркой издательства «Додэка») решительно отстали от современности по части ассортимента представленных там контроллеров. И если они вдруг у вас имеются, то из них могут пригодиться только переведенные на рус- ский подробные описания команд из официального руководства [8]. Такие же опи- сания команд на русском есть и в более современных изданиях [6,7]. Кроме Datasheets, официальная документация представлена в виде Application Notes, что можно перевести как «замечания к применению». «Аппноты» нумеру- ются подряд по мере их выхода, без какого-либо отношения к содержанию, и найти в них что-то можно только полнотекстовым поиском. За двадцать с лишним лет существования AVR «аппнот» накопилось на немаленькое собрание сочинений, и значительная часть их содержит описания каких-то не очень интересных частных случаев. Тем не менее в них можно найти и конкретные примеры программирова- ния часто встречающихся задач, и описания построения рекомендуемых алгорит- мов, и тонкости различий между родственными моделями контроллеров, и еще много разной полезной информации, так что мы будем периодически на них ссы- латься. Основные принципы маркировки AVR Все модели каждого семейства могут иметь несколько модификаций. Если не счи- тать некоторых младших моделей с объемом памяти менее 2 кбайт в серии Tiny (Tiny4, 5 и 9), то первые одна или две цифры в наименовании модели сразу после обозначения семейства (ATtiny или ATmega) означают объем flash-памяти про- грамм в килобайтах, так: ATmega8 — это контроллер с памятью программ 8 кбайт, a ATmega 16 — контроллер с памятью программ 16 кбайт. Если имеются более со- временные модификации этих контроллеров, то они обозначаются прибавлением еще одной или нескольких цифр — например, ATmega88 есть расширенная версия ATmega8 (учтите, что это фактически другая ветвь AVR, и без модификации ас- семблерные программы, написанные для Mega8, применять к Mega88 не получит- ся). ATmega88, в свою очередь, имеет ряд вариантов с разным объемом памяти программ: ATmega48 (4 кбайта), ATmegal68 (16 кбайт), в том числе и наш люби- мый ардуиновский ATmega328 (32 килобайта). Буква «L» в обозначении старых моделей говорила о расширенном диапазоне пи- тания 2,7-5,5 В при сниженной допустимой тактовой частоте (8 МГц). Отсутствие такой буквы означало диапазон питания 4,5-5,5 В при повышении допустимой так- товой частоты (16 МГц). В настоящее время появились и другие буквы: например, буква «А» говорит о переходе на новую технологию с уменьшенными технологи-
26^ Часть I. Общие принципы устройства и функционирования AtmelAVR ческими нормами (60 нм вместо 90), и такой кристалл перекрывает возможности обеих старых версий («L» и не-«Ь») при еще более уменьшенном потреблении. Буква «V» означает версии контроллеров, работающих при расширенном напряже- нии питания в пределах 1,8-5,5 вольта, буква «U»— сверхнизкое допустимое питание от 0,7 вольта, буква «Р» — понижение потребления в «спящем» режиме (не путать с «Р» в обозначении варианта исполнения, о чем см. далее). После этих букв может идти еще одна буква позднейшей модификации — например, недавно вышедшая версия ATmega328PB сильно отличается от оригинальных ATmega328 или ATmega328P. Поэтому при выборе конкретного типа микросхемы нужно быть внимательным и проверять, что означают те или иные буквы в полном наименовании. Для примера: поскольку L-версии одновременно также и менее быстродействующие, то у боль- шинства из них максимальная тактовая частота ограничена значением 8 МГц, а для «обычных» или А-версий максимальная частота составляет 16 или 20 МГц. Хотя, как правило, при разгоне L-микросхем с напряжением питания 5 В до частот 10- 12 МГц неприятностей ожидать не следует (аналогично версии без буквы «L» вполне могут работать при напряжении питания около 3 В, разумеется, не на экс- тремальных значениях частот), тем не менее при проектировании высоконадежных устройств следует учитывать это требование. После основного обозначения модели через дефис идет обозначение варианта ис- полнения. Первые одна или две цифры здесь обозначают максимальную рабочую частоту в мегагерцах, последняя буква— условное обозначение температурного диапазона (чаще всего у нас будет встречаться буквы «U» или «I», означающие ин- дустриальный температурный диапазон от -40°С до +85°С). А вот буква посереди- не, сразу после частоты, нас будет очень интересовать, потому что она означает тип корпуса (подробнее об этом рассказано в главе 4). Подчеркнем, что с точки зрения внутренней начинки, а следовательно, и програм- мирования, никакой разницы между AVR-контроллерами с разными буквенными индексами нет (упомянутый новый ATmega328PB представляет собой неприятное исключение, так что проверять все-таки надо). Основные различия относятся к по- треблению, максимальной рабочей частоте, а также отчасти к схемотехнике — микросхемы в четырехсторонних планарных корпусах TQFP имеют на четыре вывода больше, чем в двухрядных DIP. Внимательное сравнение разводки выводов в разных корпусах, однако, показывает, что никакой особой дополнительной функ- циональности лишние выводы не несут— дублируются выводы питания Vcc и GND, а также появляется пара дополнительных каналов АЦП. Интересно, что эти дополнительные каналы (ADC6 и ADC7) имеются на некоторых платах Arduino, использующих контроллер в планарном корпусе: например, они выведены на от- дельные штырьки на плате Mini, а вот на Arduino Nano отсутствуют — их место занял ISP-разъем. Кроме этих двух семейств, на базе AVR-ядра выпускаются «навороченные» AVR семейства Xmega, специализированные микросхемы с промышленным интерфей-
Глава 1. Обзор микроконтроллеров AVR сом CAN для управления ЖК-дисплеями, с беспроводным интерфейсом IEEE 802.15.4 (ZigBee) для предприятий торговли, высокотемпературные для использо- вания в автомобильной промышленности (версии Automotive). Из этого обширного перечня нас могут заинтересовать микросхемы с буквой «U» после обозначения типа — это микросхемы со встроенным USB-интерфейсом. Именно на основе тако- го контроллера (ATmega32U4) спроектирован Arduino Leonardo, отчего он при под- ключении к компьютеру может служить заменой мыши или клавиатуры. На основе подобного контроллера ATmegal6U2 также сделан встроенный адаптер USB-UART в плате Arduino Uno современной версии Rev.3.
ГЛАВА 2 Общее устройство, организация памяти, тактирование, сброс Изучение Arduino традиционно начинается с краткого ознакомления с функцио- нальностью внешних выводов и возможностями среды программирования. После этого вам предлагают написать традиционную программку мигания светодиодом, указывают на веб-ресурсы по решению типовых задач, а также на справочник по языку, и считается, что первый урок вы усвоили, — можете приступать к работе. Эта подкупающая простота оборачивается тем, что вы можете многие годы про- граммировать контроллеры Arduino и так и не узнать, как на деле реализуются функции delay о или minis о, каковы вообще возможности таймеров, для чего их еще можно использовать, и как это делать правильно, не мешая другим функциям. Поэтому мы здесь такого верхоглядства не допустим и выберем более консерва- тивный подход — начнем все-таки с общего обзора структуры AVR-контроллеров, а в следующей главе рассмотрим кратко те их возможности, которые в Arduino обычно вовсе выносятся за скобки. Практические примеры вы начнете осваивать, начиная с главы 6, так что пока наберитесь терпения — без этих предварительных сведений вообще не стоило бы затевать всю историю с ассемблером. Заметки на полях Ручаюсь, что после прочтения некоторых разделов этой главы у вас в голове образу- ется полная каша — все нюансы различий в разных моделях контроллеров запомнить нереально (хотя бы потому, что моделей 8-разрядных AVR существует около 200, и все они хоть в чем-то да различаются). Не стоит, однако, впадать в уныние — здесь вам представлена общая картина, так сказать, набросок панорамы с высоты птичьего полета. В дальнейшем (см. главу 4) мы ограничимся несколькими моделями контрол- леров, для которых детально будет расписано, что и где нужно устанавливать в тех или иных условиях эксплуатации. А руководствуясь общими сведениями из этой гла- вы, вы уже будете представлять, на что обращать внимание при необходимости ис- пользовать другие модели контроллеров. Общая структура внутреннего устройства МК AVR приведена на рис. 2.1. Здесь показаны все основные компоненты AVR (за исключением модуля JTAG, который имеется не во всех моделях, и мы его касаться не будем). В отдельных моделях
Глава 2. Общее устройство, организация памяти, тактирование, сброс 29 обоих семейств некоторые составляющие могут отсутствовать или различаться по характеристикам, неизменным остается только общее 8-разрядное процессорное ядро (GPU, General Processing Unit). Рис. 2.1. Общая структурная схема микроконтроллеров AVR Кратко опишем наиболее важные компоненты, большинство из которых мы под- робно будем изучать в дальнейшем. И начнем с памяти. В структуре AVR имеются три разновидности памяти: flash- память программ, ОЗУ (SRAM) для временного хранения данных и энергонезави- симая память (EEPROM) для долговременного хранения констант и данных. Рас- смотрим их по отдельности. Память программ Объем встроенной flash-памяти программ в 8-разрядных AVR-контроллерах со- ставляет от 0,5 кбайта у ATtiny4 до 256 кбайт у ATmega2560. Как мы уже отмеча- ли, первое число в наименовании модели (в серии Tiny — с оговорками для неко- торых младших моделей с объемом памяти менее 2 кбайт) соответствует величине этой памяти из ряда: 1, 2, 4, 8, 16, 32, 64, 128 и 256 кбайт. Память программ, как и любая другая flash-память, имеет страничную организацию (размер страницы,
30 Часть I. Общие принципы устройства и функционирования AtmelAVR в зависимости от модели, составляет от 64 до 256 байтов). Страница может про- граммироваться только целиком. Число циклов перепрограммирования достигает 10 тысяч. С точки зрения программиста память программ можно считать построенной из от- дельных ячеек — слов по 2 байта каждое. Устройство памяти программ (и только этой памяти!) по двухбайтовым словам— очень важный момент, который нужно твердо усвоить. Такая ее организация обусловлена тем, что большая часть команд в AVR имеет длину ровно 2 байта. Исключение составляют команды jmp, call и некоторые другие (например, lds), которые оперируют с 16-разрядными и более длинными адресами, — длина этих команд равна четырем байтам, и они встреча- ются лишь в моделях с памятью программ объемом свыше 8 кбайт (подробнее об этом рассказано в главе б). Во всех остальных случаях счетчик команд сдвигается при выполнении очередной команды на 2 байта (одно слово), поэтому необходи- мую емкость памяти легко подсчитать, зная число используемых ассемблерных команд (вот еще один плюс ассемблера в сравнении с языками высокого уровня). Абсолютные адреса в памяти программ (указываемые, например, в таблицах векто- ров прерываний в техническом описании МК) также отсчитываются в словах (а не в байтах!), что нужно учитывать при прямом обращении к памяти программ. Заметки на полях Приведем пример интересного случая адресации, который представляет команда для чтения констант из памяти LPM (а также ELPM в МК с памятью программ 128 кбайт и более). Эта команда подразумевает чтение по байтовому адресу, указанному в двух старших РОН (образующих т. н. регистр z, см. об этом далее). Однако, чтобы не на- рушать «чистоту» концепции организации памяти программ по словам, разработчики решили этот вопрос следующим образом: в описании команды указано, что старшие 15 разрядов регистра z адресуют слово в памяти, а младший разряд выбирает млад- ший или старший байт этого слова (при равенстве разряда 0 или 1 соответственно). Легко, однако, заметить, что байтовая и пословная организации памяти при таком подходе полностью эквивалентны. Последний адрес существующего объема памяти программ для конкретной модели обозначается константой flashend. По умолчанию все контроллеры AVR всегда начинают выполнение программы с адреса $оооо]. Если в программе нет прерыва- ний, то с этого адреса может начинаться прикладная программа. В противном слу- чае по этому адресу располагается т. н. таблица векторов прерываний, подробнее о которой мы будем говорить в главах 3 и 6. Здесь укажем лишь, что по умолчанию первым в этой таблице (по тому же адресу $оооо) почти всегда размещается вектор сброса reset, который указывает на процедуру, выполняющуюся при сбросе МК (в том числе и при включении питания). 1 В ассемблере AVR можно обозначать шестнадцатеричные числа в «паскалевском» стиле, предваряя их знаком $, при этом стиль языка С (0x00) тоже действителен, а вот «интеловский» способ (00h) не работает. В тексте книги мы будем пользоваться в основном «паскалевским» способом из-за его крат- кости. Подробнее об обозначениях чисел различных систем счисления в AVR-ассемблере рассказано в приложении 1.
Глава 2. Общее устройство, организация памяти, тактирование, сброс 31_ В последних адресах памяти программ контроллеров семейства Mega может распо- лагаться т. н. загрузчик (Bootloader) — специальная программа, которая управляет загрузкой и выгрузкой прикладных программ из основного объема памяти. В этом случае положение вектора сброса и всей таблицы векторов прерываний (т. е. фак- тически начального адреса, с которого начинается выполнение программы) может быть изменено установкой специальных конфигурационных ячеек (см. главу 5). Память данных (ОЗУ, SRAM) В отличие от памяти программ, адресное пространство памяти данных адресуется побайтно (а не пословно). Адресация полностью линейная, без какого-либо деления на страницы, сегменты или банки, как это принято в некоторых других системах. Надо отметить, что в составе семейства Tiny осталась одна модель без памяти дан- ных вообще (ATtiny28L), остальные младшие МК семейства Tiny имеют SRAM объемом от 32 байт. В других моделях объем встроенной SRAM колеблется от 128 байт в представителях семейства Tiny (например, у ATtiny2313) до 4-8 кбайт у старших моделей Mega. Как видите, ОЗУ контроллера — совсем не то, что ОЗУ компьютерных систем, где его объем измеряется гигабайтами (а если привосокупить к нему объем жестких дисков, которые формально находятся в том же пространстве памяти, то в совре- менных системах уже и терабайтами). Причем львиную часть памяти компьютеров занимают не программы, а именно данные: тексты, картинки, видео. В 8-битных контроллерах, которые для обработки таких объемных данных не предназначены, программы обычно занимают куда больший объем, чем данные. Адресное пространство статической памяти данных (SRAM) условно делится на несколько областей (рис. 2.2). Темной заливкой выделена часть, относящаяся к соб- ственно встроенной SRAM, до нее по порядку адресов расположено адресное про- странство регистров: первые 32 байта занимают регистры общего назначения (РОН), еще 64 — регистры ввода/вывода (РВВ). На рис. 2.2 показан максимальный объем SRAM, равный 8 килобайт (последний адрес $2iff— у контроллера ATmega2560, например), в большинстве моделей она меньше, но принцип остается тем же. Заметки на полях В архитектуре МК AVR понятие «ввода/вывода» употребляется в двух смыслах: во- первых, имеются «порты ввода/вывода» (I/O ports), которые мы рассмотрим далее. Во-вторых, «регистрами ввода/вывода» (РВВ, I/O registers) в структуре AVR называют- ся регистры, которые обеспечивают доступ к дополнительным компонентам, внешним по отношению к GPU, за исключением ОЗУ (в том числе и к портам ввода/вывода). Та- кое подразделение приближает структуру МК AVR к привычной конфигурации персо- нального компьютера, где доступ к любым внешним по отношению к центральному процессору компонентам, кроме памяти, осуществляется через порты ввода/вывода. У старших моделей Mega все регистры устройств, внешних по отношению к ядру, в 64 адреса не умещаются, поэтому, например, уже у ATmega88 для дополнитель- ных РВВ выделяется отдельное адресное пространство (от $60 до максимально
32 Часть I. Общие принципы устройства и функционирования AtmelAVR возможного в байтовой адресации значения $ff— итого таких дополнительных регистров может быть всего 160). Разработчики ядра в свое время не предусмотре- ли возможности этого расширения, вследствие чего такая модернизация влечет за собой изменение в способах адресации этой расширенной части регистров. Отме- тим, что в этой книге — чтобы не усложнять программы — мы намеренно не будем использовать моделей контроллеров с расширенным количеством регистров, хотя о способах их адресации, разумеется, расскажем (см. главу 7). 32 регистра общего назначения (R0-R31) 64 регистра ввода/вывода 0 ДОПОННИТйЛЬЙЫХ Внутреннее стати чес jioe ОЗУ (SRAM) Внешняя память данных $0000 $001F $0020 $005F $0060 $OOFF $0100 Рис. 2.2. Адресное пространство статической памяти данных (SRAM) микроконтроллеров AVR $025F-$21FF $0260-$2200 $FFFF Для некоторых моделей Mega (ATmega8515, ATmega64, ATmegal61, ATmegal28, ATmega2560 и др.) предусмотрена возможность подключения внешней памяти SRAM объемом до 64 кбайт (конечный адрес $ffff) с параллельным интерфейсом, так что она становится продолжением внутренней памяти. В настоящее время го- раздо удобнее применять для хранения данных внешнюю память с последователь- ным интерфейсом (аналогично тому, как в компьютерных системах хранят данные на внешних дисках— см. главу 12), потому вопросы о подключении внешней параллельной памяти мы обойдем. Отметим, что адреса РОН и РВВ не отнимают пространство у ОЗУ данных — так, если в конкретной модели МК имеется 512 байт SRAM, а пространство регистров
Глава 2. Общее устройство, организация памяти, тактирование, сброс 33_ занимает первые 96 байт (до адреса $бо), то адреса SRAM займут адресное про- странство от $0060 до $025F (т. е. от 96-й до 607-й ячейки включительно, что и со- ставит ровно 512 байт). Конец встроенной памяти данных обозначается константой ramend, которая для каждой модели контроллера имеет свое значение. Не очень зна- чимое исключение представляет адресация подключаемой внешней памяти упомя- нутых ранее моделей Mega — т. к. ее максимальный адрес ограничен значением $ffff (65535), то пространство, занимаемое регистрами, вычитается из этой вели- чины. Операции чтения/записи в память одинаково работают с любыми адресами из дос- тупного пространства, и с этим связано несколько нюансов при употреблении ассемблерных команд. При работе с SRAM нужно быть внимательным — вместо записи в память вы легко можете «попасть» в какой-нибудь регистр. Например, команда загрузки значения регистра г1б в регистр г о (mov ro,ri«6) равносильна записи в SRAM по нулевому адресу (sts $oooo,ri6), т. к. адрес в памяти для РОН совпадает с его номером. Для того чтобы записать что-то в первую ячейку именно SRAM, необходимо обратиться по адресу $60 (а в контроллерах с дополнительными регистрами— по адресу $юо). Чтобы не заниматься вычислениями для каждого контроллера, первый адрес памяти SRAM определяется константой sramstart. В то же время для непосредственной записи в РВВ по его адресу в памяти к номеру регистра следует прибавить $20— так, регистр флагов sreg, который для многих моделей располагается в конце таблицы РВВ по адресу $3F, в памяти имеет адрес $5F. Устанавливать РВВ прямой адресацией памяти необходимо лишь при наличии этих самых дополнительных регистров, а в остальном пользоваться ей неудобно — такая запись всегда отнимает 2 такта вместо одного, характерного для большинства других команд (хотя иногда это позволяет обойти ограничения на манипуляции с некоторыми РОН и РВВ). Именно поэтому мы постараемся здесь не пользоваться контроллерами с расширенным количеством регистров — увлеченному ассемб- лерщику, возможно, все эти нюансы греют душу, а нормального человека они только запутывают. В наших программах мы будем обращаться к РОН и РВВ с помощью специально предназначенных для этого команд, где абсолютные адреса скрыты за мнемониче- скими обозначениями регистров. Однако забывать о наличии всех этих нюансов не следует — так, при переносе готовой программы, работающей с SRAM, на старшие модели контроллеров нужно быть внимательным из-за того, что в них младшие адреса SRAM могут перекрываться дополнительными РВВ. Энергонезависимая память данных (EEPROM) Все модели МК AVR (кроме самых младших представителей Tiny) имеют встроен- ную EEPROM для хранения констант и данных при отключении питания. В разных моделях объем ее варьируется от 64 байт (ATtinyH) до 4 кбайт (старшие модели Mega). Конец EEPROM обозначается константой eepromend, причем, если этой па- мяти нет вовсе, константа будет равна нулю. Число циклов перепрограммирования EEPROM может достигать 100 тысяч.
34 Часть I. Общие принципы устройства и функционирования AtmelAVR EEPROM отличается от Flash возможностью выборочного программирования по- байтно (вообще-то даже побитно, но здесь этот способ недоступен пользовате- лю, — подробно об этом рассказано в главе 12). Для единообразия технологическо- го процесса EEPROM в AVR-контроллерах просто эмулируется за счет части flash- памяти, отсюда некоторые ее особенности в сравнении с «обычной» EEPROM (на- пример, то, что запись в EEPROM не может выполняться одновременно с записью во flash-память). Но на практике, как при загрузке по последовательному каналу (т. е. через SPI-интерфейс программирования), так и при записи или чтении EEPROM из программы, эта особенность не имеет значения, и доступ осуществля- ется побайтно. Чтение из EEPROM формально осуществляется в течение одного машинного цикла (правда, на практике оно растягивается на 4 цикла, во время которых CPU просто останавливается, но программисту следить за этим специально не требуется). А вот запись в EEPROM протекает значительно медленнее и к тому же с разной ско- ростью у разных моделей— цикл стирания/записи может занимать от ~3,4 до -8,5 мс (в среднем меньшая величина у Tiny и более новых Mega, и большая — у старых Mega, но если эта величина критична, то необходимо ее уточнять для каждой конкретной модели). К тому же процесс записи регулируется встроенным RC-генератором, частота которого нестабильна (при более низком напряжении пи- тания можно ожидать, что время записи будет больше). За такое время при обыч- ных тактовых частотах МК успевает выполнить несколько тысяч команд, так что программирование процедуры записи требует аккуратности— например, нужно следить, чтобы в момент записи не «вклинилось» прерывание (подробнее об этом рассказано в главах 6 и 8). Главная же сложность при работе с EEPROM — возможность повреждения ее со- держимого при недостаточно быстром снижении напряжения питания в момент выключения. Обусловлено это тем, что при уменьшении напряжения питания до некоторого порога (ниже порога стабильной работы, но недостаточного для полно- го выключения) из-за колебаний напряжения МК начинает выполнять произволь- ные команды, в том числе может осуществить процедуру записи в EEPROM. Если учесть, что типовая команда МК AVR выполняется за десятые доли микросекунды, то ясно, что никакой реальный источник питания не может обеспечить снижение напряжения до нуля за нужное время. В экспериментах автора с семейством Classic при питании от обычного стабилизатора типа LM7805 с рекомендованными значе- ниями емкости конденсаторов на входе и на выходе содержимое EEPROM неиз- бежно портилось примерно в половине случаев. В современных семействах Mega и Tiny вероятность порчи куда ниже, в основном из-за наличия встроенного монито- ра питания (Brown-out Detector, BOD — см. о нем далее в этой главе), но тоже не равна нулю, отчего в критичных случаях приходится принимать дополнительные меры (подробнее об этом рассказано далее — в разд. «Сброс» этой главы, а также в главе 10). Отметим, что, хотя в документации это не отражено, вероятность порчи содержи- мого, видимо, резко снижается, если в программе отсутствуют процедуры записи в EEPROM, а константы записывают в нее только при программировании МК.
Глава 2. Общее устройство, организация памяти, тактирование, сброс 35 Большая сохранность данных в таких случаях подтверждается и эмпирическими наблюдениями, и тем, что разрешение записи в EEPROM — процедура двухсту- пенчатая, и случайное возникновение такой последовательности команд практиче- ски исключено. Способы тактирования За тактирование в основном отвечают конфигурационные ячейки (они же fuse- биты) cksel, но их количество и комбинации включения у разных моделей могут различаться очень существенно, потому их всегда нужно проверять по документа- ции (хорошо помогают уже упомянутые ранее пособия Евстифеева [6,7]). Канонический способ тактирования МК— подключение кварцевого резонатора к соответствующим выводам XTAL1 и XTAL2 (рис. 2.3, а). Емкость конденсаторов С1 и С2 в типовом случае должна составлять 15-22 пФ (может быть увеличена до 33-47 пФ с одновременным повышением потребления). Кварцевый резонатор (для краткости его часто называют просто «кварцем») также можно заменить керамиче- ским. Такой возможностью сейчас, вероятно, уже мало кто пользуется — это когда- то керамические резонаторы стоили принципиально дешевле более стабильных кварцевых, и замена имела смысл. Все эти возможности при резонансных частотах кварца от 0,4 МГц до максимальной тактовой частоты контроллера у всех моделей Tiny и Mega реализуются одинаково, и у разных моделей различаются лишь спосо- бом задания режима. не подключен сигнал от внешнего генератора яг — XTAL2 XTAL1 Рис. 2.3. Способы тактирования МК AVR с использованием внешних элементов: а — кварцевого резонатора; б— внешнего генератора; в — RC-цепочки Подробности У многих контроллеров Меда предусмотрена возможность работы от «часового» квар- ца (частотой 32,768 кГц), для чего надо соответствующим образом выставить конфи- гурационные ячейки. Схема ничем не отличается от рис. 2.3, а, за исключением того, что рекомендуемая емкость конденсаторов С1 и С2 здесь равна 36 пФ. Если вам за- чем-то это понадобится, то проверяйте наличие этой возможности по документации (раздел «даташита» должен называться Low-frequency Crystal Oscillator) — например, у ATtiny2313 она отсутствует, и в низкочастотном режиме эта модель может быть
36 Часть I. Общие принципы устройства и функционирования Atmel AVR включена только от встроенного или внешнего генератора. Режим работы от «часового» кварца может понадобиться лишь в исключительных случаях — потребление кристал- ла при этом падает менее, чем до 100 мкА, оказывается удобно отсчитывать проме- жутки времени с большой точностью, но и множество функций остаются за пределами возможностей (например, при такой частоте невозможно «завести» последовательный порт UART со стандартными значениями скорости обмена). Заметим, что у некоторых моделей (например, у ATtiny2313— как бы в компенсацию невозможности подключе- ния «часового» кварца) имеется еще отдельный внутренний генератор 128 кГц. В та- ком режиме потребление тоже падает примерно до 100 мкА, но к нему также относят- ся все оговорки относительно работы на «часовой» частоте. Более подробно целе- сообразность эксплуатации контроллеров на сверхнизких частотах мы рассмотрим в разд. «Потребление MKAVR» главы 4. В большинстве моделей Mega имеется специальный конфигурационный бит скорт, который позволяет регулировать потребление. При установке этого бита в 1 (неза- программированное состояние) размах колебаний генератора уменьшается, однако при этом сужается возможный диапазон частот и снижается общая помехоустойчи- вость, поэтому при высоких частотах рекомендуется сменить этот режим на пол- ный размах (скорт = о). Автору этих строк удавалось запускать МК на нестандартных частотах, используя вместо кварца в том же подключении миниатюрную индуктивность (при ее значе- нии 4,7 мкГн и емкостях конденсаторов 91 пФ частота получается около 10 МГц). Заметки на полях При тактировании от кварцевого резонатора возникает законное желание получить максимальную точность отсчета промежутков времени. При этом надо учитывать, что кварцевые резонаторы бывают очень разные. Погрешность порядка 10 , которую поч- ти гарантированно можно получить от любого кварца, кажется очень маленькой, и для огромного большинства применений это так и есть — ни одну физическую величину, кроме времени, вы в домашних условиях с такой точностью (0,01%) не измерите. Но сама по себе эта величина погрешности не так уж и хороша — часы с такой точностью будут уходить на минуты в месяц, что уже почти неприемлемо. Так что при необходи- мости получить более высокую точность смотрите, что приобретаете, — при прочих равных можно ожидать, что кварц в полноразмерном корпусе HC-49U будет точнее и стабильнее, чем в обычном низком «гробике» HC-49S (проверить это можно по доку- ментации на конкретную марку). Кроме того, стабильность зависит от схемы генерато- ра, над которой вы тут не властны. Поэтому, чтобы гарантированно получить точный отсчет времени, лучше употреблять не простые кварцевые резонаторы, а готовые прецизионные кварцевые генераторы, которые гарантируют точность и стабильность частоты (самые популярные выпускает фирма Epson). Вторая возможность тактирования, наличествующая у всех без исключения совре- менных Tiny и Mega, — от встроенного RC-генератора (Internal RC Oscillator). Она не требует внешних элементов, потому включена по умолчанию. Для работы от внешнего кварца следует не забывать перестроить конфигурацию fuse-битов, иначе вы можете быть весьма удивлены результатами работы, например, таймеров. А вот возможные частоты этого встроенного генератора могут отличаться у разных моде- лей. У «наших» моделей Mega (т. е. тех, на которые мы будем преимущественно ориентироваться в этой книге) — можно выбирать из ряда: 1, 2, 4 и 8 МГц (1 МГц по умолчанию), у Tiny2313 — из двух значений: 4 и 8 МГц (8 МГц по умолчанию).
Глава 2. Общее устройство, организация памяти, тактирование, сброс 37^ А в других случаях значения могут быть сильно различаться — например, у «лю- бимого» ардуиновского ATmega328 (как и его аналогов с меньшим объемом памя- ти Mega88 и Mega 168) доступна только одна встроенная частота 8 МГц, зато есть некоторые модели (весьма редкие), у которых встроенный генератор может рабо- тать на частоте 20 МГц и на «часовой» частоте 32,768 кГц. Обратите внимание, что у многих контроллеров при тактировании от встроенного генератора выводы XTAL1 и XTAL2 могут использоваться как дополнительные выводы обычных портов. У ATtiny2313, например, это два вывода'в остальном от- сутствующего порта А (РАО и РА1), у ATmega8 — дополнительные выводы порта В (РВ6 и РВ7) и т. д. Особенно выгодно это использовать у младших Tiny с малым количеством контактов корпуса. На практике следует ожидать, что у всех моделей, вне зависимости от частоты ге- нератора, тактовая частота «чистого» кристалла по умолчанию будет равна 1 МГц, Дело в том, что даже у тех контроллеров, у которых частота встроенного генератора не может быть установлена равной 1 МГц (ATtiny2313, ATmega328 и т. п.), имеется конфигурационная ячейка под названием ckdiv8, которая по умол- чанию запрограммирована (установлена в нулевое значение), что означает деление частоты генератора на 8. Не забывайте ее переустановить перед использованием внешнего резонатора (отметим, что в русскоязычных пособиях Евстифеева [6,7] этот момент отражен нечетко, так что всегда смотрите документацию на конкрет- ный контроллер!). У некоторых моделей (ATtiny2313) имеется также внутренний делитель тактовой частоты (регистр clkpr), с помощью которого частоту тактиро- вания можно менять программно. По идее эта возможность работает при любом способе тактирования (так можно, например, регулировать потребление контролле- ра), но более актуальна она при тактировании от встроенного генератора, частоту которого саму по себе мы изменить не можем. Частоту встроенного генератора, которая и сама по себе имеет разброс от одного кристалла к другому и «плавает» от изменения внешних условий (температуры и напряжения питания), можно регулировать весьма в широких пределах. Но пользо- ваться возможностями такой калибровки нужно очень осмотрительно — докумен- тация не рекомендует менять частоту более, чем на 10% от номинальной, иначе мо- гут быть сложности, например, с записью во flash- и EEPROM-память. Для обыч- ных применений заводской калибровки вполне достаточно, и усложнять себе жизнь, пытаясь стабилизировать параметры заведомо нестабильной схемы, не име- ет смысла — проще подключить стабильный внешний кварц или, если очень надо, готовый прецизионный генератор. Заметки на полях В одной из публикаций на сайте, посвященном конструированию всяческих автомо- бильных прибамбасов, мне встретились жалоба на отказы встроенного генератора Tiny2313 при температурах ниже примерно -20 °С и указание на то, что при этом по- могает установка обычного внешнего кварца. В документации на контроллеры про это ничего не сказано, наоборот, приведен график изменения частоты RC-генератора от температуры вплоть до граничного рабочего значения минус 40 °С (причем при сни- жении температуры частота у некоторых моделей повышается, у других — снижается, но эти изменения не очень существенные, на 2-3% от среднего значения). Потому
38^ Часть I. Общие принципы устройства и функционирования AtmelAVR есть подозрение, что контроллер тут ни при чем, но в домашних условиях досконально проверить это нереально — нужны лабораторные климатические испытания при низ- ких температурах. Можно только тщательно проанализировать схему на предмет ра- ботоспособности всех компонентов при таких температурах и проверить надежность паек, которые при температурной деформации могут отходить. И все-таки стоит дер- жать возможность отказов в уме и по возможности проводить натурные испытания — не во всех реальных случаях можно теоретически учесть все возможные сочетания причин. Все модели AVR также имеют возможность тактирования от внешнего генератора (рис. 2.3, б), ограниченного только значением максимальной частоты самого кон- троллера. Сигнал, разумеется, должен соответствовать требованиям к уровням и таймингам сигналов, установленных для контроллера. На практике эта возмож- ность может быть востребована при необходимости строго синхронизировать рабо- ту нескольких контроллеров, а также для получения строго определенной тактовой частоты от внешнего прецизионного генератора. Отметим, что случайная установка в режим работы от внешнего генератора (этот режим у всех AVR задается одинаково — все имеющиеся ячейки cksel переводятся в нулевое значение) делает невозможным внутрисхемное программирование кон- троллера. Эта распространенная ошибка новичков — следствие запутанной терми- нологии в установках конфигурационных ячеек и, как следствие, разнобоя их уста- новок в разных программаторах. Подробнее этот вопрос мы обсудим в главах 4 и 5, а здесь заметим, что паника в этом случае совершенно необоснованна, — если кри- сталл перестал откликаться, соберите любой внешний генератор из деталей, кото- рые есть под рукой (подробнее об этом рассказано в главе 5). А вообще-то все рав- но следует обзавестись готовым цифровым генератором (о чем речь пойдет в гла- ве 4) — он и в работе с Arduino требуется очень часто. Еще одна возможность тактирования связана с использованием внешней RC-це- почки, подключаемой к выводу XTAL1 (рис. 2.3, в). Заметим, что этот способ дос- тупен не у всех моделей (в частности, у ATtiny2313 такая возможность отсутству- ет). Он задействуется в случаях, когда стабильность частоты не важна, а возможно- сти встроенного генератора по какой-либо причине вас не устраивают. Частота при таком подключении рассчитывается по примерной формуле f ~ 1/3RC. Из ограни- чений на элементы R и С (R в диапазоне 3,3-100 кОм, минимальное значение С = 22 пФ) вытекает, что максимальная частота, достижимая таким способом, равна примерно 5 МГц. Заметки на полях Интересное применение этого способа тактирования МК — динамическая подстройка частоты в системах измерения, регулирования и контроля. Например, когда ваш МК задействован в системе управления синхронным двигателем или генератором, рабо- тающим с частотой бытовой сети, может потребоваться точная синхронизация управ- ляющих сигналов с сетевой частотой 50 Гц. В этом случае несложно заменить сопро- тивление R на регулируемый извне преобразователь «частота — значение тока» и получить простую в налаживании аналоговую автоподстройку вместо долгой возни с алгоритмами цифрового регулирования частоты и фазы. Разумеется, при точно не известной и тем более переменной тактовой частоте некоторые функции МК будут не- доступны (передача по UART, например), но в таких ситуациях они, как правило, и не требуются.
Глава 2. Общее устройство, организация памяти, тактирование, сброс 39_ Отметим еще для галочки, что в некоторых моделях (ATmega8, ATmega8535 и др.) имеются внутренние конденсаторы 36 пФ, которые можно подключить, если запи- сать логический ноль в конфигурационную ячейку скрот. Они оказываются под- ключенными между выводами XTAL1 и XTAL2 и «землей», подобно конденсато- рам на схеме рис. 2.3, а. Иными словами, их можно использовать в схемах с низко- частотным кварцем или в схеме с внешней RC-цепочкой, а вот в основной схеме с кварцевым резонатором их лучше не употреблять, т. к. номинал их почти вдвое выше рекомендуемого (от этого может заметно возрасти потребление и генератор будет перегружаться). Способ этот не универсальный (во многих моделях контрол- леров эти конденсаторы отсутствуют), да и сами способы тактирования, где он при- годен, достаточно экзотичные, поэтому мы его употреблять не будем. Неправильным будет также не упомянуть, что во многих моделях (из «наших» это ATtiny2313) имеется предделитель тактовой частоты, управляемый программно битами clkps (не путать с fuse-битами ckdivs и cksel, управляющими собственно значением частот генератора и устанавливаемыми до загрузки программы). В ATtiny2313 можно поделить частоту генератора по всему двоичному ряду от 1 до 256. Такое может понадобиться, например, если от внутреннего генератора, ко- торый обычно сам по себе работает на частоте 8 МГц, захочется получить некую частоту тактирования, отличную от «умолчательной» 1 МГц. Возможно, у кого-то возникнет искушение попробовать работу на супернизких частотах — при исход- ных 8 мегагерцах деление на 256 позволяет получить тактовую частоту меньше 32 кГц. Для КМОП-схем это означает практическое выключение: потреблять будет только сам генератор, что, впрочем, может быть тоже немало — при 5 вольтах пи- тания он потребляет около 50 мкА. Сброс Сбросом (RESET) называется установка начального режима работы МК (аппарат- ная или, как это раньше называли, «холодная» перезагрузка). При этом все РВВ устанавливаются в состояние по умолчанию — как правило, это нули во всех раз- рядах за небольшим исключением. На практике то же самое относится и к РОН (как и к ячейкам SRAM)— они по сбросу обнуляются, но не случайно этот момент никак не оговорен официально. В некоторых случаях (например, после выхода из «сна» — об этом см. главу 14) РОН и ячейки SRAM после сброса могут принимать произвольные значения, поэтому при необходимости обязательно начинать с ка- кой-то определенной величины переменные следует устанавливать в начале про- граммы принудительно. Программа после аппаратного сброса начинает выполняться не сразу, а с некоторой задержкой (см. описание fuse-битов suto и suti в главе 5). Выполнение происходит с заданного начального адреса (по умолчанию это адрес $оооо), что позволяет при желании организовать софтверную («горячую») перезагрузку изнутри программы, просто организовав безусловный переход на нулевой адрес. При этом все регистры и память сохраняют свое состояние на момент сброса. Правда, зачем такое может понадобиться, я так и не придумал — сторожевой таймер в этом качестве задейст- вовать куда удобнее.
j40 Часть I. Общие принципы устройства и функционирования Atmel AVR А вот при наличии программы-загрузчика контроллер обращается сначала к нему, и запуск откладывается на время ожидания, — а вдруг извне придет команда на перезапись программы? Подробно об этом рассказано в главе 6, а здесь только от- метим, что время ожидания может составлять несколько секунд, что иногда бывает критично, — еще одно ограничение, присущее в том числе и платформе Arduino. Сброс всегда происходит при включении питания. Кроме этого, источниками сбро- са могут быть следующие события: □ аппаратный сброс, т. е. подача низкого уровня напряжения на вход RESET. По- скольку активный уровень сброса у всех микросхем всегда низкий (исключения мне неизвестны), то правильнее его обозначать с инверсией: RESET. Если вы встретили такое обозначение, то речь идет именно о выводе сброса, просто функция сброса, понятно, именуется без всякой инверсии; □ — окончание отсчета установленного интервала сторожевого таймера; □ — срабатывание схемы BOD. Подробности Значение четырех младших битов регистра состояния mcucsr должно сигнализировать о том, от какого источника производился сброс предыдущий раз (установка в 1 бита о — сброс при включении, бита 1 — аппаратный сброс, бита 2 —■ от схемы ВСЮ, бита 3 — от сторожевого таймера). На практике, по опыту автора, по состояниям этого ре- гистра надежно отличаются от всех остальных лишь состояния сброса по сторожево- му таймеру (прочие флаги могут оказаться установленными все одновременно). Тем не менее, эта информация может быть полезной, например, при анализе причин пе- рерывов в работе круглосуточно работающих устройств (см. главу 14). В младших МК семейства Tiny (кроме ATtiny28L) нет встроенного «подтягиваю- щего» резистора на выводе RESET, поэтому для надежной работы следует преду- смотреть подключение внешнего резистора величиной 2-10 кОм от этого вывода к напряжению питания. Автор также настоятельно рекомендует устанавливать по- добный резистор для любых моделей AVR, т. к. встроенный резистор имеет часто большой номинал (30-60 кОм), и на нем могут наводиться помехи, способные при- вести к непредсказуемому сбросу. Не помешает также (хотя в технических описаниях такой рекомендации и не со- держится) установка конденсатора 0,1-1 мкФ от вывода RESET на «землю» — это сглаживает неизбежный дребезг напряжения и при включении немного затягивает фронт нарастания напряжения на выводе RESET по сравнению с увеличением на- пряжения питания — когда наступит порог срабатывания схемы сброса, напряже- ние питания всего МК уже установится. В пользу этой рекомендации говорит также факт, что на всех платах Arduino такая внешняя цепочка 10 кОм/0,1 мкФ по выводу RESET обязательно имеется совместно с кнопкой ручного сброса. Впрочем, если питание организовано надлежащим образом, то нарушение этой рекомендации не приводит к каким-то фатальным последствиям. Конденсатор можно не устанавли- вать также, если за сбросом следит внешний монитор питания (супервизор — под- робнее о нем рассказано далее).
Глава 2. Общее устройство, организация памяти, тактирование, сброс 41 Во многих моделях контроллеров, если не требуется внешний сброс, вывод RESET может выполнять функции обычного порта ввода/вывода (подобно выводам такти- рования XTAL1 и XTAL2 — о них подробно рассказывалось ранее). С одним толь- ко нюансом — обычно для такого применения приходится режим вывода специ- ально переключать (в отличие от XTAL1 и XTAL2, которые переключаются авто- матически при выборе режима тактирования и подключении к ним внешнего резонатора). У некоторых младших Tiny вывод RESET включен в режим обычного порта по умолчанию. У ATtiny28L при конфигурировании этого контакта на выход он работает как вывод с открытым коллектором, а не как обычный логический эле- мент (о конфигурации выводов портов рассказано в главе 3). Для корректного сброса при включении и выключении питания служит встроенная схема BOD (Brown-out Detector), которая обеспечивает время срабатывания поряд- ка микросекунд с задержкой на возврат в рабочее состояние после восстановления напряжения, определяющейся теми же установками, что и задержка сброса (конфи- гурационные ячейки ckselo и sut). Для выбора режима работы BOD служат конфи- гурационные ячейки bodlevel, с помощью которых можно установить порог сраба- тывания около 4,0 В (в некоторых моделях около 4,3) для питания 5 вольт или око- ло 2,6-2,7 В для питания 3-3,3 вольта. Для предотвращения дребезга BOD имеет гистерезис по напряжению величиной около 50-150 мВ, а также задержку включе- ния около 2 мкс и выключения с типовым значением около 4-5 мс и максимально возможным порядка 65-69 мс. BOD вполне работает при штатных источниках питания — стабилизаторах с низ- ким выходным сопротивлением и рекомендованными емкостями конденсаторов. Но его характеристики могут оказаться недостаточными для обхода дребезга, воз- никающего при снижении напряжения питания всяких других источников (напри- мер, батареек, ведущих себя при истощении заряда очень по-разному, — см. врезку «Подробности» далее). Кроме того, схема BOD повышает потребление в режимах энергосбережения, и документация рекомендует ее в таких случаях отключать (экономия должна составить около 20-30 мкА). Поэтому самый надежный способ организации сброса при включении и выключении питания — установка внешнего монитора питания (супервизора), который подает сигнал сброса на вывод RESET контроллера в момент снижения напряжения питания ниже допустимого порога и снимает его при повышении напряжения обычно с некоторой задержкой. Обратим внимание, что BOD работает от того же самого встроенного опорного ис- точника (ИОН) 1,1-1,3 В, к которому может подключаться аналоговый компаратор и АЦП, и BOD наследует все связанные с этим ошибки (подробно об этом см. в гла- вах 3 и 70). Самым популярным монитором питания при питании 5 В когда-то был МС34064, но он уже решительно устарел по одному только уровню потребления (0,5 мА), к тому же имеет неоптимальный порог срабатывания 4,6 вольта. Поэтому лучше использовать более современные компоненты. Выпускают мониторы питания очень многие фирмы в неисчислимом количестве разновидностей, и нужно быть очень внимательным, чтобы не промахнуться, — есть супервизоры, у которых ак-
42 Часть /. Общие принципы устройства и функционирования Atmel AVR тивный уровень RESET высокий, а не низкий, их применительно к AVR придется включать с инвертирующим транзистором, что, конечно, лишнее. При 5-вольтовом питании подойдет, например, микросхема DS1813-15 с порогом срабатывания 4,1 В и потреблением ЗОмкА, при трехвольтовом— DS1818-20 (2,6 В и 35 мкА), оба в корпусах ТО-92. Есть также очень хорошие супервизоры МСР102/103 фирмы Microchip с потреблением 1 мкА, но их сложно достать в удобных корпусах (ТО-92 или DIP-8). Упомянутые марки самые простые— трехвыводные (питание, «земля», вывод управления сбросом). Супервизоры DS1813 и DS1818 заменяют RC-цепочку на вы- воде RESET контроллера и имеют возможность корректной работы совместно с ручной кнопкой Reset. МСР 102/103 имеют выход с открытым коллектором, пото- му требуют подтягивающий резистор, конденсатор RC-цепочки при этом оказыва- ется ненужным. Типовое время срабатывания этих микросхем при снижении на- пряжения— микросекунды, что обеспечивает сохранность данных в EEPROM. При повышении напряжения они создают достаточную временную задержку (по- рядка 0,15-0,25 секунды), что позволяет надежно осуществлять сброс МК без дре- безга. Конечно, есть и куда более «навороченные» супервизоры питания — с двумя порогами, с дополнительными таймерами и т. д. Подробности Еще один плюс применения внешнего монитора заключается в том, что можно подоб- рать оптимальный порог срабатывания под конкретный случай. Гальванические эле- менты имеют пологую кривую разряда с повышенным внутренним сопротивлением вблизи точки полного истощения, поэтому оставлять МК на произвол судьбы при ба- тарейном питании нехорошо даже при отсутствии взаимодействия с такими критичны- ми компонентами, как EEPROM, — кто знает, какие произвольные команды начнут при этом выполняться? Но с точки зрения оптимального расхода энергии автономных ис- точников — батареек и аккумуляторов — величина раз и навсегда заданного порога срабатывания монитора питания, да еще и различающегося от модели к модели, мо- жет быть далеко не лучшим выбором. Щелочные элементы имеют начальное напря- жение 1,6 вольта и конечное около 1,0 вольта, ниже этого значения у них непредска- зуемо растет выходное сопротивление. Поэтому питание 5 вольт удобно организовать из четырех таких элементов с дополнительным стабилизатором, ограничивающем общее начальное напряжение на уровне 5 вольт и монитором питания, рассчитанным на 4,0 вольта, что гарантирует достаточно полное исчерпание энергии батареек, — в этом идеальном случае уровни встроенной схемы BOD вполне на высоте. На прак- тике встроенной BOD следует избегать по другой причине — большого разброса поро- га от экземпляра к экземпляру, отчего приходится калибровать схему каждый раз ин- дивидуально. А вот если вы хотите непременно обойтись без стабилизатора, то вам придется ста- вить 3 элемента (4,8 В максимум) и величина порога BOD 2,7 вольта (0,9 вольта на элемент) маловата — из-за пологой характеристики и высокого выходного сопротив- ления элементов питания схема может начать вести себя непредсказуемо задолго до достижения минимально допустимого напряжения. Еще хуже ситуация при использо- вании литиевых элементов, которые хороши из-за пологой характеристики и мини- мального саморазряда— для них величины порога 2,7 (для элементов-«монеток» с номинальным напряжением 3 вольта) или 4,0 (для трех «пальчиковых» элементов с номинальным напряжением 1,5 вольта каждый) будут уже неприемлемо низкими, глубоко заходящими в зону полной неработоспособности элемента. Подробнее о при- менении мониторов питания в этих случаях рассказано в главе 10.
ГЛАВА 3 Периферийные устройства и прерывания Периферийными в структуре AVR называются все устройства, внешние по отноше- нию к ядру и памяти. В «компьютерных» процессорах такие устройства, как прави- ло, реализуют в виде отдельных микросхем (входящих в состав чипсетов) или целых модулей на отдельных платах. Общее для «computer-on-chip» и «обычных» вычислительных систем свойство периферийных устройств — их переменный со- став, т. е. в разных системах (для МК — в разных моделях контроллеров) те или иные компоненты могут отсутствовать. Наиболее популярные периферийные устройства (таймеры, порты UART и SPI, аналоговый компаратор или сторожевой таймер) имеются практически во всех моделях, но это не значит, что вы их обязательно должны задействовать. Тем не менее их номенклатуру следует учитывать при выборе той или иной модели. Во- первых, наличие большого числа периферийных устройств, даже если они не задействованы, увеличивает общее потребление микросхемы, во-вторых, нет ника- кого резона ставить ATmega2560 в 100-выводном корпусе там, где достаточно ATtiny2313 в корпусе с 20-ю выводами. Кроме того, некоторые периферийные уст- ройства приходится принимать во внимание даже в том случае, если они не исполь- зуются — типичным примером может служить аналоговый компаратор, который по умолчанию всегда включен и потому может оказывать влияние на потребление в «спящем» режиме (хотя и мизерное). Все периферийные устройства адресуются через регистры ввода/вывода (РВВ, I/O registers), аналогичные портам ввода/вывода в архитектуре IBM PC. Отметим, что в PIC-архитектуре взаимодействие с периферийными устройствами организо- вано проще — там можно заносить данные непосредственно в порты, без промежу- точного переноса значения РВВ в РОН и обратно с помощью команд in и out. В AVR прямая модификация значений РВВ ограничена и обставлена рядом усло- вий, но в целом это не приводит к значительным трудностям — программы для AVR в общем все равно получаются более эффективными. Сложности добавляет факт, что РВВ в новых моделях контроллеров перестали умещаться в первоначально отведенном для них пространстве адресов емкостью 64 байта (от $20 до $5f). Как мы отмечали ранее, уже у ATmega88 для дополнитель- ных РВВ пришлось выделить отдельное адресное пространство (от $60 до макси-
44 Часть I. Общие принципы устройства и функционирования AtmelAVR мально возможного в байтовой адресации значения $ff). Для регистров в этом до- полнительном пространстве не работают многие удобные команды: in/out, sbi/cbi и т. п., и их можно адресовать только универсальным способом — как ячейки па- мяти. Между тем в это пространство могут «переехать» не только регистры допол- нительных по отношению к младшим моделям устройств, но и многих из привыч- ного состава периферии: регистры управления UART, Timerl и Timer2, TWI и т. д. Чтобы не создавать лишних сложностей при изучении ассемблера, мы не станем пользоваться такими моделями контроллеров, — как вы увидите, для львиной доли любительских задач они просто не потребуются. Но, конечно, далее (см. главу 6) я расскажу в подробностях, как и чем можно заменить привычные команды при переходе к более «продвинутым» моделям, чтобы вы не потерялись при возникно- вении такой задачи, — ведь среди них, между прочим, и ардуиновская ATmega328. В отличие от «регистров ввода/вывода», в архитектуре AVR, как и в других архи- тектурах МК, термин «порты ввода/вывода» (I/O ports) обозначает параллельные порты для обмена данными с внешними устройствами. С них мы и начнем рас- смотрение периферийных устройств. Порты ввода/вывода Портов ввода/вывода в разных моделях может быть от 1 до 7. Номинально все пор- ты 8-разрядные, в некоторых случаях разрядность ограничена числом выводов корпуса и может быть меньше восьми. Порты обозначаются буквами А, В, С, D и далее, причем необязательно по порядку — в младших моделях могут наличест- вовать, например, только порты В и D (как в ATtiny2313) или вообще только один порт В (как в младших Tiny). Для сокращения числа контактов корпуса в подавляющем большинстве случаев внешние выводы, соответствующие портам, кроме своей основной функции (дву- направленного ввода/вывода) несут также и дополнительную. Отметим, что ника- кого специального переключения выводов портов не требуется — просто, если вы, к примеру, в своей программе инициализируете последовательный порт UART, то соответствующие выводы RxD и TxD (в ATmega8 они совпадают с выводами PD0 и PD1 порта D) будут работать именно в альтернативной функции — как ввод и вы- вод UART. При этом в промежутках между таким специальным использованием выводов они остаются доступны как обычные двунаправленные выводы. На прак- тике при одновременном использовании приходится применять схемотехнические меры для изоляции функций друг от друга, поэтому злоупотреблять этой возмож- ностью не рекомендуется, — особенно это критично для выводов того порта (обычно это порт А), к которому подсоединены входы АЦП. Более того — при на- личии АЦП не рекомендуется задействовать для выполнения логических функций другие выводы того же порта (которые в работе АЦП не участвуют), потому что это значительно увеличивает уровень помех (подробнее об этом рассказано в этой главе далее, а также в главе 11). Выводы портов в достаточной степени автономны, и их режим может устанавли- ваться независимо друг от друга. По умолчанию при включении питания все до-
Глава 3. Периферийные устройства и прерывания 45_ полнительные устройства отключены, а порты работают на вход, причем находятся в состоянии с высоким импедансом (т. е. высоким входным сопротивлением — та- кой режим порта еще называют «третьим» состоянием). Работа на выход требует специального указания, для чего в программе нужно установить соответствующий нужному выводу бит в регистре направления данных (этот регистр обозначается ddrx, где х — буква, обозначающая конкретный порт. Например, для порта А это будет ddra). Если бит сброшен (т. е. равен логическому 0), то вывод работает на вход (установка по умолчанию), если установлен (т. е. равен логической 1) — то на выход. Для установки выхода в состояние 1 нужно отдельно установить, а для установки в 0 — сбросить, соответствующий бит в регистре данных порта (обозна- чается portx). Направление работы вывода (вход-выход, регистр ddrx) и его состоя- ние (0-1, portx) путать не следует. В Arduino этим установкам соответствуют функции pinMode () (установка направления) и digitaiwrite () (состояние вывода). Регистр данных portx фактически есть просто выходной буфер — все, что в него записывается, тут же оказывается на выходе. Но если установить вывод порта на вход (т. е. записать в регистр направления логический 0), как это сделано по умол- чанию, то регистр данных portx будет играть несколько иную роль: установка его разрядов в 0 (так по умолчанию) означает, что вход находится в третьем состоянии с высоким сопротивлением, а установка в 1 подключит к выводу «подтягивающий» (pull-up) резистор сопротивлением 20-50 кОм. В Arduino этим действиям соответ- ствуют КОНСТаНТЫ INPUT И INPUT_PULLUP фуНКЦИИ pinMode () . «Подтягивающие» резисторы необходимы при подключении двухвыводной кноп- ки, при соединении с выходом с «открытым коллектором» (пример— шина TWI/I2C) и в ряде других ситуаций. Как и в случае вывода RESET (см. главу 2), необходимо отметить, что несмотря на некоторое снижение сопротивления «подтя- гивающего» резистора в современных семействах (в семействе Classic оно состав- ляло 35-120 кОм), встроенного pull-up-резистора во многих случаях может оказать- ся для надежной работы недостаточно, и лучше устанавливать дополнительный внешний резистор с сопротивлением от 1 до 10 кОм параллельно этому внутренне- му. Не стоит забывать, что каждый такой резистор при установке вывода в состоя- ние нуля увеличивает потребление, потому в критичных случаях его величину можно увеличить до 20-30 кОм и дополнительно стоит тщательно продумать поря- док перехода к энергосберегающему режиму (подробнее об этом рассказано в гла- ве 14). Заметки на полях Обратите внимание на тот момент (отраженный, кстати, в описаниях контроллеров), что непосредственное переключение между противоположными состояниями выводов портов (т. е . между состояниями ddrx:portx = 11 и ddrx: portx = 00, а также состоя- ниями ddrx:PORTx = 10 и ddrx:PORTx = 01) невозможно. В качестве промежуточных сами собой образуются, пусть и на очень короткое время, состояния, когда пере- ключен только один из этих разрядов портов (состояния 01 или 10 в первом случае и 11 или 00 во втором). У автора это ни разу не привело к каким-то проблемам, но не- обходимо помнить о том, что, например, при переключении из состояния логического нуля на выходе в состояние «на вход» с «подтягивающим» резистором, вся линия на
46^ Часть I. Общие принципы устройства и функционирования AtmelAVR короткий момент оказывается либо «подвешенной в воздухе», либо подключенной к высокому уровню напряжения, — для быстродействующих присоединенных устройств это может оказаться критичным. Это еще один аргумент за то, чтобы не лениться ис- пользовать внешние «подтягивающие» резисторы, в присутствии которых как мини- мум состояние «подвешенности» никогда не возникает. А если у вас вдруг возникает противоположная проблема — мешает внутренний «под- тягивающий» резистор, на короткое время подключающийся при переключении выво- да из состояния логической единицы на выходе на вход в «третьем состоянии» (хотя я лично не могу себе представить такой ситуации, когда это критично), то надо знать, что во многих моделях (например, в «наших» ATmega8 и ATmega8535) встроенными pull-up-резисторами можно оптом управлять через бит pud (бит 2) регистра sfior. По умолчанию он равен нулю, но если установить его в единицу, то все pull-up-резисторы во всех портах окажутся отключенными — входы МК превратятся в обычные высоко- омные входы КМОП (в третьем состоянии). Процедура чтения уровня на выводе порта, если он находится в состоянии работы на вход, на ассемблере не совсем тривиальна (в Arduino это функция digitaiRead ()). Возникает искушение прочесть данные из регистра данных portx, но это ничего не даст — вы получите только то, что там записано вами же ранее. А для чтения того, что действительно имеется на входе (непосредственно на выводе микросхемы), предусмотрена другая возможность— нужно обратиться к некоторому массиву, который обозначается pinx. Обращение осуществляется так же, как и к отдельным битам обычных регистров (см. главу 6), но pinx — это не регистр, а просто некий диапазон адресов, чтение по которым предоставляет доступ к информации из бу- ферных элементов на входе порта. Записывать что-то по адресам pinx, естественно, нельзя. Таймеры-счетчики В большинстве МК AVR наличествуют два или три таймера-счетчика, один из ко- торых 16-разрядный, а оставшиеся один или два— 8-разрядные (в старших моде- лях Mega общее число таймеров может достигать шести). Все счетчики имеют воз- можность предварительной загрузки значений и могут работать от тактовой часто- ты (СК) процессора непосредственно или от поделенной на 8, 64, 256 или 1024 (в отдельных случаях еще на 16 и 32), а также от внешнего сигнала. В модели ATtinyl5, содержащей внутренний умножитель частоты, таймер может работать от более высокой частоты, чем тактовая (до 16СК). В архитектуре AVR 8-разрядным счетчикам-таймерам присвоены номера 0 и 2, а 16-разрядным— 1, 3 и далее. Некоторые 8-разрядные счетчики (обычно Timer 2, если их два) могут работать в асинхронном режиме от отдельного тактового гене- ратора, причем продолжать функционировать даже в случае «спящего» состояния всей остальной части МК, что позволяет использовать их в качестве часов реально- го времени. В большинстве моделей AVR предусмотрена возможность сброса содержимого счетчика-предделителя таймеров для того, чтобы счет всегда начинался с заданного интервала. Предделитель (он общий, хотя обычно и не для всех таймеров — в «на- ших» случаях он управляет таймерами 0 и 1) работает независимо от самих тайме-
Глава 3. Периферийные устройства и прерывания 47 ров, потому для синхронизации запуска таймеров предделитель можно останавли- вать и запускать по команде, — еще одна функция, недоступная в Arduino. Подробности В Arduino все временные функции завязаны на работу TimerO, потому этот таймер в общем случае нельзя использовать для каких-либо еще надобностей — это нарушит ход внутренних часов контроллера и приведет к неработоспособности трех основных функций отсчета времени: minis о, micros о и delay о (только функция deiayMicroseconds () работает независимо от таймеров). Нельзя также запрещать (по крайней мере надолго) прерывания функцией ciio (то же самое, что nointerrupts о) — таймер при этом просто остановится (подробно об этом рассказано в этой главе далее). Наоборот, сама по себе delay о на время задержки заблокирует все прерывания, а внутри обработчиков прерывание выполняться просто не будет. С наперед заданной скоростью работы TimerO (с коэффициентом деления тактовой частоты 1:64) связано ограниченное разрешение функции micros (), равное 4-м микро- секундам — это при периоде тактовой частоты в 1Лб микросекунды! Вероятно, отме- ченные ограничения — первое, с чем сталкивается пытливый любитель, пытаясь усо- вершенствовать свою программу. При программировании на ассемблере, конечно, ни- каких таких ограничений нет, а точное знание времени выполнения команд позволяет рассчитать все вплоть до длительности одного такта. Расскажем о некоторых возможностях счетчиков-таймеров, которые в Arduino либо недоступны вовсе, либо реализуются с большими сложностями (подробнее о про- граммировании счетчиков-таймеров мы поговорим в главе 9). Для начала упомянем о возможности их работы в качестве обычных счетчиков внешних импульсов (при- чем возможна реакция как по спаду, так и по фронту импульса). При этом частота подсчитываемых импульсов не должна превышать половины частоты тактового генератора МК (кстати, при несимметричном внешнем меандре инструкция реко- мендует и еще меньшее значение предельной частоты — 0,4 от тактовой). Это обу- словлено тем, что при счете внешних импульсов их фронты обнаруживаются син- хронно (в моменты положительного перепада тактового сигнала). Кроме того, сле- дует учитывать, что задержка обновления содержимого счетчика после прихода внешнего импульса может составлять до 2,5 периода тактовой частоты. Это весьма существенное ограничение, так что создавать, например, универсальный частото- мер с помощью МК не очень удобно — быстродействующие схемы лучше проек- тировать на соответствующей комбинационной логике или на ПЛИС (программи- руемых логических интегральных схемах). При переполнении счетчика (вне зависимости от источника сигнала) возникает со- бытие, которое может вызывать соответствующее прерывание. 8-разрядный счет- чик TimerO в ряде случаев этой функцией и ограничивается. Счетчик Timer2, если он имеется, может также вызывать прерывание по совпадению подсчитанного зна- чения с некоторой заранее заданной величиной. 16-разрядные счетчики— более «продвинутая» штука и могут вызывать прерывания по совпадению с двумя неза- висимо заданными числами: А и В. При этом счетчики могут обнуляться или про- должать счет, а на специальных выводах при этом— генерироваться импульсы (аппаратно, без участия программы), что позволяет создать, например, очень про- стой, гибко настраиваемый и работающий независимо от программы генератор импульсов.
48_ Часть I. Общие принципы устройства и функционирования AtmelAVR Кроме того, 16-разрядные счетчики имеют возможность осуществлять «захват» (capture) внешних одиночных импульсов на специальном выводе. При этом может вызываться прерывание, а содержимое счетчика помещается в некий регистр. Сам счетчик при этом может обнуляться и начинать счет заново или просто продолжать счет. Такой режим удобен для измерения периода внешнего сигнала или для под- счета неких нерегулярных событий (вроде прохождения частиц в счетчике Гейге- ра). Немаловажно, что источником таких событий может быть также встроенный аналоговый компаратор. Как минимум, 16-разрядные счетчики-таймеры в семействе Mega допускают работу в различных режимах PWM, т. е. в качестве 8-, 9-, 10- или 16-битовых широтно- импульсных модуляторов (ШИМ). Если допускающих такие режимы таймеров не- сколько, то они будут работать независимо друг от друга, что позволяет осуществ- лять многоканальную ШИМ. В технической документации этим режимам в силу их сложности, многовариантности и громоздкости посвящено много страниц. В отно- шении удобства реализации ШИМ платформа Arduino с ее единственной функцией anaiogwrite (), конечно, кроет ассемблер по всем статьям — если не считать того, что в реальности от упомянутых режимов можно получить гораздо больше, чем позволяет эта функция. ШИМ, в связи с легкостью ее реализации в интегрирован- ных счетчиках контроллеров, а также с появлением в последние десятилетия деше- вых, надежных и простых ключей на МОП-транзисторах, — чрезвычайно модная тема в современной схемотехнике, и мы ей отведем надлежащее время в дальней- шем изложении. Кроме таймеров-счетчиков, во всех без исключения AVR-контроллерах есть сто- рожевой (Watchdog) таймер. Он предназначен в основном для вывода МК из режи- ма энергосбережения через определенный интервал времени, но может служить и для просто аварийного перезапуска МК, — так, если работа программы зависит от прихода внешних сигналов, то при их потере (например, из-за обрыва на линии) МК может «повиснуть», а Wathcdog-таймер выведет его из этого состояния. Под- робнее о сторожевом таймере рассказано в главе 14. Аналого-цифровой преобразователь Многоканальный АЦП входит в большинство современных моделей МК AVR: практически во все Mega и в некоторые Tiny. Число каналов, обычно привязанных к выводам порта А или С, зависит от числа выводов корпуса и в разных моделях может варьировать от 4 каналов в младших моделях семейства Tiny до 6 каналов в ATmega8 в DIP-корпусе и 8 каналов в 40-выводном корпусе ATmega8535. Есть и модели специального назначения с экстремально большим количеством каналов АЦП— например, 28 каналов в ATtiny828. Многоканальность означает, что на входе единственного модуля АЦП установлен аналоговый демультиплексор, кото- рый может подключать этот вход к различным выводам МК для осуществления измерений нескольких независимых аналоговых величин с разнесением во време- ни. Входы демультиплексора могут работать по отдельности (в несимметричном режиме для измерения напряжения относительно «земли») или (в некоторых моде-
Глава 3. Периферийные устройства и прерывания 49_ лях) объединяться в пары для измерения дифференциальных сигналов. Иногда АЦП дополнительно снабжается усилителем напряжения с фиксированными зна- чениями коэффициента усиления 10 и 200. Сам АЦП в базовой конфигурации представляет собой преобразователь последова- тельного приближения с устройством выборки-хранения и фиксированным числом тактов преобразования, равным 13 (или 14 для дифференциального входа); первое преобразование после включения потребует 25 тактов для инициализации АЦП. Тактовая частота формируется аналогично тому, как это делается для таймеров, — с помощью специального предделителя тактовой частоты МК, который может иметь коэффициенты деления от 1 до 128. Но, в отличие от таймеров, выбор такто- вой частоты АЦП не совсем произволен, т. к. быстродействие аналоговых компо- нентов ограничено. Поэтому коэффициент деления следует выбирать таким, чтобы при заданном «кварце» тактовая частота АЦП укладывалась в рекомендованный диапазон 50-200 кГц (т. е. максимум около 15 тыс. измерений в секунду). Увеличе- ние частоты выборки допустимо, если не требуется достижение наивысшей точно- сти преобразования. Подробности Следящие преобразователи такого типа, как включенные в состав МК AVR, работают по следующей схеме. Берется цифроаналоговый преобразователь (ЦАП) нужной раз- рядности. На его цифровые входы подается с некоего регистра код по определенному правилу, о котором далее. Выход ЦАП соединяется с одним из входов компаратора, на другой вход которого подается преобразуемое напряжение. Результат сравнения подается на схему управления, которая связана с этим самым регистром — формиро- вателем кодов. Для того чтобы получить фиксированную длительность преобразова- ния, правило формирования кодов следующее: сначала все разряды кода равны ну- лю. В первом такте самый старший разряд устанавливается в единицу. Если выход ЦАП при этом превысил входное напряжение, т. е. компаратор перебросился в проти- воположное состояние, то разряд возвращается в состояние логического 0, в против- ном же случае он остается в состоянии логической 1. В следующем такте процедуру повторяют для следующего по старшинству разряда. Такой метод позволяет за число тактов, равное числу разрядов, сформировать в регистре код, соответствующий вход- ному напряжению. Алгоритм имеет существенный недостаток: если за время преобра- зования входное напряжение меняется, то схема может ошибаться, поэтому здесь обязательно наличие устройства выборки-хранения входной величины, которое до- полнительно замедляет процесс и вносит свою погрешность в конечный результат. Разрешающая способность базовой конфигурации АЦП в МК AVR— 10 двоичных разрядов, чего для большинства типовых применений достаточно (около 0,1% от шкалы измерения). В некоторых модификациях AVR — например, в моделях с ин- дексом HVA, предназначенных для контроля напряжения аккумуляторных батарей (таких, как ATmegal6HVA или ATmega8HVA),— разрядность АЦП повышена до 12, хотя принцип работы остался тем же. Абсолютная погрешность преобразо- вания зависит от ряда факторов и в идеальном случае не превышает +2 младших разрядов, что соответствует общей точности измерения примерно в 8 двоичных разрядов (погрешность 0,4% от шкалы измерения). Для достижения этого результа- та необходимо принимать специальные меры: не только «вгонять» тактовую часто- ту в рекомендованный диапазон, но и снизить по максимуму интенсивность цифро-
J50 Часть I. Общие принципы устройства и функционирования AtmelAVR вых шумов. Для этого рекомендуется, как минимум, не использовать оставшиеся выводы того же порта, к которому подключен АЦП (обычно это порт А), для обра- ботки цифровых сигналов, правильно разводить платы, а как максимум — допол- нительно к тому еще и включать специальный режим ADC Noise Reduction (что, впрочем, не всегда удобно — см. главу 11). На самом деле добиться указанной в документации абсолютной точности в 8 разря- дов можно только в теории — слишком от многих факторов она зависит. Поэтому прецизионной измерительной системы на основе такого АЦП построить не удаст- ся— на практике следует ориентироваться на абсолютную точность в пределах 0,8-1% от шкалы, чего достаточно для рядовых бытовых приборов. Для подобных задач всегда важней не точность, а стабильность преобразования (чтобы прибор при одинаковом входном сигнале всегда показывал одно и то же значение и не «мельтешил» младшим разрядом), а в этом плане встроенный АЦП вполне на уровне, особенно если применить программные меры подавления дребезга (см. главу 77). К любому аналого-цифровому преобразователю обязательно прилагается источник опорного напряжения, который играет роль эталонной измерительной линейки — относительно него отсчитываются уровни входного напряжения. Обычно для встроенного АЦП за такой источник принимают напряжение питания аналоговой части AVCC (заметьте, что оно по идее должно быть отдельным или хотя бы «раз- вязанным» с цифровым, чем в Arduino пренебрегают). Но иногда необходимо иметь абсолютную шкалу относительно точно известной величины напряжения, тогда к АЦП лучше подключать внешний прецизионный опорный источник, — напри- мер, из серии REF19x фирмы Analog Devices (вывод AREF — он имеется и на неко- торых платах Arduino). В составе AVR имеется и встроенный опорный источник (ИОН) напряжением около 1,1-1,3 вольта (для АЦП он во многих моделях усили- вается до 2,56 вольта), но по разбросу от экземпляра к экземпляру (в пределах ±0,08 вольта, т. е. примерно ±6%) он годится только для не очень ответственных случаев. Заметки на полях Мне всегда было интересно, почему в некоторых моделях Tiny (включая ATtiny2313) при номинальном напряжении 1,1 вольта встроенный ИОН имеет существенно мень- ший разброс, чем у Меда: ±0,9% (от 1,11 до 1,12 В), и почему нельзя было добиться того же самого в семействе Меда? Сам по себе опорный источник AVR не так уж и плох— он основан на вполне стандартном способе получения точного напряжения: это т. н. «ибэ-стабилитрон», или стабилитрон «с напряжением запрещенной зоны». По сути, это два последовательных р-п-перехода в диодном включении, которые ком- пенсируют друг друга при изменениях токи и температуры. Тот же способ использует- ся для получения очень точных опорных источников вроде упомянутой серии REF19x фирмы Analog Devices. Источник мало потребляет (микроамперы) и имеет стабиль- ность в рабочем диапазоне температур в единицы милливольт (температурный дрейф, если верить графикам в документации, порядка 0,005% на °С, что в целом очень неплохо). Неприятный разброс от экземпляра к экземпляру (на уровне обычного стабилитрона) остается основным препятствием к его применению — так что, кроме уж очень некритичных к точности применений, схема с участием встроенного ИОН требует индивидуальной калибровки.
Глава 3. Периферийные устройства и прерывания 51_ АЦП может работать в двух режимах: одиночного и непрерывного преобразования. Второй режим целесообразен лишь при необходимости максимально возможной частоты выборок. В остальных случаях его следует избегать, т. к. обойти связанную с ним обработку цифровых сигналов, осуществляемую одновременно с АЦП-пре- образованием, как правило, невозможно, а это означает еще большее снижение точности преобразования. Заметки на полях В принципе функции АПЦ перенесены в Arduino вполне аккуратно, и удобству пользо- вания им уделено весьма значительное внимание. За кадром по сути остались лишь способы повышения точности, выбора между точностью и скоростью, не используются также прерывания АЦП (явно) и некоторые другие мелочи, — но на самом деле все это не очень-то и нужно в реальных задачах. Потому, если не считать того факта, что собственно измерения на ассемблере, в отличие от Arduino, проводятся с максималь- но возможной и предсказуемой скоростью, АЦП представляет собой тот случай, когда сторонники языка С могут с удовлетворением констатировать: вот область, где ас- семблер явно проигрывает. Поэтому, если вам нужно просто измерить аналоговый сигнал, преобразовать его в физические величины и отобразить результат на индика- торе— тут удобство Arduino вне конкуренции. Зато, если при этом еще и возникает задача поместить все это в минимальные габариты и запитать от батареек, — вот тут уже однозначно рулит ассемблер, т. к. относительная сложность инициализации АПЦ и преобразования кода в физические величины окажется при его использовании дале- ко не самой существенной частью проекта. Применение традиционно входящего в состав всех AVR аналогового компаратора в Arduino не предполагается вовсе, и это неудивительно — аналоговый компаратор есть по сути однобитный АЦП и в силу этого способен заменить АЦП во многих случаях обработки аналоговых сигналов. Но контроллер тогда оказывается просто не нужен — простейший термостат вполне можно построить на одном компараторе и полудюжине рассыпных компонентов безо всякого контроллера. Однако встро- енный компаратор, во-первых, часто может заменить АЦП тогда, когда последний отсутствует (как в ATtiny2313), во-вторых, может служить в качестве входного фильтра при подсчете внешних событий — чтобы формировать надежные логиче- ские уровни из аналоговой линии с непредсказуемыми формами сигналов, и еще во многих других практических случаях. Например, с контролем аккумуляторов с целью не допустить их глубокого разряда аналоговой компаратор при аккуратном подходе к составлению схемы справляется лучше АЦП и при этом работает совер- шенно независимо от остальных компонентов контроллера. Ввиду простоты анало- гового компаратора, мы его рассмотрим сразу в практических приложениях в гла- ве 11. А сейчас остановимся на другом важнейшем компоненте МК AVR— после- довательных портах, которые служат основным каналом коммуникации МК с внешним миром. Последовательный порт Последовательными эти порты называют, потому что в них в каждый момент вре- мени передается только один бит (в некоторых случаях возможна одновременная передача и прием, но суть дела от этого не меняется). Самое главное преимущество
52 Часть I. Общие принципы устройства и функционирования AtmelAVR последовательных портов перед параллельными (когда одновременно производится обмен целыми байтами или полубайтами — тетрадами) — снижение числа соеди- нений. Но оно не единственное — как ни парадоксально, но последовательные ин- терфейсы дают значительную фору параллельным на высоких скоростях, когда на скорость передачи начинают влиять задержки в линиях. Последние невозможно сделать строго одинаковыми, и это одна из причин того, что последовательные интерфейсы в настоящее время доминируют (типичные примеры: USB вместо LPT и SCSI или Serial ATA вместо IDE в компьютерах). В микроконтроллерных устройствах с малыми объемами данных, конечно, ско- рость передачи нас волнует во вторую очередь, но вот число соединительных про- водов — очень критичный фактор. Поэтому все внешние устройства, представлен- ные в этой книге, будут иметь последовательные интерфейсы (за исключением не- которых типов дисплеев для отображения данных). Практически любой последовательный порт можно имитировать программно, ис- пользуя обычные выводы МК. Когда-то так и поступали даже в случае самого по- пулярного из таких портов — UART. Однако с тех пор МК обзавелись аппаратны- ми последовательными портами, что, впрочем, не всегда удобно: так, по глубокому убеждению автора, аппаратная реализация порта TWI (12С) в AVR далека от идеала, и в простых программах целесообразнее пользоваться программным имитатором, не привязанным притом к конкретным выводам контроллера (вопреки тому, как это делается в Arduino). Но давайте обо всем по порядку. Интерфейс UART (USART) Сначала уточним соответствующие термины. В компьютерах есть СОМ-порт (а если и нет, то его всегда можно эмулировать через USB, как мы увидим далее), часто ошибочно называемый портом RS-232. Правильно сказать так: СОМ-порт передает данные, основываясь на стандарте последовательного интерфейса RS-232 (буквы RS и означают Recommended Standard). Последний, кроме собственно про- токола передачи, стандартизирует также и электрические параметры, и даже всем знакомые 9-контактные разъемы DB-9 (рис. 3.1), как и некоторые другие (ранее еще употреблялся разъем DB-25, но теперь, кажется, не встречается вовсе), иногда еще употребляется круглый аудиоразъем с тремя контактами. Рис. 3.1. Разъем DB-9f (гнездо) — устанавливается со стороны устройства (на кабеле) В собственно стандарте RS-232 (у компьютерного СОМ-порта, например) принята отрицательная логика, т. е. логическая единица передается низким, логический ноль — высоким уровнем. Притом RS-232 имеет дело с разнополярными уровнями
Глава 3. Периферийные устройства и прерывания 53_ сигнала: от ±3 до ±12 или даже ±15 вольт (отрицательный уровень напряжения — логическая единица, положительный— логический ноль). Отрицательная логика удобна для передачи по линиям «с открытым коллектором», а разнополярные уровни с достаточно высоким, по современным представлениям, напряжением до- бавляют помехоустойчивости. Обычный RS-232, соответствующий всем требова- ниям стандарта в части электрических соединителей, способен без сбоев работать на расстояниях до 150 метров при скорости 9600 бит/с и почти до километра— на скорости 2400. С увеличением скорости расстояние безошибочной передачи резко падает: при 19 200 бит/с — это уже только 15 метров, потому и принято использо- вать величину 9600, как наиболее удобную со всех точек зрения. Подробности На практике для UART единицу скорости обмена принято именовать бод (baud— по имени французского телеграфиста Эмиля Бодо). В случае UART и RS-232 эта едини- ца совпадает с числом передаваемых битов в секунду (включая в расчет все переда- ваемые биты, в том числе служебные: стартовый и стоповый). Но в общем случае это не так — боды означают число переданных «символов» (посылок) в секунду, каждый из которых в случае модемов может нести от 1 до 16 битов. Чтобы не заплутать в этих терминологических дебрях, автор предпочитает использовать менее подверженный разночтениям термин бит в секунду (бит/с, bps). При вычислении информационной скорости, измеряемой в байтах в секунду (Bps), нужно учитывать, что байт здесь как бы увеличился на два служебных бита, потому битовую скорость надо делить не на 8, а на 10, — 9600 бит/с эквивалентно величине 9600/10 = 960 байт/с. Под названием UART (Universal Asynchronous Receiver-Transmitter, универсальный асинхронный приемопередатчик) скрывается основная часть любого устройства, поддерживающего RS-232, и не только его (недаром он «универсальный») — на- пример, стандарты RS-485 и RS-422 также реализовываются через UART, посколь- ку они отличаются от RS-232 только электрическими параметрами и допустимыми скоростями, а не общей логикой построения. Через UART также организуется связь внешних устройств с компьютерами по интерфейсу USB (как, например, это сдела- но в Arduino). RS-232 возник еще в первое десятилетие существования микросхем, в конце 1960-х, и за полвека существования этот стандарт, как и его основа — UART, были отпо- лированы до мелочей. Поэтому сейчас это самый простой и надежный способ орга- низации связи между устройствами на различных расстояниях— от обмена дан- ными между контроллерами в пределах устройства до телеметрии на километровых расстояниях по радиоканалу. Его единственный крупный недостаток— то, что RS-232 в принципе предназначен только для связи «точка-точка» (т. е. двух уст- ройств между собой), — можно преодолеть различными способами. Но стандартов на этот счет не существует, и этот факт, наряду с невысокой скоростью передачи, стал главной причиной замены в компьютерных устройствах СОМ-порта на USB, произошедшей еще в конце прошлого века (стандарт РС-99). Тем не менее и ныне при связи внешних устройств с компьютерами через USB в системе возникает виртуальный СОМ-порт, общение с которым в дальнейшем происходит по старинке, как будто в вашем ноутбуке установлен настоящий разъем DB-9. Другую категорию представляют медиаустройства вроде USB-накопителей,
54 Часть I. Общие принципы устройства и функционирования AtmelAVR флеш-карточек или веб-камер, где решающую роль играет скорость соединения, поэтому там обмен информацией происходит по протоколам, отличным от архаич- ного СОМ-порта. Подробности Термин USART (Universal Synchronous-Asynchronous Receiver-Transmitter) — следст- вие в общем-то довольно эклектичной попытки улучшить традиционный UART, как в сто- рону синхронизации приема-передачи отдельных битов, так и в сторону мультикон- троллерного обмена. Алгоритмы приема-передачи при этом усложняются, простота использования пропадает (появляются всяческие master-slave, занимается дополни- тельный вывод и т. д.), а выигрыш не настолько велик, чтобы сопутствующая возня имела смысл. Возможно, есть какие-то применения, где без дополнительных функций USART не обойтись, но на практике в таких случаях проще использовать специально для того предназначенные интерфейс I2C (TWI) или куда более скоростной SPI, чем возиться с синхронизацией не приспособленного для этого UART. Именно поэтому далее в этой книге мы станем использовать преимущественно аб- бревиатуру UART, a USART будет возникать только изредка — как напоминание о том, что некоторые возможности этого последовательного порта при работе в обычном асинхронном режиме расширены по сравнению с каноническим UART. Собственно UART позволяет общаться между собой только двум контроллерам с одинаковым питанием 3,3 или 5 вольт. Для того чтобы UART превратить в кон- кретный порт: в СОМ-порт для общения с ПК, в интерфейс RS-485 для промыш- ленных устройств, наконец, в USB (включая и Serial-порт Arduino) — нужны спе- циальные адаптеры. Они давно стандартизированы и выпускаются в больших количествах: и в виде отдельных микросхем, и в виде готовых адаптеров на раз- личной основе. Самый простой из них — адаптер RS-232, т. к. в обычных примене- ниях для превращения UART в СОМ-порт достаточно лишь двух линий: приема RxD и передачи TxD — с преобразованием логических уровней. В стандарт RS-232 входит еще много всяких разных линий, необходимых для кор- ректной организации связи, например, с модемами. В UART дополнительные ли- нии RS-232 не используются, и при необходимости могут быть организованы с по- мощью других выводов контроллера. Эти линии иногда выручают, заменяя отсут- ствующие в разъеме RS-232 контакты питания (в первую очередь отрицательного, необходимого для нормального функционирования RS-232), — во всех компьюте- рах традиционно имеется линия питания -12 В, но на внешние разъемы она не вы- водится. Именно от этих дополнительных линий когда-то питались компьютерные мыши, подключавшиеся к СОМ-порту. Для нас имеет значение существование в стандарте UART линий RTS и DTR— одна из них посылает сигнал Reset при программировании Arduino по последовательному каналу. Для основных наших нужд эти линии не потребуются, поэтому адаптер нам подойдет любой, в том числе и простейший самодельный, но иметь в виду эти нюансы нужно (подробнее о раз- новидностях адаптеров рассказано в главах 4 и 75). Рассмотрим подробнее, как, собственно, происходит обмен. Идея интерфейса RS-232 заключается в передаче целого байта по одному проводу в виде последова- тельных импульсов, каждый из которых может находиться в состоянии 0 или 1. Если в определенные моменты времени считывать состояние линии, то можно вос- становить то, что было послано.
Глава 3. Периферийные устройства и прерывания 55_ Однако эта простая идея натыкается на определенные трудности. Для приемника и передатчика, связанных между собой двумя проводами (два сигнальных провода: «туда» — TxD и «обратно» — RxD), приходится задавать скорость передачи и приема, которая должна быть одинакова для устройств на обоих концах линии. Число передаваемых/принимаемых битов в секунду носит название битрейта (bitrate). Как мы уже говорили, в нашем случае биты в секунду совпадают с бода- ми — числом посылок в секунду, поэтому нередко скорость передачи указывают именно в бодах (в Arduino это общепринятая практика), хотя, строго говоря, это неправильно. Примечание В обозначениях интерфейсов (двухпроводный, трехпроводный и т. п.) принято считать только сигнальные линии. Но не следует забывать, что в большинстве случаев, по крайней мере, шины заземления устройств также должны соединяться, поэтому фор- мально двухпроводный UART предполагает как минимум три соединительных прово- да, трехпроводный SPI — четыре и т. д. Вместе с тем есть и «настоящие» двухпро- водные интерфейсы — к ним относится, скажем, RS-485 (с некоторыми нюансами), модемная линия или коаксиальный Ethernet. Однако установления нужной скорости передачи еще недостаточно. Проблема со- стоит в том, что приемник и передатчик — это физически совершенно разные сис- темы, и скорости эти для них не могут быть строго одинаковыми в принципе (хотя бы из-за разброса параметров тактовых генераторов), и даже если их каким-то образом синхронизировать в начале, то они в любом случае быстро «разъедутся». Чтобы избежать этого, в RS-232 передача каждого байта сопровождается старто- вым битом, который служит для начальной синхронизации. После него идут восемь (или девять — если задана проверка на четность, а также в некоторых других слу- чаях) информационных битов, а затем стоповые биты, которых может быть один, два и более (в большинстве случаев это не имеет принципиального значения, пото- му что в паузах между импульсами линия находится обычно в состоянии непре- рывного стопового бита). Общая диаграмма передачи таких последовательностей показана на рис. 3.2. Здесь она приведена в обычных логических уровнях UART, т. е. в положительной логике с логическими уровнями 0—5 вольт или 0-3 вольта, в зависимости от питания. Хит- рость заключается в том, что состояния линии при передаче стартового и стопового битов имеют разные уровни: стартовый бит передается низким уровнем напряже- ния (логическим нулем), а столовый — высоким уровнем (логической единицей), потому начало стартового бита всегда распознается однозначно. В момент начала передачи стартового бита и происходит синхронизация. Приемопередатчики UART в AVR тактируются 16-кратной частотой по отношению к установленной скорости обмена. Приемник отсчитывает от фронта стартового бита несколько тактов (чтобы попасть в середину стартового бита) и три такта подряд проверяет состояние линии (оно должно быть логическим нулем). Если все три состояния совпали, то прини- мается решение, что действительно пришел стартовый бит. Тогда восемь (или девять, если это задано заранее) раз подряд с заданным периодом регистрируется состояние линии (это т. н. процедура восстановления данных). Данные в UART
56 Часть I. Общие принципы устройства и функционирования AtmelAVR всегда передаются младшими битами вперед — надписи МЗР и СЗР на рис. 3.2 как раз и означают «младший знаковый разряд» и «старший знаковый разряд». После этого линия переходит в состояние стопового бита (высокого уровня напряжения) и может в нем пребывать сколь угодно долго, пока не придет следующий старто- вый бит. Итого посылка одного байта соответствует передаче 10 битов, потому при скорости 9600 бит/с она займет 1,04 миллисекунды. 10010110 шиш оооооооо •МЗР СЗР 8 битов данных £ пауза 2 (timeout) Рис. 3.2. Диаграмма передачи данных по стандарту RS-232 в логических уровнях UART, формат кадра 8п1 Обычный формат данных, по которому работает львиная доля всех устройств, обо- значается 8nl, что читается так: 8 информационных битов, no parity, 1 столовый бит. «No parity» означает, что 9-го бита не посылается, и проверка на четность не производится. На диаграмме (см. рис. 3.2) показана передача трех вариантов: некоего произвольного кода, а также байтов, состоящих из всех единиц и из всех нулей. Стоповых битов в формате кадра может быть задано больше одного — это нужно, например, для того, чтобы приемник «знал», какой наименьший интервал времени ему нужно ожидать следующего стартового бита (как минимум, это может быть, естественно, один период частоты обмена, т. е. один столовый бит). Если по исте- чении этого времени стартовый бит не придет, приемник может регистрировать так называемый timeout, т. е. по-русски «перерыв», и посчитать это за конец передачи блока данных. Если мы не применяем подобные протоколы с временным разделе- нием блоков, нам в общем-то все равно, сколько стоповых битов будет. Из описанного алгоритма работы понятно, что погрешность несовпадения скоро- стей обмена должна быть такой, чтобы фронты не «разъезжались» за время переда- чи/приема всех десяти битов более чем на полпериода, т. е. в общем случае факти- ческая разница частот тактовых импульсов может достигать 4-5%, особенно при небольших скоростях. На практике их стараются сделать как можно ближе к стан-
Глава 3. Периферийные устройства и прерывания 57_ дартным величинам — рекомендуемое в документации отклонение составляет не более 0,2%, иначе за помехозащищенность линии фирма ответственности не несет. Существуют специальные кварцы для удобного формирования частот, кратных стандартным скоростям передачи RS-232 (например: 1,8432 МГц, 4,608 МГц и др.), которые позволяют сформировать скорости передачи с нулевой.ошибкой. Они мо- гут применяться там, где необходима исключительная стабильность и помехоза- щищенность линии. Заметим, что в большинстве случаев в AVR точность встроен- ных генераторов тактовой частоты при «умолчательной» частоте 1 МГц позволяет обеспечить более или менее нормальную работу порта UART лишь при скоростях 1200 бит/с и менее (чем выше тактовая частота, тем выше может быть и это значе- ние скорости). Но пользоваться UART в таком режиме неудобно, помехоустойчи- вость при нестабильном генераторе резко падает, потому UART мы всегда будем подключать только в режиме тактирования от внешнего кварца. UART удобен для обмена данными на сравнительно большие расстояния при связи между собой устройств разной природы. Большинство устройств в пределах одной платы или конструктива (память, датчики, внешние АЦП) имеют иные интерфейсы для обмена данными. Их мы сейчас и рассмотрим. Интерфейс SPI Идея передачи информации побитно с определенными интервалами времени лежит в основе всех последовательных интерфейсов, они различаются только способами синхронизации. В интерфейсе SPI (Serial Peripheral Interface, последовательный пе- риферийный интерфейс) синхронизирующие импульсы передают по отдельной, специально выделенной линии. Это облегчает задачу синхронизации (не требуется специально задавать скорости обмена), но требует большего числа сигнальных проводов — не менее трех (см. врезку «Примечание» в предыдущем разделе). В по- давляющем большинстве случаев необходим еще один— четвертый— провод, который присутствует всегда при подключении более чем двух микросхем к одно- му интерфейсу, но иногда требуется и при одиночном подключении. Сначала рассмотрим функции этого четвертого провода. Очевидно, что при под- ключении более чем двух устройств к одной линии, им нужно как-то «разбираться» между собой, иначе может получиться невесть что, когда два или более устройства «захотят» вдруг одновременно что-то передать. Притом непонятно, кому именно передаваемая информация предназначается. Эта проблема решается по-разному: в самом общем случае каждому устройству присваивается индивидуальный адрес — так работает интерфейс 12С (о нем будет рассказано далее в этой главе), аналогично можно устроить многоканальный обмен по UART (его расширенный вариант USART специально приспособлен для такого обмена). В других случаях адрес заменяется выводом «выбор кристалла» (Chip Select, CS — как и в случае RESET, правильно обозначать его со значком инверсии, потому что выбор производится в отрицательной логике— при подаче низкого уровня). Конечно, аппаратно реализовать этот способ проще, чем идентификацию многих битов адреса, потому он получил широкое распространение с давних времен, при-
5Q Часть I. Общие принципы устройства и функционирования AtmelAVR меняясь, например, при адресации микросхем памяти. Таким образом, мы получаем возможность разобраться, к какому именно устройству в тот или иной момент про- изводится обращение. Несколько таких выводов могут обеспечить управление не- сколькими устройствами, подсоединенными к шине данных, при этом остальные линии шины объединяются. На вывод CS могут быть «навешены» и дополнительные функции (например, перевод микросхемы в «спящее» состояние с микропотреблением). Потому даже когда соединены всего два устройства, линия CS обычно все равно требует специ- ального управления. Рассмотрим функционирование этой линии применительно к интерфейсу SPI в МК AVR подробнее. Чтобы исключить конфликты при попытке одновременной пере- дачи данных, в каждый момент времени одно из устройств выполняет функции ве- дущего (Master), а все остальные — ведомого (Slave, что означает «слуга»). Отме- тим, что в общем случае роли ведущий — ведомый в процессе работы могут ме- няться, но ведущий на линии в каждый момент времени может быть только один. Четвертый провод в аппаратном интерфейсе SPI и является аналогом «выбора кри- сталла» и носит название SS (Slave Select, выбор ведомого). Правильно обозначать его также с инверсией— SS, т. к. он во всем аналогичен описанной ранее функ- циональности вывода CS. Естественно, в большинстве случаев ведущим устройством выступает контроллер. Для ведущего состояние его входного вывода SS не меняется, и вывод можно было бы использовать для других надобностей, с одним только нюансом — при работе с аппаратным SPI следует учесть, что если его вывод SS сконфигурирован как вход (по умолчанию), то на него должен быть подан высокий уровень напряжения. При подаче низкого уровня модуль SPI автоматически переключится в режим ведомого, даже если это произойдет во время передачи данных. Таким образом, чтобы этот вывод не мешал работе, его следует либо сконфигурировать на выход (тогда его можно задействовать как обычный вывод порта), либо оставить сконфигурирован- ным на вход с подключением «подтягивающего» резистора (т. е. с записью значе- ния 1 в этот разряд). В последнем случае для нормальной работы SPI никакие сигналы на него подавать нельзя. А для управления выводом SS (CS) ведомой микросхемы можно использовать любой цифровой выход— в том числе и SS, сконфигурированный на выход. Названия остальных линий SPI знакомы каждому, кто уже имел опыт последова- тельного программирования AVR, — это информационные линии: MISO (Master In Slave Out), MOSI (Master Out Slave In) и линия для подачи тактовых импульсов SCK. Как правило, тактовые импульсы подаются ведущим (как вариант — внеш- ним источником), в режиме ведомого вывод SCK работает только на вход. В отличие от симметричного UART, где вывод TxD может только передавать дан- ные, a RxD — только принимать (поэтому у разных устройств их следует соединять перекрестно), в интерфейсе SPI одноименные выводы MISO и MOSI соединяются между собой, а направление передачи задается выбором режима ведущий— ве- домый.
Глава 3. Периферийные устройства и прерывания 59 Схема обмена данными по SPI между контроллером и внешним устройством пока- зана на рис. 3.3. Устроен этот обмен элементарно просто — как можно видеть, два 8-разрядных регистра (источника и приемника) образуют единый регистр сдвига, соединенный в кольцо линиями MISO и MOSI. С началом передачи «заводится» генератор синхроимпульсов (в общем случае он не обязательно входит в ведущее устройство, может быть и внешним), из ведущего по линии MOSI начинают «выталкиваться» биты, одновременно вытесняя из ведомого биты по линии MISO. Через восемь тактов регистры полностью обмениваются информацией между со- бой. Если после этого генератор продолжает работать, то информация так и будет «крутиться» в этом кольце. Чтобы ее обновить, нужно после каждого цикла обмена считывать принятый байт в приемнике и записывать новый в передатчике. Таким образом запись (в ведомый) и чтение (из ведомого) фактически представляют собой одну и ту же операцию: заданное направление определяет только те или иные дей- ствия, которые при этом выполняет контроллер. !ст. 1 1 \ ! разряд Master мл. разряд 8-разрядный сдвиговый регистр Генератор синхроимпульсов MISO MISO MOSI MOSI Ст. разряд Slave Мл. разряд 8-разрядный сдвиговый L SCK SCK упр. SS регистр CS i 1 Рис. 3.3. Схема передачи данных по интерфейсу SPI Заметки на полях Заметим, что как и I С (о нем рассказано далее в этой главе), название SPI является зарегистрированной торговой маркой (в данном случае — фирмы Motorola). Потому вы можете встретить в технических описаниях разные названия и самого этого интер- фейса (что еще не так страшно), и, главное, его выводов. Чаще всего можно встретить общее название Three-Wire Serial Interface, трехпроводный последовательный интер- фейс (хотя, как мы говорили, на самом деле требуется четыре провода, включая «землю», и даже пять и более, если учитывать «выбор кристалла»). Сложнее привес- ти варианты названий выводов, потому что они сильно зависят от назначения устрой- ства, и могут различаться в каждом отдельном случае. Так, например, в АЦП фирмы Analog Devices вывод SCK носит название SCLK, MISO называется DOUT (потому что АЦП всегда— ведомый), a MOSI, соответственно, DIN. Другие обозначения вам встретятся в случае, например, энергонезависимой памяти производства той же Atmel (см. главы 10 и 13), где MOSI будет просто SI, a MISO — просто SO. Название вывода SS характерно лишь для аппаратного SPI в МК AVR. В других устройствах чаще всего имеется обычный вход «выбор кристалла» CS, а иногда совместно с ним и другие управляющие выводы: например, в памяти АТ25 есть еще вывод задержки обмена HOLD, практически во всех микросхемах энергонезависимой памяти есть вывод за- прета записи WR. Во внешних АЦП иногда встречается вывод «готовности данных» (он может называться, например, DRDY). По этим причинам полный протокол обмена
j>0 Часть I. Общие принципы устройства и функционирования Atmel AVR по SPI у различных устройств может сильно различаться, хотя в его основе всегда лежит последовательный побитный сдвиг в каждом такте частоты, задаваемой «мас- тером» на выводе SCK. Существуют также различающиеся по комбинации логических уровней режимы (mode) 0, 1, 2 и 3 (см. главу 12), а также SPI с ббльшим количеством линий данных (их мы рассматривать здесь не будем). Для сигнализации об окончании цикла сдвига в аппаратном SPI MK AVR преду- смотрено отдельное прерывание и установка специального флага spif. Буфериза- ция по передаче одинарная (т. е. нельзя записать следующий байт, пока полностью не передали предыдущий), а при чтении — двойная (т. е. принятый байт можно прочесть во время обмена). При этом необходимо следить, чтобы принятая посылка была считана из регистра данных SPI прежде, чем завершится цикл сдвига новой посылки. В противном случае первый байт будет потерян. Никаких стартовых-стоповых битов в SPI не предусматривается, и если обмен не- прерывный, то выделить начало байта, чтобы синхронизировать чтение/запись, не- возможно. Поэтому обычно на время чтения генератор приостанавливают и заодно подают сигнал высокого уровня на CS (если этот вывод используется вообще), чтобы остановить ведомого и привести его в исходное состояние. По паузе в пере- даче (во время которой уровень на линии SCK может принимать выбранное в зави- симости от режима значение) можно устанавливать синхронизацию начала очеред- ного байта или кадра, если это необходимо. Другой способ выделить начало ин- формационной последовательности приводится в главе 12 применительно к обмену с картами памяти. Значения скорости обмена по SPI-интерфейсу не стандартизованы и могут дости- гать у некоторых микросхем десятков Мбит/с (так, в микросхемах памяти серии AT45DBjcxxD тактовая частота может достигать 66 МГц). Аппаратный SPI MK AVR предполагает максимальную частоту тактирования равной 74 от частоты так- тового сигнала МК. Отметим, что SPI-интерфейс последовательного программиро- вания, имеющийся во всех моделях MK AVR, — не то лее самое, что пользова- тельский SPI для обмена информацией (обратите внимание, что на схеме, показан- ной на рис. 2.1, они размещены в разных местах), и в общем случае у них могут даже не совпадать выводы. Кроме того, не все модели MK AVR обладают полно- функциональным аппаратным SPI, несмотря на то, что в них имеется возможность последовательного программирования по этому интерфейсу. В младших моделях семейства Tiny с объемом памяти 1 кбайт SPI вообще отсутствует, а в других моде- лях (вроде ATtiny2313) он может быть реализован только через интерфейс USI (о котором рассказано в этой главе далее). Существуют, разумеется, и программные методы имитации SPI (в частности, они описаны в Application Notes AVR320). Более подробно об обращении с SPI рассказано в главе 12. SPI, как правило, служит для высокоскоростного обмена между микросхемами в пределах одной платы или, как максимум, одного устройства. Обмен данными со многими типами карточек памяти, включая и наиболее распространенные в на- стоящее время карточки Secure Digital (SD), есть также просто вариант SPI. Кроме собственно SPI, в различных микросхемах можно встретить его модификацию под названием интерфейс Microwire (3-wire). Он отличается тем, что работает сразу
Глава 3. Периферийные устройства и прерывания 61_ с 16-разрядными словами, и применяется в тех случаях, когда нужно передавать за один прием большое количество данных, — например, при управлении многораз- рядными дисплеями. Для более удаленных компонентов (датчиков, дисплеев, кла- виатур и т. п.) чаще используется более простой в схемотехнической реализации (но более сложный в программном отношении) интерфейс TWI, или, иначе, 12С. Интерфейс TWI (12С) Аббревиатурой TWI (Two-Wire Interface, двухпроводной интерфейс) компания Atmel в надежде, видимо, избежать патентных разборок, назвала свою реализацию последовательной шины данных 12С, разработанную фирмой Philips еще в начале 1980-х. Не знаю, насколько это получилось, но с 1 октября 2006 года лицензионные отчисления за использование протокола PC все равно отменены (остались только отчисления за выделение эксклюзивного адреса на шине PC, но непосредственно к МК это не относится — там адрес выделяется программистом каждый раз зано- во), так что можно называть вещи своими именами. Иногда в документации на раз- личные датчики можно встретить и название 2-Ware (или Two Ware) — обычно под этим подразумевается также 12С или его модификации. Интерфейс 12С, как и UART, требует двухпроводного соединения с обязательным объединением «земель», т. к. сигналы в нем абсолютные, а не дифференциальные, отсчитываются относительно «земли» и соответствуют уровням КМОП-логики. Как и в SPI, в интерфейсе 12С устройства могут работать в режиме «ведущий» (Master) или «ведомый» (Slave). В отличие от большинства других интерфейсов, ведомые устройства с интерфейсом 12С (память, часы реального времени, различ- ные датчики) должны иметь индивидуальный адрес, присваиваемый производите- лем (тот самый, за который платятся отчисления). Для различения одинаковых уст- ройств, если их более двух на одной линии, в некоторых типах устройств имеются дополнительные адресные линии, выводы для установки индивидуального адреса или входы типа «выбор кристалла». 12С значительно более медленный интерфейс, чем SPI, сравним в этом отношении с UART (типовое значение скорости обмена— 100 кбит/с) и потому применяется там, где не требуется скоростной передачи данных. Но он гораздо удобнее UART, т. к. позволяет без дополнительных проблем подключать большое количество внешних устройств, занимая при этом одни и те же два вывода контроллера. В об- щем-то с помощью этого интерфейса можно подключать и достаточно удаленные устройства, если не нужна высокая помехозащищенность. Есть и близкий родственник 12С под названием 1 -Ware, позволяющий осуществлять двухстороннюю связь по одной линии (причем она же обычно является и шиной питания). Интересно, что существуют устройства (например, датчики температуры фирмы Dallas Semiconductor, в настоящее время являющейся подразделением фир- мы Maxim), которые способны работать вовсе без питания, получая его от сигналь- ных линий интерфейса 12С или 1-Wire. Более подробно о работе с интерфейсом 12С рассказывается в главе 13.
Ъ2_ Часть I. Общие принципы устройства и функционирования AtmelAVR Универсальный последовательный интерфейс USI В AVR семейства Tiny, за исключением некоторых старших моделей, UART (или USART), SPI и TWI вообще отсутствуют. Вместо них предлагается интерфейс USI (Universal Serial Interface), который представляет собой по сути «голый» сдвиговый регистр (usidr) с регистром управления (usicr) и статуса (usisr) без каких-либо буферов данных. С USI связано аппаратное прерывание. В состав USI входит 4-битовый счетчик тактовых импульсов, управляющийся от регистра usisr. USI можно использовать и в качестве трехпроводного SPI, и в качестве двухпроводного TWI, и для имитации UART, и еще для ряда применений — например, тактовый счетчик совместно с TimerO может образовывать 12-разрядный таймер, с помощью USI также можно организовать дополнительное внешнее прерывание и т. п. Управ- ляется сдвиг в регистре usidr либо «вручную» (пользовательской программой), ли- бо от прерывания переполнения TimerO, либо от внешнего источника (что допуска- ет функционирование устройства с USI в качестве ведомого). С USI связаны три внешних вывода: вход и выход данных (DI, DO) и ввод/вывод тактовых импульсов (USCK). USI так и не получил широкого признания, и к тому же в большинстве моделей, включая все семейство Mega, он вообще отсутствует, поэтому этот интерфейс мы в книге не рассматриваем, — на практике проще выбрать контроллер с наличием нужного порта или выполнить имитацию программным путем. Если хотите попро- бовать USI для какого-нибудь экзотического применения, то в Tiny2313, например, нет ни SPI, ни TWI, зато есть и USART, и USI. Прерывания Возможность реагировать на события и аппаратно прерывать выполнение основной программы — важнейшая функция современных микроконтроллеров. Без нее МК никогда бы не заслужили звания «интеллектуальных устройств» и не вышли бы за уровень «навороченной» заготовки для создания сложных логических схем. На- помним, что всю деятельность любого компьютерного устройства — от миниатюр- ного смартфона и компьютерных «мозгов» автомобиля до мощной системы для обработки графики или управления сложным технологическим процессом — мож- но представить в виде разветвленной системы реакций на различные события, которую осуществляет аппаратная часть совместно с операционной системой. Через обработчики нерегулярно возникающих событий (читай— через прерыва- ния) реализуется 99% функциональности всего компьютерного устройства, и лишь небольшая ее часть представляет собой «плановое» выполнение линейного кода: в основном в начале и в конце текущего сеанса работы. Остальное время любая система проводит в ожидании событий, на которые нужно реагировать. В архитектуре IBM PC аппаратные прерывания осуществляет специальный кон- троллер прерываний, в МК же, в полном соответствии с концепцией «computer-on- chip», эта функция является встроенной. Любое из периферийных устройств может вызывать свое прерывание, а чаще всего и не одно. В этом разделе мы рассмотрим
Глава 3. Периферийные устройства и прерывания 63_ общие свойства прерываний МК AVR и различные варианты общей идеологии их использования. Событиями, на которые нужно реагировать, могут быть сигналы от других уст- ройств, как посылаемые автоматически, так и обусловленные действиями пользо- вателя. Могут такие сигналы возникать и внутри контроллера, как следствие рабо- ты его внутренних узлов. Как можно обнаружить и обработать такие события? Наиболее универсальный способ, который применяется в системах всех уров- ней^ — организация основной программы в виде бесконечного цикла. Внутри этого цикла тем или иным способом отслеживается возникновение неких событий. Такой цикл, например, представляет собой почти любая Windows-программа (как и Windows в целом). Но организация реакции на события внутри такого цикла в Windows — отдельная и весьма сложная тема, а в МК все может быть сделано просто и прозрачно: при наступлении события устанавливается определенное зна- чение некоей переменной, часто именуемой флагом (в простейшем случае это мо- жет быть один бит в специально отведенном регистре или в ячейке памяти или да- же просто состояние вывода МК, задаваемое извне — например, нажатием кнопки). Основная программа в цикле последовательно проверяет значение всех необходи- мых флагов, регистров или выводов и при изменении какого-либо из них переходит к соответствующей процедуре (подпрограмме) обработки события. Раньше, когда еще никаких встроенных прерываний в микропроцессорах не было, только так всегда и поступали (пример такой простейшей программы приведен в главе 6). Ориентирует на подобную организацию работы и Arduino (по крайней мере, на начальном этапе). Логика такой линейно выполняющейся программы по- нятна даже полному чайнику, кроме того, она проста в отладке, хоть элементарны- ми методами, хоть с применением специальных отладочных сред. В качестве альтернативы такому «ручному» способу отслеживания событий и были придуманы прерывания (interrupts). «Ручной» способ во многих отношениях неудо- бен: во-первых, события могут требовать безотлагательного внимания, а программа бывает при этом занята «своими делами». Во-вторых, при таком подходе некое со- бытие нетрудно попросту потерять — во время обработки очередного из них про- грамма выходит из цикла отслеживания, и до возврата в него может пройти доста- точно много времени, когда условия для наступления следующего события уже ис- чезнут. Если при этом первое по порядку отслеживания событие происходит весьма часто и требует много времени на обработку, то не исключена ситуация, когда ко всем остальным программа попросту не успеет перейти или будет их отслеживать выборочно, — многим пользователям Windows знакома такая ситуация, когда не- кая программа, выполняя громоздкие процедуры вроде записи больших массивов информации на флешку, просто «вешает» все остальные программы, не давая сис- теме даже отслеживать перемещение мыши. Использование аппаратных прерываний позволяет полностью избежать подобных ситуаций, если нагрузка на МК не превышает его возможностей, и сгладить по- следствия в случае, когда в отдельные моменты времени нагрузка на МК его воз- можности все-таки превысила. По сути аппаратные прерывания организуются так же, как и «ручное» обнаружение, — в цикле: при возникновении условий для пре-
64 Часть I. Общие принципы устройства и функционирования AtmelAVR рывания автоматически устанавливаются некие биты-флаги, сигнализирующие о наступлении определенного события. Но если в первом случае отслеживание зна- чений этих флагов и реагирование на событие возлагается на программиста, то при возникновении аппаратного прерывания переход к его обработке осуществляется автоматически, — программисту надо лишь установить условия для возникновения нужного прерывания и написать правильно оформленную процедуру обработки. Во время обработки каждого прерывания другие прерывания произойти не могут (если вы это специально не разрешите— о чем рассказано далее), но установленный автоматически флаг никуда не денется, и отложенное прерывание произойдет по окончании обработки предыдущего. Порядок выполнения прерываний Для надлежащего управления процессом возникновения прерываний флаги разре- шения прерываний образуют иерархию. Во главе этой структуры стоит бит i реги- стра флагов sreg, который разрешает (если установлен в логическую единицу) или запрещает (если установлен в логический ноль) аппаратные прерывания вовсе. Как правило, непосредственно с этим битом (как и вообще с регистром sreg) програм- мисту дело можно не иметь — для общего разрешения (запрещения) прерываний предусмотрены специальные команды: sei (разрешить) и cli (запретить), устанав- ливающие этот бит в нужное состояние (аббревиатуры sei/cii и означают set I/clear I). Отметим, что по умолчанию бит i регистра флагов sreg сброшен, т. е. прерывания при запуске МК запрещены. Для того чтобы их разрешить, необходимо в процедуре начальной инициализации, выполняющейся по сбросу МК, разместить команду sei. Кроме общего флага прерываний, для каждого конкретного прерывания имеется свой разрешающий/запрещающий бит, расположенный в соответствующем регист- ре (например, для таймеров — это регистры timsk или etimsk, для внешних преры- ваний— gimsk, в более поздних моделях зачем-то переименованный в gicr1, и т. п.). При использовании прерываний эти биты необходимо устанавливать в со- стояние логической единицы, в противном случае автоматического вызова преры- ваний не произойдет. Если во время обработки прерывания произошли еще какие-то события, вызываю- щие прерывания, то их флаги окажутся установленными, и процедуры их обработ- ки начнут выполняться незамедлительно (точнее, после выполнения одной коман- ды основной программы, — о чем рассказано далее) в том порядке, в каком они расположены в таблице векторов прерываний. Программист может нарушать этот порядок, разрешая вложенные прерывания, но делать это без крайней нужды не рекомендуется, — такая операция требует учета большого числа неочевидных фак- торов (некоторые примеры приведены далее) и намного усложняет отладку про- 1 В файлах макроопределений (*.inc) часто стоит и то, и другое, потому в этой книге я буду пользо- ваться преимущественно старым обозначением gimsk, которое работает для всех «наших» моделей одинаково. Если в каких-то моделях это название будет недействительно, то ассемблер укажет на ошибку.
Глава 3. Периферийные устройства и прерывания 65^ граммы. Подробнее обработку прерываний мы рассмотрим в главе 6 — в сравнении с выполнением обычных подпрограмм. Разновидности прерываний Всего в различных моделях AVR существует от 6—8 (младшие Tiny) до нескольких десятков прерываний (например, в ATmega2560 их 57). Как и в ПК, прерывания в микроконтроллерах бывают двух видов. Но если в ПК прерывания делятся на аппаратные (например, от таймера или клавиатуры) и программные (фактически не прерывания, а подпрограммы, записанные в BIOS, — с распространением Windows это понятие почти исчезло из программистской практики), то в МК, естественно, все прерывания аппаратные, а делятся они на внутренние и внешние. Внутренние прерывания могут возникать от любого устройства, которое является дополнительным по отношению к ядру системы: таймеров, аналогового компаратора, последовательного порта и т. п. Внутреннее прерывание — это событие, которое возникает в системе и прерывает выполнение основной программы. Внутренних прерываний в AVR довольно много, и в совокупности они служат для взаимодейст- вия устройств с ядром системы, — мы еще к этому вопросу будем неоднократно возвращаться при рассмотрении конкретных устройств. Отдельной главы для рассмотрения внешних прерываний в этой книге не преду- смотрено, потому что они почти всегда играют вспомогательную роль для выпол- нения каких-либо других функций. Вы встретитесь с внешним прерыванием уже в главе 6 при рассмотрении простейших программ взаимодействия с контроллером, и в дальнейшем мы еще не раз будем о них говорить. А сейчас рассмотрим основ- ные особенности внешних прерываний. Основных внешних прерываний у большинства МК AVR два или три. В старших Mega их может быть и больше — например, у ATmega2560 (основа для Arduino Mega) их целых восемь. Основные внешние прерывания могут возникать незави- симо друг от друга. Кроме основных, в ряде контроллеров, в том числе в основном «ардуновском» ATmega328, имеются дополнительные внешние прерывания типа pcint (Pin Change Interrupt, прерывания по изменению уровня на выводе), поэтому суммарное число их оказывается равно 24. Итак, внешнее прерывание — это событие, которое наступает при наличии сигнала на одном из входов, специально предназначенных для этого (например, into и inti). Выделяют четыре вида событий, вызывающих такое прерывание: это может быть низкий уровень напряжения, любой перепад (фронт) напряжения, а также от- дельно положительный или отрицательный фронт на соответствующем выводе. Любопытно, что прерывания по всем этим событиям выполняются, даже если соот- ветствующий вывод порта сконфигурирован на выход (это, в частности, позволяет вызывать такие прерывания программно). Кратко рассмотрим особенности этих режимов. Прерывание по низкому уровню (режим установлен по умолчанию, для его инициализации достаточно разрешить соответствующее прерывание) возникает всякий раз, когда на соответствующем входе присутствует низкий уровень. «Всякий раз» — это значит, что действительно
(№ Часть I. Общие принципы устройства и функционирования AtmelAVR всякий, т. е. если отрицательный импульс длится какое-то время, то прерывание, закончившись (когда выполнится соответствующий обработчик), повторится снова и снова, почти не давая основной программе работать. Поэтому обычная схема использования этого режима — сразу же по возникновении в начале обработчика запретить это прерывание (процедура обработки при этом, раз уж началась, выпол- нится до конца) и разрешить опять только тогда, когда внешнее воздействие долж- но уже закончиться, — например, если это нажатие кнопки, то его следует опять разрешить по таймеру через одну-две секунды. В отличие от прерываний по низкому уровню, прерывания по фронту или спаду выполняются один раз на каждый импульс. Конечно, от дребезга контактов здесь никакой защиты нет и быть не может, потому что МК не способен отличить дре- безг от серии коротких импульсов. Если это критично, нужно либо принимать внешние меры по защите от дребезга, либо прибегнуть к тому же способу, что и для прерывания по уровню: внутри процедуры обработчика прерывания первой командой запретить само прерывание, а через некоторое время в другой процедуре (например, по таймеру или по иному событию) опять его разрешить. Но если по нажатию кнопки просто какая-то переменная или вывод порта устанавливается в некое состояние (зажигает светодиод, например), то ничего страшного не случит- ся, если оно установится несколько раз подряд, — только следует учесть, что по- следний раз это может произойти с отпусканием кнопки. У внимательного читателя возникает законный вопрос — а зачем вообще нужен режим внешнего прерывания по уровню, которое специально еще требуется снача- ла запрещать, а потом разрешать? Дело в том, что оно во всех моделях выполняется асинхронно — в тот момент, когда низкий уровень появился на выводе МК (точно так же асинхронно, кстати, выполняются и прерывания pcint, — о чем рассказано далее). Конечно, прерывание можно обнаружить только по окончании текущей команды, так что очень короткие импульсы могут пропасть. Но в режиме управле- ния по фронту у большинства моделей прерывания into и inti определяются на- оборот, только синхронно — в момент перепада уровней тактового сигнала кон- троллера (поэтому их длительность не должна быть короче одного периода такто- вого сигнала). И по большому счету разницы в этих режимах никакой бы не было, если не учесть то обстоятельство, что синхронный режим требует непременно наличия этого самого тактового сигнала. Потому асинхронное внешнее прерывание может «раз- будить» контроллер, находящийся в одном из режимов «глубокого» энергосбере- жения, когда тактовый генератор не работает, а синхронное — нет. Старые МК се- мейства Classic вывести из глубокого «сна» путем внешнего прерывания (напри- мер, нажатием кнопки) можно было только внешним прерыванием по уровню, что не всегда удобно. У большинства же моделей семейства Mega (из младших моделей — кроме ATmega8), имеется еще одно внешнее прерывание — int2, которое, в отличие от into и inti, происходит только по фронтам и только асинхронно. У некоторых старших моде- лей Mega (ATmega64/128, ATmega 164/324/644, ATmega640-2160 и др.) асинхронно выполняются все прерывания во всех режимах. Это значительно повышает удобст- во пользования семейством Mega в режимах энергосбережения.
Глава 3. Периферийные устройства и прерывания 67 Дополнительные прерывания pcint возникают при появлении любого фронта им- пульса на входах портов, причем асинхронно. Прерывания типа pcint, ввиду их многочисленности, обычно объединены в небольшое количество групп, каждая из которых связана со своим вектором (до восьми выводов на один вектор). Например, в ATtiny828 в отдельном регистре pcicr отведено четыре бита (pcieo-з), устанавли- вающихся каждый тогда, когда наступило событие на одной из четырех групп вы- водов (PCINTO-7, PCINT8-15, PCINT16-23, или PCINT24-27). При этом возникает одно из прерываний: pcinto, pcinti, pcint2, pcint3. Выделять, на каком именно выводе (выводах) перехвачено прерывание, необходимо заранее, для чего служит регистр pcmsk. При этом прерывание типа pcint наступает при любом переключе- нии уровня (и из нуля в единицу, и обратно), и, в отлитие от обычных внешних прерываний intx, управлять этим процессом невозможно. Зато их также можно ис- пользовать для вывода контроллера из «сна» гораздо более удобным способом, чем с помощью прерываний into и inti по уровню. Из «наших» моделей контроллеров INT2 доступен в ATmega8535, а дополнитель- ные прерывания типа pcint— в ATtiny2313. Примеры использования этих преры- ваний приведены в главе 14. Об общих принципах использования прерываний При переносе основной функциональности программы в прерывания в ряде случа- ев основная программа может состоять только из единственной процедуры беско- нечного пустого цикла: cykie: r jmp cycle (что равносильно известному оператору while (1) в языке С). Все остальное (в том числе начальная инициализация по сбро- су) будет осуществляться через прерывания. Поясним подробнее, чем такая концепция лучше и в каких случаях наступают ее ограничения. Во-первых, потери событий оказываются намного менее вероятными. Прерывания могут потеряться только в том единственном случае, когда контроллер начинает захлебываться событиями, идущими одно за другим с временными про- межутками менее длительности обработки. Причем, если у вас пришло два подряд прерывания, то с ними ничего не случится при любом промежутке времени между ними (разве что при промежутке менее периода тактовой частоты некоторые собы- тия могут просто остаться незамеченными). Второе прерывание, как мы говорили, запоминается в состоянии своего флага и будет обработано, можно сказать, «свое- временно или несколько позже». Критичный случай наступает, когда с минималь- ным промежутком следуют подряд три одинаковых события или больше — вот то- гда промежуточные события действительно потеряются. Рассмотрим еще подробнее условия возникновения такой ситуации. Критичные промежутки между событиями при обычных тактовых частотах составят менее единиц-десятков микросекунд. Это цифра увеличивается до миллисекунд в случае, если задействовать в прерываниях медленные процедуры (ожидание отправки по UART, обмен по 12С, запись в EEPROM и т. п.), но и такой промежуток совершенно не является запредельно большим в обычных случаях использования контроллера. Если нужно обработать несколько событий, происходящих подряд друг за другом,
(№ Часть I. Общие принципы устройства и функционирования Atmel AVR то их и правда лучше принять с минимальными задержками, сложить в память дан- ных полученные значения и.потом спокойно обработать, но это не очень частая за- дача, которую спокойно можно выделить в частный случай. Иное дело, когда есть непрерывный поток таких событий — если действительно надо непрерывно обра- батывать события с частотами порядка сотен килогерц-мегагерц, то просто следует признать, что 8-битные AVR тут непригодны, и выбрать платформу пошустрее. Во-вторых, программы, состоящие из законченных подпрограмм, логика выполне- ния которых не размазана по всему коду, легче составляются и отлаживаются. В непрерывных прыжках от одного потока команд к другому легко запутаться, и это является одной из главных причин возникновения трудно отслеживаемых ошибок времени выполнения у новичков. Да, программу, состоящую в основном из обработчиков прерываний, отлаживать с помощью программных эмуляторов (Atmel Studio или, например, Proteus) более трудоемко, чем линейный код в глав- ном цикле, что сидит у многих привычных к этим инструментам программистов в подкорке и бессознательно заставляет их по возможности избегать прерываний. Помещение в прерывание логически законченного кода позволяет значительно об- легчить эту процедуру: обработчик просто отлаживается как отдельная программа, которая затем подключается к основному коду как готовый модуль. Конечно, такая идеализированная картина достижима далеко не всегда, но стремиться к ней вполне возможно. Разумеется, мы не будем считать такой подход за догму и стремиться непременно все выполнить по прерываниям. Все зависит от удобства и конкретной ситуации — есть вполне штатные функции, которые положено выполнять именно по прерыва- ниям, а мы, тем не менее, от этого откажемся, и ничего, как увидите, не потеряем. Но в целом наша концепция будет стремиться к переносу функциональности в пре- рывания с минимализацией действий в основном цикле — как вы увидите далее, он частенько будет оставаться пустым. Заметки на полях Нередко можно услышать мнения, противоречащие этой концепции, — чаще всего утвер- ждается, что в обработчике прерывания программа должна совершить минимум необ- ходимых действий и как можно быстрее выйти обратно в основной цикл. Эта точка зрения распространилась не без влияния автора [2] и абсолютно справедлива для тех, кто работает с многозадачными операционными системами для контролеров, — вы бы, конечно, тоже не захотели, чтобы запись на какую-нибудь флешку повесила вам все остальные процессы в Windows. К сожалению, читатели из целевой аудито- рии не смогли отнестись к ней критически и осознать границы ее применимости — ав- тор [2], благодаря большому опыту работы на самых разных системах, всегда в пер- вую очередь принимает во внимание условие переносимости кода, а семейства кон- троллеров все разные, и в некоторых системах действительно полагаться полностью на прерывания было бы крупной ошибкой. Но в частных случаях одного только 8-бит- ного AVR, без применения каких-то ОС и притом с ограниченным числом используе- мых моделей, эти соображения практически теряют свое значение. В реальности, если говорить о конкретно AVR-контроллерах, преимущественный пе- ревод кода в обработчики прерываний в большинстве случаев будет только благом. Как выразился в обсуждении этого вопроса сам автор [2], «по сути дела, это бесплат- ная многозадачность». Следует только подходить к этому вопросу разумно, и ради одной только идеи не перебарщивать ни в ту ни в другую сторону.
ГЛАВА 4 Микроконтроллеры AVR на практике В дальнейшем повествовании мы не будем пытаться объять необъятное и терять время на обсуждение всевозможных нюансов практического применения всех представителей многочисленного семейства AVR. Конечно, я буду стараться по возможности представить дело в широком контексте, чтобы вам при необходимо- сти было легче осваивать другие модели, но в основном мы сосредоточимся на не- многих самых удобных и простых в программировании — пусть они и не самые современные, но в них есть все, что нужно, и даже еще много сверх того. В преды- дущих главах такие модели упоминались под названием «наших» контроллеров: это ATmega8, ATmega8535 и ATtiny2313. В конце этой главы мы подытожим их основные характеристики. Но сначала еще уточним некоторые общие вопросы их применения на практике и разберемся с необходимыми для этого инструментами и оборудованием. Сразу укажем, что работу с любой моделью AVR надо начинать со скачивания ее «даташита» с официального сайта Microchip. Документация на конкретную разно- видность МК потребуется, как минимум, для того, чтобы картинка разводки его выводов была всегда перед глазами — обращаться к ней придется на каждом шагу. В известной степени официальную англоязычную документацию могут заменить упоминавшиеся уже книги Евстифеева [6,7] — по крайней мере для тех контролле- ров, которые в них представлены. С одним только нюансом: в них вы на русском языке найдете информацию по устройству и функциям базовых моделей, а харак- теристики, касающиеся потребления, рабочих частот и тонкостей работы в различ- ных режимах могут для контроллеров разных модификаций (с отличающимися буквами) существенно различаться. Потому в любом случае надо иметь официаль- ное описание именно той модификации, которая у вас имеется. Особенности практического использования МК AVR При использовании AVR возникает ряд вопросов практического характера, игно- рирование которых может иногда привести к порче самой микросхемы, к неработо- способности или сбоям устройства. Подобные проблемы мы еще неоднократно
70 Часть I. Общие принципы устройства и функционирования Atmel AVR будем рассматривать в соответствующих главах применительно к конкретным узлам, здесь же остановимся на некоторых общих вопросах подключения МК AVR. Начнем с выводов питания. У семейства Tiny таких выводов обычно два: VCC и GND — и подключение их не вызывает вопросов. У Mega из-за наличия аналого- цифрового преобразователя выводов питания больше: добавляется еще аналоговое питание AVCC и как минимум еще один «земляной» вывод GND. В Сети гуляет заблуждение, первоисточником которого, видимо, следует считать первые издания справочника Евстифеева: дополнительный вывод «земли», находящийся вблизи аналогового питания AVCC, по аналогии именуют «аналоговой землей» — AGND. На самом деле термин «аналоговая земля», встречающийся в описаниях AVR, от- носится не к контроллеру, а к аналоговой части остальной схемы. И никакой раз- ницы во всех «земляных» выводах (а у многовыводных AVR в планарных корпусах выводов GND может быть много) нет — все они соединены внутри кристалла, и в цифровых функциях любой из них (или все вместе) можно использовать в качестве общего провода схемы (в изданиях [6,7] эта ошибка почти везде исправлена). За- метьте, что вывод питания AVCC необходимо подключать только в том случае, если вы используете АЦП, иначе его можно оставлять без внимания. Подробности Однако есть-таки один нюанс — если схема имеет аналоговую часть, которая подклю- чается к АЦП, то общий провод этой схемы (аналоговую «землю» платы) следует под- ключать именно к тому выводу GND, который размещается вблизи AVCC. Во-первых, потому, что так банально удобнее (для того и выводили отдельную «землю» рядом с аналоговым питанием, чтобы не тащить проводники через всю плату), во-вторых, этот вывод находится рядом с узлом АЦП на кристалле, и на этот узел будет меньше попадать помех по внутренней шине питания. Иными словами, в особом наименова- нии этого вывода — AGND — имеется определенный смысл, но еще раз повторим, что он возникает только при наличии аналоговых подключений в АЦП, а в остальном все «земли» равноправны. Микросхемы AVR, как и всякая КМОП-логика, благодаря высокому порогу сраба- тывания эффективно защищены от помех по шине «земли». Однако они ведут себя гораздо хуже при помехах по шине питания. Поэтому не забывайте о развязываю- щих конденсаторах, которые нужно устанавливать непосредственно у выводов питания (керамические 0,1-1 мкФ), а также про качество сетевых выпрямителей и стабилизаторов. Собственно, все то же самое рекомендуется и для Arduino, где часть этих требований уже выполнена изготовителем при проектировании плат. У выводов портов МК при работе в качестве входных линий имеется внутренний подключаемый «подтягивающий» резистор (pullup, т. е. подсоединенный к шине питания). Это, казалось бы, решает одну из обычных схемотехнических проблем, когда наличие такого резистора требуется для подключения двухвыводных кнопок или выходов с «открытым коллектором». Однако, как мы уже говорили ранее (см. главу 5), в большинстве случаев надежность и помехоустойчивость схемы заметно повысится, если использовать внешний резистор сопротивлением 1-10 кОм (в кри- тичных для потребления случаях его можно увеличить до 30 кОм). «Подтягивающий» резистор следует устанавливать и на выводе RESET (о чем также уже говорилось в главе 2). Кроме того, такие резисторы желательно устанав-
Глава 4. Микроконтроллеры AVR на практике 71_ ливать на выводы SCK, MOSI и MISO соответствующих портов в случае, если они используются для программирования и подключены к программирующему разъему ISP (см. главу 5). Обязательно их устанавливать также по выводам внешних преры- ваний into-int2 и pcint, если они задействованы. Если эти выводы при выполне- нии названных функций не «подтягивать» к напряжению питания дополнительны- ми резисторами (пусть это и не оговорено в технической документации), то не ис- ключены ложные срабатывания внешних прерываний, перезапуски системы, а при очень мощных помехах— даже порча программы в памяти программ. С другой стороны, когда выводы программирования служат в качестве обычных портов, сконфигурированных на выход, а в устройстве применяются режимы энергосбере- жения, наличие «подтягивающих» резисторов может привести к лишнему потреб- лению тока (при установке вывода в логический ноль через резистор потечет ток от источника питания на вход МК). Так что если реализован один из режимов энерго- сбережения, нужно тщательно проанализировать схему, чтобы исключить ситуа- ции, при котором через эти резисторы протекает ток. Также всегда следует устанавливать внешние резисторы при работе выводов МК на общую шину, как в интерфейсе 12С, или просто при подсоединении входа МК к вы- ходу другого устройства с открытым коллектором — например, к выходу монито- ров питания (см. главу 2). Сопротивление встроенного резистора в таких случаях слишком велико для того, чтобы электромагнитные помехи («наводки») на нем эф- фективно «садились», а в случае 12С при большой емкости шины (т. е. если она дос- таточно длинная) могут недопустимо затягиваться фронты импульсов. При этом еще раз повторим, что каждый дополнительный «подтягивающий» резистор увели- чивает потребление схемы, и в случае экономичных режимов их установку стоит продумывать отдельно — возможно, иногда от них целесообразно и отказаться. Корпуса МК и их установка на плату Примеры некоторых типов корпусов, в которых выпускаются микросхемы AVR, приведены на рис. 4.1. Более подробную информацию на эту тему можно найти в приложении 2 и в технической документации на контроллеры. Для любительских целей при ручной пайке плат пригодна фактически только одна разновидность корпуса (см. рис. 4.1) — это DIP или, иначе, PDIP-корпус (буква «Р» впереди просто означает, что корпус из пластика, а не керамический). Такой тип корпуса обозначается буквой «Р» после значения частоты (например, ATmega8- 16PU) — и он одинаково удобен и для запайки в плату «наглухо», и для установки на панельку. Именно в таком корпусе устанавливается контроллер в плату Arduino Uno, поэтому его можно оттуда извлекать и использовать плату и контроллер от- дельно друг от друга. Надо только учитывать, что DIP-корпуса с более чем 40 кон- тактами не встречаются, поэтому не все старшие модели ATmega удобны для ра- диолюбительской практики (см. табл. П2.2 в приложении 2). Ранее, когда внутрисхемного программирования не существовало, были популярны корпуса типа PLCC с подпружиненными контактами (обозначаются буквой «J»). Вместе с обязательно необходимой в этом случае панелькой они занимают на плате
72_ Часть I. Общие принципы устройства и функционирования Atmel AVR места даже больше, чем DIP-корпуса, зато их можно неоднократно вставлять и из- влекать из панельки без риска повреждения контактов. К сожалению, современные разновидности контроллеров в таких корпусах практически не выпускаются — из доступных AVR это фактически только старинные ATmega8515 и ATmega8535. Рис. 4.1. Примеры некоторых типов корпусов для МК AVR Если у вас есть возможность быстро и недорого разводить и изготавливать платы, то тут все преимущества имеют корпуса с планарным расположением выводов (ти- па SO, SOT или, иначе, SOIC) или — для большего количества контактов — типа TQFP (с выводами со всех четырех сторон). В планарных корпусах, кроме всего прочего, доступны любые модели контроллеров. Корпуса типа SO (SOT, SOIC) обозначаются буквой «S», корпуса TQFP— буквой «А». Они занимают гораздо меньшую площадь на плате, но это преимущество почти теряется при необходимо- сти их перепрограммировать по ходу работы. Так как извлекать их нельзя, то обя- зательным атрибутом платы становится ISP-разъем, который сам занимает доста- точно много места. Bootloader— загрузчик через последовательный порт (о нем рассказано в главах 5 и 6) — тут поможет мало, потому что к нему требуются свои внешние компоненты: как минимум разъем для подключения к порту, а также адаптер. Так что SOIC/TQFP-корпус хорош в основном тогда, когда нужно изгото- вить несколько идентичных экземпляров уже готового и отлаженного на опытном образце устройства. Подробности Бывает, что в продаже имеются микросхемы нужной разновидности только в планар- ных корпусах (типа SO или SOIC), а изготовить плату под них затруднительно. В этом случае выйти из положения можно, если приобрести переходную плату на DIP-корпус,
Глава 4. Микроконтроллеры AVR на практике 73 показанную на рис. 4.2, слева и в центре. Эта плата годится для числа контактов от 8 до 28. К сожалению, она подходит только для двухрядных корпусов с шагом 1,27 мм и вдвое меньше (TSSOP 0,65 мм, на обратной стороне). Для различных микросхем (в том числе и для некоторых AVR) часто встречаются корпуса с промежуточным ша- гом 0,80 мм, для которых эта плата не подходит. В этом случае, а также для корпусов с выводами с четырех сторон (TQFP), можно поискать среди имеющихся в продаже макетных плат подходящую с площадками под нужные типоразмеры корпусов (пример для случая TQFP приведен на рис. 4.2, справа, а для двухрядных корпусов с меньшим числом выводов — на рис. 4.4, справа), из которых можно вырезать фрагмент и при- паять туда микросхему. W^v-%4' ^--' Рис. 4.2. Платы для перехода от корпусов с пленарными выводами к шагу 2,5 мм Показанный на рис. 4.1, вверху справа корпус типа QFN занимает на плате меньше всего места, но практически не подходит для любительских конструкций, т. к. обычным паяльником надежную пайку его контактов выполнить нереально, — та- кие корпуса паяются только с помощью нанесения на контактные площадки специ- альной пасты и последующего прогрева всей платы по определенному алгоритму. Необходимое оборудование и приспособления В этом разделе мы обсудим различные варианты оборудования и приспособлений, необходимых для нашей деятельности. Кстати, все то же самое рекомендуется и для облегчения работы с Arduino — просто обычно об этом стараются лишний раз не упоминать, чтобы не отпугивать новичка. Итак, нам потребуются... Панельки Любительские конструкции обычно создаются в одном экземпляре, который одно- временно служит и отладочным макетом. В этом случае отладка значительно упро- стится, если предусмотреть не просто возможность программирования прямо в схеме (т. е. установку соответствующего ISP-разъема, — см. главу 5), но и уста- новку панельки для контроллера. В последнем случае контроллер можно будет без-
_74 Часть I. Общие принципы устройства и функционирования Atmel AVR болезненно заменить, если вы своими экспериментами умудрились вывести его из строя. Впрочем, можно не устанавливать ISP-разъем, если изменений предполага- ется немного, сэкономив место на плате и время на пайку, — проще тогда извлечь контроллер, запрограммировать его отдельно и вставить обратно. Как мы уже говорили, самый удобный корпус контроллера для этих целей — DIP (PDIP). Панельки для него выпускаются двух видов: обычные с плоскими контак- тами и цанговые (рис. 4.3). Вторые существенно надежнее, но хуже приспособлены для многократного извлечения и вставки микросхемы, — требуется большее уси- лие, и наличествует риск погнуть контакты при перекосе корпуса. Рис. 4.3. Панельки для микросхем: вверху простые, внизу-— цанговые DIP-корпуса бывают узкие и широкие, и это следует учитывать при выборе панель- ки под конкретный контроллер. Так, например, показанные на рис. 4.3 панельки DIP-28— узкие (подходят под ATmega8/88/l68/328), a DIP-40— широкие (ATmegal6, ATmega8535 и т. д.). Подробности Учтите, что для макетирования на беспаечной макетной плате не годятся «голые» DIP-корпуса— их выводы просто не достанут до контактов платы в гнездах, поэтому панельки понадобятся для всех микросхем, присутствующих в схеме, а не только для контроллера. Большинство обычных панелек (с плоскими контактами) не подойдут по той же причине. Существуют, правда, панельки с длинными выводами, которые под- ходят для вставки в макетную плату, но встречаются в продаже они отнюдь не на каж- дом углу, тем более всех необходимых конфигураций. Поэтому цанговые панельки, у которых выводы длиннее, чем у обычных, — единственный надежный и беспро- блемный вариант для макетирования без пайки. Только не забывайте отформатиро- вать выводы новой микросхемы, чтобы они стояли точно под углом 90° к корпусу, что особенно актуально для цанговых панелек. Тонкая часть вывода при неточном попа- дании в отверстие легко загибается под самым причудливым углом, а вырубленные из плоского листа мягкого металла выводы DIP-корпуса этого не переносят совершен- но — обламываются уже после двух-трех перегибов.
Глава 4. Микроконтроллеры AVR на практике 75^ Макетные платы Макетные платы бывают беспаечные (со специальными зажимными контактами) и печатные (для пайки компонентов). Беспаечные макетные платы, как я надеюсь, читателям уже знакомы, т. к. с них обычно начинается обучение электронике, в том числе и Arduino. Про беспаечные платы нужно отметить только один момент — не приобретайте их в случайных интернет-магазинах! Популярность этих плат такова, что их делают очень разные производители, в числе которых много не слишком добросовестных. И если стенную розетку, из которой через месяц начинает выва- ливаться вилка, не так уж и сложно заменить на фирменную, то макетная плата приобретается не на один год, и внезапно обнаружить, что схемы перестают рабо- тать из-за хронического неконтакта в плате, бывает очень грустно, — прочих при- чин вполне хватает, чтобы из-за сэкономленных пары сотен рублей создавать себе еще и такие проблемы. Хорошие макетные платы продают в торгующих такого ро- да товарами фирменных магазинах. Рис. 4.4. Печатные макетные платы: слева — универсальная с шагом 2,5 мм; справа — для перехода от пленарных корпусов с мелким шагом Макетные печатные платы под пайку не стандартизированы и имеют самые разные конфигурации. Для обычных нужд целесообразно приобрести (или заказать) уни- версальную плату простейшей конфигурации (рис. 4.4, слева). Отверстия на ней расположены с шагом 2,5 мм, никак не соединяются между собой и обязательно должны быть металлизированными с контактными площадками с обеих сторон.
76^ Часть I. Общие принципы устройства и функционирования AtmelAVR В дополнение к такой плате неплохо приобрести макетную плату под планарные корпуса (вроде показанной на рис. 4.4, справа) — из нее можно вырезать переход- ники от планарных корпусов к шагу 2,5 мм, если DIP-вариантов каких-то позарез необходимых микросхем в продаже не оказалось. Можно, конечно, для этой цели использовать и переходные платы для микросхем с большим количеством выводов, показанные на рис. 4.2, — но так получается просто дешевле, и вариантов подоб- ных плат для различного шага выводов больше. Адаптер для UART Вообще-то адаптер последовательного порта для подключения вашего устройства к компьютеру может понадобиться и в качестве постоянного компонента схемы. Вопрос о том, как удобнее организовать интерфейс для обмена законченных уст- ройств с компьютером, мы обсудим подробнее в главе 75, а сейчас остановимся только на готовых решениях для подключения макетов— они понадобятся нам с самого начала. В работе с Arduino адаптер USB-UART (или, как вариант, UART-RS232) требовал- ся для программирования платы Arduino Mini. Для других нужд он там, в общем, не нужен, т. к. макетирование и отладку все равно удобнее производить на Arduino Uno или Nano, где адаптер уже встроен в плату. А у нас никаких таких отладочных плат не,будет (не станем же мы приобретать дорогущие фирменные отладочные комплекты, правда?), потому адаптер для общения с компьютером станет жизненно необходимым аксессуаром. СОМ-порта, как такового, в современных компьютерах не имеется, но никто не мешает его установить дополнительно. Если вы хотите иметь со стороны компью- тера именно СОМ-порт, а не USB, то лучше, если это будет отдельная вставная плата, а не переходной кабель-адаптер USB-COM, — последние часто работают из рук вон плохо. Такую плату с СОМ-портами, конечно, можно установить только в традиционный АТХ-корпус настольного компьютера (уже в мини-АТХ и, тем бо- лее, в модные «коробочки» микро-ПК лишняя плата может и не влезть, а про ноут- буки и говорить нечего). Если плату установить нет возможности, то лучше всего перестать упрямиться и перейти к варианту адаптера USB-UART, о котором речь пойдет далее. Только следует учесть, что при этом лучше не работать с двумя такими адаптерами одновременно: их драйверы могут конфликтовать, и придется отключать либо одно устройство, либо другое (см. в главе 5 врезку «Примечание» о программаторах AS-4 и AS-2). Для адаптера UART-RS232 (нередко встречающегося также под названием UART- СОМ) нужен только преобразователь уровней UART (однополярных в положи- тельной логике) в двухполярные в отрицательной логике уровни RS-232 (см. гла- ву 3). Сложность тут в том, что для двухполярных сигналов RS-232, кроме положи- тельного, необходимо и отрицательное питание. Оно имеется внутри компьютера, но на разъем СОМ-порта питание традиционно не выводится, потому приходится выходить из положения разными способами. Облегчается это положение тем, что в стандарте RS-232 допускаются очень широкие пределы для обоих напряжений: от 3 до 12-15 вольт по абсолютной величине и необязательно симметричные. По-
Глава 4. Микроконтроллеры AVR на практике 77_ требляемые токи при этом минимальны и не превышают единиц-долей миллиампе- ра, чем и пользуются в разных вариантах схем адаптеров. Положительное напряже- ние часто просто берется от питания схемы (обычно это +3 или +5 вольт), а вот для отрицательного прибегают к разным ухищрениям. Адаптеры UART-RS232 весьма просты по конструкции и легко поддаются самодельному изготовлению, им также не требуется специальный драйвер. В Сети можно найти большое количество раз- личных вариантов, начиная от простейших любительских конструкций на двух транзисторах, и если кого-то заинтересует этот вопрос в подробностях, пишите, проконсультирую. Для более универсального адаптера USB-UART (иногда их называют адаптерами USB-TTL — по названию стандарта логических уровней TTL) городить самодель- ные конструкции не имеет особого смысла: хороший USB-UART хлопотней в изго- товлении, чем RS232-UART, а в продаже имеется достаточное количество вариан- тов самой разной цены и функциональности. Существуют USB-адаптеры, в кото- рых выведены дополнительные линии СОМ-порта, — т. е. они полноценно заменяют настоящий СОМ-порт, только уровни сигналов уже приведены к уровням контроллера. Если же вы встретите название USB-RS232, то учтите, что это либо простая путаница (имеется в виду USB-UART), либо очень уж экзотическое уст- ройство для каких-то специфических надобностей, и оно нам не подходит. Заметки на полях У многих старых дешевых мобильников, имеющих разъем USES, его использовали только для подзарядки. Между тем, он вполне способен служить для связи с компью- тером, просто представляет собой интерфейс UART, а не USB. В контроллерах мо- бильников (обычно они основаны на ядре ARM) все равно имеется последовательный порт, потому его вывести на разъем не представляет труда, а дополнительный адап- тер USB-UART стоит денег и занимает место. К таким мобильным телефонам часто предлагался довольно дорогой переходный кабель, который и представляет собой этот недостающий адаптер. Если вам захочется возиться, можете попробовать под- ключить к мобильнику любой другой адаптер из имеющихся под рукой, — вся пробле- ма только в правильном подключении выводов разъема со стороны UART в мобиль- нике. Иногда наоборот, кабели от таких мобильников используют в качестве USB- адаптера — когда-то это была очень распространенная практика. Если вы хотите, чтобы ваше устройство при подключении к компьютеру выдавало собственное название, а не безликое наименование фирмы или микросхемы, на ко- торой это все спроектировано, то придется перепрограммировать содержимое EEPROM микросхемы адаптера. Но и тут необязательно городить все с нуля — как правило, для фирменного адаптера тоже можно найти возможности перепрограм- мирования. Подробную информацию на эту тему несложно разыскать в Сети. Заметки на полях Для тех, кто не ищет легких путей, а непременно хочет сделать все своими руками, укажем на публикацию, где приведена схема подключения контроллера ATmega168 к микросхеме адаптера FT232R: https://www.drive2.ru/b/2955382/. Эта схема по функ- циональности аналогична платам Arduino, но в ней может быть использован любой другой контроллер, необязательно даже из семейства Меда, лишь бы он был снабжен портом UART/USART. Попытка воспроизвести эту схему, возможно, притормозит ва- ши устремления к максимальной независимости — микросхема FT232R выпускается
78^ Часть I. Общие принципы устройства и функционирования Atmel AVR в планарном корпусе ssop-28 с шагом выводов 0,65 мм (см. переходные платы на рис. 4.2), и если вам удалось ее запаять вручную с первого раза и при этом не испор- тить, значит, вы настоящий гений паяльных наук. Поскольку USB имеет линию питания 5 вольт, то от него можно запитать схему. Причем в этом вопросе есть свои тонкости. В отличие от адаптеров USB-UART, последовательные ISP-программаторы, о которых пойдет речь далее (см. главу 5), всегда питаются от программируемой схемы. Там это сделано для того, чтобы гарантированно избежать повреждения неподключенного к питанию программато- ра при соединении его со включенной схемой. Но заодно это предохраняет и от ситуации, когда оба источника питания включены одновременно и начинают бо- роться друг с другом в электронной версии армрестлинга. Раунд заканчивается за считанные миллисекунды и без видимых последствий, за исключением того, что все перестает работать. И еще хорошо, если победу одержал USB, ну а если ваш стабилизатор оказался сильнее и повредил питание USB на материнской плате компьютера? Адаптеры USB-UART от этой проблемы не защищены, потому здесь надо внимательно следить за тем, чтобы всегда подавалось только одно из питаний. Заметки на полях А вот интересно, как этот вопрос решается в Arduino? Там ведь можно включить одно- временно кабель USB и внешний источник в разъем Vin, и они не будут конфликтовать друг с другом. Секрет простой: питание 5 В со встроенного стабилизатора Arduino и питание от разъема USB развязаны друг с другом так, что при подаче внешнего пита- ния USB-питание просто отключается от схемы. Причем исследование принципиаль- ных схем Uno и Nano показывает, что принципы развязки у них разные: в «наворочен- ном» Uno стоит транзисторный ключ, отключающий USB-питание, а в более простом Nano эту функцию выполняет обычный диод. Напомним, что сигнальные выводы UART и одноименные сигнальные выводы лю- бого адаптера (у контроллера они называются RxD и TxD, у адаптера обычно про- сто RX и ТХ) соединяются перекрестно: RxD с ТХ, и наоборот, TxD с RX (единст- венное исключение описано далее). Кроме сигнальных, обязательно соединяются выводы «земли» (GND) и, если необходимо, выводы питания Vcc (еще раз напом- ним: следите, чтобы было включено либо собственное питание схемы, либо пита- ние от адаптера, но не оба одновременно). На худой конец в качестве адаптера USB-UART можно использовать плату Arduino Uno, если извлечь из нее «родной» ATmega328 и соединить одноименные выводы платы Uno с «нашим» контроллером. Слово одноименные выделено потому, что в этом случае RxD контроллера соединяется с RX платы Uno, a TxD контроллера с ТХ платы, — ведь наш контроллер здесь выступает вместо извлеченного Mega328, и перекрестное соединение уже выполнено внутри платы. Не забудьте также объ- единить «земли» (GND) платы и вашей схемы и, если надо, подключите вывод питания Vcc. Кстати, точно таким же образом можно программировать плату Arduino Mini, если дополнительно еще соединить выводы RESET . Заметки на полях В Сети можно встретить рекомендацию — вместо извлечения «родного» контроллера из Uno просто заглушить его последовательный порт, что можно сделать двумя путя- ми: либо накоротко соединить вывод RESET с GND, погрузив контроллер в состояние
Глава 4. Микроконтроллеры AVR на практике 79^ сброса, либо загрузить в ATmega328 простенький скетч, устанавливающий оба циф- ровых вывода 0 и 1 (соответствующих RX и ТХ) в состояние работы на вход. В этом случае в качестве адаптера можно использовать также и плату Arduino Nano, из кото- рой контроллер не извлекается. Способ не очень надежный— некоторые клоны Arduino таким образом работать отказываются — и годится разве что совсем в безвы- ходных положениях, когда ничего, кроме Nano, под рукой не имеется. Заметим еще, что для нормальной работы с адаптером USB-UART нужно про- граммное обеспечение. С драйвером под Windows (а также Linux) обычно не воз- никает вопросов— по крайней мере в Windows 7 их специально устанавливать обычно не требуется, они уже имеются в составе ОС. В крайнем случае поинтере- суйтесь, на основе какой именно микросхемы сделан приобретенный вами адаптер, и разыщите драйвер на сайте производителя или просто в Интернете. Однако кроме драйвера нужна еще какая бы то ни было программа для приема данных через порт (вроде монитора порта в Arduino) — о таких программах мы поговорим подробнее в главе 15. Светодиоды-пробники Простейший пробник логических уровней на основе светодиода бывает очень поле- зен— он позволяет непрерывно контролировать состояние вывода, к которому подключен, и является, таким образом, простейшим средством отладки. Не надо ничего особенного выдумывать: возьмите двухцветный двухвыводной светодиод, который при подаче тока в одном направлении светится, например, красным цве- том, а в другом— зеленым. Предпочтительнее выбирать светодиоды с матовой линзой — так их свечение лучше видно со всех сторон. Светодиод можно взять и обычный, но двухсторонний удобнее, т. к. не придется раздумывать над поляр- ностью подключения. К одному выводу светодиода припаяйте токоограничиваю- щий резистор (не укорачивая его выводы), другой — нарастите на ту же длину от- резком луженой медной проволоки такого же диаметра, как и выводы (около 0,5 мм). Изолируйте все неизолированные участки обоих выводов тонким термо- усадочным кембриком, оставив без изоляции по 10-15 мм у концов выводов и у корпуса светодиода. Номинал резистора: 4-10 кОм — такого сопротивления достаточно для уверенного свечения светодиода, но оно практически не будет влиять на уровень напряжения на выводе. Сделайте несколько штук таких пробников с резисторами у одного и того же вывода светодиодов, чтобы цвет свечения при одинаковой ориентации от- носительно плюса и минуса приложенного напряжения был одинаковым. Такой пробник хорошо совместим с беспаечной макетной платой и заодно позволит под- цеплять щуп мультиметра или осциллографа, для которых иначе приходится спе- циально делать выводы из схемы. На изготовление полудюжины пробников вы за- тратите полчаса времени и около 100 рублей денег, зато вместо возни со щупами мультиметра будете иметь простейшее средство контроля, которого во многих слу- чаях бывает вполне достаточно.
80^ Часть I. Общие принципы устройства и функционирования AtmelAVR Мул ьти метр Мультиметр — обязательная принадлежность любого электронщика, даже самого неопытного (более того— особенно неопытного!). Контролировать уровни напря- жений в схеме приходится на каждом шагу — например, при подключении стаби- лизатора питания к схеме прежде всего необходимо убедиться, что вы не ошиблись в разводке выводов, и еще до соединения со схемой промерить напряжение на вы- ходе. Пренебрежение этой простейшей операцией, случается, приводит к печаль- ным последствиям даже у опытных радиолюбителей. Кроме того, возможно, вы «на раз» читаете цветовой код резисторов, а лично я за- мучался определять, как именно тот или иной производитель, умудрившийся по- красить резистор в густо-синий или бледно-зеленый цвет, представляет себе цвет полоски «оранжевый» или «фиолетовый». Поэтому во избежание фатальных оши- бок советую всякий раз промерять сопротивление резисторов мультиметром. Обращение с цифровыми мультметрами и описание их возможностей приводится в огромном количестве источников, и здесь нет смысла их повторять, — разве что дополнить напоминанием, что вместе с мультиметром обязательно следует приоб- рести пару зажимов-«крокодилов», иначе работать с ним неудобно. Сделаем еще несколько замечаний практического характера применительно к на- шим задачам. Так, при выборе мультиметра обращайте внимание на полноту имеющихся диапазонов. В первую очередь он должен измерять, как минимум: □ постоянное напряжение в пределах от 200 мВ (а лучше еще меньше) до 2000 вольт с шагом в порядок величины: 2, 20, 200 и т. д.; □ переменное напряжение до 1000 вольт (можно до 700 вольт действующего зна- чения); □ сопротивление от предела 200 Ом (а лучше еще меньше) до 20 МОм с шагом в порядок величины без пропусков. Все остальное в мультиметре опционально. Например, я почти никогда не пользу- юсь возможностью измерения величины тока и вам не советую: в таком режиме мультиметр нужно подключать последовательно в разрыв цепи, и он становится существенным элементом схемы, без которого она неработоспособна. А так как мультиметр подключается временно, то одновременно он оказывается и самым не- надежным элементом — никогда не знаешь, схема действительно не работает, или просто у мультиметра сработал предохранитель по току из-за неправильного выбо- ра диапазона. Кроме того, при необходимости контролировать попеременно ток и напряжение в двух-трех разных точках схемы очень муторно и долго переключать щупы мультиметра, не забывая при этом их переносить в другие гнезда, и очень легко ошибиться, случайно совершив единственную операцию, которую с мульти- метром делать нельзя, — подключить его к выводам питания в режиме измерения тока. Для измерения тока я имею несколько резисторов двух номиналов, запасенных специально для этой цели: номиналом в 1 Ом — для токов единицы-сотни милли- ампер, и 0,1 Ом (самостоятельно изготовленный из нихромовой проволоки) — для
Глава 4. Микроконтроллеры AVR на практике 81_ токов выше одного ампера. Такие резисторы легко и надежно подключаются к схе- ме, на ее работу практически не влияют (при токе 100 мА, например, на резисторе в 1 Ом «садится» всего 0,1 вольта), к ним просто подключить мультиметр в режиме измерения напряжения и быстро перенести его в другое место, не опасаясь забыть переключить режим и очередной раз пожечь предохранитель. Функция измерения емкости конденсаторов может пригодиться, но почему-то она у всех встречавшихся мне недорогих мультиметров реализована крайне неудоб- но— до контактов в гнездах выводы большинства современных конденсаторов просто не достают. Может также оказаться полезной встречающаяся у современных моделей функция измерения частоты — особенно, если у вас нет осциллографа. Только следите, что- бы пределы измерений начинались с десятков герц и заканчивались в районе 100— 200 кГц, иначе эта функция может так и остаться невостребованной. Не ждите, ко- нечно, от этой возможности портативного мультиметра чего-то выдающегося в смысле точности измерений — калибровать часовой кварцевый генератор у вас определенно не получится. Но проверять установленную частоту— например, ШИМ или звукового сигнала, таким способом очень даже удобно. И, кстати, если есть такая возможность — купите мультиметр настольного испол- нения. Они, как правило, более точные, более функциональные, не капризничают в режиме измерения тока, дисплей у них светящийся, а не слепой жидкокристалли- ческий, и пользоваться ими на постоянном рабочем месте банально удобнее. Осциллограф Конечно, в случаях необходимости измерения временных параметров сигнала ос- циллограф вне конкуренции, но и не только их. Осциллограф, по сути, заменяет собой отдельный частотомер/периодомер (за исключением особо точных образцо- вых приборов), он может выполнить значительную часть функций мультиметра (за исключением измерения сопротивления или, например, емкости) и притом делает это гораздо нагляднее и удобнее — напряжение сигнала можно измерить в точно определенный момент времени или пробежаться по всем его значениям в нужном временном промежутке. Лучше всего, конечно, иметь современный цифровой прибор, но если у вас зава- лялся за шкафом старинный аналоговый осциллограф (с этаким винтажным экран- чиком зеленого свечения), — пусть он многих удобных функций и не имеет, но тоже окажется немалым подспорьем. У осциллографов только один крупный недоста- ток — они дороги. Цена самых дешевых начинается в районе 15-20 тысяч рублей и быстро взлетает в диапазон десятков тысяч и выше. Некоторый компромисс для любительских целей могут предоставить модели, известные под названием «осцил- лограф-ручка», — они подсоединяются к компьютеру через шнурок USB и вполне выполняют свои функции, но при этом существенно дешевле стационарных. В общем, если средства позволяют— обзаведитесь осциллографом обязательно, жить сразу станет легче и веселее. А вот пытаться повторить самодельные конст- рукции осциллографов в надежде, что это выйдет дешевле, не советую — конст-
82 Часть I. Общие принципы устройства и функционирования AtmelAVR руирование осциллографа вполне может быть отдельным любительским проектом, но не в замену покупного прибора. И если вы имеете для этого достаточную квали- фикацию, значит, у вас осциллограф уже есть, без него вы, создавая свой осцилло- граф, все равно не обойдетесь. Генератор Наоборот, генератор сигналов покупать не нужно вовсе. Я за всю жизнь не встре- чал задачи, в которой мне непременно понадобился бы генератор фирменного изго- товления. То есть это я такой задачи не встречал — наверняка в какой-нибудь аку- стике или звукотехнике такие задачи попадаются на каждом шагу, но мы-то здесь собрались по другим вопросам. Не так сложно изготовить самостоятельно генера- тор синусоидального сигнала, если он вдруг вам понадобится, а цифровой, который понадобится обязательно, — еще намного проще, потому его стоит сделать в пер- вую очередь. Ничего особенного нам от такого генератора не нужно — требуется только набор точно известных частот в пределах от единиц или долей герца до десятков кило- герц. Нет особых проблем соорудить такой генератор на микроконтроллере, но, во- первых, Arduino для этого подходит не очень хорошо, а с другими контроллерами мы работать еще не умеем. Во-вторых, это просто не нужно: ничего особенного вы здесь не выиграете по сравнению с элементарной конструкцией, описанной далее, а по стоимости и трудозатратам наша окажется даже дешевле. Схема цифрового генератора представлена на рис. 4.5. Как видите, она весьма про- ста, и сборка ее требует только аккуратности при отсчете выводов микросхем. Схе- ма работает от часового кварца и выдает четырнадцать точных значений частот, кратных степени двойки: от 0,5 до 32 768 Гц, исключая два значения: 4096 и 8192 герца (это такая особенность использованного счетчика 561ИЕ16). Если хоти- те еще упростить схему, то удалите дополнительный счетчик на DD3 — тогда при- дется обходиться минимальной частотой 2 Гц (но не забудьте, что в этом случае остаются свободные элементы «И-НЕ», и их входы надо присоединить или к «зем- ле», или к питанию). Использование древних КМОП-микросхем серии 561 придает схеме одну приятную особенность — она без проблем работает в диапазоне пита- ний от 3 до 12 вольт (на самом деле даже больше— микросхемы выдержат и до 15-18 вольт, просто страшно за часовой кварцевый резонатор, на такие на- пряжения явно не рассчитанный). Поэтому схема годится для отладочных работ совместно с практически любой радиолюбительской конструкцией. Другая приятная особенность схемы, которая в некоторых случаях бывает важ- на, — все частотные сигналы образуют строго симметричный меандр, когда дли- тельность импульсов точно равна длительности промежутка между ними. При этом фронты импульсов со всех выходов точно (ну, почти точно, т. к. счетчик асинхрон- ный) сфазированы, что позволяет путем их логического сложения получать им- пульсы разной, но также точно известной скважности. Схему генератора следует собрать с помощью пайки на макетной плате и вывести все четырнадцать значений частоты на ее край, оформив в виде клеммника с соеди-
Глава 4. Микроконтроллеры AVR на практике 83 5,1 М К выв. 14 DD1,- DD3 -о+3-12 В К выв. 7 DD1, DD3 100,0 х16В - 32768Гц "22пф 22пф DD1 561ЛА7 DD2 561ИЕ16 Л DD3 561ТМ2 DD1/3 32768 Гц 10 Q13 Q12 Q11 Q10 СТ DD2 Q0 Q3 Q4 Q5 Q6 Q7 Q8 Q9 -о 16384 -о 2048 -о 1024 -о 512 1Гц о 0,5 Гц о -о 256 13 12 14 -о 128 -0 64 -0 32 -о 16 R S DD3/1 11 9 юПГ1 R S С Q D Q< DD3/2 13 12 -о 2 Гц 13 Рис. 4.5. Схема цифрового лабораторного генератора сигнала нениями под винт. Внешний вид такого клеммника показан на рис. 4.5, справа. До- полнительно в другом месте платы ставится такой же клеммник для подачи пита- ния и на него «вешаются» все положенные развязывающие конденсаторы. Питание туда подается от питания основной схемы, благодаря чему уровни напряжений все- гда полностью согласованы с обеих сторон. Единственное ограничение, о котором надо помнить: частотные выходы здесь рассчитаны на подключение к высокоом- ному входу другой КМОП-микросхемы или контроллера, но не должны подклю- чаться к какой-либо значительной нагрузке. Светодиод с резистором 3-5 кОм они еще выдержат, но более значительная нагрузка (например, в виде звукоизлучателя) может нарушить работу счетчика. Можно дополнить выходы буферными усили- тельными элементами из микросхем CD4050/CD4049 (КР1561ПУ4/КР1561ЛН2), но конструкцию это загромоздит, а на практике такое ограничение на использование генератора по назначению все равно не влияет. Отечественные микросхемы серии 561 без каких-либо изменений можно заменить на аналогичные из серии 1561 или на импортные: 561ИЕ16 на CD4020A/B, 561ТМ2 на CD4013B, а 561ЛА7 на CD4011В. Часовой кварц лучше выбирать из импортных
84 Часть I. Общие принципы устройства и функционирования AtmelAVR в цилиндрическом корпусе 8x3 мм, они без видимых проблем работают при напря- жениях питания до 12 В. Отечественный РК-206 имеет еще меньший корпус: 6x2 мм, но и рассеиваемая мощность у него меньше, так что с ним лучше ограни- читься напряжением питания 3-6 В. Функциональность генератора, собранного по этой схеме, будет перекрывать наши потребности если и не на все 100%, то по крайней мере на 99% совершенно точ- но — исключая разве что редкий случай потребности в частотах порядка мегагерц. Источники питания Все схемы в этой книге рассчитаны на универсальное питание 5 вольт — на кото- рое ориентировано большое количество внешних устройств, выпускаемых различ- ными фирмами. При этом нет особых трудностей в подключении 3-вольтовой пе- риферии к 5-вольтовому контроллеру, т. к. уровень в 3 вольта превышает мини- мально допустимый высокий уровень AVR, равный 2,4 вольта (по стандарту, пошедшему еще с древней TTL-логики). Чтобы не перегружать защитные диоды 3-вольтового входа периферии при обратном подключении, лучше ставить на 5-вольтовом выходе контроллера делитель напряжения в соотношении пример- но 3:2 (например, 33 кОм в верхнем плече и 68 кОм в нижнем). Подробности Существуют, разумеется, интегральные решения в виде преобразователей уровней 3,3-5 вольт. Есть специально предназначенная для 12С-интерфейса микросхема РСА9306 (она имеет два двунаправленных канала, — см. также главу 13). Для более общих случаев могут применяться, например, микросхемы SN74LVC1T45/SN74LVC2T45, представляющие собой один или, соответственно, два двунаправленных буфера, ра- ботающих на каждой стороне от своего питания в диапазоне 1,65-5,5 вольт, или их аналоги от других фирм (ADG3301). Очень удобен для SPI-соединений преобразова- тель ADG3304, содержащий четыре двунаправленных канала. Есть преобразователи уровней и с ббльшим количеством каналов — например, TXB0108PWR имеет 8 двуна- правленных линий преобразования сигналов напряжением 1,2-3,6 вольта на низко- вольтной стороне и 1,65-5,5 вольта на высоковольтной. Общим недостатком всех этих решений является их относительная громоздкость: SN74LVC1T45 имеет 6 выводов, ADG3304 — 14, a TXB0108PWR вообще 20, причем выпускаются они в неудобных для ручного монтажа пленарных корпусах с шагом 0,5 или 0,65 мм. Поэтому в любительских условиях может быть предпочтительнее самостоятельное решение. Помимо указанного ранее простого резистивного делителя, есть еще много вариантов схем активных согласователей уровней, один из которых показан на рис. 4.6. Схема обеспечивает двунаправленный интерфейс 3,3 вольта — 5 вольт, причем об- ладает приятным свойством предотвращать утечку тока по выводу с 5-вольтовой сто- роны при отключении питания 3,3 вольта. MOSFET-транзистор можно взять любой маломощный с п-каналом и управлением от логических уровней (logic level transistor)— кроме указанного на схеме BS108 (в корпусе ТО-92) для наших нужд подойдут маломощные типы с первыми буквами IRL в наименовании (в удобных кор- пусах— например: IRLD024PBF, IRLD110PBF и др.) Будут работать и такие, как 2N7000, BS107, BSS295 и пр. Для существенной части проектов на основе микроконтроллеров какие-либо спе- циальные источники питания не требуются. Достаточно иметь любой источник
Глава 4. Микроконтроллеры AVR на практике 85 +3,3 В +5 В Vcc - 3,3 В внешнее устройство GND |10к 10к| BS108ZL1G Vcc = 5 В AVR-контроллвр GND ' 1 ' Рис. 4.6. Схема сопряжения уровней 3,3 вольта и 5 вольт с помощью MOSFET-транзистора напряжения величиной 5 В с допуском не более 5%— от примерно 4,75 до 5,25 вольта. Это максимально допустимые величины отклонения, обычно любой стабилизатор обеспечивает гораздо лучшие значения, — не следует только исполь- зовать нестабилизированные источники. Для большей части примеров из этой кни- ги подойдет максимальный выходной ток 0,33-0,5 А, но хуже не будет, если эту величину увеличить до 1 ампера, — запас карман не тянет. Можно приобрести готовый адаптер, встроенный в вилку, — они предлагаются на всех торговых сайтах, посвященных Arduino. Выходной разъем у таких адапте- ров — круглый «джек» диаметром 5,5 мм (толще, чем у подобных адаптеров для других целей), соответственно этому размеру надо приобретать и разъем-гнездо для наших нужд, — точно такое же, как внешний разъем платы Uno. Положитель- ный вывод у него — внутренний, отрицательный («земля», GND) — внешний. Если вы приобрели тшсой стабилизированный адаптер ровно на 5 вольт, то к нему нужно приобрести только переходное гнездо с клеммником для подключения обычных проводов-перемычек, которые будут вставляться прямо в макетную плату. Заметки на полях Не выбрасывайте зарядные устройства от отработавших свой срок мобильников! Су- щественную часть своих потребностей в источниках питания автор давно уже удовле- творяет за их счет. Конечно, в каждом конкретном случае нужно внимательно читать, что написано на обязательно имеющемся на таком источнике шильдике, чтобы сильно не промахнуться, но обычно это довольно-таки качественные источники напряжением 5 вольт, часто импульсные, т. е. они могут запросто обеспечить вас током в пару ам- пер в минимальных габаритах. Более того, во многих случаях удобно встраивать такие источники в готовые конструкции — если распилить корпус и извлечь оттуда плату. Зарядники для мобильной электроники, как и любые другие адаптеры питания для бы- товых устройств, обязательно имеют гальваническую развязку от сети, и вам не при- дется приобретать отдельно трансформаторы и городить схему выпрямления. Однако обратите внимание, что вход внешнего питания Arduino допускает подачу напряжения от 5 до 12 вольт, т. к. стабилизатор там встроен в плату. По указанной причине напряжение у предлагаемых Arduino-адаптеров может оказаться не 5 вольт, а выше. В этом нет особых трудностей, т. к. мы в своих макетах всегда
86 Часть I. Общие принципы устройства и функционирования AtmelAVR можем точно так же дополнить адаптер отдельным стабилизатором: это ровно три дополнительных компонента, собственно интегральный стабилизатор и два кон- денсатора к нему. Питание следует обеспечивать фирменными интегральными стабилизаторами, са- моделки здесь недопустимы. Обычно рекомендуемые «чайникам» LM7805 (или LM78L05 в маломощном варианте) решительно устарели из-за очень высокого ми- нимально допустимого проходного напряжения 2,4 вольта, — т. е. минимальное напряжение на входе должно быть не менее 7,5 вольт, и как минимум треть всей мощности будет расходоваться впустую. Для малопотребляющих схем годятся 100-миллиамперные стабилизаторы LM2931 (5-вольтовый) или LP2950 (на напря- жение 5 В, 3,3 В и 3 В) в корпусах ТО-92, которые отличаются сверхмалым собст- венным потреблением (несколько десятков микроампер) и сверхнизкой разницей напряжений на входе и выходе, при которой стабилизатор еще выполняет свои функции, — достаточно перепада в несколько сотен милливольт (только не забы- вайте про пульсации, если на входе сетевой выпрямитель!). С мощными стабилизаторами в корпусах ТО-220, подобных LM7805, положение несколько хуже из-за того, что обычно предлагается вариант с малым проходным напряжением, но с достаточно высоким собственным потреблением (LM2940 с максимальным током 1 А, проходным напряжением 0,5-0,8 В и потреблением 10 мА, или L4940 с максимальным током 2 А, проходным напряжением 0,2-0,4 В и потреблением 5 мА). Можно применять и привычный по Arduino Uno стабилизатор LM1117-5.0, у которого характеристики скорее средние, но все-таки лучше, чем у LM7805 (максимальный ток 0,8-1 А, ток потребления 5 мА, проходное напряже- ние от 1,2 В). Есть два иногда встречающихся случая, когда стабилизированного источника на напряжение 5 вольт не хватает: во-первых, как мы уже говорили, могут встретиться периферийные устройства под напряжение 3 вольта, во-вторых, для некоторых внешних компонентов потребуется большее напряжение (до 12 вольт) и ток (до 5- 10 ампер). Для первого случая достаточно просто запастись отдельными инте- гральными стабилизаторами под напряжение 3,3 вольта в малогабаритном корпусе ТО-92 — они встраиваются прямо в схему (идеально подойдут стабилизаторы из серии LP2950). А вот второй случай потребует более основательного подхода. Просто для провер- ки работоспособности маломощных схем с повышенным напряжением, потреб- ляющих при 12 вольтах до 1 ампера, можно обойтись еще одним стабилизирован- ным адаптером на 12 В или вообще использовать таковой в качестве основного (правда, ток потребления пятивольтовой части схемы при этом не должен превы- шать примерно 0,2-0,3 А, иначе отдельный стабилизатор на 5 вольт будет перегре- ваться, и его придется ставить на радиатор). Для случаев, требующих более мощных источников, наилучшее решение — приоб- рести настольный источник необходимой мощности (до 13-15 вольт и до 10-12 ам- пер). Наилучшее перерастет в идеальное, если источник такой будет с двумя кана- лами, каждый — с регулируемым напряжением и заданием выходного тока. Один
Глава 4. Микроконтроллеры AVR на практике 87 такой источник перекроет практически любые радиолюбительские нужды, но он довольно дорог и требуется далеко не повседневно. В качестве более простого варианта для испытания всяческих силовых устройств можно приобрести зарядное устройство для автомобильных аккумуляторов, кото- рое по сути представляет собой тот же источник постоянного напряжения, причем регулируемый и рассчитанный на весьма значительные токи. Только внимательно смотрите на характеристики: устройство должно иметь только ручную регулировку выходного напряжения и тока (не должно быть всяких «интеллектуальных» функ- ций вроде определения типа аккумулятора, степени разряда, автоматического от- ключения при окончании заряда или по времени и т. п.) У подходящих для наших нужд автомобильных зарядников в инструкции должно быть написано примерно так: «подходят в качестве многоцелевого источника постоянного тока для питания аппаратуры». Конечно, если у вас есть мощный трансформатор, то соорудить подобный источник самостоятельно также не представляет особой сложности, даже с регулируемым напряжением, вот только от функций установки выходного тока, скорее всего, при- дется отказаться, — уж больно громоздки они в реализации. В этой книге мощный источник напряжением 12 вольт нам понадобится только один раз (если вы захотите повторить схему инвертора напряжения из главы 9), и на такой единичный случай можно будет обойтись каким-нибудь временным ре- шением — например, автомобильным аккумулятором. Кроме всего здесь рассмотренного, для работы с контроллерами потребуется еще программатор, о котором пойдет речь в следующей главе. Потребление МК AVR Вопросы потребления AVR-контроллеров имеют очевидную связь с темой реали- зации функций энергосбережения, но ее мы рассмотрим во всех подробностях в главе 14, а здесь остановимся только на собственно потреблении. Потребление контроллеров AVR при напряжении 5 В и тактовых частотах до 8- 16 МГц лежит в пределах от 3 мА — для младших Tiny и до 15-20 мА — для стар- ших Mega. Ток потребления также зависит от модификации — для модернизиро- ванных контроллеров с буквой «Д» в названии он в среднем почти вдвое меньше, чем у старых: например, ATmega8 при частоте 16 МГц потребляет около 20 мА, a ATmega8A— чуть больше 10 (рис. 4.7). При этом ток потребления, по крайней мере в подсемействе Mega, не очень зависит от степени «навороченности» моде- ли— например, для ATmega8 или ATmega8535, относящихся к младшей ветви семейства, этот ток почти такой же, как для мощной ATmega2560. Потребление заметно снижается только у самых простых представителей Tiny. Зато потребляемый ток сильно зависит от тактовой частоты и напряжения питания (на рис. 4.7 приведены графики зависимости тока потребления от этих параметров для ATmega8/ATmega8L и ATmega8A, а на рис. 4.8 — графики зависимости тока потребления от напряжения питания для ATmega8A при работе от встроенного генератора на разных частотах).
88 Часть I. Общие принципы устройства и функционирования Atmel AVR ATmega8(L) ACTIVE SUPPLY CURRENT vs. FREQUENCY 30 25 20 15 10 zzzz-—■ ==^ 2.7V ——- 3.0V _~——- 3.3V 5.5V 5.0V 4.5V 6 8 10 Frequency (MHz) 12 14 16 ATmega8A ACTIVE SUPPLY CURRENT vs. FREQUENCY 3 6 -d — ^^ - 27V ^; ^^ 33V ""36V "40V 55V 50V 45V 0 2 4 6 8 10 12 14 16 Frequency (MHz) Рис. 4.7. График зависимости тока потребления от напряжения питания и тактовой частоты для ATmega8/ATmega8L (вверху) и модернизированного ATmega8A {внизу) Из сравнения графиков, приведенных на рис. 4.7 и 4.8, видно, что встроенный гене- ратор съедает довольно значительную долю тока от общего потребления: на часто- те 8 МГц с тактированием от внешнего кварца контроллер с буквой «А» при 5 вольтах питания потребляет около 6 мА, а от внутреннего генератора при тех же условиях — около 7,5 мА. Из сравнения этих графиков можно сделать и другие интересные выводы. Графики, приведенные на рис. 4.7, показывают, что снижение тактовой частоты практически адекватно снижению напряжения питания: снизив то. или другое вдвое, вы вдвое снизите потребление. А при работе от внутреннего генератора (рис. 4.8) это не так:
Глава 4. Микроконтроллеры AVR на практике 89 ATmega8A 2.5 ACTIVE SUPPLY CURRENT vs. Vcc INTERNAL RC OSCILLATOR, 8 MHz -~s 7 - £ о ° 6 - с _ 3- -40 °C 25 °C 85 °C 3.5 4 VCC(V) 45 55 ATmega8A ACTIVE SUPPLY CURRENT vs. Vcc INTERNAL RC OSCILLATOR. 1 MHz 55 Рис. 4.8. График зависимости тока потребления от напряжения питания для ATmega8A при работе от встроенного генератора на тактовых частотах 8 МГц (вверху) и 1 МГц (внизу) снижение тактовой частоты в 8 раз снижает потребление не в 8 раз, а всего только в 4,5 раза (при 5 вольтах — с 7,6 мА до почти 1,7). В то же время двукратное сни- жение напряжения питания при 8 МГц по-прежнему вдвое снижает потребление, а при 1 МГц зависимость сложнее и куда более пологая (в «даташитах» есть еще отдельный график для частот ниже одного мегагерца). Потому при желании про- гнозировать потребление в различных сочетаниях этих величин нужно тщательно анализировать конкретную ситуацию. Например, из приведенных графиков следу- ет, что тактирование от внешнего кварца энергетически выгоднее тактирования от
$Ю Часть I. Общие принципы устройства и функционирования Atmel AVR встроенного генератора, хотя не забудем, что последнее осуществляется гораздо проще и без дополнительных компонентов на плате. График, показанный на рис. 4.9, может пролить свет на заманчивую на первый взгляд возможность кардинально снизить потребление при работе на сверхнизких частотах. Действительно, потребление снижается до уровня десятков микроампер, что уже вполне приемлемо для батарейного питания: от щелочных батареек ААА, имеющих емкость около 1500 мА- часов, такой контроллер сам по себе проработает около трех лет. Но следует при этом принять во внимание, что многие функции окажутся недоступными (работа через UART, например), а выполнение других операций замедлится настолько, что они окажутся бесполезными, — скажем, будет практически невозможной работа с LCD-дисплеем, под вопросом запись во внеш- нюю память и т. д. АТтедавА ACTIVE SUPPLY CURRENT vs. Vcc EXTERNAL OSCILLATOR, 32 kHz 70 65 60 45 40 25 °C 25 35 4 Vcc(V) 45 5.5 Рис. 4.9. График зависимости тока потребления от напряжения питания для АТтедавА при работе от внешнего кварца на тактовой частоте 32 кГц Потому и с точки зрения нормального функционирования контроллера, и с точки зрения экономии энергии куда выгоднее использовать режимы энергосбережения при обычной частоте тактирования от внешнего кварца в пределах 1-16 МГц. Подробности Давайте подтвердим эти соображения ориентировочным расчетом. Если подсчитать количество энергии, приходящейся на один такт работы контроллера, то при 8 МГц тактовой частоты и 5 вольтах питания (см. рис. 4.7) оно окажется примерно равным 5 мА х 5 В х 125 не = 3,125 нДж, а при 32 килогерцах (рис.-4.9) — 65 мкА * 5 В * 30 мкс « = 10 нДж, т. е. в три раза больше. Программа продолжительностью в 100 тактов по- требует в первом случае затрат около 0,3 мкДж, а во втором — около 1 мкДж, причем в первом случае она займет время 12,5 мкс, а во втором — целых 300 мкс. Остальное время контроллер тоже потребляет, но если мы применим во втором случае энерго- сбережение, то теоретически можем свести потребление в паузах до совершенно не-
Глава 4. Микроконтроллеры AVR на практике значительной величины в единицы микроампер (см. обсуждение режимов энергосбе- режения в главе 14). При частоте 8 МГц выполнение за секунду 10 процедур по 100 тактов займет всего /вооо часть времени, потому при подсчете среднего потребле- ния время работы вообще может не учитываться. Итого, работа при частоте 32 кГц окажется в разы или даже десятки раз менее выгодной, чем использование режимов Power Down или Power Save, причем доставит намного больше проблем. Еще один интересный вывод из графиков, показанных на рис. 4.7-4.8, заключается в том, что при использовании основного режима энергосбережения (Power Down mode, — см. главу 14) тактовая частота с точки зрения суммарного потребления практически не имеет значения. В пределах 1-16 МГц количество энергии на один такт обратно пропорционально частоте и прямо пропорционально длительности такта, потому при изменении частоты оно не изменится. А вот при снижении час- тоты ниже 1 МГц количество энергии на такт, как мы видели, растет, и использо- вать такой режим уже невыгодно. Но еще раз повторим, что каждый конкретный случай нужно тщательно анализировать отдельно, — так, например, у старых кон- троллеров кривые потребления могут проходить иначе, чем у выполненных по мо- дернизированной технологии. По поводу потребления отметим еще такой момент. Выводы AVR могут в долго- временном режиме отдавать значительный ток (до 20—40 мА), однако не следует забывать об общем суммарном ограничении на потребление по выводу питания (обычно 200 мА, — см. табл. П2.3 в приложении 2). Следует также отметить, что при подаче аналоговых напряжений на входы АЦП входной цифровой КМОП- элемент (вход соответствующего цифрового порта) не отключается, и при значении такого напряжения вблизи порога срабатывания элемента это может приводить к возрастанию потребления за счет протекания сквозного тока через выходные кас- кады КМОП (в том числе иногда и при нахождении микросхемы в «спящем» ре- жиме, — см. главу 14). Указанного недостатка лишены некоторые контроллеры, выполненные по модернизированной технологии, но в общем случае и этот момент надо учитывать в проектировании. Примеры AVR-контроллеров В нашей небольшой книжке невозможно описать не только все нюансы всех моде- лей контроллеров семейства AVR, затруднительно даже бегло пробежаться по са- мым употребительным. Посмотрите на толщину изданий [6,7], в которых представ- лена лишь небольшая часть не самых современных представителей этого семейст- ва, и только в виде перевода сухой официальной документации, без развернутых примеров и пояснений. Поэтому мы поневоле ограничимся несколькими моделями, возможности которых покрывают большую часть потребностей пользователя при решении типовых задач. А по ходу дела постараемся обозначить пути распростра- нения этих решений на более «продвинутые» и современные модели. Три основных контроллера AVR, с которыми мы будем иметь дело в дальнейшем изложении, следующие: □ ATtiny2313 — старший (и один из первых) представителей семейства Tiny, он представляет собой модернизированную версию одной из самых удачных моде-
92^ Часть I. Общие принципы устройства и функционирования AtmelAVR лей еще семейства Classic. Весьма функциональный и одновременно компакт- ный контроллер в корпусе с 20-ю выводами. Обладает многими возможностями младших Mega, исключения: отсутствует АПЦ и некоторые расширенные функ- ции таймеров. Из других особенностей: отсутствуют аппаратные последова- тельные порты SPI и TWI (UART имеется). У ATtiny2313 также нет внутреннего монитора питания (системы BOD), потому при работе с EEPROM необходимо принимать отдельные меры. Зато, в отличие от младших Mega, снабжен допол- нительным внешним прерыванием типа pcint. Современная версия ATtiny2313V отличается расширенным диапазоном напряжений питания, в остальном все версии идентичны и работают при тактовых частотах вплоть до 20 МГц. □ ATmega8 — этот контроллер, выпускаемый в варианте DIP в корпусе с 28-ю вы- водами, у нас будет базовым. До сих пор он один из самых популярных кон- троллеров, в котором простота программирования сочетается с большей частью возможностей современных Mega. Из недостатков в сравнении с более совре- менными моделями (ATmega88 — предшественником ардуиновского ATmega328): отсутствуют внешние асинхронные прерывания INT2 и pcint, урезаны некоторые возможности таймеров и еще кое-что по мелочи. ATmega8 работает на тактовой частоте до 16 МГц, и в более современной версии— ATmega8A— отличается пониженным потреблением. Иногда мы будем обращаться к его ближайшему родственнику ATmegal6, также завоевавшему заслуженную популярность. Эта модель почти ничем не отличается от ATmega8, за исключением наличия пре- рывания int2, несколько более «продвинутыми» возможностями таймеров, и, главное, большим количеством выводов. □ ATmega8535— как и Tiny2313, представляет собой модернизацию одноимен- ного контроллера семейства Classic. Основные преимущества по сравнению с ATmega8: больше выводов (40-контактный DIP-корпус) и имеется асинхрон- ное прерывание INT2. Программирование столь же простое, потому мы его будем использовать вместо ATmega8 в случаях, когда требуются именно эти функции. Как и ATmega8, ATmega8535 допускает тактовую частоту до 16 МГц. Заметки на полях Отметим еще также давнюю модель АТтеда8515, которая по названию кажется похо- жей на АТтеда8535, но в реальности существенно от нее отличается (например, в АТтеда8515 отсутствует АЦП). У нее есть одна довольно редкая функция, которая еще присутствует только в некоторых старших моделях (у АТтеда328 ее нет, но эта функция имеется в АТтеда2560, лежащем в основе Arduino Меда). К контроллерам с такой функцией можно подключать внешнюю память типа SRAM с параллельным интерфейсом так, что последняя становится надстройкой, расширяющей адресное пространство встроенной SRAM. Всего таким образом становится доступно дополни- тельно 64 кбайт памяти, что может потребоваться в случаях обработки больших мас- сивов данных. Энергонезависимая память типа EEPROM так же просто не расширяет- ся, но при необходимости сохранять данные при отсутствии питания можно использо- вать решение фирмы Dallas Semiconductor (ныне подразделение фирмы Maxim) под названием NV SRAM (Nonvolatile SRAM). Там обычную SRAM просто-напросто запи- тали от встроенной литиевой батарейки, и обещают сохранность данных в течение 9 лет. При этом появляются такие, например, возможности, как восстановление памя- ти контроллера при сбоях в подаче питания, — с обычной EEPROM это не проходит из-за ограничений на количество допустимых перезаписей содержимого.
ГЛАВА 5 Подготовка к программированию МК AVR В работе с микроконтроллерами очень легко забыть, что контроллер — это всего лишь электронный компонент, пусть и достаточно сложный, но точно такой же, как светодиоды, транзисторы, усилители, компараторы и любые другие составляющие единой схемы. В компьютерных устройствах акцент давно перенесен с «железа» на программное обеспечение, и это совершенно справедливое положение, т. к. компо- ненты компьютера — отдельная отрасль электроники, полностью отданная на от- куп фирмам-производителям. Поэтому там все творчество «масс» давно сместилось в область создания программ, а по части «харда» остается лишь выбор между гото- выми компонентами того или иного вендора. У универсальных микроконтроллеров совсем другое предназначение — они разра- батываются, чтобы стать «кирпичиками» при создании самых разнообразных устройств. Причем зачастую успех зависит от верного выбора всех компонентов в совокупности— т. е. от того, что когда-то авторы замечательного учебника [12] назвали «искусством схемотехники». Программа для микроконтроллера в решении этой задачи иногда может занимать вообще последнее место и по трудозатратам, и по уровню необходимой квалификации. И успех в такой деятельности нередко зависит от схемотехника в гораздо большей степени, чем от программиста. О сказанном слишком часто забывают, и создание электронных приборов незамет- но перемещается в руки профессиональных программистов, которые слыхом не слыхивали о предельных мощностях и токах, насыщении магнитных сердечников, глубине обратных связей, показателях наработки на отказ, метрологических харак- теристиках и прочих премудростях науки электроники. Программисты привыкли к высокому качеству компьютерных компонентов и подсознательно считают, что фирма «хуже не сделает». А индустрия с радостью идет им навстречу, выпуская дешевые компоненты, условно-оптимизированные под представления среднестати- стического покупателя. Результаты можно видеть на примере, скажем, многочис- ленных датчиков температуры-влажности, совместимых с Arduino. За редчайшим исключением производители этих датчиков никогда не слышали о понятии «ка- либровка», а 9 из 10 моделей годятся в лучшем случае только для комнатных усло- вий.
94_ Часть I. Общие принципы устройства и функционирования AtmelAVR Заметки на полях Больше всего примеров неграмотного подключения плат Arduino связано с напряже- нием питания 5 В. По какой-то причине производители традиционно не указывают пределы величины тока, который может выдержать встроенный стабилизатор платы Arduino Uno при различных напряжениях подключенного адаптера Vin. Понять их мож- но — слишком сложная для любителя получается картина зависимости выделяющей- ся на стабилизаторе мощности от входного напряжения и нагрузки, чтобы можно было однозначно указать параметры. Между тем, для выбранного создателями платы Uno стабилизатора NCP1117 в миниатюрном корпусе SOT-223 предельная температура кристалла (150 °С) при рекомендуемом напряжении адаптера 12 вольт будет достигнута уже при нагрузке около 130 мА, а при некотором ее превышении сработает защита, и схема начнет вести себя непредсказуемо. И ведь во избежание такой ситуации дос- таточно ограничить входное напряжение — например, при Vin = 7,5-8 вольт предель- ное значение тока нагрузки сразу возрастает до 300 мА. С другой стороны, NCP1117 может выдержать и 20 вольт входного напряжения при соответствующем ограничении потребления (если больше ничего потребляющего более нескольких миллиампер, кроме самой платы, к нему не подключено). А для напряжения 3,3 вольта, наоборот, совершенно необоснованно указано ограничение тока 50 мА, тогда как примененный в Uno стабилизатор LP2985 допускает ток до 150 мА, и при входном напряжении 5 вольт, к которому он подключен на плате, далеко не достигнет опасных температур корпуса даже при этом максимальном значении тока (у меня есть подозрение, что цифра 50 мА задержалась в документации с каких-то старых ревизий платы). Пример некорректного подключения нагрузки к Arduino Uno при не оговоренном входном на- пряжении представляет почти любой сетевой ресурс, описывающий применение сер- вомоторов (см. главу 16). Ограничение тока также может наступать про подключении светодиодных дисплеев с достаточно большим числом светящихся сегментов. Если учесть, что производители клонов Arduino могут ставить вместо указанных типов ста- билизаторов все, что окажется у них под рукой, то неопределенность с питанием ока- зывается уже совсем неприемлемой, — типичный результат программистского подхо- да к проектированию. Мы не пойдем на поводу у этой тенденции. И в первую очередь постараемся отне- стись к программам для контроллера не как к самостоятельной сущности, требую- щей отдельной квалификации, а просто как к еще одному этапу при создании схем. Отсюда и принципы подхода к процессу программирования, который можно сфор- мулировать в трех словах: ничего сверх необходимого. Мы не станем терять время в поисках красивых и удобных инструментов отладки, в обсуждении возможностей и целесообразности построения микроконтроллерной операционной системы, в сравнительных оценках различных сред программирования. Мы потому и выбра- ли ассемблер, что он позволяет ничего этого не делать, а сосредоточиться строго на целевой задаче. Ассемблер без излишних сложностей Начнем мы со средств создания ассемблерных программ. За многие годы ситуация в этой области мало изменилась — ничего принципиально нового не предлагается, только обновления некоторых уже известных инструментов, и притом не всегда удачные. Мы пока попробуем обойтись вовсе без них — минимально необходимым набором инструментов. Это редактор текста, собственно ассемблер (компилятор текста в загружаемый НЕХ-файл), программа-загрузчик (соответственно выбран- ному вами программатору), а также еще некоторые необходимые файлы.
Глава 5. Подготовка к программированию MKAVR 95 Редактор ASM Editor Программы на ассемблере можно писать в любом текстовом редакторе. Не подой- дут разве что MS Word или OpenOffice — они слишком «навороченные», и простые файлы типа «чистый текст» в них получаются плохо. А нам здесь нужны именно средства создания чистого текста без каких-либо служебных заголовков, разметки, форматирования и иных включений, не относящихся к делу. Причем для ассембле- ра важны лишь символы из первой половины таблицы кодировки ASCII — т. е. цифры, знаки препинания и некоторые общеупотребительные значки, а также анг- лийские буквы, большие и маленькие. Поскольку эта часть таблицы кодов симво- лов одинакова для всех кодировок (исключение представляет разве что редко упот- ребляемый четырехбайтовый Unicode), то и установленная кодировка не имеет зна- чения, — а комментарии можете писать на любом языке, компилятором они все равно игнорируются. Иными словами, программу на ассемблере можно писать хоть в Блокноте или его многочисленных заменителях — компилятор вас поймет. Но на практике делать это неудобно. В текстовом редакторе для эффективного создания ассемблерных про- грамм желательны некоторые особые «фичи», которые мы сейчас приведем: □ автоматическая нумерация строк — она совершенно необходима потому, что компилятор при выводе ошибок и предупреждений указывает номер строки, и наличие их автоматической нумерации позволяет сразу и без разночтений найти ошибочный оператор в программе; □ подсветка синтаксиса — обычное свойство всех редакторов для создания про- граммных текстов. В тексте программы автоматически выделяются цветом или шрифтом служебные слова или целые фрагменты, отмеченные специальными старт-стопными символами (например, комментарии). Подсветка задается обычно в отдельных настройках (потому что для каждого языка программиро- вания она своя), и ее можно редактировать, подгоняя под конкретные условия; О возможность осуществлять компиляцию, не выходя из редактора, — если пер- вые две особенности имеют множество текстовых редакторов разной степени фирменности, то возможность компиляции прямо из редактора— считанные единицы. Конечно, можно обойтись и без этого (вообще без всего можно обой- тись, как мы говорили), но необходимость прыгать из приложения в приложение сильно замедляет процесс создания программы,— проверять созданный код с помощью попытки его скомпилировать иногда приходится ежеминутно. В итоге выбор-то получается не такой уж и большой. Идеально, конечно, если бы еще и загрузка программы в контроллер происходила в той же среде, но как раз это свойство необязательное. Для компиляции достаточно вызвать программу- ассемблер из командной строки, чтобы получить всю ее функциональность, и это не так уж сложно интегрировать в любую программу. А загружаем мы уже готовый вариант, что происходит относительно редко, и желательная функциональность загрузчика слишком обширна, чтобы ее было удобно отображать таким же об- разом.
96^ Часть I. Общие принципы устройства и функционирования Atmel AVR Всяческие интегрированные среды вроде Atmel Studio и прочих Proteus'oB все упо- мянутое умеют, конечно (и еще многое сверх того), но мы договорились обходить- ся простейшими методами. Вы можете порыться в закромах Интернета и рассмот- реть популярные среди программистов и весьма «крутые» MultiEdit'bi вместе с Notepad'aMH++ — они всем хороши, кроме запредельной «навороченное™» в со- временных версиях. Я вот уже два десятилетия не изменяю скромному ASM Editor, который идеально заточен именно под ассемблер. Притом, что последняя версия (2.2d) недавно справила пятнадцатилетие, и редактор заметно устарел— при активной работе начинает замечаться отсутствие некоторых привычных удобств. В современных версиях Windows приходится вспомнить, что есть такая штука, ко- торая называется «режим совместимости», — при установленной «совместимости с Windows XP» пропадают некоторые баги в пользовательском интерфейсе, отнюдь не присущие ASM Editor изначально. Но на основную функциональность это все никак не влияет, а замены этому редактору по простоте работы и удобству настроек под личные вкусы я так и не нашел, хотя регулярно предпринимаю поиски. Надо сказать, что само название ASM Editor достаточно распространенное, и по такому запросу вы можете найти очень много разных продуктов, похожих по на- значению. Вам же нужен тот ASM Editor, который располагается в разделе Про- граммы сайта avt-lab.ru. Устанавливать его не надо — просто разверните архив в удобное место на диске, причем желательно не в Program Files, а в отдельную пап- ку (у меня она традиционно носит название AVRTOOLS). Там же вы в дальнейшем разместите ассемблер и другие необходимые файлы, а также программу для загруз- ки и каталог со своими проектами. Такое компактное размещение позволит вам при необходимости скопировать на другой компьютер или на флешку всю папку AVRTOOLS целиком и сразу получить копию готовой и настроенной среды вместе со всеми проектами. Единственная привязка к Windows, которую целесообразно сделать, — ассоцииро- вать расширение .asm с редактором ASM Editor. Делается это обычным спосо- бом — щелкните в Проводнике Windows на файле с таким расширением правой кнопкой мыши, выберите опцию Открыть с помощью, отыщите ASM Editor (файл asm_ed.exe) и установите ассоциацию с ним. Это удобно еще и потому, что ASM Editor при открытии нового файла с программой через меню File | Open или через список недавно открытых файлов закрывает старое окно, а при вызове через Про- водник по расширению файла можно создать сколько угодно окон редактора с раз- ными проектами. Можно еще привязать расширение .hex к программе-загрузчику (о ней рассказано далее), если вы сочтете, что так удобнее. Там же, на сайте автора ASM Editor, отдельно доступен файл AVR.shk, содержащий схемы подсветки синтаксиса для AVR-ассемблера. Его надо будет подключить, т. к. по умолчанию ASM Editor настроен на работу с ассемблером MASM. Однако имеющийся там файл охватывает лишь небольшую часть служебных слов для очень ограниченного круга контроллеров. Поэтому я предлагаю вам сразу скачать с моего сайта по адресу http://revich.lib.ru/AVR/AVRshk.zip его доработанный ва- риант. Для подключения файла подсветки скопируйте его в папку, где находится редактор (файл asm_ed.exe). Затем запустите программу, обратитесь к опции меню
Глава 5. Подготовка к программированию MKAVR 97_ Highlight | Add new и введите имя файла AVR.shk в появившейся строке ввода. По- сле этого название AVR появится в меню Highlight отдельной строкой, которая должна быть вами выбрана. Если необходимо что-то изменить (в дополненном файле также представлены не все возможные варианты для различных контролле- ров), то подсветку можно будет исправить или дополнить по имеющимся образцам через опции меню Highlight | Keywords и Highlight | Start-stop keys. Много других настроек ASM Editor можно найти в меню Service | Properties, из них нам в дальнейшем понадобится изменить некоторые из находящихся на вклад- ке Project. Но сначала давайте обзаведемся другими необходимыми компонентами. Ассемблер Avrasm Во-первых, нам понадобится собственно ассемблер. В настоящее время целесооб- разно пользоваться последней его версией, которая носит название avrasm2 (а ис- полняемый файл, соответственно, avrasm2.exe). Нет никакого криминала в том, что- бы использовать устаревший вариант под названием avrasm32, но это просто не- удобно: он не поддерживает модели контроллеров, выпущенные примерно после 2010 года, не имеет некоторых расширенных возможностей, введенных в avrasm2, а также ориентирован на файлы определений, несколько отличающиеся по содер- жанию от более современных. Нужно учитывать, что avrasm2 строже старой версии по отношению к синтаксису, потому не исключено, что некоторые давно написан- ные программы для компилирования с его помощью придется подправлять. Кроме того, если у вас уже имеется старая версия Atmel Studio (не выше 4jc), to прила- гающиеся к ней файлы определений (inc-файлы, — о них рассказано далее) придет- ся заменить. Для того чтобы добыть avrasm2.exe, проще всего установить Atmel Studio и извлечь его из каталогов этого пакета. Так как до версий 4.x включительно в Atmel Studio входил старый avrasm32, то годится любая более поздняя. Проще всего скачать с сайта Microchip то, что там предлагается (на момент подготовки книги — это вер- сия Atmel Studio 7). При установке можете оценить, в какого монстра превратилась когда-то вполне компактная среда программирования, — достаточно указать, что заодно установится и Microsoft Visual Studio, и еще несколько подобных компонен- тов, так что придется раз десять, не меньше, нажимать на кнопочку I accept. Ассемблер должен находиться в папке с названием ...AtmelWmel Studio <номер вер- aw>\avrassembler. Скопируйте оттуда файл avrasm2.exe в папку под условным назва- нием AVRTOOLS, куда мы ранее поместили ASM Editor. Во-вторых, нам обязательно понадобятся включаемые файлы определений, в кото- рых мнемонические названия регистров привязаны к абсолютным адресам кон- кретных контроллеров. Без такого файла ассемблер будет выдавать ошибку на каж- дой строке в вашей программе, поскольку не будет знать, что означает PortA или DDRB (подробнее об inc-файлах рассказано далее в этой главе). В настоящее время эти файлы для всех контроллеров можно добыть только из Atmel Studio. В папке avrassembler должен находиться каталог с названием INCLUDE, который можно ско- пировать себе целиком, поместив его все в ту же AVRTOOLS.
98 Часть I. Общие принципы устройства и функционирования AtmelAVR После копирования ассемблера и папки с inc-файлами саму Atmel Studio со всеми сопутствующими компонентами можно и удалить, но советую подождать до под- робного обсуждения ее достоинств и недостатков в этой главе далее. Обустройство ассемблера Далее я буду ориентироваться на то, что вы используете ASM Editor, но в общем-то схема обустройства рабочей среды одинакова для любого редактора, поддержи- вающего компиляцию прямо из своего окна. Для начала работы нам предваритель- но нужно создать командный файл. Предположим, файл avrasm2.exe находится в упомянутой ранее папке C:\AVRTOOLS. Запустите Блокнот и введите следующий текст (соответственно измените путь, если папка другая): c:\avrtools\avrasm2 -fl %l.asm Строка эта может выглядеть и несколько иначе (пример вы найдете в главе 6). Со- храните эту строку в командном файле, дав ему имя, например asm.bat, и обязатель- но в той же папке, что и avrasm2.exe. Теперь запустите ASM Editor, выберите в меню пункт Service | Properties, в открывшемся окне найдите вкладку Project, а в ней — строку Assemble ASM file (на рис. 5.1 она выделена). В эту строку, как и показано на иллюстрации, введите путь к нашему bat-файлу. Больше ничего настраивать не требуется — остальные пункты можно оставить, как есть, или очи- стить — по желанию. Рис. 5.1. Настройка редактора ASM Editor Теперь по нажатию комбинации клавиш <Alt>+<A>, вне зависимости от того, где находится файл с редактируемой в настоящий момент программой, он скомпилиру- ется, и результат (т. н. hex-файл, о чем далее), если нет ошибок, окажется в той же папке, где и исходный текст. Одновременно с компиляцией отдельно открывается консольное окно с сообще- ниями компилятора (рис. 5.2). Сразу смотрите на последние строки, где должно
Глава 5. Подготовка к программированию MKAVR 99_ быть сообщение Assembly complete, 0 errors (ассемблирование выполнено, 0 оши- бок). Дополнительно в этой же строке должно стоять и 0 warnings (т. е. О преду- преждений), хотя, даже если предупреждения и есть, на процессе компиляции это не отразится. Если ошибок действительно не найдено, то ищите готовый hex-файл в папке с исходным текстом. В противном случае hex-файла не окажется, а в этом окне вам будут выведены номера содержащих ошибки строк в исходном тексте (самой программы или включаемых файлов) и примерное описание проблемы. На- пример, сообщение Undefined variable reference (ссылка на неопределенную пере- менную) означает, что у вас в этой строке имеется идентификатор, который нигде не определен, и т. д. Благодаря таким сообщениям, программный текст удается сра- зу вычистить от синтаксических ошибок. Рис. 5.2. Консольное окно результатов компиляции Обратите внимание, что предыдущий вариант hex-файла, если он существовал пе- ред началом новой компиляции, будет стерт в любом случае, и при наличии оши- бок вы окажетесь без какого-либо скомпилированного варианта. Поэтому при же- лании сохранить промежуточные варианты отлаживаемой программы оба файла — ASM с исходным текстом и скомпилированный HEX — надо скопировать в какую- нибудь другую папку или переименовать. Кроме этого, при успешной компиляции в окне сообщений можно увидеть точный размер скомпилированной программы в байтах, общий объем всех разновидностей памяти, включая EEPROM, и процент, который в них займет программа (см. рис. 5.2). Отметим, что предыдущие версии компилятора Atmel выдавали толь- ко объем скомпилированной программы в словах (words), и для получения размера в байтах приходилось умножать это число на два, а неиспользованную память вы- числять самостоятельно. Учтите еще, что объем hex-файла размеру программы со- ответствовать не будет (подробнее об этом рассказано далее), так что узнать точ- ный объем занимаемой памяти можно либо только из окна компиляции, либо из программы-загрузчика, прилагаемой к программатору.
100 Часть I. Общие принципы устройства и функционирования AtmelAVR Об AVR Studio Приступая к этой книге, я долго размышлял над целесообразностью включения в нее описания работы в Atmel Studio (до версии 5 включительно — AVR Studio). С одной стороны, это заслуженный инструмент, которым пользуются тысячи раз- работчиков, с другой — существенная часть ее функций без особых проблем реали- зуется куда более простыми методами, а без оставшихся в наших несложных зада- чах можно обойтись. Окончательное решение помогла мне принять попытка уста- новить последнюю (седьмую) версию Atmel Studio— это оказался тяжеленный и неповоротливый монстр объемом под гигабайт, который уже одним только коли- чеством проблем в процессе установки напрочь отбивает всякое желание разби- раться с ним далее. Недаром достаточно подробные описания работы в Atmel Studio, которые можно встретить в литературе и в Интернете, большей частью относятся к версии 4jc, до- вольно-таки компактной и не предъявлявшей особых требований к компьютеру. Как мы уже говорили, с «нашими» весьма древними контроллерами можно разо- браться и с помощью этой старой версии, включавшей ассемблер avrasm32, но он все-таки сильно устарел, так что это будет уже не изучение AVR, а одноразовое решение для конкретного случая. Давайте разберемся, чего же мы лишаемся при отказе от освоения Atmel Studio (на- помню, что речь идет не о сложных программных проектах, а о решении относи- тельно простых любительских задач). Прежде всего, мы будем вынуждены обхо- диться без отладчика. Профессиональные разработчики уделяют этому вопросу большое внимание — такое, что средства отладки внедряются в сами контроллеры, невзирая на их усложнение (речь идет об интерфейсах debugWare или более «про- двинутом» JTAG). Для работы с ними нужны соответствующие программные инст- рументы (и Atmel Studio необязательно самый удобный из них), а также и опреде- ленные схемотехнические меры. Именно для таких целей выпускаются специаль- ные фирменные отладочные комплекты, достаточно дорогие, кстати. Подробное обсуждение этих вопросов представляет отдельную и довольно обширную тему, поэтому тех, кто хочет получить хотя бы поверхностное представление о ней, от- сылаем к [2]. Заметки на полях Хотя наша книга и не посвящена Arduino, но стоит отметить интересную возможность программирования Arduino через Atmel Studio (версии не ниже 6). Для этого необхо- дима загрузка надстройки Visual Micro, с которой можно познакомиться на посвящен- ном ей сайте visualmicro.com (в том числе и на русском языке). Создатели надстрой- ки уверяют, что там «все, как в Arduino» (включая доступность всех инструментов и библиотек для Arduino IDE), но при этом доступны также и все возможности Atmel Studio: т. е. эмулятор, трассировщик, отладчик и т. д. Отладку можно производить и без специальных аппаратных средств, на программ- ном эмуляторе — наличием такого инструмента и отличается Atmel Studio (и она не единственная такая). Беда в том, что разработчики подобных средств всегда пы- таются объять необъятное и напихать в них максимум функциональности, отчего
Глава 5. Подготовка к программированию MKAVR 101_ они превращаются в неподъемных чудищ, требующих специального освоения, зна- ния ограничений и методов обхода содержащихся в них ошибок, которых там пре- достаточно (Atmel Studio последних версий тому типичный пример). Если вы дос- таточно хорошо знакомы с интегрированными средствами разработки приложений под Windows вроде Visual Studio или Delphi, то, несомненно, будете страдать от отсутствия трассировщика, возможности установить точки остановки выполнения программы и поменять по ходу дела значение переменных или условий. Легче будет тому, кто такого никогда не изучал, — не зная о таких возможностях, обхо- диться без них гораздо проще. Ведь обходятся как-то в Arduino без официального отладчика, и ничего, правда? Большая часть программ в этой книге также поддается простейшей отладке с по- мощью мультиметра или светодиодов-пробников, в крайнем случае понадобится осциллограф (но он все равно рано или поздно понадобится и вовсе не только для именно отладки, сами увидите). Мы будем иногда оставлять в программах заком- ментированные строки, предназначенные специально для отладки такими способа- ми. Однако уже в главе 7 вы встретите изрядно запутанную по логике работы про- грамму перемигивания разноцветными светодиодами, которую так и хочется запи- хать в отладчик и пройти по шагам. Для этой цели мы далее (см. главу 15) обсудим практические способы выполнения отладки программ единственным доступным без дополнительных сложностей способом — в точности так же, как это делается в Arduino, т. е. с помощью последовательного порта. Это, безусловно, примитивный суррогат настоящего отладчика, но в большинстве случаев его будет вполне доста- точно. Я обсуждение UART потому и отнес в конец книги, что в решении типовых задач мы постараемся обходиться без специальных средств отладки, а потребовать- ся они могут при самостоятельном проектировании уже законченных устройств. Способы загрузки программ в контроллер Когда-то, в начале эры господства микропроцессоров, это была, пожалуй, самая сложная и специфическая тема, представлявшая существенное препятствие для инженера-электронщика, вознамерившегося перейти от обычных логических схем к микроэлектронике. Не так сложно освоить программирование, как смириться с необходимостью городить специальный программатор, требующий нескольких напряжений питания (или искать, где бы приобрести готовый, что в те годы было непросто). Ко всему прочему, никакой флеш-памяти еще не существовало, потому программы в микропроцессорные устройства можно было либо прошивать одно- кратно, без возможности их изменения (так называемая OTR-память), либо возить- ся с предварительным стиранием памяти с помощью ультрафиолетовой лампы (UV-память). Все это, конечно, популярности микропроцессорной технике не до- бавляло. Появление в 1990-х годах EEPROM и флеш-памяти с возможностью многократного перепрограммирования и притом не требующих при этом отдельного повышенного напряжения питания, совершило настоящую революцию. Контроллеры AVR по- явились в разгар этого процесса, и своей популярностью в немалой степени обяза-
102 Часть I. Общие принципы устройства и функционирования Atmel AVR ны тому обстоятельству, что с самого начала предусматривали возможность в том числе и программирования прямо в устройстве. Все AVR, конечно, поддерживают и возможность загрузки программы через от- дельный (параллельный) программатор, как наиболее универсальный способ. При отсутствии источника внешнего тактового сигнала параллельный программатор является единственно возможным способом оживить контроллер, если в нем слу- чайно были неправильно запрограммированы конфигурационные ячейки (см. далее в этой главе). Но параллельный программатор — сложное универсальное устройст- во, которое обычно рассчитано на многие тысячи разновидностей микросхем, стоит немалых денег, и приобретать его только для этой цели, мягко говоря, нецелесооб- разно. Для полноты картины отметим, что, помимо всего этого, для многих моделей есть еще один способ программирования— с помощью JTAG, стандартизированного (IEEE Std 1149.1-1990) интерфейса для загрузки, отладки и тестирования микро- контроллерных устройств, о котором мы уже упоминали. Но мы не будем здесь на нем останавливаться — в любительских конструкциях он не применяется. Поэтому мы перейдем сразу к последовательному способу программирования «прямо в схе- ме» с помощью ISP-программаторов. ISP-программаторы Сокращение ISP расшифровывается как In-System Programmer (внутрисистемный программатор). Программирование при этом производится через последовательный интерфейс, аналогичный по устройству интерфейсу SPI, потому названия выводов программирования такие же, как и у этого интерфейса: MISO, MOSI и SCK. В большинстве контроллеров выводы программирования совпадают с выводами собственно SPI, но не у всех, так что надо быть внимательными. Для очистки со- вести отметим также, что последовательное программирование поддерживают не все контроллеры AVR из семейства Tiny, но сейчас из таких остался, кажется, только один ATtiny28L (который ущербен еще и в других отношениях), потому можно спокойно об этом ограничении забыть. ISP-программаторы не предполагают отдельного устройства для подсоединения и питания программируемой микросхемы, а заканчиваются обычным плоским кабе- лем с двухрядным гнездовым разъемом типа IDC, который подсоединяется к спе- циально предусмотренной штыревой части, располагаемой на плате вашего макета. Питание на программатор при этом поступает от самой схемы, поэтому в разъеме есть отдельная линия питания. Если вы не хотите загромождать готовую схему лишними элементами, то несложно соорудить отдельную платку с панелькой и таким разъемом и, при необходимости что-либо изменить, загружать через нее исправленную программу в контроллер, извлеченный из схемы (или просто соорудить временный программатор на макет- ной плате, — о чем рассказано далее). Мы уже упоминали в главе 4, что много раз так делать не рекомендуется, поскольку можно повредить выводы микросхемы в DIP-корпусе.
Глава 5, Подготовка к программированию MKAVR 103 Подробности Процесс последовательного программирования начинается с того, что на вывод SCK подается напряжение уровня «земли», далее на RESET — короткий положительный импульс, и затем последний также оставляют в состоянии низкого уровня не менее чем на 20 мс (можно предварительно подключить эти выводы к низкому уровню, а за- тем включить питание контроллера). При этом микросхема переходит в режим ожида- ния команд программирования. Поэтому установка программирующего разъема может в некоторых случаях снижать помехоустойчивость схемы — такое сочетание уровней иногда может возникнуть в процессе эксплуатации из-за наводок. Однако на практике для этого нужно слишком невероятное сочетание условий при очень высоком уровне помех, так что вероятность возникновения такой ситуации можно не принимать во внимание. Но и добиться ее полного исключения тоже не очень сложно: на всякий случай стоит устанавливать по выводам программирования внешние подтягивающие резисторы (как это обсуждалось в главе 4) — они эффективно гасят возможные помехи. Надо также отметить, что использованию программирующих выводов в качестве обычных портов ничто, в общем, не мешает. Единственное, чего нельзя делать с вы- водами программирования, если предполагается их использовать в таком качестве, — подсоединять их к выходам какой-то другой микросхемы (или, например, к коллектору открытого транзистора), — тогда и программирования не получится, и вообще можно повредить и схему, и программатор. Можно защититься и от этого (установить по дио- ду последовательно с резисторами 510 Ом — анодом к выводу МК и контактам разъ- ема), но смысла городить подобные сложности нет, — проще взять контроллер с большим числом выводов или программировать его отдельно от схемы. Способы подключения ISP-программаторов к контроллеру стандартизированы, и их разнообразие сводится всего к двум разновидностям одного и тоже типа двух- рядных игольчатых разъемов типа IDC: 6- или 10-контактной. На плате устанавли- вают штырьковую часть, на плоском кабеле, идущем от программатора, — гнездо- вую. Следует отметить, что название IDS относится к разъемам с корпусом, исклю- чающим неверную ориентацию при подключении, но он занимает больше места, и его наличие усложняет разводку платы, потому в реальности на платах обычно ставят бескорпусной вариант того же типа разъема, более известный под названием PLD. Внешний вид штыревой части одной из разновидностей программирующего разъ- ема— 6-контактной— можно увидеть на любой плате Arduino (рис. 5.3). При установке бескорпусного разъема PLD на плату следует всегда помечать вывод номер 1, чтобы не перепутать ориентацию при подключении (на платах Arduino он обычно помечен точкой или цифрой 1). На кабельной части первый вывод всегда находится с той стороны разъема, куда подходит крайний проводник плоского ка- беля, помеченный красным. Сразу разберемся с нумерацией выводов штырьковых разъемов и с двумя разно- видностями стандартной разводки. Нумерация контактов разъемов типа PLD и IDC отличается от обычной и делается не в обход, как у микросхем, а следующим обра- зом: если смотреть на штыри сверху (т. е. со стороны подсоединения ответной час- ти), то вывод номер 1, как и положено, находится слева внизу, вывод 2 — над ним, вывод 3 — следующий за первым в нижнем ряду и т. д., т. е. нижний (левый на рис. 5.4) ряд— это все нечетные, а верхний (правый на рис. 5.4)— все четные контакты. Это сделано потому, что такие разъемы могут иметь не строго фиксиро-
104 Часть I. Общие принципы устройства и функционирования AtmelAVR ISP-разъем Рис. 5.3. Программирующий ISP-разъем на платах Arduino Uno (слева) и Arduino Nano (справа) Рис. 5.4. Разводка выводов 6-контактного (слева) и 10-контактного (справа) ISP-разъемов в схеме переходника от одной конфигурации к другой ванное количество контактов, но при любом их количестве имеющиеся контакты будут нумероваться одинаково. На рис. 5.4 показана разводка выводов для обеих разновидностей. Отличаются они, как видите, только количеством выводов «земель» (GND). Вывод 3 в 10-контактной разновидности никуда не подключается и иногда используется различными про- граммами для дополнительных функций вроде подключения сигнального свето- диода. Сама Atmel когда-то ввела минимальную 6-контактную разновидность, ко- торая встречается чаще и считается «более стандартной» — на нее ориентирована большая часть программаторов, как фирменных, так и самодельных. Поскольку на платах могут встречаться оба вида разъемов (6-контактный занимает банально меньше места, потому и встречается чаще, но это вовсе необязательно), то в хозяй- стве удобно иметь переходник с одного вида на другой. На таком переходнике со стороны, которая подключается к плате, должна быть гнездовая часть штырькового разъема (IDC-10F или IDC-6F в корпусе с выступом или соответствующий PBD без
Глава 5. Подготовка к программированию MKAVR 1(05 корпуса), со стороны программатора— штырьковая (IDC-6M/IDC-10M, соответст- венно, или PLD, как на плате). Подробности Можно не заморачиваться никакими переходниками, если макет устройства собран на обычной беспаечной макетной плате. Необходимые для программирования выводы контроллера: MISO, MOSI и SCK, а также RESET «земля» (GND) и питание Vcc (см. разводку выводов в «даташите» на конкретный контроллер) — соединяются с разъемом программатора обычными проводами-перемычками, идущими в комплекте к макетной плате. Штекеры перемычек отлично входят в гнездовой разъем типа ЮС, и соединение при этом не зависит от числа контактов со стороны программатора. «Лишние» выводы GND в 10-контактном варианте можно при этом для надежности соединить с «землей» макетной платы все параллельно, а можно ограничиться только одним (любым) из них. Точно так же можно быстро соорудить временный переходник для загрузки любого контроллера, извлеченного из схемы, где программирующий разъем не был установ- лен, — для этого, кроме макетной платы с перемычками в комплекте, понадобится па- нелька, а также кварцевый резонатор с конденсаторами или внешний генератор, если в контроллере установлен соответствующий режим работы. Причем при программи- ровании частота кварца или внешнего генератора не имеет значения — можно всегда использовать одни и те же компоненты. Но такая благостная картина всеобщей стандартизации касается только разъемов. Самих программаторов— тьма-тмущая. Так как протоколы программирования AVR расписаны в подробностях в «даташите» каждого контроллера, то каждый второй когда-то считал своим долгом изобрести собственный программатор. С тех пор ситуация несколько нормализовалась, но в результате встречающиеся на рынке программаторы очень разные по удобству, сложности конструкции (и, соответст- венно, по цене) и пригодности работы в различном окружении. Еще можно встре- тить распространенные в свое время простейшие программаторы, которые может за полчаса изготовить каждый, — они подключались к портам LPT или СОМ и пред- ставляли собой просто переходник от одного разъема к другому. При этом вся нагрузка по реализации протокола ложилась на программу, которую написать по силам даже не очень опытному программисту. Другим полюсом стали фирменные программаторы со всяческими гарантиями, в том числе и по части работы на со- временных компьютерах, и по части совместимости с Atmel Studio или другими системами разработки и отладки. Есть и совсем «продвинутые» модели, совме- щающие программатор и эмулятор-отладчик, с соответствующей стоимостью. Разнообразие ISP-программаторов столь велико, что я не буду даже пытаться здесь обозреть хотя бы основные виды. Если кто заинтересуется, то автор [2] попробовал немало разновидностей (хотя и достаточно давно) и приводит их плюсы и минусы. По большому счету все программаторы делают (или, по крайней мере, должны делать) одно и то же, и вся задача выбора сводится к поиску модели, которая бы делала все это наиболее надежным и беспроблемным образом. На мой взгляд, цена при этом не имеет принципиального значения: программатор вы приобретаете не на один раз, нужен он будет постоянно, и вопросы удобства пользования тут на первом месте. Цена фирменных ISP-систем, если не считать очень уж «наворочен-
106 Часть I. Общие принципы устройства и функционирования Atmel AVR ных», не выходит за пределы эквивалента примерно 50 долларов, что величина со- вершенно не смертельная, — приличный мультиметр обойдется дороже. Я тут укажу на программатор, которым в разных модификациях пользуюсь уже в течение двух десятилетий, и ни одного значимого недостатка до сих пор не вы- явил. В последней версии этот программатор называется AS-4E (рис. 5.5), подклю- чается к U SB и имеет гальваническую развязку схемы и компьютера, что значи- тельно снижает вероятность повреждения и того, и другого при каких-либо про- блемах с питанием. При этом питается программатор, как мы уже неоднократно говорили, от схемы, а не от USB. Программирующий разъем— 10-контактный (разводка как на рис. 5.4, справа), и на него будут ориентированы все схемы в этой книге. т. J00 : й' '-v\- '■:•'- '"- > tS;<% :';% Рис. 5.5. Программатор AS-4E (фото с сайта фирмы AS-kit hardware) Программатор AS-4E, как и предыдущие версии AS-программаторов, совместим с Atmel Studio, без проблем работает в любых версиях Windows и позволяет «про- шивать» прямо в системе многие микросхемы Atmel (не только 8-разрядные AVR). Причем интерфейс управляющей программы ASISP настраивается на русский язык и очень нагляден. Когда с появлением семейств Tiny и Mega стали появляться со- общения о сложностях с программированием fuse-битов (о них рассказано далее), я долго не понимал, в чем там проблема — в программе для AS-4 ничего перепу- тать просто невозможно. Поддерживается пакетная работа (когда стирание, запись и проверка объединяются в одну операцию), а также имеется функция перепро- шивки самого программатора, если он вдруг начинает сбоить. Функция эта мне ни разу не пригодилась, но, вероятно, может быть полезной при необходимости рас- ширить список поддерживаемых микросхем. Методику работы с программой ASISP мы продемонстрируем в главе 6, когда перейдем непосредственно к про- граммированию. Заметки на полях Если вы работаете на настольном ПК и у вас есть возможность вставить в него до- полнительную плату с аппаратными СОМ-портами (об этом мы говорили в главе 4), то лучше приобрести не USB-вариант программатора (AS-4E), а традиционный AS-2M,
Глава 5. Подготовка к программированию MKAVR 1_07_ подключаемый через RS-232, — он вполне доступен на сайте as-kit.ru наряду с со- временными версиями. AS-2M делает все то же самое, работает с той же программой ASISP, но имеет одно маленькое преимущество, заключающееся в том, что аппарат- ный СОМ-порт физически отделен от виртуальных, создаваемых различными адапте- рами USB-COM. С точки зрения Windows, AS-4E — точно такой же адаптер, потому если вы подключите в USB одновременно, например, программатор и адаптер USB- UART для чтения данных с контроллера (что очень удобно для отладки программ, — см. главу 75), то два драйвера этих двух портов, не исключено, будут конфликтовать, и придется их подключать по очереди. Подключать к СОМ-порту именно программа- тор проще, чем адаптер, т. к. он, кроме физического СОМ-порта, больше ничего не требует, и все остальное останется по-старому. Arduino как ISP-программатор Честно говоря, завести разговор за эту тему меня заставила исключительно ее по- пулярность — на различных сетевых ресурсах можно встретить более или менее дилетантские откровения «чайников», вдруг обнаруживших, что контроллеры AVR — это совсем не только ATmega328, и решивших, что именно таким путем их проще всего осваивать. Давайте для начала немного разберемся в том множестве возможностей, скрывающихся под единым заголовком «Программирование кон- троллеров AVR через Arduino», попробуем их классифицировать и оценить с точки зрения практической полезности. Самая простая и очевидная возможность обусловлена наличием ISP-разъема — плата Arduino при этом используется просто как адаптер. Конечно, так можно ис- пользовать только Arduino Uno, поскольку «родной» контроллер при таком исполь- зовании извлекается, чтобы не мешал. Далее вы соединяете программируемый кон- троллер с ISP-разъемом через выводы платы (12 — MISO, 11 — MOSI, 13 — SCK, Res — Reset, а также GND и Vcc) и программируете его обычным способом, через любой ISP-программатор. Вы уже, конечно, заметили неудобства: если ваш кон- троллер запрограммирован на работу от внешнего кварца, то нужно еще дотяги- ваться до выводов XTAL1 и XTAL2 в панельке извлеченного «родного» контрол- лера, а если от внешнего генератора, то как-то пристраивать его к плате. Потому относительно удобно этим способом пользоваться только в случае ATmega8 и всех его родственников в 28-выводном корпусе DIP — у них совпадает разводка выво- дов, потому этими контроллерами просто заменяют «родной» в панельке, и ника- ких проводов никуда тащить не требуется. Хотя, на мой взгляд, это ничуть не удобнее описанного ранее способа подключения через макетную плату. Вторая возможность— использование среды Arduino IDE вместо программы- загрузчика для загрузки AVR-контроллеров через какой-либо из обычных про- грамматоров (без участия платы Arduino). Подразумевается, что при этом вы за- гружаете туда скетч, созданный в Arduino IDE. Это возможно только для програм- маторов, поддерживаемых средой, причем на первый взгляд их достаточно много (см. меню Arduino IDE Инструменты | Программатор...), но из популярных там только стандартный AVR ISP и USBasp, остальные встречаются на практике значи- тельно реже. Поэтому такую возможность целесообразно применять для загрузки
108 Часть I. Общие принципы устройства и функционирования Atmel AVR в саму плату Arduino внутреннего загрузчика, работающего через последователь- ный порт (Bootloader'a), если он был по каким-то причинам испорчен (вопрос о возможности и целесообразности установки Bootloader'a на обычные контролле- ры мы рассмотрим в главе 6). Третья возможность интереснее: плату Arduino, как и любой универсальный кон- троллер, можно запрограммировать для работы в качестве программатора (в том числе, кстати, и универсального параллельного, на чем мы не будем останавливать- ся). Для этого удобнее применять опять же Uno, но в принципе можно в любую из плат загрузить соответствующий скетч, который называется ArduinoISP. Он нахо- дится в папке с интегрированными примерами (Examples) и доступен прямо из сре- ды через меню Файл | Примеры (в Arduino IDE 1.6.9 он расположен последним, под номером 11). Загрузив его в контроллер платы обычным путем из Arduino IDE, вы превратите Arduino в последовательный программатор, с помощью которого можно осуществлять все описанное в предыдущем абзаце, только с меньшим коли- чеством проблем. Внешний контроллер (или другая плата Arduino) подключается так же, как в первом случае, т.е. к выводам 12 (MISO), 11 (MOSI), 13 (SCK) и т. д. — теперь они играют роль просто интерфейса SPI. За одним важным исклю- чением — вывод RESET программируемого контроллера подключается к выводу 10 (вывод SS интерфейса SPI), а не соединяется с выводом Reset платы, как ранее. Заметки на полях Я лично для указанных целей не стал бы долго разбираться со средой, а просто ис- пользовал бы имеющийся программатор в штатном режиме. В случае Bootloader'a нужный НЕХ-файл можно найти в папке установленной у вас Arduino IDE в каталоге hardware\arduino\avr\bootloaders. He забудьте еще установку «фьюзов», необходимую конфигурацию которых можно найти в файле hardwareXarduino\avr\boands.txt, — при за- грузке из Arduino IDE среда их устанавливает сама, а здесь придется вручную (под- робности приведены в следующем разделе). Но не все знают, что подготовленный и скомпилированный в среде Arduino скетч тоже можно загрузить в любой контроллер таким же способом. Нужные НЕХ-файлы придется, правда, поискать, но это не очень сложно. Результаты деятельности Arduino IDE в случае Windows 7 и более поздних версий размещаются в недрах папки Пользователи\<имя nonb3oeamenn>\AppData\ LocahTemp. Там вы найдете кучу папок с расширением tmp, название которых начина- ется с build (например, build290388496895462656.tmp) — внутри одной из них и находится искомый НЕХ-файл, имя которого должно совпадать с именем файла скетча. Заметим еще, что при программировании из среды Arduino нет доступа к конфигу- рационным ячейкам, а принудительное их изменение внешним программатором может привести к неработоспособности загруженного скетча, и почти наверняка выведет из строя Bootloader, если вы загружали именно его (подробности на эту тему приведены далее). Из этого краткого обзора видно, что из Arduino программа- тор получается плохой и пригодный в основном для каких-то специфических це- лей. Тех же целей можно достичь быстрее и без проблем с помощью обычного про- грамматора в штатном режиме.
Глава 5. Подготовка к программированию MKAVR 109 Конфигурационные ячейки (fuse-биты) В англоязычной инструкции конфигурационные ячейки по традиции называют fuse bits, что не совсем точно отвечает их назначению: fuse в переводе означает «предо- хранитель», а fuse-биты всего лишь отвечают за начальную конфигурацию кон- троллера. В русскоязычной среде их часто называют фъюзами. Они задают началь- ное состояние контроллера и могут быть изменены только с помощью программа- тора— загруженная программа к ним может иметь доступ только на чтение. Причем наличие загрузчика через последовательный порт (Bootloader'a) уже пред- полагает определенную конфигурацию fuse-битов, при их изменении он может просто не заработать, и именно поэтому в Arduino конфигурационные ячейки дос- тупны только теоретически, и достаточно сложным путем (через команды загруз- чика avrdude). Прибегать к этому способу и менять что-то в установках конфигура- ционных ячеек в Arduino решительно не рекомендуется. «Фьюзы» представляют собой байтовые ячейки обычной памяти EEPROM, с чем и связаны сложности в истолковании их состояния. Ячейка EEPROM по умолчанию находится в состоянии логической единицы, и для перевода в состояние логическо- го нуля ее надо «запрограммировать». Сотрудники фирмы Atmel когда-то посчита- ли терминологию «запрограммированная» {programmed, состояние логического нуля) и «незапрограммированная» (unprogrammed, состояние логической единицы) естественной, в чем жестоко промахнулись, а исправлять задним числом было уже, конечно, поздно. Автор [2] уверяет, что все дело в разнице между мышлением про- граммистов и электронщиков, — именно первые заварили кашу, интерпретировав programmed как «включено» (In), a unprogrammed как «выключено» (Out), что мо- жет быть и естественно с точки зрения языка, но обычно в электрике и электронике «включено» все-таки подразумевает логическую единицу, а не наоборот. В результате в одних прошивающих программах отмеченная ячейка (установленная галочка или крестик) означает, что опция «включена» («запрограммирована», т. е. в fuse-бит записывается ноль), а в других, написанных под контролем электронщи- ков, вся промежуточная терминология игнорируется, и отмеченная ячейка просто означает, что в нее записана единица. В этой ситуации меньше шансов что-то пере- путать имеет тот, кто не изменяет одной и той же программе прошивки, как автор этих строк. Но иногда приходится переходить из программы в программу и на этот случай приведем общее правило для всех загрузочных программ без исключения: перед тем, как что-либо менять в конфигурационных ячейках, обязательно загрузите их текущее состояние. Это надо не только для обретения однозначности в вопросе, что означает установленная галочка, но и потому, что иначе вы рискуете записать про- извольную конфигурацию, пропустив установку тех «фьюзов», которые должны быть установлены для нормальной работы контроллера. Из таких ячеек по крайней мере одна заведомо будет отмечена, как «запрограмми- рованная», т. е. находящаяся в состоянии логического нуля. Эта ячейка имеется во всех контроллерах, допускающих последовательное программирование (к тем не- многим, у которых ее нет, вы все равно доступа через ISP-программатор не имеете),
110 Часть I. Общие принципы устройства и функционирования Atmel AVR и она носит название spien (SPI Enable). При логической единице в этой ячейке ISP-программирование запрещается. После загрузки текущей конфигурации по со- стоянию spien можно определить, что именно в вашем загрузчике означает отме- ченная или неотмеченная ячейка. Есть и второй такой же «фьюз», который при последовательном программирова- нии, наоборот, всегда должен быть «^запрограммирован», т. е. находиться в со- стоянии логической единицы — он называется rstdisbl (reset disable), и при уста- новке в состояние нуля отключает вывод RESET, превращая его в один из обыч- ных выводов. В отличие от spien, ячейка rstdisbl имеется не во всех контроллерах, а только в тех, где выводов у корпуса относительно немного, и каждый вывод пор- та на счету (например, на рис. 5.7 и 5.8 она есть, а на рис. 5.9 — отсутствует). Оба этих «фьюза» могут отрубить возможность ISP-программирования, но важнейшее отличие spien от rstdisbl состоит в том, что spien невозможно изменить через по- следовательный программатор, а вот rstdisbl— запросто. И по этой причине его лучше не трогать вовсе: при случайном его программировании для восстановления доступа к контроллеру придется добывать дорогущий параллельный программа- тор — более простыми методами тут не отделаешься, так что проще бывает кон- троллер просто выбросить. По сути «фьюзом» rstdisbl возможные катастрофические вмешательства в конфи- гурационные ячейки и ограничиваются. Все остальное можно исправить через ISP- программатор повторным изменением состояния просто так или с применением некоторых несложных подручных средств. К последнему случаю относится извест- ная ошибка с fuse-битами группы cksel (обычно их четыре — от ckselo до cksel3), задающими режим тактирования. Их состояние перепутать проще всего, т. к. со- стояние оооо у всех контроллеров означает тактирование от внешнего генератора, а оно симметрично состоянию mi, которое означает работу от кварцевого резона- тора. Почему-то страх перед возможностью их ошибочного программирования ис- пытывают даже опытные пользователи. Как мы уже говорили, исправить положение можно за пару минут, если у вас есть готовый цифровой генератор (см. главу 4\ или максимум за полчаса, которые потребуются, чтобы найти в закромах детали для сборки на макетке простейшего генератора. Частота не имеет значения — кое-кто из радиолюбителей уверяет, что освоил способ временной подачи тактовых импульсов с помощью пальца, прило- женного к выводу XTAL1. Подробности На всякий случай приведем на выбор две схемы простейших генераторов, пригодных для этой цели. Для схемы, показанной на рис. 5.6, слеве, имеется возможность выбо- ра из весьма широкого ассортимента микросхем — кроме указанных на схеме отече- ственных микросхем серии 561 и их импортных аналогов, подойдут микросхемы с ин- верторами отечественных серий 1561, 1564, импортных 74НС и т.д.. Разводка выво- дов на рис. 5.6 показана для 561ЛА7 или ЛЕ5, для их аналогов из других серий разводка будет отличаться. Можно также использовать любую микросхему с одновхо- довыми инверторами (561ЛН2, CD4049 и их аналоги из других серий). В схеме, пока- занной на рис. 5.6, справа, взят за основу таймер 555 (отечественный аналог КР1006ВИ1,
Глава 5. Подготовка к программированию MKAVR 111 его можно заменить на КМОП-версию ICM7555 или КР1441ВИ1). Эта схема сложнее и требует больше деталей, но сам таймер по неведомой мне причине жутко популярен у радиолюбителей и вполне может оказаться у вас под рукой. +5 В к выводу 14, GNO к выводу 7 10-100 нФ | п DD1.1 1-100кОм 5 DD1.2 тт OD1 561ЛА7, 561ЛЕ5 CD4011, СО4001 1-100 кОм I • R2*R1 I «■ 10-100 нФ ■ h La ■ < 1 и- +5 В 555 4=' ; юиоо нф Рис. 5.6. Простейшие генераторы При указанных на схемах величинах резисторов и конденсаторов частота окажется в пределах от десятков герц до десятков килогерц. Никто не мешает раздвинуть гра- ницы указанных предельных значений, только следите, чтобы произведение R (в омах) на С (в фарадах) не выходило за границы примерно 10"2—10", иначе частота окажется либо слишком маленькой, либо слишком большой для нормальной работы классических серий КМОП 561 или CD4000. На трех иллюстрациях, приведенных далее (рис. 5.7-5.9), в окне установки fuse- битов программы ASISP показано состояние по умолчанию конфигурационных ячеек трех «наших» типов контроллеров, упомянутых в конце главы 4. Как видите, перепутать тут что-либо достаточно сложно: отжатая кнопка означает единичное состояние ячейки, нажатая— нулевое, а для ясности с «запрограммированным» состоянием разработчики программы специально предусмотрели соответствующую памятку. Конфигурационные ячейки делятся на три группы размером в один байт каждая, называемые Fuse Low Byte, Fuse High Byte и Fuse Extended Byte. В программе ASISP эти названия не приведены (одна из мелких недоработок программы), и группы выделены только их расположением: на рис. 5.7-5.9 в секции под заголов- ком Fuse биты слева размещается Fuse Low, посередине — Fuse High и справа — Fuse Extended. Группа Extended, как видите, из трех представленных контроллеров присутствует только в ATtiny2313, в остальных хватило и двух байтов. Нумерация битов каждого байта идет сверху вниз (в таблицах, которые вы можете найти в «да- ташитах» на каждый контроллер, в разделе Memory Programming— наоборот!), т. е. во всех трех примерах бит по названием CKSEL0 — младший бит группы Fuse Low. Подробности В упомянутой ранее задаче по ручной загрузке Bootloader'a в контроллеры Arduino значения «фьюзов» придется извлекать из hardware\arduino\avr\boards.txt, где они запи- саны в шестнадцатеричной форме для каждой из групп Low, High, а также Extended, если она имеется. Исходя из сказанного, интерпретировать такую запись очень про-
112 Часть I. Общие принципы устройства и функционирования AtmelAVR сто — например, для случая, показанного на рис. 5.7, байт группы Low запишется как 01100Ю0, или $64 в шестнадцатеричной форме (0x64, если вам так привычнее). Есть программы для загрузки (например, extreme Burner, работающая с программатором USBasp), где «фьюзы» вообще загружаются именно в шестнадцатеричной форме. Так, конечно, менее наглядно, зато труднее перепутать «запрограммированное»- «незапрограммированное» состояния, а также забыть установить в нужное положение какую-то из ячеек. Рис. 5.7. Конфигурационные ячейки ATtiny2313 Рис. 5.8. Конфигурационные ячейки ATmega8
Глава 5. Подготовка к программированию MKAVR 113 Рис. 5.9. Конфигурационные ячейки ATmega8535 Кратко охарактеризуем назначение конфигурационных ячеек, показанных на иллю- страциях. Будем для начала ориентироваться на рис. 5.7, где показан ATtiny2313, а в конце дополним описанием тех ячеек, которые имеются в других «наших» моделях. Группа Fuse Low обычно начинается с одной из самых востребованных подгрупп fuse-битов: ячеек CKSEL, задающих режим тактирования. В «наших» контролле- рах их по четыре штуки: от CKSEL0 до CKSEL3. Для краткости их записывают в виде обычного двоичного числа, которое обозначается CKSEL3:0 (т. е.: CKSEL3 — старший разряд, a CKSEL0— младший). Для ATtiny2313, как видите, их значение по умолчанию равно оюо. Если справиться с таблицей в документации (она для всех контроллеров находится в разделе System Clock and Clock Options), то мы уз- наем, что такое значение задает режим работы от встроенного генератора 8 МГц. Постойте, а почему 8 МГц, когда частота тактирования по умолчанию, как мы зна- ем из главы 2, всегда подгоняется под 1 МГц? Все просто — внутренний генератор ATtiny2313 может работать только в трех режимах: кроме 8 МГц, это 4 МГц (CKSEL3:0 = оою) и 128 кГц (CKSEL3:0 = оно). А чтобы обеспечить 1 МГц, уста- навливается в логический ноль («программируется»!) еще и специальный «фьюз» под названием CKDIV8 — старший бит в группе Fuse Low, который обеспечивает при запуске деление тактовой частоты в 8 раз. В других контроллерах, где генератор сам может настраиваться на частоту 1 МГц по умолчанию, fuse-бит CKDIV8 отсутствует (см. рис. 5.8-5.9), и, очевидно, был введен только для этой цели. Хотя документация говорит, что CKDFV8 может ис- пользоваться еще и в случае, если при тактировании от внешнего генератора его частота превышает допустимую для этой модификации контроллера. В остальном он не очень нужен, т. к. именно в ATtiny2313 есть еще довольно гибкая система
114 Часть I. Общие принципы устройства и функционирования Atmel AVR управления тактовой частотой изнутри программы через регистр CLKPR, в кото- ром с помощью битов CLKPS3:0 можно установить коэффициент деления частоты от 1 до 256. При работе от внешнего «кварца» следует устанавливать CKSEL3:0 в состояние mi, а от внешнего генератора— в обратное состояние оооо. Эти установки уни- версальные для всех AVR-контроллеров, и справляться с таблицей в «даташите» на практике нужно только для случая внутреннего генератора, для которого установки для разных типов чаще всего различаются. Никаких, конечно, керамических резо- наторов мы не употребляем, потому остальные варианты установок CKSEL нам не потребуются. При включении кварцевого резонатора для ATmega8 и ATmega8535 не забывайте также про ячейку СКОРТ, о которой далее. Следующие по порядку биты SUT (SUT0 и SUT1) управляют временем запуска по- сле сброса, в том числе и при включении питания. По умолчанию они устанавли- ваются в состояние SUT1:O = ю, что при запуске от внутреннего или внешнего RC-генератора означает время около 65 мс + 6 тактов. При работе от внешнего кварца контроллер запускается быстрее: это же состояние означает время запуска около 4 мс + 16 000 тактов (4 мс при 4МГц, всего 8 мс), — в первом случае RC-re- нератору дается дополнительное время на «раскрутку». Для наших целей эти ячей- ки лучше оставлять в «умолчательном» состоянии. Бит CKOUT из «наших» контроллеров имеется также только в ATtiny2313 и под- ключает выход тактового сигнала к одноименному внешнему выводу контроллера (вывод 6, он же вывод номер 2 порта D). Это представляет определенный интерес при желании синхронизировать тактовые сигналы нескольких контроллеров или внешних устройств, а также просто при необходимости иметь в схеме генератор стабильной частоты. На практике эта возможность используется редко (вывод но- мер 2 порта D чаще всего требуется другими своими функциями), но следует учесть, что она имеется не только в ATtiny2313, но и в некоторых других контрол- лерах Tiny и Mega (включая знакомых нам «продвинутых» наследников Mega8 в лице ATmega88/ATmega328). Переходим к группе Fuse High. О fuse-битах RSTDISBL и SPIEN мы уже говорили ранее — в наших случаях их трогать категорически не рекомендуется (а в случае SPIEN при последовательном программировании пытаться его переключить еще и бесполезно). «Фьюзы» BODLEVEL, которых здесь аж три штуки, задают уровни срабатывания системы мониторинга питания BOD. В состоянии BODLEVEL2:0 = m, как по умолчанию, система BOD выключена, остальные комбинации задают уровни сбро- са при падении напряжения до 1,8 вольта (но), 2,7 вольта (ioi) и 4,3 вольта (юо). Все комбинации при нулевом состоянии старшего бита (BODLEVEL2 = 0) помече- ны, как Reserved, т. е. их устанавливать нельзя. В контроллерах ATmega8 и ATmega8535 (см. рис. 5.8 и 5.9) управление BOD реа- лизовано несколько проще: там ячейка BODEN управляет включением (BODEN = о — BOD включена, BODEN = i — BOD выключена), а ячейка BODLEVEL — уровнем
Глава 5. Подготовка к программированию MKAVR порога сброса (BODLEVEL = о — уровень 4,0 вольта, BODLEVEL =1 — уровень 2,7 вольта). При питании ниже 2,7 вольта эти контроллеры работать не могут. «Фьюз» WDTON — принадлежность всех контроллеров, оснащенных сторожевым таймером WatchDog, т. е. можно считать, что абсолютно всех. Но этот бит обеспе- чивает не простое включение-выключение сторожевого таймера, как можно поду- мать, исходя из названия. WDTON = l (по умолчанию) обеспечивает полную дос- тупность к управлению «вотчдогом» из программы, на чем мы подробно остано- вимся в главе 14. А вот WDTON = о просто навсегда включит этот таймер в режим периодического сброса по истечении заданного интервала времени (сам интервал можно задавать из программы). Есть и другие нюансы в использовании этого бита в различных моделях, поэтому здесь только отметим, что в обычных случаях этот бит трогать не надо. Ячейка EESAVE есть в большинстве контроллеров, оснащенных памятью EEPROM (не оснащены ей только некоторые младшие Tiny). По умолчанию она установлена в единичное состояние и ни на что не влияет, а установка ее в состоя- ние нуля защищает EEPROM от стирания в режиме полного сброса контроллера (при подаче команды «Стирание кристалла» перед загрузкой программы). Так вы можете записать в EEPROM, например, серийный номер изделия, индивидуальные калибровочные коэффициенты датчиков и тому подобные данные, меняющиеся от экземпляра к экземпляру, и, выключив стирание EEPROM, сколько угодно менять основную программу без необходимости записывать эти данные каждый раз за- ново. Fuse-бит DWEN характерен для контроллеров со встроенным отладочным интер- фейсом debugWare (более примитивный аналог JTAG), и просто запрещает или разрешает его работу. По умолчанию работа запрещена (и слава богу, мы туда ла- зать не будем). Не понадобится нам и последний бит у ATtiny2313, который един- ственный оказался в группе Fuse Extended, и у нас тут носит название SPMEM. В других моделях, где он имеется, он может называться SELFPRGEN, в том числе это относится и к более поздним модификациям ATtiny2313 (в «даташите» вы встретите именно его). Второе название, несмотря на неблагозвучное звучание, логичнее, т. к. этот бит управляет возможностью самопрограммирования (self programming). Нам он не понадобится, т. к. мы внутренний загрузчик использовать в наших задачах не будем. А при необходимости записи Bootloader'a в Mega 8 или в его «ардуиновских» наследников (ATmega 168-328) этот fuse-бит учитывать не надо, так там он все равно отсутствует. Остановимся на «фьюзах» с рис. 5.8 и 5.9 для контроллеров ATmega8 и ATmega8535, которые мы не описывали ранее. Во-первых, в этих моделях, как и во многих других Mega, имеется не простой self programming, а полномасштабный контроль загрузки в виде fuse-бита BOOTRST и двух битов: BOOTSZ0 и BOOTSZ1. Первый из них (при установке в нулевое значение) задает перенос адре- са старта выполнения программы после загрузки в начало специальной boot- области для размещения загрузчика (Bootloader'a) ближе к концу памяти, а вторые два задают размер этой области (максимально 1024 байта).
116 Часть I. Общие принципы устройства и функционирования AtmelAVR Подробности Для нас «фьюзы» управления загрузкой могут иметь значение для понимания, что именно мы делаем, когда загружаем Bootloader в контроллер. Значение Fuse High, указанное в ...avr\boardstxt для контроллера ATmega328 равно Oxda. Учитывая, что эти биты в нем, как и в ATmega8, являются тремя младшими битами этого байта, получа- ем, что BOOTRST = о, a BOOTSZ1:0 = 01. Можем проверить по «даташиту», что такое сочетание означает перенос старта программы в область загрузки, величина которой равна 512 байтов, — столько отнимается от основного массива flash-памяти для раз- мещения Bootloader'a. И последняя конфигурационная ячейка, также отсутствующая в ATtiny2313, носит название СКОРТ и выполняет весьма существенные функции. Мы о ней уже упо- минали в главе 2 при рассмотрении тактирования — режим по умолчанию («неза- программированное» состояние, СКОРТ = l) предусматривает уменьшение размаха колебаний генератора, за счет чего вроде бы снижается потребление. При частотах, близких к предельной (для ATmega8 и ATmega8535 это 16 МГц), СКОРТ необхо- димо «запрограммировать», т. е. установить в нулевое состояние, иначе генератор может вовсе не заработать. Потому этот fuse-бит при тактировании от кварцевого резонатора вообще рекомендуется сразу переключать в нулевое состояние, а не ос- тавлять в положении по умолчанию — экономия там мизерная, а так проще не за- быть его «включить» при смене кварца. Обращать внимание на состояние СКОРТ необходимо также при включении от внешнего генератора или низкочастотного внешнего кварца — при соответствующей конфигурации ячеек CKSEL он «заведу- ет» подключением внутренних конденсаторов 36 пФ к выводам XTAL. В случае внешнего генератора с не слишком мощным выходом (выход микросхемы КМОП «классической» серии 4000 или отечественной 561) его, наверное, лучше отклю- чать, переводя в единичное состояние. Вы заметили, конечно, что на рис. 5.7-5.9 представлена еще одна секция под назва- нием Биты защиты (Lock bits). Они защищают загруженную программу от изме- нений и копирования. Рекомендуем к этой секции не обращаться вовсе — возмож- но, прочесть вы программу еще сможете (в зависимости от установок), но что-то изменить уже не получится, в том числе и в части установки Lock/Fuse-битов. При случайной установке каких-то из битов защиты единственная возможность полу- чить доступ к контроллеру — полностью его очистить, сделать все установки зано- во и снова загрузить программу. * * * В первой части книги вы немного познакомились с возможностями контроллеров семейства AVR и теперь в первом приближении знаете, что можно от них полу- чить, если не ограничивать себя куцыми рамками Arduino. Вы также узнали, какие приспособления и инструменты минимально необходимы для разработки и загруз- ки программ и как их настроить. В следующей части мы, наконец, перейдем к практическому воплощению полученных знаний. Начнем с простейших примеров ассемблерных программ, познакомимся с их общей структурой, сравним со знако- мыми вам программами Arduino, а также кратко рассмотрим систему команд.
ЧАСТЬ II Программирование микроконтроллеров AVR на ассемблере Глава 6. Основы программирования МК AVR Глава 7. Система команд AVR Глава 8. Арифметические операции и операции в двоично-десятичном формате
ГЛАВА 6 Основы программирования МК AVR В ассемблере, как ни в каком другом языке программирования, важно различать основу— средства, которые абсолютно необходимы для создания работающей программы, — от всяческих надстроек, призванных сделать жизнь программиста удобнее. Вряд ли вас сочтут грамотным программистом, если вы не знаете, что такое макросы и как их употреблять, но для электронщика это совершенно нор- мально. Поскольку мы тут не пишем учебник по языку ассемблера, а всего только показываем элементарные способы его применения в приложении к реальным за- дачам, то в дальнейшем изложении мы постараемся ограничиваться минимумом необходимых средств, а об остальном упоминать лишь для общего образования. Концептуальная особенность, отличающая ассемблерные программы, которые вы встретите в этой книге, от программ для AVR, написанных на любом языке высо- кого уровня, заключается в том, что мы здесь будем пользоваться памятью данных SRAM (аналогом оперативной памяти обычного микропроцессора) только в от- дельных случаях, строго по необходимости. Как мы уже знаем, в ядре AVR присут- ствуют 32 байтовых регистра общего назначения, которые мы преимущественно и станем использовать для хранения текущих переменных. Этим мы, во-первых, из- бавляемся от многочисленных операций чтения/записи в память, которые занимают много процессорного времени, во-вторых, делаем программу более компактной и читаемой. Уменьшается также опасность залезть в область стека или, наоборот, в область регистров РВВ и РОН, потому что они тоже размещаются в линейном пространстве общей памяти, и запись в них может осуществляться теми же коман- дами, что и в область памяти данных SRAM. А память данных можно использовать строго по ее назначению, вытекающему из названия: для хранения именно оперативных данных, т. е. величин, которые в про- цессе выполнения программы могут меняться, но достаточно редко (например, зна- чение даты: числа, дня недели, месяца и года в часах реального времени). Хранить в оперативной памяти неизменные константы — варварство даже в среде Arduino, причем среди программистов, если судить по примерам на официальном сайте arduino.cc, это весьма распространенное заболевание. Константы, которые вообще не изменяются, нужно хранить вместе с программой, для чего любой язык дает достаточно возможностей, — примером могут служить адреса регистров конкрет- ного контроллера, хранящиеся в отдельном подключаемом include-файле. Констан-
120 Часть II. Программирование микроконтроллеров AVR на ассемблере ты, индивидуальные для каждого экземпляра устройства (например, калибровоч- ные коэффициенты датчиков) следует хранить в EEPROM, откуда их легко про- честь и при необходимости изменить отдельной процедурой калибровки, не затра- гивая саму программу. Такое соответствие каждой разновидности памяти ее назна- чению позволяет оптимальным образом использовать ресурсы контроллера и выжать из него намного больше, чем позволяет любой язык высокого уровня. Как вы уже знаете, при работе МК последовательно выполняет команды програм- мы, записанной в памяти программ. Программист может менять порядок выполне- ния команд, организуя циклы и различные переходы. Одно из самых мощных средств программирования — вызов подпрограмм или процедур (в нашем случае это одно и то же), т. е. кусков кода, которые могут использоваться неоднократно. Во всех ассемблерах вызов процедур предусмотрен обязательно. Заметки на полях В Pascal и других классических алголоподобных языках подпрограммы делятся на процедуры и функции, а в языке С и всех остальных, основанных на его синтаксисе, есть только функции. В ассемблере, строго говоря, существуют только подпрограммы, хотя в основе это все одно и то же — пример еще одной путаницы в головах програм- мистов, в которую вникать совсем не хочется. Потому пусть читатель меня извинит, если я иногда буду вперемешку употреблять термины «процедура» и «подпрограм- ма», подразумевая, что это означает одинаковые сущности. А вот «функций», как та- ковых, собственно в ассемблере не бывает вовсе. Функция — это всего лишь такой особый способ передачи параметров из подпрограммы, характерный именно для язы- ков высокого уровня. Для ассемблера функция ничем не отличается от любой другой подпрограммы. Кроме выполнения просто процедур-подпрограмм, важное значение имеет обра- ботка прерываний. По сути, это такая же процедура, но ее выполнение отличается некоторыми существенными аппаратными нюансами, потому необходимые коман- ды для обработки прерываний другие. Давайте разберемся во всем этом подробнее. Общая структура ассемблерной программы и ее выполнение Естественно, программу сначала нужно записать в память МК, причем так, чтобы МК «знал», откуда начинать при включении питания или после подачи импульса на вывод RESET . Это его «знание» в случае современных МК AVR также програм- мируется, однако пока для простоты будем считать, что программа всегда начинает выполняться с самой первой ячейки памяти программ — т. е. с нулевого адреса. Исходя из этих обстоятельств, программа должна иметь определенную структуру. По этому начальному (нулевому) адресу почти всегда располагается одна и та же команда безусловного перехода, название которой во всех ассемблерах происходит от английского jump (прыжок). Для AVR она может записываться следующим образом: rjmp RESET ИЛИ jmp RESET
Глава 6. Основы программирования MKAVR 727 Форма написания (jmp или rjmp) зависит от выбранного контроллера — если в нем объем памяти программ меньше или равен 8 кбайт, то используется команда rjmp (relative jump, т. е. относительный безусловный переход). Она занимает в памяти два байта — как и практически все остальные команды AVR. Код самой команды в этих двух байтах занимает обычно старшую тетраду старшего байта — т. е. четыре бита, остальные 12 битов представляют собой адрес, куда переходить — в рассмат- риваемом случае компилятор подставит адрес команды, следующей сразу за меткой reset, с которой и начнется собственно выполнение программы. Метка с именем reset, естественно, всегда должна присутствовать, но может быть расположена уже в любом другом удобном месте программы, за исключением, возможно, еще нескольких первых адресов, о назначении которых далее. Метка, впрочем, может называться и не reset, а любым другим именем (например, Main, или begin, или Setup, или как-то еще), просто у нас так будет принято для удобства чтения: хотите найти в любой программе ее начало — ищите метку reset. Вернемся к форме записи команды. 12 битов адреса могут представлять 4096 раз- личных адресов. Так как единицей объема памяти программ служит слово из двух байтов, то общий объем адресуемой таким образом памяти и составит 8 кбайт. А вот если памяти больше, то приходится прибегнуть к команде jmp (абсолютный безусловный переход) — она состоит из четырех байтов, в которых адрес займет 22 бита, и потому может адресовать до 4 М слов (до 8 Мбайт) памяти. Те же соображения относятся к другим командам, адресующим память про- грамм, — к паре rcaii и call (а также, с некоторыми нюансами, к lpm и eipm). В примерах этой книги со старшими моделями семейства Mega мы не работаем, и потому ограничимся командами rjmp, lpm и rcaii, но постараемся о сказанном не забывать. Заметим, что в системе команд AVR семейства Mega есть еще команды icaii и i jmp (косвенный вызов и косвенный переход), которые по определению могут адресо- вать 64 К слов (до 128 кбайт) памяти,— для этих команд адрес задается 16-би- товым регистром z (о нем будет рассказано далее). Однако их употребляют не- часто, а в начале программы (в таблице прерываний, описанной далее) их вообще указывать нельзя чисто технически (нужно заранее задавать значение z, а до начала программы это сделать невозможно). Инструкции и нотация AVR-ассемблера Для начала отметим, что все последующее изложение рассчитано на использование ассемблера avrasm2, т. к. старый avrasm32 имеет заметно отличающийся набор ди- ректив компилятора. Правда, различия в основном касаются расширений для avrasm2 (и приближения к нотации языка С), потому много переделывать не при- дется. Учебное пособие [11], кстати, единственный более или менее подробный учебник по современной версии AVR assembler, который я смог разыскать (если не считать, конечно, справочника с официального сайта [4]), но опыт показывает, что и краткой русскоязычной справки [3] вкупе с описаниями команд по-русски [6,7] или по-английски [8] для вполне полноценной работы тоже достаточно.
122 Часть II. Программирование микроконтроллеров AVR на ассемблере Особенности мнемонической записи большинства команд в AVR-ассемблере такие же, как и в любых других ассемблерах. Сначала идет собственно команда (в AVR команды бывают двух-, трех- и четырехбуквенные), затем через пробел или знак табуляции (этих знаков может быть любое количество больше нуля) следуют опе- ранды. Некоторые команды операндов не имеют (lpm, reti), в других есть один операнд (inc ri6, rjmp reset). Если команда имеет два операнда, то сначала указы- вают приемник, затем источник (это т. н. прямая польская запись). Между прием- ником и источником обязательно должна стоять запятая (с любым числом пробелов до или после нее, или вообще без них). Так, выражение sub ri6, ri7 означает, что из содержимого г1б нужно вычесть содержимое г 17, а результат окажется в г 16. Общее правило для использования пробелов и знаков табуляции такое: нельзя раз- бивать идентификатор на части и, наоборот, нельзя сливать разные идентификато- ры между собой — хотя бы один пробел, знак табуляции (или знак препинания, если это предусмотрено форматом команды) между наименованиями инструкций, переменных, регистров и т. п. должен быть. Сразу заметим, что AVR-ассемблер регистр букв не различает, в том числе и в при- своенных программистом именах переменных, констант, меток и т. п. (одинаково правильной будет форма записи Jmp, jmp и jmp, так же как Reset, reset и reset). С легкой руки А. А. Зубарева [24] по сайтам пошло гулять представление о том, что avrasm2, в отличие от старого avrasm, различает регистр символов, но, как легко убедиться методом проб и ошибок, это представление, к счастью, оказалось непра- вильным, так что переписывать все программы нам не придется. Зато новый ас- семблер не терпит переопределений и «внештатного» использования зарезервиро- ванных слов — назвать процедуру обработки прерывания по внешнему воздейст- вию into у вас не получится, потому что into уже употребляется в файлах определений для обозначения вывода, соответствующего этому прерыванию. Каждая команда должна занимать отдельную строку (в большинстве языков высо- кого уровня операторы можно записывать в одной строке, например, разделяя их знаками препинания, — здесь это не допускается). Длина строки ограничена 120-ю символами. Разбивать команду на части разрывом строки нельзя, в avrasm2 (но не в старой версии!) можно применять прием из языка С, где строки исходных кодов могут быть продолжены посредством символа \— обратной косой черты (обратного слеша) в конце строки, что полезно для длинных макроопределений и для длинных директив . db. Кроме команды, строка может содержать метки и примечания. Метка (label) — идентификатор произвольной длины, придуманный программистом и заканчиваю- щийся двоеточием без пробела перед ним (metka:). Метку можно располагать и в отдельной строке. Кроме простого указания на адрес перехода для команд ветв- ления, метки служат также указанием на адрес подпрограмм (процедур) и заодно являются их именем. Примечания можно добавлять в конце строки после знака «точка с запятой». Все, что расположено после точки с запятой и до знака конца строки (обычно в тексто- вом формате DOS/Windows это пара символов $оа $od, которая вводится при нажа-
Глава 6. Основы программирования MKAVR 123_ тии клавиши <Enter>. Редакторы их не показывают, но нумеруют строки именно по наличию этих символов), игнорируется, поэтому комментарий может быть и на русском. Если нужно продлить комментарий на следующую строку, то эту строку нужно опять начинать со знака «точка с запятой». В avrasm2 допускается С-подобная нотация, когда примечание вместо точки с запятой отделяется двумя прямыми косыми чертами (прямыми слешами): //, или выделение целого блока примечания вот такими скобками: /*здесь многострочное примечание*/. Подсветка подобных комментариев в ASM Editor серым фоном отчасти уже введена в экземп- ляре файла подсветки ASM Editor, который вы найдете в архиве, доступном по уже упомянутому в главе 5 адресу http://revich.lib.ru/AVR/AVRshk.zip, а для скачанно- го с оригинального сайта ее придется настраивать отдельно (через меню Highlight | Start-Stop keys...). Числа и выражения В некоторые команды можно включать выражения и числовые значения. Числа по умолчанию считаются десятичными, за исключением чисел с ведущим нулем, кото- рые, если не имеют дополнительных признаков, воспринимаются как восьмерич- ные. Шестнадцатеричные числа можно записывать двумя способами: как в языке С (ОхОа) и как в языке Pascal или в ассемблере для контроллеров Motorola ($oa). Мы, как правило, будем употреблять последнюю форму записи, как более короткую. «Интеловская» форма записи (оаь) не допускается. Двоичные числа записывают по аналогии с шестнадцатеричными в языке С: оьооооюю. В команды можно включать алгебраические и логические выражения — например: idi r30,ci+c2 (где el и с2 — константы). В выражениях допустимы все арифмети- ческие и логические операции, включая даже операции сравнения (за их полным перечнем я отсылаю к фирменному описанию ассемблера). Однако действия в вы- ражениях могут, естественно, производиться только над константами, а не над содержимым регистров, которое при компиляции неизвестно. Хитрый нюанс тут заключается в том, что адреса в программе — тоже константы, поэтому допустима такая, например, конструкция: rjmp metka+i. По этой команде произойдет переход не на команду, помеченную меткой metka, а на следующую за ней. Впрочем, увле- каться этим не стоит, т. к. дейкстровская1 «лапша» условных и безусловных пере- ходов и без того затрудняет чтение ассемблерных программ. Укажем на одну операцию с участием выражений, которой мы часто будем пользо- ваться, — это логический сдвиг влево, обозначающийся знаком «. Оператор этот хорошо известен знатокам языка С, для всех остальных поясним, что выражение х«п равносильно выражению jc-2", или, другими словами, это число х9 сдвинутое 1 Эдсгер Дейкстра (1930-2002)— голландский программист, один из авторов концепции структурно- го программирования, лежащей в основе всех современных языков высокого уровня. Называл про- граммы с активным использованием операторов условного и безусловного перехода «лапшой» за многочисленные линии перехода на метки. О его критике оператора безусловного перехода goto и современном состоянии этого давнего спора см. статью Владимира Мегре на сайте Habr.com (https://habr.com/ru/post/271131/).
124 Часть II. Программирование микроконтроллеров AVR на ассемблере влево на п двоичных разрядов. В совокупности с побитовым ИЛИ (« | ») операцию эту удобно применять для установки поименованных битов сразу «всей кучей» — например: ldi temp, (1«INTO) | (1«INT1) out GIMSK,temp Эта последовательность операторов установит разрешение двух прерываний: into и inti — в один прием. Запись i«into означает число 1, сдвинутое на into разрядов влево, т. е. оказавшееся в позиции into. Можно устанавливать сразу два и более бита, если они идут подряд: например, запись ldi temp, (3«into) равносильна записи ldi temp, (1«into) I (1«inti), причем указывать нужно самый младший бит. Раздельная запись лучше читается и употребляется чаще. Вместо логического побитного сложения « | », кстати, в этом случае можно применить обычное ариф- метическое «+». Скобки в выражениях используются по тем же правилам, что и в обычной алгеб- ре, — для явного указания старшинства операций. Старшинство (приоритет) опе- раций соответствует их положению в таблице, приведенной в описании AVR- ассемблера (чем ниже положение, тем приоритет выше). В приведенном только что примере при указании единственного знака логического побитного сложения скоб- ки можно не ставить. Однако при записи нескольких операций подряд лучше не разбираться в старшинстве, чтобы не плодить источники возможных ошибок, и всегда использовать скобки. В выражениях можно употреблять некоторые стандартные функции. Подчеркнем, что эти функции — свойство самого ассемблера, и выполняются они в процессе подготовки текста программы к компиляции. Для вычисления по ходу программы различных функций переменных (синуса, экспоненты и т. п.) в языках высокого уровня есть готовые стандартные операторы, но в ассемблере придется реализовы- вать их самостоятельно. Из доступных функций (их достаточно много, — см. [8]) наибольшее практическое значение имеют те, что предназначены для выделения отдельных байтов из кон- стант или результатов вычислений, если эти результаты размером более одного байта. Наиболее часто из них употребляются функции Low (выделяет младший байт из многобайтового числа) и High (выделяет второй байт, если число двухбайтовое, то он же старший). Например, загрузка значения 62 500 в регистры сравнения тай- мера-счетчика Timer 1 может происходить так: ldi temp,high(62500) out OCRlAH,temp ldi temp,low(62500) out OCR1AL, temp Если число имеет размер, больший 16 битов, то можно воспользоваться функция- ми: ВYTE3 (выражение) — ВОЗВращает третий байт выражения И BYTE4 (выражение) — возвращает четвертый байт выражения.
Глава 6. Основы программирования MKAVR 125 Директивы Кроме собственно команд, в ассемблерной программе могут встречаться директивы компилятора. Их предостаточно, но самых употребительных, которые есть практи- чески в каждой программе, три: .def (definitions), .equ (equvalent) и .include. Пер- вые две предназначены для определения имен пользовательских переменных и констант, соответственно (обратите внимание на точку перед именем директивы): .equ max_value = $11 /константа max_value = 17 .def temp = rl6 ;регистр rl6 есть переменная temp .def counter = rO5 /регистр гО5 есть переменная counter Эти определения в целях структурирования программы обычно располагают в на- чале текста. Только учтите, что никаких проверок, кроме синтаксических, тут не производится, потому возможно объявить два разных имени для одного регистра, и они будут восприниматься как синонимы: .def temp = г.16 /регистр г16 есть переменная temp .def counter = rl6 /регистр г16 есть переменная counter Изменение temp будет автоматически означать изменение counter и наоборот. Ино- гда этим пользуются, если в разных частях программы один регистр применяется для разных по смыслу значений (и вы можете встретить примеры этого в фирмен- ных «аппнотах»). В общем случае такую возможность советую использовать лишь в исключительных случаях — слишком много ошибок можно наделать. При нали- чии в программе такого двойного определения avrasm2 будет выдавать предупреж- дение (warning), хотя компиляции это не помешает. С помощью директивы .equ, вообще говоря, можно определять достаточно слож- ные выражения, но этим пользуются редко — гораздо чаще ее применяют для оп- ределения переменных, которые располагаются не в регистрах, а в области SRAM. Например, следующая последовательность директив и команд (листинг 6.1) запи- шет содержимое регистра counter в SRAM по адресу $60 (для «наших» моделей — это первый свободный адрес после занятых адресами регистров). .equ counter_addr = $60 .def counter = rl6 clr ZH ldi ZL,counter_addr st Z,counter Все принятые в технических описаниях Atmel наименования регистров и прочие необходимые константы вводят точно таким же способом и собирают в специаль- ных файлах определений с расширением .inc, о которых мы уже упоминали, — та- кие файлы придаются к каждой модели контроллера (например, tn2313def.inc — для
126 Часть II. Программирование микроконтроллеров AVR на ассемблере модели ATtiny2313, m8535def.inc — для модели ATmega8535 и т. п.). В обновленном наборе inc-файлов, которые прилагаются к современным версиям Atmel Studio, вы можете встретить даже разные файлы для модификаций одного и того же контрол- лера,— например: m8def.inc и m8adef.inc (но не m8hvadef.inc или m8u2def.inc— это другие контроллеры!), что только путает, потому что по содержанию они, естест- венно, одинаковые. Файлы определений, как мы уже упоминали, нужны потому, что ассемблер абсо- лютно не «подозревает» о существовании таких понятий, как PortA или ddrc, a «знает» только числовые адреса соответствующих регистров (единственное, о чем ассемблер «осведомлен от рождения», — это о существовании РОН с названиями ro-r3i). Соответствие между этими мнемоническими обозначениями и адресами и устанавливается с помощью inc-файлов, причем для разных моделей эти адреса могут различаться. Для того чтобы включить эти соответствия в текст вашей про- граммы, служит директива . include: .include "tn2313def.inc" Здесь подключается файл с определениями констант и адресов для контроллера ATtiny2313 (разумеется, если файл находится не в текущем каталоге, то следует указать полный к нему путь). Подобной строкой должен начинаться текст любой AVR-программы, иначе ассемблер может вас «не понять». Разумеется, директивой . include можно вставить в ваш текст содержимое любого другого файла — напри- мер, содержащего типовые процедуры или макросы (и мы этим будем пользовать- ся). Имя файла здесь может быть абсолютно произвольным — по директиве include компилятор, не раздумывая, разыщет файл с указанным именем (напомним, что он должен находиться в текущем каталоге, или для него должен быть указан полный путь), скопирует из него текст и вставит этот текст в том месте вашей программы, где расположена директива, и только потом начнет разбираться. Потому с директи- вами include следует быть аккуратными— если файл содержит команды, а не только определения, то вставлять его нужно уже не в начале текста, а после всех векторов прерываний (см. далее), иначе выполнение программы начнется с него. Весьма полезна директива .device, которая должна ставиться в начале програм- мы, — она отдельно указывает ассемблеру на применяемую модель МК: .device ATtiny2313 Если jnc-файлы указывают ассемблеру на истинные адреса регистров для конкрет- ной модели, то такая директива не позволит использовать команды, которые этой моделью не поддерживаются. В avrasm2 эта простая и лаконичная директива более не поддерживается, вместо нее действует одна из множества директив #pragma, ко- торыми можно указать компилятору много чего разного. Вообще говоря, самостоя- тельно указывать ни старую директиву (в случае, если у вас старая версия ассемб- лера), ни новую в программе не нужно, т. к. с них все равно начинается любой inc-файл. Тем не менее директиву .device вы встретите в программах далее— как дань традиции (ее рекомендовалось указывать для первых версий avrasm) и как памятку для нас самих об используемой модели контроллера.
Глава 6. Основы программирования MKAVR 727^ Рассмотрим еще директиву . db (define byte), которая позволяет хранить непоимено- ванные константы или массивы во flash-памяти программ или в долговременной памяти EEPROM (для памяти данных SRAM используется другой способ, — об этом рассказано далее). В языках высокого уровня (в том числе и для Arduino) та- кая возможность имеется только теоретически (во встроенном ассемблере), по- скольку модель «общения» с памятью у них совершенно другая, и никому не при- дет в голову хранить данные «внавал», когда их можно поименовать и заодно ука- зать тип. А в ассемблере это используется достаточно часто — здесь нет массивов, как таковых, потому удобно прибегать к такому способу. Причем, кроме .db, есть еще директива . dw (define word, т. е. двухбайтовое слово) и даже . dd и . dq (двойное и четвертное слова), но мы не будем отвлекаться на все нюансы адресации, кото- рые при этом возникают, и обойдемся одной . db — на практике она употребляется наиболее часто. Для того чтобы показать, куда именно писать данные, совместно с . db можно ука- зать директиву .eseg (для EEPROM). Память программ при этом обозначается директивой .cseg (code segment). Некоторые авторы советуют всегда записывать в начале программы, как минимум, директиву . cseg, чтобы указать, что последую- щий текст представляет собой именно текст программы, а не набор констант, но мне кажется, что это только запутывает дело, потому и советую употреблять эти директивы тогда, когда это действительно надо. Если директива явного указания на область памяти отсутствует, то данные по ди- рективе .db будут сохраняться вместе с командами в памяти программ (пример приведен в разд. «Команды пересылки данных» главы 7). Если же присутствует директива .eseg, то потом, чтобы указать, что область данных EEPROM в тексте закончилась, и следует опять перейти к памяти программ, необходимо это явно определить, т. е. поставить директиву . cseg (code segment). Естественно, распола- гать в зоне действия директивы .eseg что-либо, кроме констант, определенных директивой .db, бессмысленно. Все сказанное иллюстрируется, например, фраг- ментом текста, заимствованным из «аппноты» Atmel № 240 (листинг 6.2). .eseg ;EEPROM segment .org 0 .db 1,2,3/15,4,5,6,14,7,8,9,13,10,0,11,12 . ***• Source code ******************* .cseg ;CODE segment .org 0 rjmp reset ;Reset handler Применение директивы . org мы рассмотрим в этой главе далее, а здесь только от- метим следующее. Согласно листингу 6.2, все, что записано через запятую после
128 Часть II. Программирование микроконтроллеров AVR на ассемблере директивы .db, будет помещено в область EEPROM, начиная с нулевого адреса. Если директивой . org этот нулевой адрес не указывать, то размещение будет осу- ществлено все равно с него, за исключением случая, когда где-то еще в тексте ранее встречалась директива . eseg, — тогда размещение произойдет по порядку адресов. Отметим, что содержимое EEPROM для контроллеров AVR предлагается загру- жать через отдельный файл того же формата (*.hex), что и для кода программы, но с расширением .еер. Для того чтобы такой файл создавался при наличии директивы . eseg в коде программы, требуется указать специальную опцию компилятора, тогда строка в нашем ВАТ-файле (см. разд. «Обустройство ассемблера» главы 5) будет выглядеть так: c:\avrtools\avrasm32 -е %1.еер -fl %l.asm В этом случае в той же папке, где находится asm-файл, дополнительно создаст- ся файл с расширением .еер, который будет содержать данные для загрузки в EEPROM. Если директива . eseg в тексте программы не встречается, то в создан- ном еер-файле данных не окажется, и компилятор удалит его по окончании процес- са, сообщив вам об этом. Для размещения данных в SRAM есть директива .dseg. Специальных опций ком- пилятора указывать в командной строке тут не требуется, зато почему-то данные по директиве .dseg помечаются не директивой .db (или .dw), a .byte, которая имеет иной синтаксис, — после нее должна идти константа, указывающая число резерви- руемых байтов. Других параметров не допускается, потому .byte может приме- няться только для выделения места под переменные в SRAM, но не для инициали- зации ее содержимого. К тому же есть некоторая путаница с адресацией в отноше- нии SRAM — счетчик адресов здесь по умолчанию равен не нулю, как в других случаях, а 32-м (поскольку адреса 0-31 заняты РОН), но с этого адреса вообще-то начинается файл РВВ, т. е. регистров ввода/вывода, а не собственно SRAM. Чтобы не путаться и не попасть в какой-нибудь регистр или вообще выйти за пределы памяти, здесь лучше использовать константы sramstart и ramend, указывающие на начало и конец свободной SRAM для каждого контроллера. По сути, единственное преимущество .byte в том, что нам не приходится думать в процессе программирования об адресах в SRAM, а можно просто располагать данные по метке (причем адресация здесь побайтовая, а не пословная, как в памяти программ, о чем далее). Такой способ загрузки данных используют значительно реже остальных— заполнять память конкретными значениями все равно прихо- дится программно, так что много от применения .byte мы не выигрываем. Пример применения этой директивы вы найдете в программе вывода на дисплей с 12С-ин- терфейсом в главе 13, Применение директивы .macro мы рассмотрим далее отдельно. Стоит упоминания, что, как и любой другой серьезный язык программирования, AVR-ассемблер пред- полагает возможность условной компиляции (директивы if, endif, else, ifdef и им подобные). В совокупности с директивой .device (ранее) или конструкцией #pragma
Глава 6. Основы программирования MKAVR 129_ (теперь) они позволяют писать универсальные программы, например, для разных контроллеров. Но я этого делать не советую, потому что логика таких программ бывает слишком запутанная, разобраться в них сложно, а наделать ошибок легко. В некоторых простых случаях, однако, эти директивы бывают полезны — пример использования условной компиляции для выбора из двух вариантов вы найдете в главе 16. Рассматривать другие директивы мы здесь не будем, т. к. без них в большинстве случаев можно обойтись (интересующихся отсылаю к описанию AVR-ассемблера [3,4] или к пособию [11]). Подведем некоторый итог: мы уже узнали многое о структуре типовой ассемблер- ной программы для AVR. Текст должен начинаться с директивы . include, ссылаю- щейся на файл с определениями имен для конкретного процессора, далее обычно идут пользовательские определения переменных (директива .def) и констант (директива . equ), а программа должна начинаться с безусловного перехода на мет- ку reset. Начало собственно программы будет располагаться сразу после этой мет- ки где-то в другом месте программы. А почему так странно — нельзя ли начать прямо сразу с нулевого адреса, строка за строкой, зачем нужны какие-то переходы? Можно, отвечают нам авторы описаний AVR. Простейшая программа может начинаться с нулевого адреса, и никаких пере- ходов не потребуется, но только в одном случае: если мы обязываемся отказаться от прерываний. Мы далее встретим несколько примеров программ, которые совсем не используют прерываний, и увидим, что нередко они все равно начинаются с пе- рехода на метку reset — так просто удобнее. Оформление вызова подпрограмм Это, наверное, самая простая тема во всем введении в ассемблер, потому что со- держит очень немного оговорок, касающихся особенностей разных типов AVR. В общем виде это выглядит примерно так: если у вас есть какой-либо повторяю- щийся участок кода, то вместо того, чтобы его копировать много раз, следует запи- хать его в подпрограмму (процедуру), а потом вызывать каждый раз, когда потре- буется. Пусть у нас есть некая программа, которая требует несколько раз в разных местах осуществить задержку на строго определенное время. Тогда саму задержку целесо- образно оформить в виде подпрограммы (как это реализовать в действительности, мы рассмотрим далее) и вызывать, когда потребуется (листинг 6.3). ; процедура Delay Delay: <код, осуществляющий задержку> ret
130 Часть II. Программирование микроконтроллеров AVR на ассемблере ; программа: < что-то делаем > rcall Delay < что-то делаем > rcall Delay < что-то делаем > Здесь метка Delay указывает на начало подпрограммы (и одновременно является ее именем), а команда ret осуществляет корректный выход из подпрограммы с воз- вратом именно в то место кода, из которого ее вызывали. В реальности происходит следующее: команда вызова rcall сначала сохраняет текущее значение адреса в стеке (области в памяти SRAM, которая располагается в самом конце этой памяти и растет в сторону начала), потом в программный счетчик PC загружается адрес метки Delay. Далее выполняется код задержки, и по команде ret осуществляется обратная операция— сохраненный в стеке адрес, откуда прервали, помещается в программный счетчик, и выполнение продолжается со следующей команды вы- зывавшего участка кода. Оговорка здесь только одна: для контроллеров с памятью более 8 кбайт вместо rcall полагается вызывать более длинную команду call, которая действует на весь диапазон такой памяти. На самом деле это необязательно, потому что руками на ассемблере вы все равно не напишете код, превышающий 8 кбайт, а если и напише- те, то на ошибку вам укажет компилятор. Причем у контроллеров с памятью 8 кбайт и менее команда call (так же, как и аналогичная по адресуемой памяти команда jmp) вовсе отсутствует, и там ее вы применить не сможете. Существенный нюанс здесь состоит в том, что команда rcall занимает 3 такта про- цессорного времени, а команда ret — еще 4. Потому выполнение подпрограммы всегда длится на 7 тактов больше, чем если бы ее код оставили просто в том месте, где он выполняется. При этом мы тут еще не учитываем, что может захотеться со- орудить настоящую процедуру-функцию, передав в нее какие-то параметры и по- том получив результаты обратно (см. далее в этой главе), или просто сохранить в стеке некоторые регистры на время выполнения подпрограммы. Каждое такое сохранение и извлечение — это еще по такту процессорного времени на выполне- ние каждой из команд push и pop, а таких пар может быть много. И если здесь мы сможем почти всегда избегать такой работы со стеком, пользуясь добротой фирмы Atmel, предоставившей нам 32 регистра общего назначения, то языки высокого уровня даже не стараются как-то оптимизировать вызовы функций в этом отноше- нии. Отсюда понятно, почему Arduino-программы намного больше ассемблерных по размеру и выполняются намного дольше. Есть альтернативное средство, которое позволяет избежать замедления выполнения программы, и в то же время сократить ее текст, сделав ее более читаемой и понят- ной. Это средство— макросы, и когда применять целесообразно именно их, а когда — подпрограммы, мы обсудим немного далее.
Глава 6. Основы программирования MKAVR 131_ Обработка прерываний Как мы уже знаем (см. главу 3\ AVR по умолчанию ожидает, что сразу после пер- вой команды с адресом $оооо идет таблица т. н. векторов прерываний. Вектор — это просто отсылка по нужному адресу с помощью команды г jmp (или jmp — в за- висимости от степени «продвинутое™» контроллера). Вообще-то вектор, разме- щенный по нулевому адресу, на который программа переходит по сбросу {вектор сброса или вектор начальной загрузки), тоже считается прерыванием, хотя он занимает особое место (о векторе сброса рассказано в следующем разделе). Адрес обозначается меткой, может располагаться в любом месте программы и ука- зывает на начало процедуры обработки прерывания. Первый вектор располагается по адресу $oooi (а для МК с памятью более 8 К— по адресу $0002, потому что по адресу $oooi по идее должна находиться вторая половина более длинной команды jmp от вектора сброса), причем напомним, что для памяти программ адрес этот оз- начает номер двухбайтового слова в памяти, а не отдельного байта. В контроллерах с памятью меньше 8 кбайт нам вообще можно не думать про абсолютные адреса и их нумерацию — первый вектор программы (гjmp reset) автоматически располо- жится по нулевому адресу, второй— по адресу $oooi, и т. д. Найдя какую-нибудь команду перехода по метке, компилятор автоматически подставит абсолютные ад- реса. Порядок следования векторов и их число в таблице жестко заданы в соответствии с типом МК. Потому самое первое, что вы должны сделать, приступая к програм- мированию, — открыть руководство по применению выбранного типа контролле- ров и скопировать оттуда эту таблицу. Можно сделать это прямо через буфер обме- на из PDF-описания (если вам позволят — в последних версиях описаний копиро- вание текста через буфер обычно запрещено, но всегда можно вывернуться с помощью программ, удаляющих или обходящих ограничения PDF) — так меньше вероятность что-то пропустить, только придется потом удалить указанные там абсолютные адреса, стоящие в начале каждой строки. Листинг 6.4 иллюстрирует начало программы для МК ATmega8. ;=========прерывания================ rjmp RESET ; Reset Handler rjmp EXT_INT0 ; IRQ0 Handler rjmp EXT_INT1 ; IRQ1 Handler rjmp TIM2_COMP ; Timer2 Compare Handler rjmp TIM2_OVF ; Timer2 Overflow Handler rjmp TIM1_CAPT ; Timerl Capture Handler rjmp TIM1_COMPA ; Timerl CompareA Handler rjmp TIM1_COMPB ; Timerl CompareB Handler rjmp TIM1_OVF ; Timerl Overflow Handler rjmp TIM0_OVF ; TimerO Overflow Handler rjmp SPI_STC ; SPI Transfer Complete Handler rjmp USART_RXC ; USART RX Complete Handler
132 Часть II. Программирование микроконтроллеров AVR на ассемблере rjmp USARTJJDRE ; UDR Empty Handler rjmp USART_TXC ; USART TX Complete Handler rjmp ADC ; ADC Conversion Complete Handler rjmp EE_RDY ; EEPROM Ready Handler rjmp ANA_COMP ; Analog Comparator Handler rjmp TWSI ; Two-wire Serial Interface Handler rjmp SPM_RDY ; Store Program Memory Ready Handler Но постойте: мы что, обязаны использовать все прерывания? Конечно, нет. Для не- используемых прерываний в контроллерах с памятью программ менее 16 кбайт команду rjmp <метка> следует заменить на reti— выход из прерывания (return interrupt). На самом деле можно было бы указать и команду пор — пустую опера- цию (см. описание команд далее). Но мы будем ставить именно reti, т. к. тогда нам не будет важно, что какое-либо прерывание оказалось случайно инициализирова- но, — оно все равно не станет выполняться, а писать и отлаживать программы так удобнее. Я в своих программах просто дополняю стандартные строки командой reti и точкой с запятой, чтобы закомментировать команду rjmp (листинг 6.5). .include "m8def.inc" .def temp = rl6 ;рабочая переменная rjmp RESET ; Reset Handler reti ;rjmp EXT_INT0 ;IRQ0 Handler reti ;rjmp EXT_INT1 ;IRQ1 Handler Теперь заготовка начала программы готова: при необходимости в дальнейшем ис- пользовать какое-то прерывание, мы удаляем из соответствующей строки фрагмент reti;, а затем где-то в программе ставим нужную метку и пишем обработчик, за- канчивающийся командой reti (листинг 6.6). Листинг i.6 rjmp RESET ;Reset Handler rjmp EXT_INT0 ;IRQO Handler EXT_INT0: /процедура обработки прерывания INTO <код обработчика> reti ;окончание процедуры обработки прерывания INTO Чаще используется более корректный универсальный способ оформления таблицы векторов прерываний. Его особенно целесообразно применять для старших Mega,
Глава 6. Основы программирования MKAVR 133 где число прерываний может достигать нескольких десятков, а из-за четырехбайто- вого формата команды jmp автоматически заменить ее на единственную reti или пор не получается, и легко наделать ошибок. Способ основан на использовании директивы .org, которая устанавливает абсолютный адрес в памяти программ. В jnc-файлах есть специальные определения констант для адресов прерываний, на- пример (взято из файла m16def.inc для модели ATmegal6): External Interrupt Request 0 External Interrupt Request 1 Timer/Counter2 Compare Match Timer/Counter2 Overflow Timer/Counterl Capture Event Timer/Counterl Overflow Timer/CounterO Overflow External Interrupt Request 2 Timer/CounterO Compare Match Store Program Memory Ready equ equ equ equ equ equ equ equ equ equ INTOaddr INTladdr OC2addr OVF2addr ICPladdr OVFladdr OVFOaddr INT2addr OCOaddr SPMRaddr = 0x0002 = 0x0004 = 0x0006 = 0x0008 = 0x000a = 0x0010 = 0x0012 = 0x0024 = 0x0026 = 0x0028 Тогда, если вам, к примеру, никакие иные прерывания не требуются, кроме внеш- них прерываний into и int2, а также прерывания переполнения Timeri, то начало программы может быть таким, как показано в листинге 6.7. ;Установка векторов прерываний .org 0 /начало программы после сброса rjmp RESET .org INTOaddr ;адрес прерывания INTO rjmp Int_0 .org OVFladdr ;адрес прерывания OVF1 rjmp OVF_Timerl .org INT2addr ;адрес прерывания INT2 rjmp Int_2 .org INT_VECTORS_SIZE ; Конец таблицы прерываний - начало кода <программа> Здесь into, ovFTimeri и int_2 — процедуры, которые выполняются при возникно- вении соответствующих прерываний. Если первым идет вектор сброса rjmp reset, то .org о в начале ставить необязательно. В общем-то необязательно и направлять в конец таблицы прерываний (intvectorssize), но только если остальные указа- тели идут строго по их очередности в этой таблице. Обратите внимание на то, что при таком способе записи при смене модели не требуется что-либо исправлять (при
134 Часть II. Программирование микроконтроллеров AVR на ассемблере условии, конечно, что эта другая модель будет поддерживать такие же прерыва- ния — INT2, например, есть не у всех Mega). Ставить именно двухбайтовую коман- ду jmp вместо rjmp также необязательно— как и в случае команд caii/rcaii, до- пустить ошибку в объеме ассемблерного кода сложно, а в случае неправильного употребления на ошибку вам укажут. Причем jmp, как и call, в младших моделях вовсе отсутствует. Не следует также высчитывать конец таблицы прерываний по их общему числу и указывать начало кода в абсолютном значении адреса — в других моделях это обя- зательно будет другое значение, т. к. количество прерываний различается, в то вре- мя как константа intvectorssize уже указывает на правильный адрес для любого контроллера. Внимание! При использовании прерываний не забывайте после всех установок устанавливать общий флаг разрешения прерываний командой sei. Следует учесть, что если не использовать никаких прерываний, кроме самого пер- вого (сброса), то команду sei можно не указывать. В этом случае стоящая на первом месте в программе команда rjmp reset, очевидно, вырождается в простой безусловный переход на метку. Общая схема обработки аппаратных прерываний следующая. При возникновении любого прерывания флаг i регистра sreg аппаратно сбрасывается, тем самым за- прещая обработку других прерываний. При нормальном течении событий он авто- матически устанавливается опять, когда обработка прерывания заканчивается (по команде reti). Отметим, что при необходимости этот флаг можно «вручную» уста- новить в подпрограмме-обработчике (напрямую или командой sei), разрешив вло- женные прерывания. После сброса флага i контроллер определяет, запрос на обра- ботку какого именно прерывания произошел, — это делается по флагу конкретного прерывания, который также автоматически устанавливается при возникновении прерывания (например, для таймеров эти флаги находятся в регистре tifr или etifr, для внешних прерываний— в регистре gifr или eifr и т. п.). Отметим, что эти регистры при инициализации МК рекомендуется очищать, что делается записью единиц (не нулей!): ser temp ;temp=$FF out GIFR,temp ;сбросить флаги внешних прерываний out TIFR,temp ;сбросить флаги прерываний таймеров Критично это только в случае, когда мы вообще используем прерывания, и их непреднамеренное возникновение может что-то испортить, иначе можно этим пре- небречь. После определения типа прерывания контроллер автоматически вычисляет адрес соответствующего вектора прерывания. Перед тем как перейти по вектору преры- вания, МК сбрасывает флаг произошедшего прерывания (тем самым разрешая его на будущее) и автоматически сохраняет содержимое счетчика команд в стеке.
Глава 6. Основы программирования MKAVR Отметим, что иногда обработка прерываний может мешать выполнению каких- нибудь длинных процедур, которые нельзя прерывать (например, не допускается возникновение прерывания во время записи в EEPROM). В этом случае прерывания всегда можно временно запретить командой cli. Естественно, потом их следует опять разрешить командой sei (обратите внимание на то, что инструкция сразу за sei будет выполнена в любом случае до начала обработки любого прерывания). При вызове таких длительных процедур изнутри обработчика прерывания пробле- ма снимается, если, конечно, не разрешены вложенные прерывания, что еще один аргумент за перенос функциональности программы преимущественно в прерыва- ния (см. далее). Не следует также забывать, что содержимое регистра флагов sreg при переходе к обработке прерывания не сохраняется. Регистр sreg, кроме флага разрешения прерываний, содержит другие флаги, задействованные в арифметических операци- ях, командах сравнения и др. Поэтому, если прерывание «вклинится», например, между командой сравнения и командой перехода в зависимости от его результата, то при наличии в обработчике команд, модифицирующих sreg, выполнение этой последовательности команд может оказаться некорректным. При опасности такого развития событий регистр sreg также следует сохранять в стеке (ну, или запрещать прерывания во время выполнения процедур выбора). Переход в обработчик всегда осуществляется по окончании выполнения текущей команды (даже если она занимает несколько тактов), а при выходе из прерывания всегда выполняется, по крайней мере, одна команда основной программы, прежде чем контроллер перейдет к выполнению следующего прерывания. Обработчик пре- рывания всегда (кроме reset) должен заканчиваться командой reti. Только с ее помощью программа, во-первых, снова разрешит прерывания (которые автомати- чески запрещаются при переходе по вектору сбросом флага i в регистре sreg), во- вторых, вернется в то место программы, на котором ее выполнение было прервано. Обращение к прерыванию и возврат из него (команды rjmp + reti) без учета вы- полнения команд до и после добавляет 6 тактов к длительности обработки преры- вания. Вообще говоря, обработчик можно вызвать в любой момент и программно, подобно обычной процедуре, но встретить ситуацию, в которой это может приго- диться, мне ни разу не довелось. Подробности об обработке прерываний вы найде- те далее, в конкретных примерах программ. Заметки на полях Если мы рассмотрим прерывания с точки зрения общей организации процесса выпол- нения программы в контроллере, то можем заметить, что выполнение его обработчика очень похоже на вызов обычной подпрограммы. Тут уместно напомнить об одном очень важном принципе, лежащем в основе всего микропроцессорного подхода к соз- данию электронной аппаратуры, о котором часто забывают упомянуть, потому что для всех он становится очевидным уже на начальных стадиях изучения микропроцессоров и программирования, — это принцип эквивалентности аппаратного и программного обеспечения. Понятные иллюстрации этого принципа представляют собой некоторые команды выполнения арифметических действий, которые мы будем рассматривать далее. Выполнение аппаратных прерываний также является ярким примером этого принципа — в основе это тот же самый вызов подпрограммы по некоторому условию, а вот оформление (и аппаратное, и программное) этих вызовов принципиально раз- ное.
136 Часть II. Программирование микроконтроллеров AVR на ассемблере Задумаемся над вопросом: а почему обычные процедуры вызываются специальной командой caii/rcaii, а обработчики прерываний — простым безусловным переходом jmp/r jmp? Если в принципе это одно и то же, то нельзя ли все привести к единой фор- ме? Вы уже, наверное, сообразили, почему нельзя: переход по вектору прерывания происходит автоматически, и также автоматически перед этим адрес возврата поме- щается к стек. Функциональность команд caii/rcaii здесь просто излишняя. И наобо- рот — вызов обычной процедуры простым «прыжком» (jmp) на метку ее начала в луч- шем случае (если в стек еще ничего не писалось, и в ячейках стоят нули) по окончании процедуры приведет к перезагрузке контроллера, т. к. он по завершающей команде ret отправится по нулевому адресу. А выходы из обработчика или подпрограммы так- же различаются (reti или ret), но по другой причине: потому что перед выходом из обработчика надо опять разрешить прерывания, установив сброшенный ранее флаг разрешения прерываний i. Если вы вдруг его уже установили программно (команда sei), разрешив вложенные прерывания, то повторная установка с помощью reti ниче- му не помешает. По этому поводу еще заметим, что команду ret можно указать в любом месте про- граммы, и опытные программисты, манипулируя содержимым стека, могут выделы- вать разные необычные кульбиты, для которых в ассемблере есть все условия. Но мы подобными вещами заниматься не станем, а любопытствующих я отправляю к посо- бию [2], автор которого всякие такие финты просто обожает. Процедура RESET Теперь обратимся к процедуре reset, т. е. к истинному началу программы — что там должно быть? Как мы уже неоднократно говорили, когда контроллер доходит до команды вызова подпрограммы, или в нем происходит прерывание, он должен сохранить состояние программного счетчика с тем, чтобы потом знать, куда вер- нуться. Это, как мы уже слышали краем уха, происходит в специальной области памяти SRAM, которая называется стек (stack). Потому в любой программе на AVR-ассемблере, допускающей прерывания или просто использующей подпро- граммы, как мы уже обсуждали в главе 4, первыми после метки reset должны идти следующие строки: RESET: ldi temp,low(RAMEND) /загрузка указателя стека out SPL,temp ldi temp,high(RAMEND) /загрузка указателя стека out SPH,temp Этими операторами вы указываете, где компилятору расположить программный стек — а именно, в конце SRAM (что обозначается константой ramend, объявленной в соответствующем inc-файле). Для моделей Tiny с аппаратным стеком (ATtiny28) эти строки следует опустить, а для тех моделей, у которых стек программный, но объем SRAM не превышает 256 байт (ATtiny2313, ATtiny26 и др.), запись сокраща- ется: RESET: ldi temp,RAMEND ;загрузка указателя стека out SPL,temp
Глава 6. Основы программирования MKAVR 137 После задания стека иногда ставят следующие строки: ldi temp,l«ACD out ACSR,temp ;выкл. аналог, компаратор Почему «иногда», а не всегда? Потому что по умолчанию аналоговый компаратор всегда включен, и, соответственно, расходует питание. Если вы его не используете, то зачем лишнее потребление? Правда, это практически не скажется на потребле- нии в нормальном режиме работы — доля компаратора очень мала. Потому кри- тичной вставка этих строк становится только в случае, если задействованы режимы энергосбережения, а в обычных режимах эти команды просто ни на что не повлия- ют. Если компаратор необходим (см. главу 77), то, конечно, эти строки нужно ис- ключить. После всего этого в процедуре reset обычно идет секция инициализации, где раз- решаются конкретные прерывания, устанавливаются состояния выводов портов, инициализируются начальные значения переменных, и т. п. Примеры мы еще встретим в тексте этой книги неоднократно. Секция инициализации, как мы гово- рили ранее, обязательно должна заканчиваться командой sei — общим разрешени- ем прерываний, т. к. по умолчанию они запрещены. Теперь вроде бы все готово к работе, но что станет делать контроллер в ожидании прерываний? Ведь основная функциональность наших программ будет сосредото- чена именно в обработчиках прерываний, и в простейшем случае контроллер по- просту ничего не должен делать, пока не придет сигнал очередного прерывания. Поэтому простейшая программа должна заканчиваться пустым циклом, перед которым обязательно должно идти общее разрешение прерываний, если вы их, ко- нечно, используете (команда sei): sei ;разрешаем прерывания LOOP: rjmp LOOP Я намеренно употребил здесь для наименования метки хорошо знакомое вам слово loop, потому что замкнутый цикл здесь делает ровно то же самое, что и функция loop () в среде Arduino (название метки, понятно, никакой роли не играет). Именно внутри такого цикла работают программы, не использующие прерываний (или ис- пользующие их неявно, как в Arduino). И правда, в этом цикле можно делать что-то полезное — например, войти в один из режимов энергосбережения, или отслежи- вать изменение состояния какого-либо вывода, или, например, ожидать прихода байта через UART и т. п. — в дальнейшем мы увидим примеры подобных дейст- вий. Но мы во всех случаях, когда это возможно, постараемся без таких действий обойтись — так программа получается компактнее, а ее логика работы становится более понятной и проще проверяемой. Заметки на полях Если вы рассмотрите внимательно структуру любой программы для Arduino, то увиди- те, что она устроена совершенно аналогично нашей ассемблерной. Единственное различие — там тип контроллера указывается не в программе, а определяется от-
138 Часть II. Программирование микроконтроллеров AVR на ассемблере дельно, указанием разновидности используемой платы через меню. А вот далее все так же: сначала идут включаемые файлы через директиву #±nciude, потом определе- ния имен переменных (в немного отличающейся форме, потому что обязательно ука- зывается тип) и констант. В последнем случае применяется либо та же директива (псевдооператор) #define, либо специальное служебное слово const. У нас не встре- тишь только одного почти обязательного компонента Arduino-программы — в случае использования библиотек C++ их надо инициализировать, создавая экземпляры объ- ектов, а у нас такие библиотеки отсутствуют. Потом в Arduino обычно идут тексты ис- пользуемых функций (которые здесь у нас носят более корректное название подпро- грамм или процедур) — и у нас далее будет точно так же. Среди этих функций есть две главные, которые встречаются практически в любой Arduino-программе: setup о и loop (). В случае AVR-ассемблера все начальные установки будут делаться в начале программы (после метки Reset, так что эту метку можно считать неким аналогом setup), а заканчиваться программа будет также бесконечным циклом, пустым или со- держащим выполняемые команды. Вообще любая законченная программа в мире, которая должна работать сколько- нибудь продолжительное время, представляет собой подобный бесконечный цикл, в который она переходит после некоторой начальной инициализации при включении. Примеры на виду: скажем, и вся ОС Windows в целом, и каждое из ее приложений в отдельности представляют собой такой цикл. Исключением являются однократно выполняемые алгоритмы, которые срабатывают один раз после включения и потом останавливаются. Примеров таких программ тоже не счесть: это, скажем, стиральная машина, которая с точки зрения математика является автономным конечным автома- том1 — она выполняет заданную последовательность действий и останавливается. Все подпрограммы (процедуры, функции) также относятся к таким примерам, незави- симо от того, автономный конечный автомат они собой представляют или нет, — во втором случае подпрограмма просто имеет несколько (иногда даже очень много) ва- риантов действий в зависимости от каких-то внешних событий. Но чтобы программа в целом могла выполнять что-то полезное долгое время, все такие процедуры-под- программы обычно связаны с неким главным циклом, где по нужным событиям вызы- ваются те или иные алгоритмы их обработки. Причем в простейшей программе такой вызов осуществляется просто переходом по некоему условию (вспомните прямо- угольнички-ромбики в типовых блок-схемах программ), а в реальности их часто проще и надежнее делать с помощью прерываний — по сути это то же самое, но проверка условий и сам переход осуществляются аппаратно, оставаясь для основной програм- мы «за кадром». Именно в этом моменте будут отличаться наши ассемблерные про- граммы от обычно предлагаемого подхода в Arduino — там все в основном предлага- ется делать в главном цикле, а у нас — по большей части в прерываниях. Использование макросов Макросом называется средство, которое позволяет упаковать в единую конструк- цию сразу несколько команд или действий пользователя и вызывать их одной командой или действием. Макросы широко распространены в профессиональных программах и в компьютерных играх. Как только кто-то преодолевает свою при- 1 Чтобы не запутать читателя, следует пояснить, что здесь термин «конечный автомат» используется в абстрактно-математическом смысле, как синоним понятия алгоритма (известная машина Тьюринга есть пример такого конечного автомата). В программировании этим термином часто называют просто один из способов создания программ, реализующих некую сложную логическую функцию с различ- ными действиями для различных состояний входа.
Глава 6. Основы программирования MKAVR 139 родную лень и осваивает пользование макросами в MS Word или Photoshop, то очень быстро переходит в состояние «а как я без этого раньше жил?». Конечно, возня с макросами обретает смысл только тогда, когда приходится повто- рять некоторую последовательность действий очень часто. Делать что-то такое на один раз особого смысла не имеет, хотя в самой процедуре создания макроса нет ничего сверхсложного. Здесь мы коснемся только принципов обращения с макро- сами в AVR-ассемблере, а использовать их в повседневной деятельности или нет — принимайте решение сами. С одной стороны, развитая система макросов позволяет практически создать свой собственный язык программирования, с другой — как и любая индивидуализация, удаляет вас от стандарта, осложняет перенос и распро- странение программ. Создание несложного макроса мы покажем на примере модификации операции Ис- ключающее ИЛИ (XOR), которая в AVR имеет ограничения. Сама операция может потребоваться, например, для быстрого инвертирования битов какого-нибудь реги- стра — всех сразу или выборочно по заданной маске. Выполняется она командой, которая здесь называется еог, а ограничение ее в том, что эта команда действует лишь на пару регистров общего назначения. Следующая команда произведет опе- рацию XOR содержимого регистра г 17 с содержимым г1б (и поместит результат, понятно, в г17): еог г17,г16 На практике, однако, нам обычно требуется в качестве второго операнда не ре- гистр, а число — заданная маска битов. Поэтому мы создаем следующий макрос под названием eori (листинг 6.8) .macro eon push rl6 ldi rl6,@l eor @0,rl6 pop rl6 .endm Вызов такого макроса для преобразования регистра г17: eori rl7,mask где mask — битовая маска в виде числа (о ней подробно рассказано ъразд. «Коман- ды логических операций» главы 7). Отметим, что текст макроса в тексте программы обязательно должен стоять до его вызова, иначе компилятор его не найдет. Иными словами, макросы надо располагать в начале программы — либо там же, где дирек- тива . include и определения переменных, либо сразу после таблицы прерываний. Имя мы присвоили по аналогии с другими подобными операциями с непосредст- венными значениями (immediate), а не с регистрами, — таким же образом авторы
140 Часть II. Программирование микроконтроллеров AVR на ассемблере AVR-ассемблера образовали subi от sub и т. д. Фактически мы дополнили список команд еще одной, причем не задействовав ни одного лишнего регистра— г 16, ко- торый тут используется временно, благодаря помещению в стек после операции остается в неприкосновенности. Параметры, необходимые для выполнения макро- са, нумеруются просто по порядку, начиная с нуля, причем, как видите, никакого указания типа не требуется: вместо @о компилятор подставит то, что будет стоять на первом месте при вызове, вместо @1 — на втором, и т. д. Выполнение макроса займет ровно 4 такта (вместо одного в случае чистой еог), но ни единого лишнего сверх того. Так в чем же тут собака порылась — почему нельзя всегда вместо подпрограмм, вызов которых занимает лишние такты, требует инициализации стека (даже если в самой процедуре стек не используется) и тем самым свободного пространства в SRAM, применять макросы, которые никаких ресурсов не требуют? Очень про- сто: макрос, строго говоря, не является средством программирования — это лишь удобный способ сокращения записи, не более того. Компилятор во всех местах, где встретит имя макроса, тупо подставит соответствующую последовательность ко- манд, и при обратном дизассемблировании вы никаких следов макросов уже не об- наружите. И кстати, указание компилятора на ошибку при вызове макроса также отправит совсем не на ту строку, где она допущена. И если вынос повторяющихся участков кода в подпрограммы реально снижает объем кода и экономит память, то макросы ничего такого не делают. Вместо процедур макросы можно применять просто для удобства тогда, когда вынести за скобки нужно две-три назойливо по- вторяющиеся строчки кода, выполнение которых будет длиться меньше, чем вызов подпрограммы. Но надо при этом учитывать, что читаемость программы тоже ухудшится, и ошибки будет искать труднее. НЕХ-файлы и их загрузка в контроллер Большинство команд в AVR имеют размер в два байта, из которых собственно код команды может занимать первые 4-7 битов, остальное занимают параметры, если они предусмотрены форматом команды. Все это вместе носит название командное слово, или код операции (КОП, опкод), и у AVR его формат выгодно отличается относительным единообразием: выбиваются из двухбайтового формата только jmp, call и немногие другие, у которых сами параметры могут занимать по два байта и более. Компилятор записывает каждый код операции в выходной файл с расширением .hex, который затем используется программатором для записи в контроллер. Кроме hex-файлов, есть и другие форматы записи готовых программ (самый распро- страненный— бинарный, который используется, например, во всех известных windows/dos-форматах ЕХЕ и СОМ), но hex-формат для микроконтроллеров самый распространенный, и мы будем рассматривать только его. Рассматриваемый формат придуман фирмой Intel (есть и другие «гексы»!) и отли- чается тем, что содержит числа в текстовом представлении, причем в шестнадцате-
Глава 6. Основы программирования MKAVR 141_ ричной записи. Поэтому в случае чего его можно даже править в обычном тексто- вом редакторе. Кстати, точно такой же формат применяется для записи констант в EEPROM, если это потребуется. Рассмотрим формат HEX подробнее. На рис. 6.1 представлен файл короткой про- граммы, открытый в обычном Блокноте. На первый взгляд, тут сам черт ногу сло- мит, но на самом деле все достаточно просто, хотя чтение затрудняется тем, что строки не поделены на отдельные байты (и никаких, конечно, добавок в виде $ или Ох тут не имеется). Разбираться будет проще, если вы скопируете этот файл под другим именем и расставите в нем пробелы после каждой пары символов. , Рис. 6.1. Файл формата HEX в Блокноте Основную часть файла занимают информационные строки, содержащие непосред- ственно КОП. Они состоят из ряда служебных полей и собственно данных. Каждая строка начинается двоеточием и заканчивается парой символов: «возврат карет- ки»/«перевод строки», на экране не отображаемых. После двоеточия идет число байтов в строке — кроме первой и последней, везде стоит, как видите, число ю (де- сятичное 16), т.е. в каждой строке будет ровно 16 информационных байтов {ис- ключая служебные). Затем следуют два байта адреса памяти — куда писать (в пер- вой строке оооо, во второй это; естественно, оою, т. е. предыдущий адрес плюс 16, и т. д.). Наконец, после адреса расположен еще один служебный байт, обозначаю- щий тип данных, который в информационных строках равен о о, а в первой и по- следней — 02 и 01, о чем далее. Только после этих шести байтов начинаются собст- венно байты данных, которые означают соответствующие КОП, записанные по- словно, причем так, что младший байт идет первым. КОП для AVR, напоминаю, занимают в основном два байта, и память в этих МК также организована пословно. Таким образом, запись в первой информационной строке засо в привычном нам «арабском» порядке, когда самый старший разряд располагается слева, должна вы- глядеть, как со за.
142 Часть II. Программирование микроконтроллеров AVR на ассемблере Заметки на полях Обратите внимание, что путем такого формата можно адресовать максимум $FFFF+1 (65 536) адресов (наследие Intel 8086, в котором память делилась на сегменты по 64 К), т. е. охватить объем памяти программ в 131 072 байта (128 К). Для контролле- ров, содержащих больший объем памяти (ATmega2560), этот адрес должен, очевидно, интерпретироваться как-то иначе. Мы не станем на этом останавливаться, т. к. на ассемблере вы никогда не напишете код такого объема, — просто запомните этот факт на всякий случай. В первой строке служебный байт типа данных равен 02, и это означает, что данные в ней представляют сегмент памяти, с которого должна начинаться запись (в рас- сматриваемом случае оооо). Заканчивается НЕХ-файл всегда строкой :00000001ff — значение типа данных oi означает конец записи, данных больше не ожидается. А что означает ff? Самым последним байтом в каждой строке идет контрольная сумма (дополнение до 2, иначе она называется LRC, Longitudinal Redundancy Check) всех остальных бай- тов строки, включая служебные. Алгоритм вычисления LRC очень простой — нужно вычесть из числа 256 значений всех байтов строки (не обращая внимание на перенос) и взять младший байт результата. Соответственно, проверка целостности строки еще проще — нужно сложить значения всех байтов (включая контрольную сумму), и младший байт результата должен равняться нулю. Так, в первой строке число информационных байтов всего два, оба равны нулю, плюс (в начале) число информационных байтов, равное 2, плюс служебный байт типа данных, равный также 2, итого контрольная сумма всегда равна 256 -2-0-0-2 = 252 = $FC. В последней строке одни нули, кроме типа данных, равного 1,— соответственно, контрольная сумма равна 256 - 1 = 255 = $FF. Теперь попробуем немного расшифровать данные. Первое слово в первой инфор- мационной строке, как мы выяснили, равно $соза. Если мы возьмем фирменное описание команд, то обнаружим, что значению старшей тетрады в КОП, равной $с (1100 в двоичной системе), соответствует команда rjmp— как мы знаем, практиче- ски любая программа начинается с безусловного перехода на метку reset. Теперь очевидно, что остальные биты в этом значении ($оза) представляют абсолютный адрес в программе, где в тексте стояла метка reset. Попробуем его найти — для этого вспомним, что адреса отсчитываются по словам, а не по байтам, т. е. число $за (десятичное 58) нужно умножить на 2 (получится 116 = $74) и искать в этой об- ласти. Разыщем строку с адресом $0070, отсчитаем от начала шесть служебных бай- тов, потом три слова (три пары байтов) от начала данных, и найдем там фрагмент F894, который в нормальной записи будет выглядеть как $94F8, а это, как легко убе- диться по справочнику, есть код команды cli, запрещающей прерывания (которая в начале программы лишняя, т. к. они все равно запрещены, но, видимо, поставлена на всякий случай). Следующая команда будет начинаться с байта $Е5, и первая тет- рада в ней обозначает код команды idi (1110— проверьте!), а пятерка, очевидно, есть фрагмент адреса конца памяти (ramend), который в силу «зеркального» форма- та записи получается на самом деле равным $025f (cm. значение младшего байта, равное $2f). Это соответствует значению ramend, определенному в inc-файлах для
Глава 6. Основы программирования MKAVR 143 МК с 512-ю байтами встроенного ОЗУ ($025F = 607, т. е. всего адресов 608, из кото- рых 96 ($5f) занимают регистры, итого получается 512 ($0200) незанятых байтовых ячеек, составляющих ОЗУ). Все, как и должно быть, — если мы обратим внимание опять на первую-вторую строки с данными, то увидим повторяющийся фрагмент 1895, который, как легко догадаться, должен быть командой reti из таблицы пре- рываний — если проверите по справочнику, то так оно и окажется. Как видите, разобраться хоть и сложно, но при некотором навыке и наличии под рукой таблицы двоичных/шестнадцатеричных кодов команд вполне можно. Имен- но так работает программа, которая превращает код обратно в текст, — дизассемб- лер (он входит в AVR Studio). Впрочем, в дизассемблированной программе разо- браться бывает еще сложнее, чем в самом hex-файле, т. к. там, естественно, нет никаких адресных меток и определений, все в абсолютных числах. Так что если действительно надо дизассемблировать программу, то лучше делать это «вживую» в AVR Studio. А зачем это может понадобиться на практике? Дело в том, что в памяти программ часто хранят константы — те, что предположительно не будут изменяться в про- цессе эксплуатации, — например, устанавливаемые по умолчанию значения каких- либо величин. Но, разумеется, по истечении некоторого времени или при переносе на другое устройство эти константы обязательно захочется изменить. И если у вас текст программы по каким-то причинам отсутствует (например, программа взята из публикации в журнале или скачана с радиолюбительского сайта), а загрузочный hex-файл имеется, то всегда можно «хакнуть» исходный код и немного подправить его под свои нужды. Загружаются hex-файлы программой, которая прилагается к вашему ISP-про- грамматору. В AVR Studio это делается прямо из среды программирования, для чистого ассемблера необходимо программу-загрузчик запустить отдельно. На рис. 6.2 показано окно программы ASISP, прилагающейся к описанным в главе 5 программаторам AS-2/3/4. Подробно я ее описывать не стану — к ней прилагается вполне толковое описание на русском, и даже без него разобраться в ней не так уж сложно. Укажем только правильную последовательность действий — при ее нару- шении вы, скорее всего, ничего не испортите, но и загрузки не произойдет. В прин- ципе, те же самые действия необходимо произвести в любом другом загрузчике. Первым делом программу следует настроить на нужный последовательный порт. Для этого подключите программатор к компьютеру, обратитесь в Диспетчер уст- ройств, где в группе Порты СОМ и LPT должен появиться новый последователь- ный порт, и запомните его номер (например, СОМ 6). Этот порт нужно указать в меню Настройки | Настройки интерфейса. Номер порта сохраняется, и если вы в дальнейшем не будете менять гнездо USB, к которому подключен программатор, то настраивать его придется только при первом включении. Затем подключите контроллер через выводы ISP (см. главу 5) к программатору и включите питание устройства, где установлен контроллер. В программе нажмите на кнопку Чтение сигнатуры, после чего программа должна автоматически вы-
144 Часть II. Программирование микроконтроллеров AVR на ассемблере светить название контроллера в окошке выше этой кнопки (на рис. 6.2 это ATmega8535). Если вы этого не сделаете, то в дальнейшем вам укажут на ошибку. Это действие одновременно служит проверкой, что все подключено правильно. После этого необходимо через меню Flash | Открыть указать hex-файл, который вы собираетесь загрузить. Затем можно уже произвести загрузку, для чего последо- вательно нажать на кнопки Стирание микросхемы и Программир. Flash. В об- щем-то этого достаточно, но для очистки совести можно еще осуществить провер- ки: после стирания нажать на кнопку Проверка на чистоту, а после загрузки — на кнопку Проверка Flash. Рис. 6.2. Программа ASISP Все это можно делать гораздо быстрее, если настроить действия кнопки Автопро- граммирование, для чего в окне, открывшемся по команде меню Настройки | На- стройки проекта, расставить пять флажков: Стереть, Проверить, Перезагрузить, Запрограммировать, Проверить в группе Настройки автопрограммирования. При этом у вас автоматически каждый раз будет заново загружаться измененная версия hex-файла, и опасность забыть ее обновить после правки программы исчезнет. Только не забудьте загрузить вручную hex-файл с другим именем, если вы перешли к другой программе, — читать ваши мысли программа ASISP пока еще не обучена. Настройки автопрограммирования не сохраняются, и при каждом за- пуске программы их надо настраивать заново, что совершенно оправданно: мало ли чего вы там понастроили прошлый раз.
Глава 6. Основы программирования MKAVR 145_ И еще одно обязательное действие обычно требуется выполнить перед началом эксплуатации нового контроллера — установить ему fuse-биты. Причем это делает- ся один раз, и выносить в автопрограммирование это действие я не рекомендую, — повторять его нет нужды. Настройка fuse-битов вынесена в отдельное меню На- стройки | Lock/Fuse биты (см. рис. 5.7-5.9 в главе 5) и единственное, что требует- ся по этому поводу выучить наизусть: перед внесением любых изменений в fuse- биты следует обязательно прочитать из контроллера их текущее состояние соответ- ствующей кнопкой. Надо учитывать, что после программирования контроллер перезагрузится (даже два раза — после программирования и после проверки Flash) и начнет работать сразу, т. к. на него подано питание. Поэтому я по мере возможности стараюсь не занимать выводы программирования каким-либо функциями, но, конечно, это соблюдать необязательно, а иногда и невозможно. На этот счет можно быть спокойными — чтобы испортить тут что-то неправильным подключением, надо очень постараться. Единственное неудобство в том, что, как правило, при занятых выводах програм- мирования приходится каждый раз для проверки работы схемы отключать про- грамматор. О Bootloader Наличие в Arduino предустановленной загрузочной программы Bootloader несрав- ненно облегчает и упрощает взаимодействие с платами контроллеров. Если бы не эта «фича», позволяющая загружать и отлаживать программы буквально одним щелчком мыши, то Arduino никогда бы не получила такой популярности. Однако существенная часть ограничений платформы Arduino также проистекает из этого факта. Для начала Bootloader отъедает кусок памяти программ. Но на удивление незначи- тельный — если посмотрите на результат компиляции любого скетча, то по поводу занимаемой памяти там будет написано, что «всего доступно 32 256 байт». До пол- ного объема памяти ATmega328, равной 32 К, недостает 32 768 - 32 256 = 512 бай- тов, что и есть объем, занимаемый Bootloader'ом. 0,5 кбайта из 32 (1,5%)— согла- ситесь, это небольшая цена за такое удобство. Гораздо хуже другое: Bootloader есть точно такая же программа, как любая другая, потому он привязан, во-первых, к определенному типу контроллеров, во-вторых, что самое главное, — к конкретной рабочей частоте. Изменение типа МК в преде- лах группы контроллеров, обладающих схожим набором периферийных устройств, в принципе возможно, но никогда не знаешь, какие при этом встретятся подводные камни. Изменение тактовой частоты, наоборот, невозможно, иначе перестанет ра- ботать последовательный порт, и Bootloader окажется бесполезным (можно это обойти, если выбирать кратные частоты тактирования и, соответственно, менять скорость порта, но такое решение тоже не универсальное). И наконец, наличие Bootloader'а принципиально замедляет загрузку: он обязан перехватывать управле- ние в первый момент и ждать, не «постучится» ли что-то в последовательный порт, и лишь потом передавать управление основной программе.
146 Часть II. Программирование микроконтроллеров AVR на ассемблере Кроме того, к Bootloader'y должна прилагаться периферия, соответствующая задей- ствованному порту (это может быть не только UART, хотя это и самый частый слу- чай). Все это вместе взятое ограничивает применение Bootloader'a определенной сферой. В профессиональной практике наличие встроенного загрузчика не столь уж редкое явление — дистанционная смена прошивки всяких там смартфонов именно так и производится. А вот в нашем деле — с изготовлением уникальных единичных экземпляров аппаратуры — возня с Bootloader'ом скорее помешает основному де- лу. Исключение могут составить случаи, когда вы хотите наладить удобную заме- ну, например, калибровочных коэффициентов в EEPROM без доступа к «телу» контроллера. Но не забывайте при этом, что Bootloader'bi не бывают универсаль- ными, и их всегда надо подбирать под конкретный тип МК, а потом еще, возможно, и подстраивать под выбранную тактовую частоту. И напоследок одно замечание: имеющийся Bootloader почти наверняка будет ис- порчен, если попытаться запрограммировать содержащий его контроллер через обычный внутрисхемный ISP-программатор,— Bootloader после этого придется загружать заново. Поэтому применять тот и другой способ вперемешку нельзя: либо последовательный порт, либо ISP. Простейшая программа Сначала мы покажем, что простейшие ассемблерные программы для AVR не нуж- даются ни в каком специальном оформлении. Для этого рассмотрим уже ставший классическим пример с попеременной установкой вывода в высокий и низкий уро- вень (простейшую генерацию частоты,— см. подробный разбор этого примера в [1]). Заодно продемонстрируем, почему язык С (не говоря уж об Arduino) все-таки отчуждает нас от того, что реально происходит в контроллере. Если у вас есть под рукой Arduino Uno или любая другая плата Arduino на основе ATmega328, загрузи- те в нее такой простой скетч (листинг 6.9). void setup() { pinMode(5, OUTPUT); //вывод 5 - на выход } void loop () { digitalWrite(5, HIGH); digitalWrite(5, LOW); Если вы подключите к выводу 5 Arduino осциллограф, то сможете убедиться, что на нем генерируются прямоугольные импульсы с частотой чуть больше 70 кГц. Есть много других вариантов выполнения подобного алгоритма, но если в них
Глава 6. Основы программирования MKAVR 147_ установка уровня на выводе осуществляется функцией digitaiwrite (), то частота не выйдет за пределы 70-80 кГц. Но что же это такое, в самом деле? В Arduino контроллер работает на частоте 16 мегагерц, и 70-80 килогерц — это максимум того, что он может выдать в таком простом алгоритме?! Убедиться, что виноват в этом случае чересчур навороченный язык Arduino несложно, даже не выходя за рамки среды Arduino, если использовать прямое программирование порта на С. Тот же самый скетч, но напрямую обра- щающийся к портам контроллера, будет выглядеть для Arduino так (листинг 6.10). void setup() { DDRD = 0x20; while (true) { PORTD = 0x20; PORTD = 0; void loop() {} Цифровой вывод 5 — это бит номер 5 порта D (см. таблицу соответствия выводов ATmega328 и плат Arduino, которая имеется на любом официальном сайте Arduino), и маска 0x2 о (= ОЬОО100000), приложенная к порту ddrd, установит этот вывод на выход. Этот скетч выдаст на выводе 5 частоту, почти равную 4 МГц, что куда больше похоже на работу 16-мегагерцевого контроллера. Теперь повторим то же самое на ассемблере, выбрав в качестве «подопытного кро- лика» ATmega8. Хотя с таким же успехом могли бы выбрать любой другой кон- троллер, в котором присутствует 5-й вывод порта D (а у тех, у которых порт D от- сутствует или его выводов меньше пяти, можно заменить его на любой другой вы- вод любого порта). Тактовую частоту можно установить любую желаемую, от внутреннего генератора или внешнего кварца, как удобнее (см. по этому поводу разд. «Конфигурационные ячейки» главы 5). Ассемблерная программа, соответст- вующая последнему скетчу, будет состоять из следующих строк (листинг 6.11). Листинг 6,11 ; .include ffm8def.inc" ldi г16,0Ь00100000 ;регистр г16 = 0x20 out DDRD,rl6 ;вывод PD5 на выход clr г17 ;регистр rl7 = 0 Gcykle: out PortD,rl6 ;PD5=1 - 1 такт out PortD,rl7 ;PD5=0 - 1 такт rjmp Gcykle ;переход обратно 2 такта
148 Часть II. Программирование микроконтроллеров AVR на ассемблере Ее можно еще упростить, если, во-первых, отказаться от включаемого INC-файла, во-вторых, удалить команду обнуления регистра ri7 (cir ri7), которая здесь вклю- чена больше для порядка: регистры общего назначения и без того обнуляются при сбросе в момент включения питания. А inc-файл, содержащий мнемонические на- именования регистров, включен для упрощения переноса на другие модели — проще раз и навсегда запомнить название PortD, чем лазать по даташитам в поисках абсолютных адресов регистров ввода/вывода для каждой модели. В этом случае для другой модели нужно будет только заменить название inc-файла и, конечно, прове- рить, существуют ли у нее используемые порты. Эта программка содержит 6 команд и займет в памяти 12 байтов. Первые две команды выполняются один раз, а далее в цикле по одному такту занимают коман- ды out и еще два такта — команда безусловного перехода г jmp, так что цикл зани- мает четыре такта, и, следовательно, при любой тактовой частоте выдаст на выводе PD5 (выводе ATmega8 под номером 11) частоту, равную ровно четверти от тактовой. Если у вас есть осциллограф, можете попробовать различные кварцы или менять частоту встроенного генератора и убедиться, что частота каждый раз дейст- вительно равна ровно 74 от установленной. Но программа из листинга 6.11 не очень хороша с точки зрения экономии ресурсов контроллера: мы занимаем два регистра общего назначения г1б и г 17 на все время, пока работает цикл. К тому же эти регистры не меняют своего состояния, т. е. хра- нят константы. Нельзя ли действовать как-то попроще, если нам всего-то надо установить или сбросить бит? Конечно, можно (листинг 6.12). . include flm8def. inc" ldi г16,0Ь00100000 /регистр г16 = 0x20 out DDRD,rl6 ;вывод PD5 на выход Gcykle: sbi PortD,5 ;PD5=1 - 2 такта cbi PortD,5 ;PD5=0 - 2 такта rjmp Gcykle /переход обратно 2 такта Эта программа делает то же самое, но регистров не занимает, а непосредственно устанавливает/сбрасывает бит в порту D. Единственное отличие: каждая операция sbi /cbi занимает не один такт, а два. Потому частота на выходе будет равна уже не V4 от тактовой, a V6. Можете проверить этот факт осциллографом. Вы еще увидите далее примеры того, что любую программу можно оптимизировать по числу зани- маемых регистров, можно — по времени выполнения, а можно и по количеству ко- манд, т. е. по объему, занимаемому в памяти. Разительное отличие от Arduino, где мы можем вроде бы делать то же самое, но даже не знаем, как именно организован вывод в порт по команде portd = о, и сколько времени это занимает, правда? Примечание Напомню, что все законченные программные примеры из этой книги читатель может найти в архиве по адресу, указанному во введении, — в данном случае в папке GLAVA6.
Глава 6. Основы программирования MKAVR 149 Для более серьезного примера в следующем разделе мы, как положено новичкам, станем мигать светодиодами, но не просто так, а по нажатию кнопки. Это будет несложная тренировочная программа, в которой не нужны прерывания. В ней мы покажем, как можно создать аналоги функций delay о и deiayMicrosecondO, а так- же отслеживать события без привязки и с привязкой к внешним прерываниям. Таймер без прерываний Программа будет считать нажатия кнопки и демонстрировать их в двоичном коде на светодиодах (LED). В схеме для простоты мы ограничимся тремя светодиодами, т. е. будем считать до 8 нажатий (хотя без каких-то переделок программы можно увеличить число светодиодов до 8, и соответственно, считать до 256). Выберем одну из самых простых и удобных моделей ATmega8. Схема, для которой мы на- пишем программу, представлена на рис. 6.3. + 5В С1 » + 5В R1 + 5В 1 Reset 2RXD 3TXD 4PD2 5 PD3 6PD4 7Vcc 8GND 9 XTAL1 10 XTAL2 11PD5 12PD6 13PD7 14PB0 PC4 27 PC3 26 PC2 25 PC124 PCO 23 GND22 AREF 21 AVCC 20 SCK19 MISO 18 PB216 PB1 15 ATmega8 R1,R2 5,1 к R3-R5 330 C1.C2 0,1 мк R3-R5 VD1-VD3 Рис. 6.З. Схема двоичного счетчика нажатий В этой схеме серым цветом показано подключение программирующего 10-кон- тактного разъема, включая «подтягивающие» резисторы по выводам программиро- вания, о необходимости установки которых «так долго говорили большевики» (см. главу 3). Сам разъем, включая разводку выводов, описан в главе 5. На макетной плате разъем этот будет только мешать (и резисторы там тоже не нужны), и здесь он показан для образца — как это должно выглядеть в конечном изделии, если вы его туда будете устанавливать. Больше на схемах мы этот разъем показывать не станем, но подразумевается, что каким-то образом контроллер программируется, так что программирующие выводы постараемся не использовать в других целях. RC-цепочка на выводе RESET стандартная (см. главу 2), керамический «развязы-
150 Часть II. Программирование микроконтроллеров AVR на ассемблере вающий» конденсатор С2 по питанию устанавливается всегда, его емкость можно выбирать любой в пределах от 0,1 до 2,2 мкФ. Кнопку Кн1 для нашего случая можно подсоединять к любому из свободных кон- тактов любого порта, но именно к выводу PD2 я подключил ее с далеко идущими целями, о чем вы узнаете далее. Резистор R2 также можно не устанавливать, если обойтись встроенным «подтягивающим» резистором, но, как мы уже говорили, без него помехоустойчивость схемы будет значительно ниже: без такого резистора, может, например, происходить ложное срабатывание кнопки при бросках питания (скажем, при переключении питания с сетевого блока на резервную батарею). На- личие внешнего резистора гарантированно решает такие проблемы, поэтому лучше его устанавливать Всегда. Проблема, которую нам предстоит преодолеть, заключается в том, что любая кноп- ка дребезжит, и потому при ее нажатии или отпускании генерируется множество импульсов, из которых придется выбрать один. Использование прерывания, как следовало бы сделать «по-правильному», не избавляет от необходимости учиты- вать дребезг, поэтому в принципе решение проблемы и там, и здесь одинаково, — придется делать искусственную задержку реакции МК, для чего нам и понадобится имитатор таймера. Задержка Давайте начнем с формирования временного интервала. Нам нужно сформировать задержку порядка секунд или долей секунды. Метод без таймера основан на том, что каждая команда в МК выполняется за строго определенное время. В AVR счи- тать время вообще очень просто: большинство простых команд выполняется за один такт, и потому, например, для формирования интервала в одну десятую секунды при тактовой, частоте 1 МГц (которая, напомним, устанавливается для контроллера «по умолчанию» за счет встроенного RC-генератора), нам требуется выполнить какую-нибудь (в общем-то неважно, какую) последовательность из ста тысяч команд. Обычно программисты используют декрементирование (т. е. последовательное уменьшение на единицу) какой-либо величины. Предположим, что необходимое число займет два регистра-разряда, что даст максимальную величину числа 65 535. Схема действий такая: мы последовательно уменьшаем самый младший разряд (на- зовем его Razro) на единицу, когда его величина достигает нуля, уменьшаем на единицу следующий (Razri) и переходим опять к уменьшению младшего, начиная со значения 255 (это значение загружать специально не требуется, т. к. при вычита- нии единицы из нуля результат получится равным 255 автоматически), и так до тех пор, пока все разряды не станут нулями. Предварительно следует загрузить в пере- менные RazrO и Razri нужное число. Какое? Это зависит от конкретного алгоритма. Следующая последовательность команд (листинг 6.13) реализует этот алгоритм «в лоб».
Глава 6. Основы программирования MKAVR 151 Delay: dec RazrO brne Delay dec Razrl brne Delay <все равны 0 — конец задержки> Обратите внимание, что при использовании команды dec никаких дополнительных команд сравнения при достижении нуля не требуется (подробно об этом будет рас- сказано в следующей главе). Некрасивость приведенного решения заключается в наличии команд перехода, которые выполняются за один такт, если условие (в нашем случае равенство нулю) не выполняется, и за два такта, если оно выпол- няется. К тому же число циклов в каждой итерации, вообще говоря, разное. Поэто- му точно подсчитать число циклов становится достаточно сложно. Это хорошо, что в этой нашей задаче необязательно выдерживать точный интервал, а если надо? Значительно более компактной и предсказуемой будет реализация алгоритма на основе команды вычитания с учетом переноса (листинг 6.14). Delay: subi RazrO,1 sbci Razrl,О ;sbci Razr2,0 - если потребуется 3-й регистр brcc Delay Работает это так: команда sbci вычитает сразу две величины: то, что записано в самой команде, плюс флаг переноса с. Если результат предыдущего вычитания устанавливает флаг переноса (что происходит при переходе через ноль, когда из ноля вычитается единица), то команда sbci вычтет его значение, равное единице, если нет, то не вычтет ничего (точнее, вычтет ноль). В результате в каждой итера- ции выполняется строго определенное число команд и за строго определенное вре- мя: по одному такту на каждое вычитание, плюс два такта на переход обратно к началу цикла (для команды brcc условие перехода выполняется, если флаг пере- носа не установлен), — всего при двух регистрах четыре такта. (Для особо въедли- вых отметим, что самый последний цикл будет на один такт короче). Итак, для того чтобы получить ровно 100 000 тактов, нам нужно записать в регист- ры Razr2 - RazrO число 100 000/4 = 25 000, или $61А8. Это даст интервал в 0,1 с при тактовой частоте 1 МГц. Можно считать и иначе: одно вычитание происходит за четыре такта, что равносильно снижению тактовой частоты вчетверо, до 250 кГц. 1 секунда будет тогда равносильна 250 тыс. таких суммарных тактов, а 0,1 секун- ды — 25 тысячам. В общем случае число N9 соответствующее нужному интервалу времени Т (с) при тактовой частоте/^акт (Гц), можно получить по формуле N= Т/тгкт/(г + 2), где г —
152 Часть II. Программирование микроконтроллеров AVR на ассемблере число регистров (каждый регистр — еще одна команда sbci продолжительностью в 1 такт). Всего с двумя регистрами и тактовой частотой 1 МГц мы можем получить задержку до 0,26 с, если запишем в них число 65 535 = $ffff, а с тремя регистрами при той же тактовой частоте — задержку почти до 84 с, если запишем в них число 16 777 215 = $FFFFFF. Программа счетчика Сначала разберемся с нажатием кнопки. Отслеживать состояние вывода удобно командами sbic или sbis (Skip if Bit in I/O register Clear/Set, пропустить следующую команду, если бит в РВВ очищен/установлен) применительно к второму биту мас- сива PinD, к которому и подключена кнопка. Листинг 6.15 иллюстрирует простей- ший цикл слежения за состоянием кнопки (когда кнопка нажимается, состояние вывода меняется с единицы на ноль). Pincykle: ;цикл отслеживания кнопки sbic PinD,2 ;пропустить, если нажата rjrnp Pincykle /вернуться обратно, если не нажата <кнопка нажата — что-то делаем> rjmp Pincykle /вернуться обратно к отслеживанию Даже когда нам требуется отслеживать только нажатие (а не последующее отпус- кание, как описано далее), то такой простейший цикл работал бы отвратительно: вся процедура вместе с возможными действиями по факту нажатия займет микро- секунды, а даже при самом быстром ударе по кнопке замкнутое состояние контак- тов будет продолжаться доли секунды. Потому, обнаружив при переходе к началу цикла, что замкнутое состояние продолжается, процедура будет выполняться снова и снова, пока вы кнопку не отпустите (сравните с описанием работы прерывания по уровню в главе 3). Чтобы этого не происходило, как минимум, необходимо ввести задержку перед возвращением к началу цикла, а еще лучше при этом проверять со- стояние кнопки несколько раз, чтобы уберечься от случайной помехи. Чтобы потом не повторять процедуру задержки в коде несколько раз с разными значениями, мы сразу оформим задержку в виде макроса (можно в виде функции, но тогда надо еще не забыть инициализировать стек, и мы будем терять по 7 тактов при каждом вызове, что в других случаях может оказаться существенным при ма- лых задержках). Тогда вся программа, включая секцию определений, будет такой, как в листинге 6.16. ;Программа счета нажатий кнопки в двоичном коде .device АТтедаЭ .include "in8def.inc" ;частота по умолчанию 1 МГц
Глава 6. Основы программирования MKAVR 153 .macro Delay /процедура задержки ldi Razrl,@0 ;старший байт задержки ldi RazrO,@1;младший байт задержки R_sub: subi RazrO,1 sbci Razrl,0 brcc R_sub .endm .def temp = rl6 ;рабочая переменная .def RazrO = rl7 ;разряды задержки .def Razrl = rl8 .def Counter = r20 ;счетчик .org 0 ;необязательно, просто для ориентировки ;============ Программа ============ ldi temp, ОЬОООООЮО ;для второго разряда порта D out PORTD,temp ;подтягивающий резистор на всякий случай ldi temp, 0Ы1111111 ;порт С все контакты на выход out DDRC,temp clr Counter ;очищаем счетчик Pincykle: ;цикл отслеживания кнопки sbis PinD,2 /пропустить, если не нажата rjmp push_pin rjmp Pincykle /вернуться обратно, если не нажата push_pin: /кнопка нажата Delay 0,$19 /задержка 100 мкс с, N = $0019 sbic PinD,2 /пропустить, если по-прежнему нажата rjmp Pincykle /вернуться обратно к отслеживанию inc Counter / точно нажата - увеличиваем счетчик out PORTC,Counter /выводим счетчик в порт В Delay $СЗ,$50 /задержка 0,2 с, N = $С350 = 50000 Delay $СЗ,$50 /задержка 0,2 с, N = $С350 = 50000 rjmp Pincykle /вернуться обратно к отслеживанию Как видите, программа небольшая — в памяти она займет 56 байтов. Здесь сначала дважды с паузой в 100 мкс кнопка проверяется на состояние нажатия (на PinD,2 должен быть тогда логический ноль). Если оба состояния совпадают, то увеличива- ем счетчик, выводим его в порт со светодиодами и, сделав длинную паузу, возвра- щаемся к отслеживанию кнопки. Здесь пришлось ввести две задержки по 0,2 с — это было сделано по результатам практических испытаний, т. к. 0,2 секунды слиш- ком мало, чтобы успеть снять палец с кнопки, и скажется эффект многократного повтора, который мы тут пытались обойти. Можно избежать двукратного повторе- ния кода задержки, если добавить еще один регистр. Подробности Разумеется, значения старшего и младшего байта можно не высчитывать вручную — компилятор сделает все за вас, если вы оформите вызов задержки вот так: Delay high(50000),low(50000) /задержка 0,2 с, N = 50000
154 Часть II. Программирование микроконтроллеров AVR на ассемблере Я обычно пишу в hex-форме только потому, что так короче и наглядней, и под рукой у меня всегда калькулятор, который переводит в Лех-форму и обратно одним щелчком мыши. Счетчик counter будет считать «вкруговую» — при переполнении он опять начнет с нуля. Программа выводит состояние счетчика в порт С целиком, таким образом работа ее не зависит от того, сколько именно светодиодов вы подключите в выво- дам этого порта, — все шесть (доступных в ATmega8) или только три, как на схеме. Подробности Пояснений здесь требует момент, связанный с определением переменных, — почему мы начали сразу с регистра г 16, а не с го, например? Одно из самых больших не- удобств AVR заключается в том, что команды, оперирующие с константами (idi, sbci и subi — в нашем случае, а также команды cpi, andi и др.) не работают с первыми шестнадцатью РОН (от го до ri5), их можно использовать только для регистров ri6 - r3i (заметим сразу про еще одно аналогичное ограничение: команды поразрядного досту- па к РВВ sbi, cbi, sbis и sbic работают только для первых тридцати двух РВВ, до но- мера $if включительно). Для сокращения программы рабочие переменные (temp, счетчики) всегда желательно выбирать из этой половины регистрового файла. Поло- жение осложняется тем, что регистры из старшей половины наиболее дефицитны — последние шесть из них объединены в пары х, y и z для работы с памятью и некото- рых других операций (см. далее), г24 и г25 задействованы в команде adiw, и т. п. Если переменных не хватает, то, чтобы не связываться с локальными переменными или не переходить на работу с памятью, загрузку регистра из первой половины регистрового файла (допустим, это ri5) непосредственным значением приходится осуществлять парой команд: Idi temp,10 mov r15,temp Но самое главное не это— возникает вопрос, нельзя ли улучшить алгоритм так, чтобы не приходилось думать о том, как быстро надо снять палец с кнопки? Это можно сделать, если дополнительно еще отслеживать отпускание кнопки, что не так уж и просто, поскольку из-за дребезга нажатие и отпускание — с точки зрения контроллера — по большому счету различаются только начальной и конечной фа- зой, а в остальном они представляют собой одинаковые пачки импульсов общей длительностью, как правило, несколько десятков микросекунд. Поэтому общая схема «отлова» «настоящего» отпускания кнопки должна быть в этом случае такой: «ловим» нажатие (т. е. появление низкого уровня на выводе PD2), делаем паузу, чтобы пропустить дребезг, и начинаем «ловить» отпускание (т. е. первое появление высокого уровня на выводе). Затем проделываем необходимые действия и опять выдерживаем «антидребезговую» паузу перед тем, как все начать сначала. Дли- тельность пауз нужно достаточно точно рассчитать, иначе возможен пропуск коротких и быстро следующих друг за другом нажатий. Примем для простоты, что обе паузы будут составлять 0,1 с (возможно, в реальной конструкции эти величины потребуется подогнать «по месту»). Заметки на полях Наверное, вы замечали, что экранные кнопки в графическом интерфейсе Windows реагируют именно на отпускание кнопки мыши. Психологически нажатие есть опера- ция, к которой человек — особенно нетренированный «чайник» — должен подгото-
Глава 6. Основы программирования MKAVR 155_ виться: прицелиться пальцем (или курсором) и выбрать свободный ход кнопки, чтобы четко зафиксировать нажатие в определенный момент времени. В то же время отпус- кание никакой специальной подготовки не требует— просто расслабьтесь, и палец сам соскользнет с кнопки. Потому всегда, когда требуется зафиксировать определен- ный момент времени, кнопка должна реагировать на отпускание (сравните с подготов- кой гранаты-«лимонки» к бою, когда сначала вынимается кольцо, а граната срабаты- вает лишь после того, как вы ее выпустите из рук, освободив рычаг чеки). Иной случай представляет собой, например, компьютерная клавиатура, где задача стоит не зафик- сировать момент времени, а обеспечить как можно больше нажатий в единицу време- ни, — там кнопки реагируют именно на нажатие (но и работу с клавиатурой приходит- ся специально осваивать). Также именно на нажатие следует реагировать в случае однократного действия, когда момент отпускания безразличен, а дребезг не оказывает никакого влияния, — примером может служить кнопка «Пуск» с блокировкой на пульте управления каким-нибудь станком. /Программа счета отпусканий кнопки в двоичном коде .device ATmega8 .include "m8def.inc" /частота по умолчанию 1 МГц .macro Delay /процедура задержки ldi Razrl,@0 /старший байт задержки ldi RazrO,@l/младший байт задержки R_sub: subi RazrO,l sbci Razrl,0 brcc R_sub .endni .def temp = rl6 /рабочая переменная .def RazrO = rl7 /разряды задержки .def Razrl = rl8 .def Counter = r20 /счетчик .org 0 /необязательно, просто для ориентировки /============ Программа =========== ldi temp,ObOOOOO100 /для второго разряда порта D out PORTD,temp /подтягивающий резистор на всякий случай ldi temp,Obllllllll /порт С все контакты на выход out DDRC,temp clr Counter /очищаем счетчик Pincykle: /цикл отслеживания кнопки sbis PinD,2 /пропустить, если нажата rjmp Pincykle /вернуться обратно, если не нажата /кнопка нажата - пауза push_pin: /кнопка нажата Delay 0, $19 /задержка 100 мкс с, N = $0019 sbic PinD,2 /пропустить, если по-прежнему нажата
156 Часть II. Программирование микроконтроллеров AVR на ассемблере rjmp Pincykle /вернуться обратно к отслеживанию Delay $61,$А8 /задержка 0,1 с, N = $61А8 Pin_release: /отслеживаем отпускание sbis PinD,2 /пропустить, если отпущена rjmp Pin_release /вернуться обратно, если еще нажата inc Counter /если отпущена, увеличиваем счетчик out PORTC,Counter /выводим счетчик в порт С /кнопка отпущена - пауза Delay $61,$А8 /задержка 0,1 с, N = $61А8 rjmp Pincykle /вернуться обратно к отслеживанию Как видите, программа (листинг 6.17) не больше предыдущей — в памяти она зай- мет 58 байтов. Работает она гораздо лучше: не дребезжит, если вы забыли вовремя снять палец, и на меньшее время «тормозит» контроллер— 0,2 с вместо целой секунды с лишним в предыдущем случае. Можно еще снизить эту величину, если подогнать паузы «по месту» — реальный дребезг, конечно, никогда не продлится целых 100 миллисекунд. Подробности Да, кстати, а почему мы тут не проверяем дважды состояние при отпускании? А пото- му, что никакая помеха не сможет пробиться на вывод, который накоротко замкнут с «землей». На «подтягивающий» резистор вполне может (особенно если вы забыли поставить внешний и обошлись внутренним), а если уж на замкнутой ранее кнопке вдруг образовался высокий уровень — это ее однозначно отпустили. Ради любопытства давайте проверим упрощенный Arduino-аналог второго варианта (листинг 6.18). byte count=0; void setup() { DDRC = Oxff; PORTD=0x04; } void loop() { if (digitalRead(2)!=1){ delayMicroseconds(10000) ; if (digitalRead(2)==l){ delayMicroseconds(10000); count++; PORTC=count; Здесь мы по возможности минимизировали код, не проверяя состояние дважды и не пользуясь штатными ардуиновскими функциями pinMode () и digitaiwrite () (иначе
Глава 6. Основы программирования MKAVR 157_ пришлось бы к тому же придумывать конвертер значения count в двоичное пред- ставление для вывода в отдельные биты порта С — выводы А0-А2, работающие в нашем случае как обычные цифровые порты). Не стали также возиться с собст- венной программной задержкой, использовав штатную deiayMicroseconds () (о ве- личине задержки 10 000 мкс— см. врезку «Подробности» далее). По идее deiayMicroseconds () является аналогом того же самого подхода с задержкой под- счетом тактов, никаких таймеров она не использует, так что мы тут ничем не по- грешили по сравнению с ассемблерной программой. Исключение составляет digitaiRead (), но можете заменить и ее тоже на непосредственное чтение PinD, от этого мало что изменится. А вот что важно: такая совсем несложная программа, вдвое меньше по объему текста, чем наша ассемблерная, тем не менее после ком- пиляции оказывается примерно в 20 раз объемнее и занимает в памяти 762 байта. Подробности ПОСМОТРИМ, ЧТО МЫ еще Тут ВЫИГраЛИ. Да, фуНКЦИЯ deiayMicroseconds О реализована не на прерываниях, а в виде простой программной задержки, — по идее полный ана- лог нашего подхода. Но! Во-первых, в комментариях на официальном сайте Arduino про эту функцию сказано, что «...наибольшее число, позволяющее сформировать точ- ную задержку — 16383». То есть более 16 мс она может работать кое-как, и проверить это трудно, почему мы и выбрали 10 000 мкс в своей программе. Но еще хуже она се- бя должна вести при очень малых задержках, т. к. оформлена в виде функции (макро- сов нам в С не предлагается). А вызов функции в Arduino — это совершенно отдель- ная песня, которая требует также отдельного обсуждения (если кому-то это интерес- но, можете поизучать вопрос путем дизассемблирования кода Arduino в AVR Studio). Наш же макрос будет безупречно работать при любых необходимых задержках: захо- тим — добавим в него еще регистр, получим секунды и десятки секунд, доведем число регистров до 4-х— получим уже часы, и т. д. Точно отмерить задержки порядка мик- росекунд и менее можно только так — даже в Arduino это делают с помощью inline- ассемблера. Другое дело, что при использовании прерываний и задержках более миллисекунд к такому способу прибегать нецелесообразно: прерывания внесут неоп- ределенность, если разместить задержку в главном цикле, или могут потеряться, если ее задействовать в каком-нибудь из обработчиков. В реальности в Arduino с кнопкой правильно обращаться именно так, как мы сейчас показали, может быть чуть усложненным способом, отслеживая несколько состоя- ний с небольшой паузой между ними. Я предлагаю вам самим попробовать разо- браться в коде, который получается, если подойти к этому делу грамотно (привожу без пояснений фрагмент Arduino-программы для одного из моих приборов). Выгля- дит это примерно так (листинг 6.19): boolean yes = false; byte i=0; const byte button = 2; кнопка while(!yes){ //проверяем кнопку i++; boolean statel = (digitaiRead(button)&HIGH); deiayMicroseconds(100); //задержка в 100 микросекунд yes=(statel & !digitaiRead(button));
158 Часть II. Программирование микроконтроллеров AVR на ассемблере if (i>4) break; //пробуем пять раз } if (yes) ... < выполнение кода далее> Если вы посчитаете, что этот текст понятнее, чем наша ассемблерная программа, то вам прямая дорога в С-программисты. Использование прерываний Теперь давайте попробуем сделать ту же самую программу «по-человечески» — а именно с использованием прерываний. Обычно контроллер не только следит за кнопками, а еще делает разные другие полезные операции, поэтому нехорошо за- нимать все его время пустыми циклами в ожидании, когда наконец кто-то соизво- лит нажать на кнопку. Хотя, отметим, пустые циклы, в которых МК больше ничего не делает, кроме отслеживания подобных крайне редких, с его точки зрения, собы- тий (даже за время ожидания пересылки байта через UART, занимающее при ско- рости 9600 бит/с около одной миллисекунды, контроллер успеет выполнить не- сколько тысяч команд), как раз представляют собой наименьшее зло, поскольку легко могут быть прерваны на время выполнения других процедур без опасности что-то потерять. Но в общем случае лучше, если мы будем стараться по возможно- сти освободить контроллер от бесцельного зацикливания в ожидании событий — при этом, например, невозможно задействовать режимы энергосбережения, о кото- рых пойдет речь в главе 14. Итак, вернемся к схеме и обратим внимание, что кнопка Кн1 у нас подсоединена к контакту PD2, который, если вы взглянете в описание модели ATmega8, одновре- менно служит входом внешнего прерывания into. Таким образом, чтобы ловить кнопку на отпускание, у нас вырисовывается следующая схема действий: сначала мы инициируем прерывание into по спаду и, определив таким образом, что кнопка была нажата, делаем небольшую паузу (2 мс), чтобы отфильтровать дребезг при нажатии. Затем опять разрешаем прерывание into, но уже по фронту, по возникно- вению прерывания определяем момент отпускания, производим нужные манипу- ляции со счетчиком, снова разрешаем прерывания по спаду, и все повторяется сна- чала. Программа счетчика с использованием прерываний Здесь нам придется развернуться «по полной программе», т. е. включить в состав программы таблицу прерываний, написать секцию инициализации (reset) и т. п. Таблицу прерываний мы включим полностью, т. к. для ATmega8 она невелика, а в дальнейшем, если потребуется, такой текст программы гораздо легче дорабатывать (с этой же целью я сохранил в тексте названия даже неиспользуемых прерываний). Учтите только, что предыдущие программы без использования прерываний (см. листинги 6.12-6.13), в целом годятся без переделок для любой модели МК AVR, имеющей порты D и С, а вот новую придется перед переносом внимательно просмотреть на предмет совпадения адресов прерываний и наименований регист-
Глава 6. Основы программирования MKAVR 159_ ров. Первые три строки в таблице прерываний (считая reset) одинаковые для всех контроллеров с памятью 8 Мбайт и менее, а вот дальше могут начинаться разно- чтения, в зависимости от наличия тех или иных устройств. Именно этим универ- сальный способ записи (с помощью директивы .org) удобнее прямого указания всей таблицы, но он менее наглядный и приспособленный для дальнейших дорабо- ток. Отметьте также, что, разумеется, именовать обработчики прерываний (в т. ч. и вектора сброса reset) можно любыми именами, необязательно использовать то, что приводится в описании, но мы ради единообразия сохраним фирменные имена. Код программы приведен в листинге 6.20. /Программа счета нажатий по прерыванию INTO .device ATmega8 .include Ifm8def.inc" /частота 1 МГц .def temp =rl6 .def RazrO = rl7 /разряды задержки .def Razrl = rl8 .def Counter = r20 /счетчик /============ прерывания ============ rjmp RESET /Reset Handle rjmp EXT_INT0 /External InterruptO Vector Address reti /rjmp EXT_INT1 /External Interruptl Vector Address reti /rjmp TIM2_COMP ; Timer2 Compare Handler reti /rjmp TIM2_OVF ; Timer2 Overflow Handler reti /rjmp TIM1_CAPT ; Timerl Capture Handler reti /TIM1_COMPA / Timerl CompareA Handler reti /rjmp TIM1_COMPB ; Timerl CompareB Handler reti /rjmp TIM1_OVF ; Timerl Overflow Handler reti /rjmp TIM0_OVF ; TimerO Overflow Handler reti /rjmp SPI_STC ; SPI Transfer Complete Handler reti /rjmp USART_RXC ; USART RX Complete Handler reti /rjmp USARTJJDRE ; UDR Empty Handler reti /rjmp USART_TXC ; USART TX Complete Handler reti /rjmp ADC ; ADC Conversion Complete Handler reti /rjmp EE_RDY ; EEPROM Ready Handler reti /rjmp ANA_COMP ; Analog Comparator Handler reti /rjmp TWSI ; Two-wire Serial Interface Handler reti /rjmp SPM_RDY ; Store Program Memory Ready Handler; .macro Delay /процедура задержки ldi Razrl,00 /старший байт N ldi RazrO,01/младший байт N R_sub: subi RazrO,1
160 Часть II. Программирование микроконтроллеров AVR на ассемблере sbci Razrl,0 brcc R_sub .endm EXT_INTO: ;кнопка нажата clr temp ;запрещаем прерывание INTO out GICR,temp Delay 1,$F4 ;задержка 2 мс, sbic PinD,2 ;пропустить, если нажата rjinp push_butt ; иначе ничего не делаем inc Counter ;если нажата, увеличиваем счетчик out PORTC,Counter ;выводим счетчик в порт С push_butt: ldi temp, (1«INTO) ;разрешаем прерывание INTO out GICR,temp reti /конец прерывания INTO Reset: ldi temp,low(RAMEND) ;устанавливаем указатель.на стек out SPL,temp ldi temp,high(RAMEND) out SPH,temp ldi temp,ObOOOOOlOO ;для второго разряда порта D out PORTD,temp ;подтягивающий резистор на всякий случай ldi temp,Obi1111111 ;порт С все контакты на выход out DDRC,temp clr Counter ;очищаем счетчик ldi temp, (l«ISC01) | (l«ISC00) ) ; npep. INTO по фронту out MCUCR,temp ldi temp, (1«INTO) ;разрешаем прерывание INTO out GICR,temp sei /разрешаем прерывания Gcykle: ;цикл отслеживания кнопки rjmp Gcykle /вернуться обратно, если не нажата Как видите, логика работы упростилась, но это не единственный выигрыш. Другой положительный момент заключается в том, что контроллер у нас основное время совершенно свободен. В прерывании по ручному нажатию имеется задержка в 2 мс, и с точки зрения контроллера эти события настолько редкие, что их можно не учи- тывать. Если вы при этом параллельно будете еще отслеживать какие-нибудь дат- чики, выводить данные на дисплей и передавать их по радиоканалу, то контроллер этого даже не заметит. И даже если вдруг какие-то другие действия случайно сов- падут с моментом нажатия таймера, и задержка продлится на миллисекунду боль- ше, чем запланировано, — велика ли беда? Причем задержка в 2 мс — это с боль- шим запасом, в расчете на самые плохие кнопки, обычно достаточно задержки
Глава 6. Основы программирования MKAVR 161 в пределах 100-300 микросекунд. А если вдруг даже такая задержка будет мешать, то всегда можно подключить к делу таймер и отсчитывать задержки с его помощью аппаратно, вообще не тормозя контроллер. Программа при этом получается слож- нее, потому мы здесь ее не приводим, т. к. на практике такой метод требуется не- часто (пример такой программы приведен в главе 12). Примечание Напоминаю, что все эти программы на счет по нажатию (count__push.asm и county pushjnt.asm), а также по отпусканию (count_release.asm и countjreleasejntasm) вы можете найти в архиве по адресу, указанному во введении. Наверняка читатель еще далеко не во всем разобрался, и в применении некоторых команд многое ему непонятно. В последующих главах мы рассмотрим многие во- просы подробнее, а сейчас пойдем дальше и посмотрим, что происходит с про- граммами после того, как вы их написали и откомпилировали. Сравнение ассемблерной программы с программами Arduino и другими языками высокого уровня Как писал классик программирования Дональд Кнут, «каждый, кто всерьез интере- суется компьютерами, должен рано или поздно изучить по крайней мере один ма- шинный язык». Ассемблер — это первично, а все остальное вторично. Но ассемб- лер обладает одним большим недостатком: программы на его основе получаются громоздкими и неудобочитаемыми. Это вызвано тем, что здесь любую операцию приходится разлагать на составляющие. Особенно это характерно для RISC- архитектур (к которым относится и AVR) — например, в системе команд AVR нет даже операции деления, да и операция умножения работает не во всех моделях. Никакой речи о том, чтобы следовать знаменитому лозунгу Дейкстры «программи- рование без goto», тут идти не может, т. к. ассемблерная программа, если можно так выразиться, состоит из сплошных goto— «лапши» условных и безусловных переходов, по выражению Дейкстры (он намекал на многочисленные линии ветв- ления, которые возникают в блок-схемах таких программ). Таким образом, для ра- боты на ассемблере требуется учить реализацию всех типовых приемов програм- мирования — различных циклов с условными и безусловными переходами, мате- матических операций, самостоятельно организовывать деление на локальные и глобальные переменные и т. п. Это и служит основным аргументом в пользу языков высокого уровня, где подобные реализации в большинстве случаев уже сделаны за вас. «Обычные» программисты, как огня, боятся замкнутых циклов, условие выхода из которых может не выполниться хотя бы теоретически. Бесконечное ожидание при- хода символа с клавиатуры или из СОМ-порта, в котором не предусмотрено ника- ких альтернативных средств выхода из цикла, повесит не только саму программу, но и всю систему, если речь идет об однозадачной DOS или «кооперативной много-
162 Часть II. Программирование микроконтроллеров AVR на ассемблере задачности» Windows Зле, доставляло немало неприятностей в «компромиссных» Windows 9х, и даже в настоящих многозадачных ОС, по крайней мере, заставит об- рывать работу программы принудительно. Потому в таких задачах программисты «на уровне подкорки» приучены применять средства, позволяющие выйти из тако- го цикла альтернативными методами: по таймеру или какими-то пользовательски- ми действиями (нажатием клавиши <Esc> или кнопки Cancel). Вся очередь задач в Windows, по сути, есть одно из таких решений, встроенное в систему изначально и направленное на то, чтобы не позволить компьютеру зациклиться в ожидании какого-то события. В программировании для МК подход иной. И в фирменных «аппнотах», и в приме- рах в тексте технических описаний контроллеров вы запросто можете встретить бесконечный цикл ожидания очистки какого-нибудь бита, и в теории, если этот бит никогда не очистится, МК так и «повиснет». ПК-программист от такой ситуации пришел бы в ужас, и в Arduino, в основном как раз программистами и создавав- шемся, такой подход тоже не афишируется. Конечно, есть средства вывести кон- троллер из этого состояния— наиболее кардинальным является использование сторожевого таймера, который в конце концов перезапустит систему. Более гра- мотно было бы заставить контроллер ожидать не самой по себе очистки бита, а свя- занного с этим прерывания (что позволит выполнять остальные функции без за- держек), но далеко не всегда это возможно — например, запись байта во встроен- ную EEPROM заставляет МК «висеть» несколько миллисекунд, пока этот процесс не закончится, но соответствующее прерывание в ряде моделей просто отсутствует, а если даже имеется, то задействовать на практике его неудобно из-за значительно- го усложнения логики построения программы. Однако если вдуматься, стоит задать себе вопрос: а так ли это страшно в случае контроллеров? Если по ходу дела требуется записать что-то в EEPROM, а она ока- зывается неисправной, то, очевидно, функциональность системы и так окажется нарушенной, и тут уже выходом из «зависания» делу не поможешь. В критичных случаях, разумеется, системы оснащают резервированием с возможностью автома- тического переключения между запасными модулями, но для обычных бытовых устройств принятие подобных мер только безосновательно удорожает изделие. Но если вы будете бояться подобных ситуаций, то это только хорошо: понимание, что вы делаете что-то не совсем так, как «надо бы», еще никому не мешало. Зато с ассемблером вы имеете все преимущества, о которых уже упоминалось: прежде всего это полное владение всеми системами контроллера с точным расче- том того, что происходит, когда, по каким причинам и чем закончится. Даже «чис- тый» С, как уже упоминалось, не может дать гарантии того, сколько именно про- длится та или иная процедура и сколько времени займет ее вызов. В Arduino, ис- пользующем теневые прерывания и Bootloader, эта неопределенность намного больше, что в значительной степени обесценивает, например, режимы энергосбе- режения. И кроме этого есть еще много всяких задач, где необходима лаконичность кода и точный расчет, которые без ассемблера провести было бы затруднительно.
ГЛАВА 7 Система команд AVR Познакомившись в предыдущей главе с простейшими программами для AVR, мы теперь попробуем рассмотреть систему команд AVR в целом, чтобы понять, какие возможности нам предоставляются. Всего для AVR насчитывается от 90 до 133 команд (в зависимости от контроллера), и их подробное описание в офици- альном PDF-справочнике [8] занимает 160 страниц. Потому все команды, которые к тому же часто взаимозаменяемы, мы описывать не станем, для этого существуют справочники. С некоторыми из тех команд, что выпадут из рассмотрения в этой главе, мы познакомимся по ходу дела в дальнейшем. Очень рекомендуется всегда иметь перед собой полный справочник по командам— лучше всего указанный официальный с подробными описаниями (в формате PDF или онлайн [8]), а не про- сто перечень команд, который приводится в конце каждого «даташита» на кон- кретные контроллеры. К тому же, в некоторых последних описаниях, выпущенных уже под лейблом Microchip, к составлению этих перечней отнеслись как-то ...легко- мысленно, если не сказать жестче. Обе версии официального справочника имеют очень удобную навигацию по командам, а отсутствие некоторых редких команд (в PDF-версии их всего 128) не должно вас смущать, потому что они относятся к тем контроллерам, которые вы никогда на ассемблере программировать не будете. В книгах Евстифеева [6,7] есть перевод на русский подробных описаний команд из официального справочника и, кроме того, удобные сводные таблицы команд, сгруппированных по выполняемым функциям с краткими комментариями на рус- ском языке к их использованию. Но перевод этот все-таки несколько сокращенный, и англоязычный оригинал остается основным пособием. Заметки на полях Как и в отношении главы 2, я уверен, что при первом прочтении вы из материала этой главы мало что усвоите. Но я все-таки советую прочесть ее внимательно в качестве занимательного чтения перед сном, а потом регулярно сюда возвращаться, когда вы в практических действиях столкнетесь с той или иной непонятной командой, — так ма- териал лучше запомнится. Обращу также ваше внимание на то, что большинство ко- манд устанавливают те или иные флаги в регистре sreg, что позволяет их применять для сокращения программ в нестандартном качестве. Примером может служит коман- да уменьшения на единицу dec, которую мы в главе 6 использовали одновременно в качестве детектора достижения нуля. Другой пример вы встретите в главе 9, где
164 Часть II. Программирование микроконтроллеров AVR на ассемблере команда логического И с константой andi будет применена для отсчета каждого чет- вертого прерывания. Такие подробности в этой главе про каждую команду привести невозможно — они есть только в английском оригинале справочника [8,12], и это еще одна причина всегда держать его под рукой. Обзор команд Конечно, мы не сможем и не будем пытаться описать все команды. Это и не очень нужно, во-первых, потому что свыше трети (а может, и поболее) команд — сино- нимы, т. е. делают одно и то же и даже имеют одинаковый КОП, и введены только для удобства и лучшей читаемости текста программы. Во-вторых, потому, что все команды никто никогда и не использует — у каждого программиста есть свой лю- бимый набор основных команд. А в-третьих, потому, что некоторые команды опи- сывать особо и не требуется— их применение понятно из употребления, а если нет — всегда можно обратиться к пособиям, упомянутым ранее. Необходимо отдельно отметить, что все операции с константами (idi, cpi, subi, andi, ori и т. п.) действуют только в пределах второй половины регистрового фай- ла, т. е. для РОН с номерами от г1б до г31. Поэтому во всех ассемблерных про- граммах стараются использовать под переменные именно эти регистры. Так как число их ограничено, а регистры, начиная с г24, еще и бывают задействованы в различных шестнадцатиразрядных операциях, то часто бывает необходимо при- влечь регистры из первой половины с номерами гО-г15. При этом нужно либо про- следить, что операции с константами с ними не проводились, либо использовать промежуточные операции с передачей значения констант временным регистрам из старших. Так, пара команд: mov rl6,10 ср г5,г16 равносильна недопустимой операции cpi г5,ю. Команды передачи управления и регистр SREG В языках высокого уровня была всего одна команда перехода на метку (goto), и то Дейкстра на нее набросился. А в ассемблере AVR таких команд — пруд пруди, бо- лее 30! Зачем? На самом деле без доброй половины из них, если не больше, можно обойтись во всех жизненных случаях, т. к. они в значительной степени взаимозаме- няемы, а немалая часть из них просто синонимы. Разнообразие это, если угодно, дань памяти великому программисту Эдсгеру Дейкстре — для повышения читае- мости программ. Мы рассмотрим только ключевые команды из этого перечня. С командами безусловного перехода г jmp и jmp, а также вызова подпрограмм rcaii и call мы уже достаточно подробно познакомились в предыдущей главе, так что только напомним несколько моментов. Четырехбайтовые команды jmp и call пред- назначены для контроллеров с памятью программ более 8 К, и AVR с меньшим объемом памяти их просто не поддерживают. Потому в дальнейшем в этой книге вы встретите только г jmp и rcaii. Вызовы подпрограмм rcaii и call — это те же
Глава 7. Система команд AVR 765 самые безусловные переходы на метку, но отличаются от команд безусловного пе- рехода тем, что в момент перехода к процедуре контроллер автоматически сохра- няет в стеке адрес текущей команды, чтобы потом знать, куда вернуться (потому длительность выполнения этих команд на такт больше, чем для простого перехо- да, — об этом рассказано далее). А как МК «узнает», когда именно нужно возвра- щаться? Для этого каждая процедура-подпрограмма заканчивается специальным образом — не отличаясь сначала ничем от любого другого участка программного кода, обозначенного меткой, в месте возврата она содержит команду ret (от return, возврат). По этой команде МК извлекает из стека сохраненное содержимое счетчика команд и продолжает выполнение прерванной основной программы. Подробности Кстати, хочу обратить ваше внимание, что никто не запрещает записать команду rjmp о, передав тем самым управление самой первой команде программы, находящейся по нулевому адресу. Вы уже сообразили, что это полный аналог того, что называется программной («горячей») перезагрузкой, — запуска программы на выполнение заново. В отличие от аппаратного сброса Reset (по включению питания, внешнему импульсу или по срабатыванию сторожевого таймера), программная перезагрузка, во-первых, происходит без задержек, во-вторых, сохраняет все регистры (и РОЙ, и РВВ), а также память SRAM, в неприкосновенности — в том состоянии, в котором их застал момент перехода. Я с трудом могу себе представить случай, когда такой прием может пона- добиться в простой однозадачной системе, — возможно, как простой вариант выхода из ситуации с недопустимым сочетанием каких-то условий (в результате, например, ошибочных действий пользователя). В обычных случаях (при зависании программы, например) все равно выручает только аппаратная перезагрузка. Но возможность эту надо иметь в виду. Аналогично обрабатываются прерывания — только специальной команды, как вы знаете, там нет, вызов производится обычным переходом rjmp (или jmp), но по- скольку он осуществляется с определенного адреса (там, где стоит вектор прерыва- ния), то контроллер делает то же самое— сохраняет в стеке адрес командного счетчика, на котором выполнение основной программы было грубо нарушено, начинает выполнять прерывание и ожидает команды возврата — только здесь она записывается как reti (return interrupt) — в отличие от ret, эта команда еще и вос- станавливает состояние флага прерываний i в регистре sreg. Укажем еще, что простой переход rjmp выполняется за 2 такта (jmp за 3), вызов rcaii — за 3 такта (call за 4), во