Текст
                    Л1ЭКА
eca.ru
ПРОГРАММИРУЕМЫЕ СИСТЕМЫ
необходимо знать
микроконтроллеры
Springer
Сид Катцен

THE QUITESSENTIAL PIC® MICROCONTROLLER Second Edition
ПРОГРАММИРУЕМЫЕ СИСТЕМЫ С. Катцен Р1С-МИКР0К0НТР0ЛЛЕРЫ Все, что вам необходимо знать Перевод с английского ОАЭКА Москва Издательский дом «Додэка-ХХ1» 2008
УДК 004.312.46 ББК 32.973.26-04 К29 Катцен, Сид К29 PIC-микроконтроллеры. Все, что вам необходимо знать / С. Катцен; пер. с англ. Евстифеева А. В. — М.: Додэка-ХХ1, 2008. — 656 с.: ил. (Се- рия «Программируемые системы»). ISBN 978-5-94120-134-1 Данная книга представляет собой исчерпывающее руководство по микроконтроллерам семейства PIC компании Microchip, являющегося промышленным стандартом в области встраиваемых цифровых устройств. В книге подробно описывается архитектура и система команд 8-битных микроконтроллеров PIC, на конкретных примерах изучается работа их пе- риферийных модулей. В первой части излагаются основы цифровой схемотехники, математической логики и архитектуры вычислительных систем. Вторая часть посвящена различным аспектам про- граммирования PIC-микроконтроллеров среднего уровня: описывается набор команд, рас- сматривается написание программ на ассемблере и языке высокого уровня (Си), а также поддержка подпрограмм и прерываний. В третьей части изучаются аппаратные аспекты вза- имодействия микроконтроллера с окружающим миром и обработки прерываний. Рассмат- риваются такие вопросы, как параллельный и последовательный ввод/вывод данных, вре- менные соотношения, обработка аналоговых сигналов и использование EEPROM. В заклю- чение приводится пример разработки реального устройства. На этом примере также демонстрируются простейшие методики отладки и тестирования, применяемые при разра- ботке реальных устройств. Книга рассчитана на самый широкий круг читателей — от любителей до инженеров, при этом для понимания содержащегося в ней материала вовсе не требуется каких-то специаль- ных знаний в области программирования, электроники или цифровой схемотехники. Эта книга будет также полезна студентам, обучающимся по специальностям «Радиоэлектрони- ка» и «Вычислительная техника», которые смогут использовать ее в качестве учебного посо- бия при прослушивании соответствующих курсов или выполнении курсовых проектов. УДК 004.312.46 ББК 32.973.26-04 Translation from the English language edition: The Quintessential PIC® Microcontroller By Sid Katzen Copyright © Springer-Verlag London Ltd, being a part of Springer Science+Business Media All Rights Reserved ISBN 978-5-94120-134-1 (pyc.) © Springer-Xferlag London Limited, 2005 © Издательский дом «Додэка-XXI», 2008
ОГЛАВЛЕНИЕ Часть I. Основы ........................................................13 Глава 1. Цифровое представление.........................................16 Глава 2. Логические схемы...............................................30 Глава 3. Обработка хранимой программы ..................................57 Центральный процессор................................................60 Память...............................................................60 Интерфейсные порты...................................................61 Шина данных..........................................................62 Счетчик команд.......................................................63 Конвейер.............................................................64 Дешифратор команд....................................................65 Регистр адреса.......................................................65 Регистр данных.......................................................65 Арифметико-логическое устройство.....................................66 Регистр состояния....................................................66 Рабочий регистр......................................................66 Память программ......................................................66 Память данных........................................................66 Прямая адресация регистра данных.....................................68 Операции с константами...............................................69 Примеры..............................................................80 Вопросы для самопроверки.............................................84 Часть II. Программное обеспечение ......................................85 Глава 4. Микроконтроллер PIC16F84.......................................87 Блок выборки.........................................................90 Исполнительный блок..................................................93 Примеры..............................................................108 Вопросы для самопроверки.............................................112 Глава 5. Набор команд ..................................................114 Адресация кодом команды..............................................116
6 Оглавление Адресация константы.................................................116 Абсолютная адресация памяти программ................................117 Прямая адресация памяти данных......................................118 Косвенная адресация памяти данных................................. 123 Битовая адресация...................................................128 Команды пересылки данных............................................128 Команды арифметических операций.....................................131 Команды логических операций и операций сдвига.......................141 Команды передачи управления.........................................152 Примеры.............................................................156 Вопросы для самопроверки............................................165 Глава 6. Подпрограммы и модули ........................................168 Примеры.............................................................193 Вопросы для самопроверки............................................204 Глава 7. Обработка прерываний .........................................207 Примеры.............................................................224 Вопросы для самопроверки.......................................... 235 Глава 8. Инструментальные средства для работы с языком ассемблера .....238 Примеры.............................................................268 Вопросы для самопроверки............................................272 Глава 9. Язык высокого уровня..........................................275 Примеры.............................................................290 Вопросы для самопроверки............................................298 Часть III. Окружающий мир..............................................299 Глава 10. Реальное окружение...........................................302 Примеры.............................................................322 Вопросы для самопроверки............................................324 Глава 11. Ничего, кроме байтов ........................................325 Примеры.............................................................352 Вопросы для самопроверки............................................366 Глава 12. Ох уж эти биты! .............................................368 Примеры.............................................................435 Глава 13. Главное — время .............................................450 Примеры............................................................ 479 Вопросы для самопроверки............................................486 Глава 14. Этот безумный аналоговый мир.................................488 Примеры.............................................................527 Вопросы для самопроверки............................................540 Глава 15. Хранить вечно! ..............................................542 Примеры.............................................................559 Вопросы для самопроверки............................................569
Оглавление 7 Глава 16. Дальнейшее развитие...........................................571 Блок выборки.........................................................572 Исполнительный блок..................................................575 Периферийные устройства..............................................581 Обработка прерываний.................................................583 Система команд.......................................................584 Глава 17. Учебный пример.............................................. 595 Конфигурирование кристалла...........................................607 Выполнение программы................................................ 607 Приложение А. Список сокращений, символических имен и аббревиатур ......618 1. Русская нотация .. J..............................................618 2. Английская нотация................................................619 Приложение Б. Регистры специального назначения микроконтроллеров PIC16F87XA .. 632 Приложение В. Элементы языка Си ........................................635 Приложение Г. Набор команд микроконтроллеров с 14-битным ядром .........637 Предметный указатель....................................................639
Предисловие ко второму изданию Поводом к выпуску второго издания данной книги стадо большое количество предложений и замечаний от моих студентов и читателей из разных уголков зем- ного шара — от Шотландии до Гавайских островов. Со времени выхода первого издания книги в конце 1990-х микроконтроллеры PIC компании Microchip стали самыми продаваемыми 8-битными микроконтроллерами. Возможности моделей среднего уровня, рассматривавшихся в первом издании, значительно возросли, так что некоторые из использовавшихся ранее моделей безнадежно устарели. Кроме того, значительно увеличилось количество моделей с 16-битным словом команд. В то же время появились новые представители линейки микроконтрол- леров младшего (или базового) уровня. Поскольку все выпускаемые линейки микроконтроллеров имеют очень много общего, в новом издании основное вни- мание будет по-прежнему уделяться микроконтроллерам среднего уровня. Практически все рисунки были изменены, причем многие довольно сущест- венно; было добавлено множество новых иллюстраций. При переработке книги особое внимание уделялось ясности изложения базовых концепций. По этой причине, а также для улучшения связи с 4-й и 5-й главами третья глава была зна- чительно переработана. К слову сказать, в обеих упомянутых главах от первона- чального текста вообще практически ничего не осталось. Также с целью подроб- ного разъяснения сложных для понимания вопросов была существенно перера- ботана глава 7, посвященная обработке прерываний. Третья часть книги была не только обновлена в связи с использованием современных моделей микроконт- роллеров, но и расширена, с тем чтобы охватить новые периферийные модули, такие как аналоговый компаратор и встроенный источник опорного напряжения. Кроме того, была добавлена глава, знакомящая читателя с линейкой наиболее развитых микроконтроллеров PIC18XXX. Все главы книги, за исключением двух первых и последней, снабжены рабо- чими примерами, а также вопросами для самопроверки. Кроме того, к вашим ус- лугам имеется сайт1} http://www.engj.ulst.ac.uk/sidk/quintessential, '’Этот сайт посвящен оригинальному изданию книги на английском языке, и все перечис- ленные ниже материалы представлены также на английском. — Примеч. ред.
Предисловие ко второму изданию 9 на котором вы сможете найти: • Ответы к вопросам для самопроверки. • Дополнительные вопросы для самопроверки. • Дополнительные материалы. • Исходные тексты всех примеров и задач, встречающихся в книге. • Ссылки на инструментальные средства разработки, а также на документа- цию к микросхемам, упоминающимся в книге. • Список опечаток. • Отзывы читателей. Рукопись книги1} набиралась автором на различных ПК, работающих под уп- равлением Microsoft® Windows™ с использованием среды LATeX2e (реализация Y&Y) и шрифта Lucida Bright. Векторные иллюстрации были созданы или отре- дактированы в программе Autocad R13 и внедрены в файл рукописи в виде EPS- файлов. Все фотографии были сделаны самим автором при помощи различных цифровых фотоаппаратов фирмы Olympus, кстати, битком набитых микроконт- роллерами! Надеюсь, что мне удалось изгнать из рукописи всех гремлинов, однако, если вы все же найдете ошибки или у вас возникнут какие-либо предложения, я буду рад, если вы свяжетесь со мной через сайт. Сид Катцен Ольстерский университет, Джорданстаун Июль, 2005 г. ” Имеется в виду оригинальное издание книги на английском языке. — Примеч. ред.
Предисловие к первому изданию Микропроцессоры и производные от них — микроконтроллеры — являются широко распространенным и при этом незаметным элементом инфраструктуры современного общества, основанного на электронике и коммуникациях. Иссле- дования1), проведенные в 1998 году, показали, что в каждом доме незаметно для нас «живет» около 100 микроконтроллеров и микропроцессоров. Они присут- ствуют буквально всюду: в звуковых открытках, стиральных машинах, микровол- новых печах, телевизорах, телефонах, персональных компьютерах и разных дру- гих устройствах. Даже в самом обыкновенном автомобиле скрывается более двад- цати таких элементов, где они, в частности, контролируют состояние беспроводных датчиков давления в шинах и отображают критичные данные, по- лучаемые по сети CAN. Каждый год продается около четырех миллиардов подобных изделий, предна- значенных для реализации «мозгов» разнообразных «умных» устройств, начиная от интеллектуальных таймеров для яйцеварок и заканчивая системами управле- ния самолетом. Эволюция микропроцессоров, первые из которых были выпуще- ны компанией Intel в далеком 1971 году, привела к коренному изменению струк- туры общества, спровоцировав в начале XXI века вторую промышленную рево- люцию. Несмотря на то что микропроцессоры, являясь основным компонентом вездесущих ПК, известны лучше, объем продаж различных микропроцессоров, таких как Intel Pentium, составляет всего около 2% от общего объема продаж по- добных устройств. Подавляющее же большинство продаж приходится на деше- вые микроконтроллеры, встраиваемые в специализированные электронные уст- ройства, такие как смарт-карты. Причем если основной задачей микропроцессо- ров является обеспечение собственно вычислительной мощности, то во втором случае акцент смещается в сторону объединения на одном кристалле центрально- го процессора, памяти и устройств ввода/вывода. Такая интегрированная вычис- лительная система называется микроконтроллером. Задумывая книгу по этой тематике, автор ставил перед собой задачу дать чита- телю базовые знания в области разработки небольших встроенных систем на базе микроконтроллеров, а не просто рассказать об архитектуре ЭВМ в традиционном ” New Scientist, vol.59, no. 2141,4 July 1998, p.139.
Предисловие к первому изданию 11 понимании этого слова на примере микроконтроллеров. Будем надеяться, что подобный подход даст читателю уверенность в том, что даже на таком начальном уровне он сможет разработать, изготовить и запрограммировать полностью гото- вую рабочую встроенную систему. Учитывая практический характер излагаемого материала, для его иллюстра- ции используется реально существующее аппаратное и программное обеспече- ние. Основную долю на рынке занимают устройства, оперирующие 8-битными данными (хотя имеются как 4-, так и 16-битные устройства), во многом схожие с первыми микропроцессорами и кардинальным образом отличающиеся от совре- менной «тяжелой артиллерии» в лице микропроцессоров Intel Pentium и Power PC. В отличие от последних, сущностью микроконтроллера является высокая степень системной интеграции при низкой стоимбсти. Суммарная вычислитель- ная мощность системы может быть увеличена путем распределения процессоров по системе. Так, в каждом сочленении манипулятора робота может использовать- ся свой микроконтроллер, выполняющий простые локальные операции и обме- нивающийся данными с более мощным процессором,‘определяющим функцио- нирование всего робота. При выборе конечной архитектуры принимались во внимание ее популяр- ность на коммерческом рынке, доступность и наличие недорогого ПО для разра- ботки. В итоге выбор был сделан в пользу микроконтроллеров фирмы Microchip— одного из наиболее популярных семейств, использующихся при изучении микроконтроллеров/микропроцессоров на самых разных этапах учеб- ного процесса, начиная со старших классов школы и заканчивая университе- том. Освоение микроконтроллеров этой фирмы, в частности, облегчается не- большим набором команд и относительно простой передовой архитектурой. По- мимо использования в промышленности и образовательном процессе, микроконтроллеры семейства PIC® применяются в большинстве любительских устройств, в чем можно убедиться, открыв любой журнал, посвященный радио- любительству. Компания Microchip Inc. — относительно молодой игрок на рынке микро- контроллеров, на который она вышла в 1989 году после разработки нового се- мейства микроконтроллеров с гарвардской архитектурой. К концу 1999 года ком- пания Microchip была уже вторым по величине производителем 8-битных микро- контроллеров, уступая только компании Motorola. Книга, которую вы держите в руках, состоит из трех частей. В первой части излагаются основы цифровой схемотехники, математической логики и архитек- туры вычислительных систем. Приведенных сведений будет достаточно для по- нимания вопросов, рассматриваемых в остальных двух частях книги. Наличие в книге информации такого рода позволяет обойтись без изучения дополнитель- ной литературы. Вторая часть книги посвящена главным образом различным аспектам про- граммирования PIC-микроконтроллеров среднего уровня: набор команд, напи- сание, программ на ассемблере и языке высокого уровня (Си), поддержка подпро- грамм и прерываний. Несмотря на то что при изложении материала используется
12 Предисловие к первому изданию линейка 14-битных моделей, рассмотренные принципы и архитектура справедли- вы как для 12-битных, так и для 16-битных0 представителей семейства. В третьей части изучаются аппаратные аспекты взаимодействия микроконт- роллера с окружающим миром, а также обработки прерываний. Разумеется, па- раллельно продолжается изучение аппаратных и программных средств микро- контроллера. Рассматриваются такие вопросы, как параллельный и последова- тельный ввод/вывод данных, формирование сигналов и измерение их временных параметров, обработка аналоговых сигналов и использование EEPROM. В заклю- чение рассматривается процесс разработки реального устройства, позволяющий объединить разрозненные знания, полученные при чтении книги, в одно целое. На этом примере также демонстрируются простейшие методики отладки и тести- рования, применяемые при разработке реальных устройств. Сид Катцен Ольстерский университет, Джорданстаун Декабрь, 2000 г. |; Здесь имеется в виду не размер данных, которыми оперирует микроконтроллер, а число битов, использующихся для записи слова команды. — Примеч. пер.
ЧАСТЬ 1 ОСНОВЫ Diaea 1. Цифровое представление Diaea 2. Логические схемы Diaea 3. Обработка хранимой программы
14 Часть I. Основы Эта книга посвящена микроконтроллерам. Микроконтроллеры представляют собой цифровые устройства, построенные по образу и подобию ЭВМ с хранимой программой и объединенные вместе со вспомогательными узлами, памятью раз- личного типа и блоками сопряжения в микросхемах сверхвысокой степени интег- рации. Хотя, говоря о микроконтроллерах, часто имеют в виду их более извест- ных «двоюродных братьев» — микропроцессоры, которые являются важнейшим узлом персональных компьютеров, подавляющее большинство как микроконт- роллеров, так и микропроцессоров, помимо ПК, используются и во многих дру- гих электронных устройствах. Первые микропроцессоры, появившиеся на рынке в начале 70-х, позиционировались в качестве альтернативного способа реализа- ции цифровых схем. Выполняемые функции определялись последовательностью инструкций, хранящихся в виде двоичных чисел в постоянном запоминающем устройстве (ПЗУ). Это решение обладало большей гибкостью по сравнению с традиционной схемой соединения различных микросхем. Современный микро- контроллер является одним из воплощений такого интегрированного вычис- лителя. Использованию встраиваемых микроконтроллеров в контексте собственно цифровых вычислений посвящены 2-я и 3-я части книги. Пока же нам требуется заложить фундамент для понимания этого материала. Итак, в первой части мы с вами рассмотрим: • Цифровые коды. • Двоичную арифметику. • Основы цифровой схемотехники. • Архитектуру вычислительных устройств и их программирование. Разумеется, мы не сможем в полной мере охватить все указанные вопросы, однако существует много других превосходных книг1) по этой тематике, с по- мощью которых вы сможете продолжить изучение на более глубоком уровне. ! ) См., например: Рональд Дж. Точчи, Нил С.Уидмер. Цифровые системы. Теория и прак- тика: 8-е изд.: Пер. с англ. — М.: Издательский дом «Вильямс», 2004.
Часть I. Основы 15 Заглядывая внутрь микросхемы
ГЛАВА ЦИФРОВОЕ ПРЕДСТАВЛЕНИЕ Как для компьютера, так и для микроконтроллера окружающий мир пред- ставляется в виде различных чисел. В десятичной системе счисления числовые ве- личины описываются с помощью десяти цифр: 0, 1,..., 9. Используя при необхо- димости символы «+», «—» и можно выразить любое число из диапазона ±оо. На самом деле, с помощью чисел можно выражать даже нечисловые понятия. К примеру, в коде ASCII (американский стандартный код обмена информацией) символу «А» соответствует число 65, символу «В» — 66,..., «Z» — 90, «а» — 97, «Ь» — 98,..., «z» — 122 и т.д. Соответственно, слово «Microcontroller» можно зако- дировать в виде последовательности чисел «77, 105, 99, 114, 111, 99, 111, 110, 116, 114, 111, 108, 108, 101, 114». При условии, что нам известен контекст, т.е. какие числа описывают реальные числовые величины, а какие — текст, с их помощью можно закодировать практически любые символы* 2). Электронные схемы не очень хорошо подходят для хранения и обработки мно- жества различных значений. Да, первая американская цифровая вычислительная машина ENIAC (электронный цифровой интегратор и калькулятор), созданная в 1964 году, выполняла арифметические операции в десятичном виде3), однако все компьютеры, появившиеся впоследствии, оперировали уже данными в двоичной (с основанием 2) системе. В действительности десятичная система счисления удобна только для человека, поскольку у нас на руках 10 пальцев4). Так что в этой главе мы будем рассматривать исключительно свойства двоичных разрядов, их группирова- ние, а также операции над двоичными числами. Прочитав главу, вы: • Поймете, почему двоичное представление данных является наиболее удоб- ным для цифровых схем. • Узнаете, как одну и ту же величину можно выразить в двоичном, шестнад- цатеричном и двоично-десятичном (BCD) виде. ° В данной книге для отделения целой части числа от дробной используется точка, а не запятая. — Примеч. ред. 2) Разумеется, существует множество других цифровых кодировок, к примеру 6-точечный код Брайля для слепых. 3) Как и механическое вычислительное устройство Бэббиджа, появившееся столетием раньше. 4) И десять пальцев на ногах, однако система счисления по основанию 20 используется очень редко (но все-таки она существует).
Глава 1. Цифровое представление 17 • Научитесь выполнять сложение и вычитание двоичных чисел. • Узнаете, как выполнять умножение посредством сдвига влево. • Узнаете, как выполнять деление посредством сдвига вправо с копированием знакового бита. • Познакомитесь с логическими операциями НЕ, И, ИЛИ и Исключающее ИЛИ. В основе информационных технологий лежит обработка, вычисление и пере- дача информации, представленной в цифровом виде. Эта информация в подавля- ющем большинстве случаев представлена в виде множества двоичных разрядов (битовХ)). Как правило, такая обработка осуществляется с использованием мик- ропроцессоров* 2) и микроконтроллеров. Интересно отметить, что вычислительная мощность современной звуковой открытки превышает совокупную мощность всех вычислительных устройств, имевшихся на планете в 1950 году! Двоичная система — это универсальный способ представления данных, пос- кольку простейшим устройством, которое можно реализовать на одном транзисто- ре, является электронный ключ. Такие ключи, имеющие только два состояния, очень малы; они способны очень быстро изменять свое состояние и потребляют незначительный ток. Более того, поскольку требуется различать только два состо- яния, очевидно, что двоичное представление менее подвержено воздействию по- мех. Из сказанного становится ясно, что и плотность компоновки элементов на кристалле, и скорости переключения этих элементов могут достигать очень боль- ших значений. Хотя сам ключ как таковой не обладает какой-либо вычислитель- ной мощностью, 5 миллионов ключей, переключающихся 100 миллионов раз в се- кунду, способны продемонстрировать, по крайней мере, видимость интеллекта! Два состояния бита обычно называются логическим нулем (лог. 0) алогической еди- ницей (лог. 1) или просто 0 и 1. Один бит может быть представлен двумя состояниями любой физической величины, например напряжения или силы электрического тока, освещенности, давления воздуха. В большинстве микроконтроллеров состоянию лог. 0 соответствует напряжение 0 В (или «земля»), а состоянию лог. 1 — напряжение +3...5 В, хотя это правило и не универсально. Например, в последовательном порту RS-232 вашего ПК для индикации состояния лог. 0 используется напряжение +12 В, а для индикации состояния лог. 1 — напряжение -12 В. Итак, один бит может представлять только два состояния. Более сложные эле- менты можно выразить с помощью комбинаций битов. Например, обычные ал- фавитно-цифровые символы3) можно представить с помощью 7-битных групп ’) Не думайте, что двоичная система была придумана специально для цифровых вычис- лительных машин! Многие древние культуры пользовались двоичным счетом, например хараппская цивилизация, существовавшая более 4000 лет назад в бассейне реки Инд. В разва- линах одного из кварталов хараппского города Мохенджо-Даро был найден набор каменных гирь, веса которых подчинялись соотношению 1, 1, 2, 4, 8, 16, ..., т.е. вес каждой гири был равен удвоенному весу предыдущей (вес самой маленькой гири был равен примерно 25 г, или одной унции). Таким образом, веса этих камней выражались числами, являющимися степе- нями двойки, т.е. в двоичном коде. 2) Микропроцессоры и микроконтроллеры очень тесно связаны друг с другом (см. Рис. 3.8 на стр. 78), поэтому мы попеременно будем использовать оба термина. 3) Имеется в виду английский алфавит. — Примеч. пер.
18 Часть I. Основы двоичных разрядов, как показано в Табл. 1.1. Таким образом, ASCII-представле- ние строки «Microcontroller» будет иметь вид 1001101 1101001 1100011 1110010 1101111 1100011 1101111 1101110 1110100 1110010 1101111 1101100 1101100 1100101 1110010 В кодировке Юникод (Unicode), являющейся дальнейшим развитием коди- ровки ASCII, используются уже 16-битные группы, поэтому с ее помощью можно выразить символы всех существующих языков, а также различные математичес- кие и прочие специальные символы. Таблица 1.1. 7-битные символы ASCII Ст. полубайт — Мл. полубайт h’00’ b’000’ h’01’ Ь’ООГ h’02’ b’010’ h’03’ b’011’ h’04’ b’100’ h’05’ Ь’ЮГ h’06’ b’110’ h’07’ b’lll’ Ь’00’ b’0000’ NUL DLE SP 0 @ P P Ь’ОГ b’0001’ SOH XON I 1 A Q a q h’02’ b’0010’ STX DC2 2 В R b r h’03’ b’0011’ ETX XOFF # 3 C S c s h’04’ b’0100’ EOT DC4 $ 4 D T d t h’05’ b’0101’ ENQ NAK % 5 E и e u h’06’ b’0110’ ACK SYN & 6 F V f V h’07’ b’Olir BEL ETB 7 G w g w h’08’ b’1000’ BS CAN ( 8 H X h X h’09’ Ь’ЮОГ HT EM ) 9 I Y i У h’OA’ b’1010’ LF SUB * J Z j z h’Ob’ b’lOir VT ESC + J К 1 k { h’OC’ b’1100’ FF FS у < L \ 1 1 h’OD’ b’1101’ CR GS - = M ] m 1 h’OE’ b’1110’ SO RS > N A n h’OF’ b’llir SI US / 7 0 0 DEL Код ASCII называется невзвешенным, поскольку отдельные биты не несут ка- кого-либо смысла; значение имеет только вся совокупность битов. В качестве других примеров невзвешенных кодов можно отметить код значения на гранях игральной кости и семисегментный код, изображенный на Рис. 6.8 (стр. 183). Мы же в основном будем работать с обычным двоичным взвешенным кодом, в котором позиция бита определяет его величину или, иначе, вес. В целом двоичном числе самый правый бит имеет вес 2° = 1, находящийся слева от него — 21 = 2 и так да- лее до л-й позиции, бит в которой имеет вес 2" " \ В частности, десятичное число 1998 представляется таким образом: 103 102 101 10° 19 9 8
Глава 1. Цифровое представление 19 т.е. 1 х 103 + 9 х 102 + 9 х 101 + 8 х 10°, или 1998. В обычном двоичном коде то же самое число представляется следующим образом: 210 29 28 27 26 25 24 23 22 21 2° 1 11110 0 1110 т.е. 1 х 210 + 1 х 29+ 1 х 28 + 1 х 27 + 1 х 26 + 0 х 25 + 0 х 24 + 1 х 23 + 1 х 22 + 1 х 21 + + 0x2°, или b’l 111 1100111О’!). Точно так же можно представлять и дробные чис- ла, при этом позициям, расположенным справа от десятичной точки, соответ- ствуют отрицательные степени двойки. Так, двоичное число b’l 101.11’ эквива- лентно десятичному 13.75. Из примера видно, что двоичное представление чисел гораздо длиннее их десятичных эквивалентов — в среднем не менее чем в 3 раза. Однако 2-позиционный ключ гораздо проще 10-позиционного, поэтому двоич- ное представление предпочтительнее. Биты любой л-разрядной двоичной последовательности могут образовывать в общей сложности 2" комбинаций. При этом большинство компьютеров хранят и обрабатывают биты группами. Например, первый микропроцессор Intel 4004 обра- батывал данные по четыре бита (полубайт) за раз. Большинство современных про- цессоров оперируют с 8-битными (байт), 16-битными (слово), 32-битными (двой- ное слово) и 64-битными (счетверенное слово) блоками. Характеристики некоторых из указанных групп перечислены в Табл. 1.2. Приведенные названия являются в ка- кой-то мере стандартом де-факто, однако иногда встречаются и другие варианты. Как и в десятичной системе счисления, большие двоичные числа часто выра- жаются с использованием приставок К (кило), М (мега) и Г (гига). В двоичной системе приставка «кило» соответствует множителю 210, например 64 Кбайт (или КБ) памяти. Аналогично, приставка «мега» соответствует множителю 220= 1 048 576, например дискета объемом 1.44 Мбайт (или МБ). Точно так же емкость 20 Гбайт (или ГБ) винчестера составляет 2Ох2зо= 21 474 836 480 байт. Естественно, 1-й вариант записи предпочтительнее. Таблица 1.2. Наиболее распространенные группировки битов Тип Число битов Десятичное значение Двоичное значение Бит 1 0,1 0,1 Полубайт 4 0...15 0000...1111 Байт 8 0...255 00000000...1111 1111 Слово 16 0...65 535 0000 0000 0000 0000...1111 1111 1111 1111 Двойное слово 32 0...4 294 967 295 0000 0000 0000 0000 0000 0000 0000 0000... ...11111111111111111111111111111111 Длинные двоичные числа очень неудобны для человеческого восприятия. В Табл. 1.2 двоичные числа специально были разбиты на 4-битные группы, чтобы их удобнее было читать. Предположим теперь, что адрес какого-либо элемента в памяти равен Ь’ 1000 1100 0001 0100 0000 1010’. Если каждой комбинации из четы- Запись вида Ь’...’ не универсальна, часто используются и другие варианты нотации, например (1111011110)2. Если основание очевидно (известно из контекста), признак основания может опускаться.
20 Часть I. Основы рех битов сопоставить свой символ (0...9 и A...F, как показано в Табл. 1.3), то этот адрес можно будет записать в виде h’SCMOA’^, что гораздо удобнее. Этот код на- зывается шестнадцатеричным, поскольку для обозначения разрядов в нем ис- пользуется 16 символов. Шестнадцатеричные числа (числа с основанием 16) — это вполне жизнеспособные самостоятельные числа, а не просто какое-то допол- нительное представление двоичных чисел. Разряды шестнадцатеричного числа имеют веса соответственно 16°, 161, 162,..., 16"2). Двоично-десятичный код (Binary-Coded Decimal — BCD) является гибридом двоичного и десятичного представлений, широко используемым при работе с пор- тами ввода/вывода цифровых устройств (см. Пример 11.5 на стр. 360). При таком представлении каждый десятичный разряд заменяется своим двоичным эквива- лентом. Так, число 1998 записывается в виде (0001 1001 1001 1000)BCD. Это пред- ставление очень сильно отличается от эквивалентного обычного двоичного кода, несмотря на то, что при его записи тоже используются только нули и единицы. Как и следовало ожидать, выполнение арифметических операций с числами, записан- ными таким образом, представляет собой не простую задачу. Поэтому, как правило, на входе системы BCD-числа преобразовываются в обыкновенные двоичные чис- ла, а после обработки преобразовываются обратно (см. Программу 5.7 на стр. 159). Таблица 1.3. Различные формы записи чисел от 0 до 20 Десятичная система Двоичная система Шестнадцатеричная система Двоично-десятичный код 00 00000 00 0000 0000 01 00001 01 0000 0001 02 00010 02 0000 0010 03 00011 03 0000 ООН 04 00100 04 0000 0100 05 00101 05 0000 0101 06 00110 06 0000 0110 07 00111 07 0000 0111 08 01000 08 0000 1000 09 01001 09 0000 1001 10 01010 0А 0001 0000 11 01011 ОВ 0001 0001 12 01100 ОС 0001 0010 13 01101 0D 0001 ООН 14 OHIO 0Е 0001 0100 15 01111 0F 0001 0101 16 10000 10 0001 оно 17 10001 11 0001 0111 18 10010 12 0001 1000 19 10011 13 0001 1001 20 10100 14 0010 0000 ° Это же шестнадцатеричное число можно записать как 8C140Ah, или 0х8С140А. 2) Многие научные калькуляторы, в том числе и программа «Калькулятор» из состава Microsoft Windows, могут производить вычисления в двоичной и шестнадцатеричной системах счисления.
Глава 1. Цифровое представление 21 Двоичная арифметика1) подчиняется тем же правилам, что и более привычная для вас арифметика по основанию 10. Более того, это утверждение справедливо для любой системы счисления. Простейшей арифметической операцией является операция сложения, представляющая сокращенную форму записи операции на- хождения общего количества чего-либо по сравнению с более примитивным про- цессом счета или прибавления единицы. Так, запись 2 + 4 = 6 гораздо удобнее, чем 2 + 1 = 3,3 + 1 = 4,4+1 = 5, 5+1 = 6. Однако при этом необходимо помнить правила сложения. Для десятичных чисел существует 45 правил, если учесть, что порядок слагаемых не важен, — от 0 + 0 = 0 до 9 + 9 = 18. Двоичное сложение го- раздо проще, поскольку подчиняется всего трем правилам: 0 + 0 = 0 0+1 1 _ j 1+0 J ' 1 1+1 =10 (0 и1 в переносе) Сначала эти правила применяются к самым младшим значащим битам (Least Significant Bit — LSB); при возникновении переноса он передается в бит, располо- женный левее. Процесс вычисления заканчивается старшими значащими битами (Most Significant Bit — MSB). Если из этой позиции происходит перенос, то имен- но он становится самым старшим битом суммы. Например: а) Десятичное число б) Двоичное число 1 0 1 1 2 6 3 1 0 0 1 84268421 96 1-е слагаемое 1100000 1 -е слагаемое + 37 2-е слагаемое + 0100101 2-е слагаемое 1 1 Переносы -j—t Переносы 133 Сумма 10000101 Сумма Подобно тому как при сложении осуществляется прямой счет, операция вычи- тания соответствует обратному счету, при котором от исходного значения отни- маются единицы. Так, операция 8 — 5 = 3 эквивалентна последовательности опе- раций 8-1 = 7, 7-1 = 6, 6-1 = 5, 5-1=4, 4-1 = 3. В соответствии с известной методикой вычитания десятичных чисел правила вычитания применяются и к двоичным числам, начиная с младших битов и за- канчивая старшими. Для каждого бита, в котором из меньшего числа вычитается большее, из ближайшего старшего бита занимается единица. С учетом заема пра- вила вычитания в двоичной системе имеют вид 11 Обычный двоичный код иногда называют кодом «8-4-2-1» по значению весов четырех младших разрядов.
22 Часть I. Основы 0-0 = 0 *0-1 = 1 Из старшего бита занимается 1 1-0 = 1 1-1 = О Например: а) Десятичное число б) Двоичное число 1 0 1 6 3 1 4 2 6 8 4 2 1 96 Уменьшаемое 1100000 Уменьшаемое — 37 Вычитаемое — 0100101 Вычитаемое —1— Заемы 1 1 1 1 -И- Заемы 59 Разность 0111011 Разность Несмотря на то что эти знакомые методы прекрасно работают, при реализа- ции их в цифровых схемах возникает ряд проблем: • Что делать, если вычитаемое меньше уменьшаемого? • Как нам различать положительные и отрицательные числа? • Можно ли выполнить вычитание с помощью блока суммирования? Чтобы понять суть описанных проблем, взгляните на следующий пример: а) Десятичное число б) Двоичное число 37 Уменьшаемое — 96 Вычитаемое 41 Разность (—59) 0100101 Уменьшаемое — 1100000 Вычитаемое 1000101 Разность (-01 ПОП) Обычно, если мы знаем, что уменьшаемое меньше вычитаемого, мы меняем операнды местами и добавляем знак минуса к результату, т.е. вычисляем выраже- ние — (вычитаемое — уменьшаемое). Если мы не выполним такой перестановки, как показано в примере (а), приведенном выше, то результат окажется неверным. На самом деле число 41 является правильным в том смысле, что представляет со- бой разность между числом 59 (правильный результат) и 100. То есть число 41 представляет собой дополнительный код числа 59 в десятичной системе (10’s complement). Более того, сам факт заема из старшего разряда числа указывает на то, что результат операции отрицателен и представлен соответственно в дополни- тельном коде. Для преобразования числа, представленного в дополнительном ко- де, в «нормальный» вид достаточно просто проинвертировать каждый десятич- ный разряд и к полученному значению прибавить единицу. Инвертирование де- сятичного разряда заключается в вычитании его значения из 9. Таким образом, дополнительный код числа 3941 в десятичной системе равен —6059: 3941 -> 6058;+1 =—6059.
Глава 1. Цифровое представление 23 Как бы там ни было, единственной причиной, по которой мы не оставляем отрицательные числа в дополнительном коде, является непривычность для нас такого представления чисел. Разумеется, использование дополнительного кода для представления отрица- тельных значений применимо и к двоичным числам. Причем, простота инверти- рования (0 —> 1, 1 ->0) делает этот метод очень привлекательным. Обратимся к приведенному выше примеру: 1000101 0111010; + 1 = -0111011. И опять же отрицательные числа следует оставлять в дополнительном коде (2’s complement)0. Обратите внимание, что операция преобразования в дополнитель- ный код является обратимой, т.е. дополнительный код <=> прямой код. При работе с десятичными числами для обозначения положительных и отрицательных чисел используются знаки «+» и «—» соответственно. В системе же с двумя состояниями мы можем оперировать только единицами и нулями. Тем не менее, взглянув на последний пример, можно получить ключ к решению этой проблемы. Как уже было сказано, отрицательное значение получается в ре- зультате заема в старший разряд числа. Так что мы можем использовать этот раз- ряд в качестве знакового бита (sign bit), причем 0 будет эквивалентен знаку «+», а 1 — знаку «—». Таким образом, число b’l 1000101 ’ будет соответствовать значе- нию —59, ab’00111011’ — значению +59 (в примерах знаковый бит выделен полу- жирным шрифтом). Преимущество такого представления заключается в том, что при любых арифметических операциях с ним можно обращаться так же, как и с обычным битом. При этом результат операции будет иметь верный знак: а) Уменьшаемое б) Уменьшаемое меньше вычитаемого больше вычитаемого 01100000 11011011 —1 (+96) (-37) 00100101 - 10100000 (+37) (-96) 00111011 (+59) 11000101 (-59) Из примера видно, что если отрицательное число представлено в дополни- тельном коде, то нам не нужно изобретать аппаратный «вычитатель», поскольку прибавление отрицательного числа эквивалентно вычитанию положительного. Другими словами, А — В = А + (—В). Более того, если числа будут записаны в до- полнительном коде, результаты всех последующих арифметических операций также будут в дополнительном коде. ° Если вы введете в программе «Калькулятор» Microsoft Windows десятичное отрицатель- ное число и переключитесь в двоичную систему, то это число будет отображено в дополнитель- ном коде.
24 Часть I. Основы С арифметическими операциями над отрицательными числами, представлен- ными в дополнительном коде, связаны две проблемы. Первая из этих проблем — переполнение (overflow). Она заключается в том, что при сложении двух положи- тельных или двух отрицательных чисел может возникнуть переполнение в знако- вом бите, например: получается положительной а) Сумма двух положительных чисел б) Сумма двух отрицательных чисел получается отрицательной 01000 (+8) + 01011 (+11) -ч----- 10011 (-13!!!) 11000 (-8) + 10101 (-11) ---- 01101 (+13!!!) В примере (а) результат сложения (+8) + (+11) равен —13. В данном случае произошло переполнение из четвертого значащего бита в знаковый (в действи- тельности число 10011b — 19 является корректным результатом). В примере (б) показана та же ситуация при сложении двух отрицательных чисел. Переполнение может возникнуть только в том случае, если оба операнда имеют одинаковые зна- ковые биты. Поэтому для обнаружения переполнения следует отслеживать значе- ние знакового бита результата, отличающееся от значения знаковых битов опе- рандов. Логическая схема, реализующая обнаружение переполнения, показана на Рис. 1.5. Вторая проблема касается выполнения арифметических операций над знако- выми операндами разной разрядности, например: а) Расширение положительного числа б) Расширение отрицательного числа 00011001 (+25) + ООН (+03) —1-------- 9999 I 00011001 (+25) + 00000011 (+03) ---------н— 00011100 (+28) 00011001 (+25) + 1101 (-03) 9999 I 00011001 (+25) + 11111101 (-03) 11111—г- 00010110 (+22) В обоих примерах показано сложение 8-битного числа с 16-битным. Если первый операнд положителен, его разрядность можно увеличить до 16 бит, запол- нив свободные позиции нулями. Если же требуется расширить отрицательное число, то решение уже не так очевидно. В этом случае расширение числа произ-
Глава 1. Цифровое представление 25 водится путем заполнения пустых разрядов единицами. Общее правило звучит так: при расширении данных дополнительные разряды слева следует заполнять знаковым битом. Этот метод называется расширением знака (sign extension). Умножение числа на и-ю степень двойки реализуется сдвигом исходного зна- чения на п позиций влево. Таким образом, последовательность операций 00110 (6) « 01100 (12) « 11000 (24) эквивалентна умножению числа 6 на 22; опе- ратор «<<» используется для обозначения сдвига влево. Это же правило примени- мо и к отрицательным числам: а) +3 х 8 = +24 б) -3 х 8 = -24 в) +3 х 10 = 30 000000011 ( 3) 111111101 (-3) 000000110 (3x2) << << + 000011000 (3x8) 000000110 (6) 111111010 (-6) << << 000011110 (Зх 10 = 30) 000001100 (12) 111110100 (-12) 000011000 (24) 111101000 (-24) Смена значения знакового бита означает переполнение в старшем бите моду- ля числа. Некоторые компьютеры (микропроцессоры) поддерживают операцию арифметического сдвига влево, которая сигнализирует о такой ситуации в отличие от обычной операции логического сдвига влево, используемой для сдвига беззнако- вых чисел. Умножение на число, не являющееся степенью двойки, можно реализовать, комбинируя операции сдвига и суммирования. В частности, как показано в пре- дыдущем примере (в), выражение 3x10 вычисляется следующим образом: (3 х 8) + (3 х 2) = (3 х 10) или (3 « 3) + (3 « 1). Аналогичным образом деление числа на п-ю степень двойки реализуется сдвигом значения на п позиций вправо, т.е. 1100 (12) >> ОНО (6) >> ООП (3) >> 0001.1 (1.5). Этот же способ применим к знаковым числам: а)+15/8 = 1.875 б)-15/8 =-1.875 в) 15/10 =1.5 01111.000 (+15) 10001.000 (-15) 0001.1 10101 1111.0 «111.100 (+7.5) 11000.100 (-7.5) -1010 «11.110 (+3.75) 1И 00.010 (-3.75) 0101 -101.0 «11.111 (+1.875) 1ЙИ10.001 (-1.875) 000.0
26 Часть I. Основы Обратите внимание, что освободившиеся при сдвиге влево позиции заполня- ются не нулями, а содержимым знакового бита. Таким образом, при сдвиге поло- жительных чисел слева вдвигаются нули, а при сдвиге отрицательных чисел — единицы. Данная операция известна как арифметический сдвиг вправо, в отличие от логического сдвига вправо, при котором всегда вдвигаются нули. Деление на число, не являющееся степенью двойки, показано в примере (в). Эта операция осуществляется аналогично операции деления столбиком в десяти- чной системе. При ее выполнении по аналогии с умножением используется ком- бинирование операций сдвига и вычитания. Арифметические действия — не единственные операции, которые можно осу- ществлять над двоичными числами. Английский математик Джордж Буль1) (George Boole) в середине 19-го столетия создал раздел алгебры, касающийся символической обработки логических отношений. Этот раздел алгебры, называе- мый Булевой алгеброй, оперирует величинами, которые могут иметь только два со- стояния: истина или ложь. В 30-х годах стало понятно, что этот раздел математи- ки может быть с успехом использован для анализа коммутационных схем и, соот- ветственно, устройств двоичной логики. Мы ограничимся рассмотрением базовых логических операций этой алгебры переключательных схем. Инверсия, или операция НЕ (NOT), обозначается символом подчеркивания. Таким образом, выражение f — А означает, что переменная f является обратной величиной переменной А. То есть если А = 0, то f = 1, и, наоборот, если А = 1, то f — 0. На Рис. 1.1, а эта зависимость представлена в виде таблицы истинности (truth table). По определению двойная инверсия переводит переменную в перво- начальное состояние: f=f* 2). б) Альтернативные варианты изображения логического элемента Рис. 1.1. Операция НЕ (NOT) А 1 f 7 0 а) Таблица истинности ° Джордж Буль — первый профессор математики Куинз-колледжа (Queen’s College) в графстве Корк. 2) Давным-давно, когда логические схемы реализовывались на дискретных компонентах, таких как диоды, резисторы и транзисторы, часто возникала проблема паразитных токов. При выполнении одной из лабораторных работ свечение выходной лампы получилось довольно тус- клым, и преподаватель предположил, что два элемента НЕ, последовательно включенных в подозрительную линию, смогут предотвратить нежелательную утечку тока, не нарушив при этом логику работы схемы. Позже студенты пожаловались, что рекомендуемая мера не возы- мела никакого эффекта. При исследовании схемы преподаватель обнаружил два узелка на про- блемном проводе, специально затянутых не до конца!
Глава 1. Цифровое представление 27 Как правило, реализации логических функций представляются с помощью абстрактных символов, а не подробных электрических схем. Общепринятое изоб- ражение элемента НЕ приведено на Рис. 1.1, б1*. Кружок на изображении логи- ческих схем всегда означает инверсию и очень часто используется в сочетании с другими логическими элементами (см., например, Рис. 1.2, в). Оператор И (AND) реализует функцию «все или ничего». Результат операции будет истинным только в том случае, если все п входов истинны. На Рис. 1.2 име- ется две входные переменные, и выражение для выходного значения записывает- ся как f = В • А, где символ «•» — булевый оператор И* 2). Количество входных пере- менных может быть любым, и в общем случае f = А(0) • А(1) • А(2) •...• A(w). Опера- цию И иногда называют операцией логического умножения, поскольку (по аналогии с обычным умножением) результат этой операции между любым битом и 0 всегда будет равен 0. ВА If 00 01 о ol > =0 1 0 0"| S. — Л 1 1 1 J г — А а) Таблица истинности 00 01 1 0 1 1 1 1 1 0 б) Альтернативные варианты изображения логического элемента в) Операция H-HE(NAND) Рис. 1.2. Операция И (AND) Если предположить, что вход В является управляющим входом, а вход А — входом данных, то, обратившись к таблице истинности, мы увидим, что при В = 1 на выходе будут присутствовать входные данные, а при В = 0 на выходе постоян- но будет 0. Таким образом, эту схему можно рассматривать как управляемый вен- тиль. В общем случае термин вентиль применим к любой логической схеме, реа- лизующей базовые логические операции. В большинстве практических реализаций вентиля И используется инвертиро- ванный выход. Логическая функция такого элемента называется И-НЕ (NOT AND, или NAND), а ее изображение приведено на Рис. 1.2, в. Действие оператора ИЛИ (OR) можно описать словом «что-нибудь». Резуль- тат этой операции будет истинным, если истинно хотя бы одно из входных значе- ний (поэтому на символе изображено «> 1»). Хотя элемент, показанный на Рис. 1.3, имеет только два входа, операция ИЛИ применима к любому числу входных переменных. Часто операцию ИЛИ называют логическим сложением, соответственно в качестве математического оператора используется знак «+»3): Верхний символ используется в зарубежной литературе, а нижний — в отечественной. — Примеч. пер. 2) Иногда для обозначения оператора И используется знак «/\». — Примеч. пер. 3) В отечественной литературе оператор ИЛИ часто обозначается также знаком «V»- — Примеч. пер.
28 Часть I. Основы f — В + А. Подобно тому как вентиль И позволяет обнаружить ситуацию, когда на всех входах присутствуют единицы, вентиль ИЛИ может использоваться для об- наружения ситуации «все нули». Использование его в этом качестве показано на Рис. 2.20 (стр. 49), где 8-битное нулевое значение вызывает появление лог. 1 на выходе элемента ИЛИ-HE. Результат логического сложения любого бита с лог. 1 всегда будет равен лог. 1. ВА 00 01 10 1 00 1 01 0 10 0 1 1 0 1 1 1 а) Таблица б) Альтернативные варианты истинности изображения логического элемента в) Операция ИЛ И-НЕ (NOR) Рис. 1.3. Операция ИЛИ (OR) Если предположить, что вход В является управляющим входом, а вход А — входом данных (или наоборот), то из Рис. 1.3, а видно, что данные проходят через вентиль при В = 0 и задерживаются (на выходе постоянно присутствует 1) при В = 1. Такое поведение отчасти похоже на инверсное действие функции И. В са- мом деле, функция ИЛИ может быть выражена через функцию И посредством двойственного соотношения А+В = В + А. Из этого соотношения следует, что функцию ИЛИ-HE можно реализовать инвертированием сигналов, подаваемых на вход элемента И. Мы познакомились с тремя основными логическими операторами: И, ИЛИ и НЕ. Однако существует еще одна операция, часто используемая в электронике, — операция Исключающее ИЛИ (exclusive OR — XOR). Функция XOR истинна, ес- ли истинен только один из входов (поэтому на символе изображено «=1», см. Рис. 1.4, б). В отличие от обычной операции ИЛИ, при 1 на обоих входах на выходе будет 0. В Alt 00 о 01 1 id i 1 1 о f=B®A В Alt 0 011 01 о 10 о 11 1 в) Операция Исключающее WIH-HE(XNOR) а) Таблица б) Альтернативные варианты истинности изображения логического элемента Рис. 1.4. Операция Исключающее ИЛИ (XOR)
Глава 1. Цифровое представление 29 Если предположить, что вход В — управляющий, а вход А — вход данных (или наоборот), тогда • Если В = 0, то f = А — данные с входа передаются на выход. • Если В = 1, то f = А — выходной сигнал представляет собой инвертирован- ный входной сигнал. Таким образом, вентиль Исключающее ИЛИ может использоваться в качест- ве программируемого инвертора. Другим полезным применением функции Исключающее ИЛИ можно назвать использование ее в качестве логического дифференциатора. Из таблицы истин- ности (Рис. 1.4, а) видно, что выход элемента Исключающее ИЛИ истинен толь- ко тогда, когда состояния обоих входов различны. Аналогично, из таблицы ис- тинности оператора Исключающее ИЛИ-HE (XNOR), показанной на Рис. 1.4, в, видно, что выход такого элемента истинен при одинаковых сигналах на обоих входах. Таким образом, вентиль Исключающее ИЛИ-HE можно рассматривать в качестве 1-битного компаратора. Равенство двух w-битных значений можно про- верить, объединив по И набор вентилей Исключающее ИЛИ-HE (см. Рис. 2.7 на стр. 37), каждый из которых реализует функцию Вк Ф Ак, т.е. f*A=B ~ ® • к=0 В качестве простого примера использования элементов Исключающее ИЛИ и Исключающее ИЛИ-HE рассмотрим задачу определения переполнения в знако- вом бите (см. стр. 24). Эта ситуация возникает, если знаковые биты обоих операн- дов одинаковы (SB ®SA), а знаковый бит С результата отличается от них, ска- жем SB Ф Sc. Схема такого детектора, показанная на Рис. 1.5, описывается логи- ческой функцией: (SB Ф SA) • (SB Ф Sc). И наконец, функцию Исключающее ИЛИ можно использовать для определе- ния четного количества истинных входов. При каскадном соединении п + 1 вен- тилей Исключающее ИЛИ выходной сигнал будет равен 1, если входное «-битное число содержит четное число единичных битов. Добавляя к слову данных допол- нительный бит, так чтобы общее число битов было четным, можно реализовать простейшую защиту от ошибок. Приемное устройство будет контролировать чет- ность принимаемых данных, и любое несоответствие будет означать их поврежде- ние. V - ИСТИНА, если: (Знак А = Знак В) И (ЗнакС* Знак В) Рис. 1.5. Обнаружение переполнения в знаковом бите
ГЛАВА ЛОГИЧЕСКИЕ СХЕМЫ Итак, мы с вами выяснили, что цифровая обработка данных заключается в пересылке, обработке и хранении двоичных значений. В этой главе мы несколько расширим представления, введенные в предыдущей главе, чтобы можно было приступить к рассмотрению собственно архитектуры компьютеров и микроконт- роллеров. Мы познакомимся с несколькими важными логическими функциями, рассмотрим выпускаемые микросхемы, которые реализуют эти функции, а также их практическое применение. Прочитав эту главу, вы: • Познакомитесь с областями применения и характеристиками выходных каскадов с активной подтяжкой (двухтактный выход), с открытым коллек- тором и с тремя состояниями. • Поймете логическую структуру и назначение дешифратора. • Познакомитесь с интегральной микросхемой, представляющей собой на- бор элементов Исключающее ИЛИ-HE и использующейся для определения равенства двух значений. • Поймете, как можно реализовать на логических элементах 1-битный сумма- тор и как его можно доработать для сложения двух w-битных чисел. • Разберетесь, почему АЛУ имеет такое большое значение для программируе- мых систем. • Ознакомитесь со структурой и областями применения постоянных запоми- нающих устройств (ПЗУ). • Поймете, как из двух логических элементов, объединенных перекрестными связями, можно создать RS-триггер. • Разберетесь, чем отличается D-защелка от D-триггера. • Поймете, как из набора D-триггеров или защелок можно реализовать регистр. • Узнаете, как с помощью каскадного соединения D-триггеров можно реали- зовать сдвиговый регистр. • Поймете, как можно использовать D-триггер в качестве делителя на 2 и как посредством каскадного соединения D-триггеров можно реализовать дво- ичный счетчик.
Глава 2. Логические схемы 31 • Узнаете, как с помощью связки АЛУ/регистр можно реализовать блок акку- мулятора процессора. • Разберетесь в принципах работы оперативного запоминающего устройства (ОЗУ). В первых интегральных микросхемах, появившихся в конце 60-х годов, реа- лизовывались главным образом логические элементы И-НЕ, ИЛИ-HE и НЕ. Наиболее популярным семейством логических микросхем тогда были, да и сей- час в какой-то мере остаются микросхемы 74-й серии, построенные по техноло- гии ТТЛ (транзисторно-транзисторная логика). Эта серия была разработана фир- мой Texas Instruments и впоследствии скопирована всеми ведущими производите- лями микросхем. Микросхема 74LS001),2) содержит четыре двухвходовых элемента И-НЕ, объ- единенные в 14-выводном корпусе. Для питания микросхемы используется на- пряжение 5 ±0.25 В, прикладываемое между выводами Vcc3) (обычно около 5 В) и GND. Напряжения логических уровней для этой серии составляют: 2.4...5 В — для ВЫСОКОГО уровня и 0...0.4 В — для НИЗКОГО. Для большинства семейств логических микросхем требуется напряжение питания 5 В, однако существуют и 3-вольтовые версии. При этом большинство КМОП-микросхем могут работать в диапазоне питающих напряжений от 3 до 15 В. Цоколевка микросхемы 74LS00 в корпусе DIP показана на Рис. 2.1, а. Функ- ция этой микросхемы полностью описывается четырьмя двухвходовыми элемен- тами И-НЕ в положительной логике, поскольку НИЗКИЙ и ВЫСОКИЙ логичес- кие уровни эквивалентны логическим значениям 0 и 1. Если же принять, что 0 со- ответствует ВЫСОКОМУ уровню, а 1 — НИЗКОМУ (отрицательная логика), то микросхема будет выполнять функцию четырех двухвходовых элементов ИЛИ- НЕ. На изображениях логических элементов по стандарту ANSI/IEC4) НИЗКИЙ уровень обозначается символом полярности (см. Рис. 2.1, б). Таким образом, изображение символа И-НЕ по стандарту ANSI/IEC основано на реальном функ- ционировании схемы. В данном случае логика работы схемы совпадает с функци- ей И-НЕ в терминах положительной логики. Оператор & (И), изображенный в верхнем прямоугольнике, относится и к остальным трем элементам. Символы «LS» означают «low-power shottky transistor» (маломощные ТТЛ ИС с диодами Шоттки). Существуют и другие разновидности этой серии, такие как ALS (усовершенствован- ные маломощные ТТЛ ИС с диодами Шоттки), AS (усовершенствованные ТТЛ ИС с диодами Шоттки) и НС (быстродействующие КМОП ИС). Эти микросхемы отличаются быстродей- ствием и потреблением, однако все микросхемы с одинаковым номером выполняют одни и те же функции и имеют одинаковую цоколевку. 2) Отечественный аналог — микросхема К555ЛАЗ. — Примеч. пер. 3) Исторически сложилось так, что положительный вывод источника питания в цифровых ИС обозначается как Vcc (символ «С» взят потому, что питание подается на коллектор биполяр- ного транзистора). Аналогичным образом ИС, построенные по технологии КМОП, используют обозначение VDD (символ «D» указывает на напряжение, подаваемое на сток). Вывод общего провода обычно обозначается как «GND», однако иногда используются обозначения Vee (для эмиттера) или Vss (для истока). 4) Национальный Институт Стандартизации США/Международная Электротехническая Комиссия.
32 Часть I. Основы 74LS00 а) Корпус DIP б) Обозначение по ANSI/IEC Рис. 2.1. Микросхема 74LS00 (К555ЛАЗ) Выходы логических элементов микросхемы 74LS00 построены по двухтакт- ной схеме. При такой структуре выходного каскада каждый из уровней формиру- ется путем подключения выхода через низкоомный ключ к линии Vcc или GND соответственно. На Рис. 2.2, а эти ключи изображены в виде обычных переклю- чателей, хотя на самом деле они, разумеется, выполнены на транзисторах. +Усс а) Двухтактный Внутреннее логическое состояние Расщепитель фаз ... Внутреннее логическое состояние б) Открытый коллектор/ в) С тремя состояниями открытый сток Рис. 2.2. Типы выходных каскадов В логических микросхемах (например, таких как 74LS00) изменение состоя- ния выхода происходит за время около 10 нс1*. Чтобы получить такие значения, емкости всех соединительных проводников и входов других микросхем должны быстро разряжаться. Главным образом именно по этой причине в большинстве цифровых микросхем используется двухтактный выход (называемый также выхо- дом с активной подтяжкой — active pull-up). Однако в некоторых ситуациях пре- имущество имеют выходные каскады других типов. Конфигурация открытый коллектор (или открытый сток), показанная на Рис. 2.2, б, обеспечивает «жест- кий» НИЗКИЙ уровень, при этом состояние ВЫСОКОГО уровня соответствует разомкнутой цепи. Напряжение ВЫСОКОГО уровня может формироваться под- 0 Одна наносекунда равна 10 9 с, так что за одну секунду может произойти 100 000 000 переключений.
Глава 2. Логические схемы 33 ключением внешнего резистора либо к линии Vcc, либо к отдельной шине пита- ния. Роль подобного резистора могут выполнять некоторые устройства, такие как реле, лампы накаливания или светодиоды. Выходной транзистор таких каскадов часто имеет большую, чем обычно, нагрузочную способность по напряжению и/или току. Один из наиболее интересных для нас вариантов применения выхода с от- крытым коллектором показан на Рис. 2.3. В этой схеме четыре элемента с выхо- дом типа «открытый коллектор» подключены к одному и тому же подтягивающе- му резистору. Обратите внимание на символ , используемый для обозначения выхода с открытым коллектором. Предположим, что на рисунке изображены че- тыре периферийных устройства, любое из которых может обращаться к процес- сору (компьютеру или микроконтроллеру). Если этот процессор имеет только один вход для внешнего сигнала прерывания, то четыре сигнальные линии от уст- ройств должны быть объединены вместе по схеме монтажное ИЛИ, как показано на рисунке. Когда все сигнальные линии находятся в неактивном состоянии (лог. 0), выходы всех буферных элементов НЕ выключены (ВЫСОКИЙ уровень) и общая линия подтянута к Исс резистором T?L. Если какая-либо из сигнальных линий становится активной (лог. 1), скажем, линия Sig_l, то на выходе соответ- ствующего буфера появляется НИЗКИЙ уровень. В результате, независимо от со- стояния остальных сигнальных линий, общая линия переключается в состояние НИЗКОГО уровня, прерывая таким образом работу процессора. Выходной каскад третьего типа (с тремя состояниями), приведенный на Рис. 2.2, в, обладает свойствами выходов обоих рассмотренных типов. При разре- шенном выходе логические состояния формируются обычным образом, т.е. выда- чей ВЫСОКОГО и НИЗКОГО напряжения. При запрещении выхода он стано- вится разомкнутой цепью, независимо от функционирования внутренней логи- ческой схемы и любых изменений на ее входах. Выход с тремя состояниями обозначается символом . В качестве примера использования выхода указанного типа рассмотрим ситу- ацию, показанную на Рис. 2.4. В данном случае основному контроллеру требуется прочитать данные с одного из нескольких устройств, подключенных к нему груп- пой общих линий. Поскольку эта магистраль, или, иначе, шина данных, является
34 Часть I. Основы общим ресурсом, в любой момент времени доступ к шине предоставляется толь- ко выбранному устройству. Доступ должен быть закрыт сразу же после считыва- ния данных, с тем чтобы шиной могло воспользоваться другое устройство. Как показано на рисунке, все выходы, подключаемые к шине, обозначаются симво- лом \7. После выбора устройства управление линиями шины будет осуществ- ляться только активными логическими уровнями. Микросхема сдвоенного 4-бит- ного буфера с тремя состояниями 74LS2441* имеет выходы с повышенной нагрузоч- ной способностью (обозначаемые символом [>), специально предназначенные для работы на длинных линиях, имеющих большую емкость. От основного контроллера Рис. 2.4. Совместное использование шины Интегральные микросхемы, содержащие до 12 логических элементов, отно- сятся к микросхемам малой степени интеграции. Если в корпусе микросхемы со- держится до 100 логических элементов, то она относится к классу микросхем средней степени интеграции; до 1000 — к классу больших интегральных схем или, сокращенно, БИС. Все микросхемы, имеющие более 1000 логических элементов, относятся к классу сверхбольших интегральных схем (СБИС). К последнему классу, в частности, относятся микросхемы памяти и микроконтроллеры. Изображенные на Рис. 2.5 микросхемы, содержащие определенным образом соединенные элементы И-НЕ, являются типичным примером интегральных микросхем средней степени интеграции. Если вспомнить, что на выходе элемен- та И-НЕ лог. 0 присутствует только в том случае, если на всех его входах присут- ствует лог. 1 (см. Рис. 1.2, в на стр. 27), то можно увидеть, что при любых сочета- ниях сигналов на входах выборки в А (21 2°) (Рис. 2.5, а) сигнал лог. 0 будет при- сутствовать на выходе только одного вентиля. Так, выход Y2 будет активным при в А = 10. После рассмотрения таблицы истинности становится понятно, что данная схема декодирует двоичный адрес в А таким образом, что при подаче ад- реса п становится активным выход Y„. Полностью название микросхемы Отечественный аналог — микросхема К555АП5. — Примеч. пер.
Глава 2. Логические схемы 35 74LS1391) звучит так: сдвоенный натуральный дешифратор 2 на 4. Сдвоенным он называется потому, что в одном корпусе расположены две такие схемы. Символ X/Y обозначает преобразование кода X (натуральное двоичное число) в код Y (унарный — один из п). Вход разрешения G подключен параллельно ко всем эле- ментам. Таким образом, дешифратор выполняет свои функции только в том слу- чае, если на входе G присутствует НИЗКИЙ уровень (лог. 0). Если на входе G присутствует ВЫСОКИЙ уровень, то независимо от состояния входов В и А (в таблице истинности эта ситуация обозначается символом «X» — безразличное со- стояние) все выходы устанавливаются в неактивное состояние (лог. 1). Пример использования микросхемы 74LS139 приведен на Рис. 2.25 (стр. 54). Uy0 Uy, Uy2 Uy3 ГТ- И-15) В (3,13) А ,.(2,14) X/Y [1/2 X74LS139] EN G ВА 3 2 J6,10)Y9 J5,11)Vi J4,12)Yq 0 0 '0 ’1 '2 Y3 a) 74LS139 — сдвоенный дешифратор 2 на 4 X/Y [74LS138] 7 ^Z1y7 G2B (5) & 6 ь!91Уб G2A* (4) EN 5 s[10)Y5 G1— 4 Ш)?4 3 1Ж3 с—(21 2 2 J13)Y2 В—121 1 1 Л4)?1 А—111 0 0 H5)y0 б) 74LS138 — дешифратор 3 на 8 о о о о 00 0 1 1 о 0 1 1 О EN СВА 1 XX Уо^Уг 1 Y3 1 Y4 1 Y5 1 Y6Y7 0 000 0 1 1 1 1 1 1 1 0 001 1 0 1 1 1 1 1 1 0 010 1 1 0 1 1 1 1 1 0 011 1 1 1 0 1 1 1 1 0 100 1 1 1 1 0 .1 1 1 0 101 1 1 1 1 1 0 1 1 0 110 1 1 1 1 1 1 0 1 0 111 1 1 1 1 1 1 1 0 1 XXX 1 1 1 1 1 1 1 1 О о Рис. 2.5. Микросхемы дешифраторов 74LS138 (К555ИД7) и 74LS139 (К531ИД14) Микросхема 74LS1382), показанная на Рис. 2.5, б, похожа на только что рас- смотренную, однако выполняет функцию дешифратора 3 на 8. При «-м значении на линиях адреса с в А (22 21 2°) активным становится только один из восьми выходов Y„. Микросхема 74LS138 имеет три входа стробирования, формирующие ° Отечественный аналог — микросхема К.531 ИД 14. — Примеч. пер. 2) Отечественный аналог — микросхема К555ИД7. — Примеч. пер.
36 Часть I. Основы внутренний сигнал разрешения G2B • G2A • G1. То есть функционирование мик- росхемы разрешено только в том случае, если на обоих входах G2B и G2A при- сутствует НИЗКИЙ уровень, а на входе G1 — ВЫСОКИЙ. Микросхема 74LS138 используется в схеме на Рис. 11.12 (стр. 350) в качестве дешифратора линий порта микроконтроллера для подключения к одному порту нескольких устройств. Приоритетный шифратор 74LS1481), показанный на Рис. 2.6, выполняет об- ратное преобразование. Подача на один из входов НИЗКОГО уровня вызывает появление на выходе эквивалентного 3-битного значения. Так, если вход 5 = 0, то a2aiao = 010 (число 101 в инверсной логике). Ejn 76543210 МА 0 11111110 111 0 1111110Х 110 0 111110ХХ 101 0 11110ХХХ 100 0 111ОХХХХ 011 0 110ХХХХХ 010 О 1ОХХХХХХ 001 О ОХХХХХХХ 000 1 ХХХХХХХХ 000 б) Условное обозначение и нумерация выводов а) Таблица истинности Рис. 2.6. Микросхема приоритетного шифратора 74LS148 Если активный сигнал присутствует на нескольких входах, то выходное значе- ние соответствует входу с наибольшим номером. Так, если НИЗКИЙ уровень присутствует на обоих входах 5 и 3, то выходное значение все равно будет состав- лять 010. Символы HPRI на условном обозначении микросхемы, приведенной на Рис. 2.6, означают «наивысший приоритет» (Higest PRIority). Работа микросхемы разрешается при НИЗКОМ уровне на входе Ein. Выходы Eout и GS используются при каскадном соединении микросхем для увеличения количества линий. Большой класс ИС реализует различные арифметические операции. Матрица логических элементов, показанная на Рис. 2.7, используется для обнаружения ра- венства между двумя 8-битными числами Р и Q. Каждый из восьми элементов Исключающее ИЛИ-HE формирует лог. 1, если оба входных бита Рп и Qn одина- ковы (мы уже встречались с этйм элементом на стр. 28). Соответственно, НИЗ- КИЙ уровень на выходе элемента И-НЕ появится только в том случае, если все 8 пар битов одинаковы. Микросхема компаратора 74LS688 имеет также вход G, сигнал с которого подается на один из входов элемента И-НЕ и выполняет функ- цию глобального разрешения. На условном обозначении микросхемы по стандарту ANSI/IEC, приведенном на Рис. 2.7, б, функция сравнения указывается аббревиатурой СОМР. Префикс Отечественный аналог — микросхема К555ИВ1. — Примеч. пер.
Глава 2. Логические схемы 37 а) Логическая схема Рис. 2.7. Микросхема 8-битного компаратора 74LS688 б) Обозначение по ANSI/IEC «1» в обозначении выхода указывает на то, что выполнение операции «Р = Q» за- висит от входа, обозначенного тем же номером, т.е. G1. Таким образом, вход раз- решения G1 управляет выходом IP = Q (и вход, и выход — с активным НИЗКИМ уровнем). Одной из первых функций, реализованных в ИС помимо обычных логичес- ких элементов, было сложение. В таблице истинности, показанной на Рис. 2.8, а, приведены значения бита суммы S и флага переноса Сь образующихся при сло- жении двух битов А и В и бита переноса из предыдущего разряда Со. Например, из 6-й строки таблицы следует, что при сложении двух единиц и 0-го переноса сумма будет равна 0, а перенос —1(14-1+0 = *0). Для реализации этой строки таблицы нам нужно распознать комбинацию битов ПО, описываемую уравнени- ем А - В • Со- Эту операцию выполняет 6-й элемент схемы. Таким образом, мы просто объединяем по ИЛИ все возможные комбинации входных переменных: S = (A-B-C0) + (A-BC0) + (A-B-C0) + (AB-C0) Q = (A-B-C0) + (AB-C0) + (A-B-Q) + (A-B-C0) Применяя такую схему для каждого разряда и подключая при этом выход пе- реноса разряда с номером к - 1 к входу переноса разряда с номером к, мы сможем выполнять сложение любых л-битных чисел.
38 Часть I. Основы На Рис. 2.8, б показана структурная схема микросхемы 74LS2831), которая складывает два 4-битных числа за 25 нс. На практике для формирования итогового бита переноса С4 используется дополнительная схема, чтобы избежать задержек, вызванных прохождением битов переноса через все стадии суммирования, от младшего бита к старшему. Несколько (п) микросхем 74LS283 можно каскадиро- вать для реализации функции сложения слов разрядностью 4 х п. Таким образом, две микросхемы 74LS283 выполняют 16-битное сложение за 45 нс (учитывая до- полнительную задержку распространения переноса между двумя микросхемами). А В Со S С1 О 0 0 0 0 0 1 0 0 1 10 2 0 10 10 3 0 110 1 4 10 0 10 5 10 10 1 6 110 0 1 7 11111 а) Однобитное сложение В4 А4 Вз Аз Вг Аг Bi А, б) Микросхема 4-битного сумматора 74LS283 . Рис. 2.8. Сложение Разумеется, сумматоры можно использовать и для вычитания, если перевести операнды в дополнительный код. Схему сумматора/вычитателя можно реализо- вать при помощи набора логических элементов Исключающее ИЛИ, выступаю- щие в роли программируемых инверторов (см. стр. 28). Вход выбора режима ADD/SUB, управляющий этими инверторами в схеме на Рис. 2.9, подключен также к входу переноса, что вызывает добавление единицы в режиме вычитания. Отечественный аналог - микросхема К555ИМ6. — Примеч. пер.
Глава 2. Логические схемы 39 Рис. 2.9. Реализация программируемого сумматора/вычитателя Расширяя набор аргументов, мы постепенно придем к арифметико-логическо- му устройству (АЛУ). АЛУ представляет собой схему, выполняющую определен- ный набор арифметических и логических операций над входными данными в со- ответствии со значением на входах выбора режима. Микросхема 74LS382, пока- занная на Рис. 2.10, выполняет 8 операций над двумя 4-битными числами. Выполняемая операция задается тремя битами выбора режима S0S1S2 (Рис. 2.10, а). Кроме сложения и вычитания, это АЛУ выполняет также операции И, ИЛИ и Исключающее ИЛИ. Микросхема формирует даже признак переполне- ния дополнительного кода (см. стр. 24). S2Si Операция 0 0 0 Сброс (F=0000) 0 0 1 Вычитание (В-А) 0 1 0 Вычитание (А-В) 0 1 1 Сложение (А+В) 1 0 0 Искл. ИЛИ (А®В) 1 0 1 ИЛИ (А+В) 1 1 0 И (А*В) 1 1 1 Установка(F =1111) б) Условное обозначение и нумерация выводов Рис. 2.10. Микросхема АЛУ 74LS382 а) Таблица функций
40 Часть I. Основы Как мы увидим чуть позже, АЛУ является «сердцем» любого компьютера или микропроцессора. Подавая на входы выбора режима некоторую последователь- ность двоичных значений, можно заставить АЛУ выполнить соответствующую последовательность операций. Эти коды операций хранятся во внешней памяти и последовательно считываются схемами управления. Обычно последовательность кодов операций, составляющих программу, хра- нится в какой-либо БИС ПЗУ. Обратимся к структуре, показанной на Рис. 2.11. На этом рисунке изображен дешифратор 3 на 8, управляющий матрицей диодов 8x2. Для каждой л-й комбинации сигналов, подаваемых на вход адреса, выбира- ется л-я строка. Если к этой строке подключен диод, то он открывается и на ли- нии соответствующего столбца появляется НИЗКИЙ уровень. Соответственно, инвертирующий буфер с тремя состояниями формирует ВЫСОКИЙ уровень для каждого подключенного диода и НИЗКИЙ уровень для разомкнутой цепи. Таким образом, для каждого входного кода совокупность подключенных диодов опреде- ляет выходной код. Для наглядности матрица запрограммирована на реализацию полного 1-битного сумматора, изображенного на Рис. 2.8, а, однако может быть задана и любая другая функция трех переменных. Рис. 2.11. Реализация 1-битного сумматора на ПЗУ Диодная матрица, показанная на Рис. 2.11, называется постоянным запомина- ющим устройством (ПЗУ), поскольку «память» представляет собой комбинацию диодов, формируемую на этапе изготовления микросхемы. Старые устройства, имевшие, как правило, дешифратор и матрицу 32 х 8, обычно выпускались в вер- сиях, программируемых пользователем, в которых связи формировались плавки- ми перемычками. Требуемые диоды можно было исключить из матрицы при по-
Глава 2. Логические схемы 41 мощи высокого напряжения. Такие устройства называются программируемыми ЯЗУ(ППЗУ). При реализации СБИС ППЗУ больших объемов, необходимых для хранения программ, плавкие перемычки очень неудобны. Например, небольшое ППЗУ 27С641), показанное на Рис. 2.12, имеет объем, для формирования которого пот- ребовалось бы 65 536 пар «перемычка — диод». То есть это относительно неболь- шое устройство способно хранить 8192 байта данных. В микросхеме 27С64 в ка- честве программируемой перемычки используется электрический заряд на плава- ющем затворе МОП-транзистора. Второй МОП-транзистор выполняет роль диода. Как и в варианте с плавкими перемычками, инжекция заряда в изолиро- ванный затвор осуществляется с помощью высокого напряжения. Образующееся электрическое поле удерживает МОП-транзистор в состоянии проводимости. Для полного рассасывания этого заряда требуется достаточно длительный срок в несколько десятков лет, однако это значение можно уменьшить до 20 мин, под- вергая затвор интенсивному ультрафиолетовому излучению. Поэтому устройства, подобные 27С64, называют стираемым ППЗУ (СППЗУ). В корпусе микросхем, предусматривающих многократное использование, напротив кристалла размеща- ется кварцевое окошко (см. Рис. 2.12), которое можно увидеть на фотографии, приведенной на стр. 15. vpp[T А12 |Т А7 [Т А6 [4 А5 [1 А4 [£ АЗ [Т А2 [Г А1 [аГ АО Qo DO [Н D1 [Г2 D2 [ТЗ VSS[14 VDD 23 PGM 2б| 25] А8 24] А9 23| А11 2^ОЁ 2] А10 20] СЁ 19] D7 18] D6 17| D5 1б| D4 15| D3 EPROM 8192x8 АО 0 х [2764] А1 А2 AV АЗ AV А4 AV А5 0 AV А6 rA8i£Fi Av А7 AV А8 AV А9 AV А10 А11 А12 12' [POWER DOWN] & EN DO D1 D2 D3 D4 D5 D6 D7 а) Корпус DIP б) Обозначение no ANSI/IEC Puc. 2.12. Микросхема стираемого ППЗУ (СППЗУ) 27С64 (К573РФ4/6) Программирование таких микросхем осуществляется специальными устрой- ствами — программаторами. Версии микросхем без окошка называются одно- кратно-программируемыми ПЗУ, поскольку их нельзя стереть после программи- 0 Отечественный аналог — микросхемы К573РФ4 и К573РФ6. — Примеч. пер.
42 Часть I. Основы рования. Однако они намного дешевле и поэтому используются в мелко- и сред- несерийном производстве. На Рис. 2.13 приведена упрощенная схема перемычки на МОП-транзисторе с плавающим затвором. Вместо диода узлом матрицы является «-канальный МОП- транзистор ИЛ. Затвор этого транзистора подключен к линии X, а его исток S1 — к линии Y. Если сток D1 транзистора подключен к источнику положительного напряжения и выбрана линия X, то на линии Y тоже появляется ВЫСОКИЙ уро- вень (лог. 1 в терминах положительной логики). Однако если ИЛ отключен от Vdd, то он не проводит ток, и на линии Y присутствует лог. 0. Транзистор VT2 включается последовательно с линией VDD и, таким образом, выполняет роль программируемого элемента. Этот транзистор имеет дополнительный, никуда не подключенный затвор, скрытый в слое изолирующего диоксида кремния. В нор- мальном состоянии заряд на затворе отсутствует, и транзистор VT2 закрыт. Если на затвор подать импульс напряжения программирования величиной 20...25 В, то отрицательные заряды туннелируются через очень тонкий слой изолятора, окру- жающий скрытый затвор. В результате транзистор VT2 перейдет в открытое со- стояние и таким образом подключит ИЛ к шине питания. Это приведет к появле- нию лог. 1 на линии Y при выборе данной ячейки внутренним дешифратором. Величина инжектированного заряда остается более или менее постоянной до тех пор, пока затвор не будет подвергнут ультрафиолетовому облучению. Фото- ны, обладающие большой энергией, выбивают электроны (отрицательный заряд) из скрытого (плавающего) затвора1), за 20 мин разряжая его и стирая всю запи- санную информацию. Существуют также структуры ППЗУ, которые можно стереть электрическим путем, причем часто непосредственно в устройстве. Наиболее распространены Это явление называется эффектом Эйнштейна. Эйнштейн получил Нобелевскую пре- мию именно за открытие и исследование этого явления, а вовсе не за свою теорию относитель- ности, поскольку она была сочтена слишком революционной для того времени!
Глава 2. Логические схемы 43 две разновидности структур — электрически стираемые ППЗУ (ЭСППЗУ, или EEPROM) и FLASH-ППЗУ В первом случае импульс отрицательного напряже- ния Ррр большой амплитуды приводит к просачиванию электронов из плавающе- го затвора. Обычно отрицательное напряжение формируется схемами, располо- женными непосредственно на кристалле, что исключает необходимость в допол- нительном источнике питания. FLASH-вариант ЭСППЗУ основан на эффекте инжектирования горячих электронов в затвор. Площадь, занимаемая ячейкой, в этом случае почти в 2 раза меньше обычной ячейки ЭСППЗУ, что увеличивает плотность упаковки памяти. Одна из промышленно выпускаемых микросхем EEPROM-памяти показана на Рис. 12.26 (стр. 439). Большинство современных ЭППЗУ/ЭСППЗУ довольно быстрые, со време- нем доступа около 150 нс. Процесс программирования происходит гораздо мед- леннее, около 10 мс на слово, однако это достаточно редкая операция. Програм- мирование FLASH-памяти осуществляется почти в 1000 раз быстрее (на одну ячейку требуется около 10 мкс). Все схемы, рассмотренные на данный момент, относились к классу комбина- ционных. Они не обладают «памятью» в том смысле, что значение их выходов за- висит только от состояния входов в данный момент времени и совершенно не за- висит от предыдущих событий, имевших место на входах. Такие же логические схемы, как защелки, счетчики, регистры и оперативная память (допускающая как чтение, так и запись), относятся к классу последовательностных схем. Состояние выходов таких схем зависит не только от текущего состояния входов, но и от пре- дыстории сигналов на этих входах. Возьмем обыкновенную кнопку, которая используется в дверном звонке. Зво- нок звонит, когда вы нажимаете на нее, и прекращает звонить, когда вы ее отпус- каете. Такой ключ не обладает памятью. Сравним эту кнопку с не менее обыкновенным выключателем. Вы нажимаете на него и свет загорается. Более того, он продолжает гореть даже тогда, когда вы убираете управляющее воздействие (палец). Чтобы выключить свет, вы должны перевести выключатель в выключенное состояние, и опять же, он останется в этом состоянии даже при отсутствии входного воздействия. Ключи такого типа называются бистабильными, поскольку они имеют два устойчивых состояния. Каждый такой ключ ведет себя как 1-битная ячейка памяти, которая может запо- минать либо включенное, либо выключенное состояние. В микросхемах оперативной памяти, таких как 6264 (Рис. 2.26), каждая биста- бильная ячейка формируется с помощью двух перекрестно включенных транзис- торов. Здесь мы не будем касаться конкретной реализации этих ячеек. Вместо этого рассмотрим два логических элемента ИЛИ-НЕ, объединенных перекрест- ными обратными связями (Рис. 2.14). Вспомним, что при появлении лог. 1 на ка- ком-либо входе элемента ИЛИ-HE на его выходе появляется лог. 0 независимо от состояния остальных входов. Вооружившись этим знанием, попытаемся проана- лизировать схему:
44 Часть I. Основы • Если на вход S подать 1, то выход Q переключится в 0. На обоих входах верх- него элемента появится 0, что приведет к появлению 1 на выходе Q. Если теперь на входе S снова появится 0, то нижний элемент останется в 0 (пос- кольку на входе обратной связи с вывода Q присутствует 1) и состояние вы- хода верхнего элемента также не изменится. Таким образом, триггер уста- навливается при подаче положительного импульса на вход S. • Если на вход R подать 1, то выход Q переключится в 0. На обоих входах нижнего элемента появится 0, что приведет к появлению 1 на выходе Q. Ес- ли теперь на входе R снова появится 0, то верхний элемент останется в 0 (поскольку на входе обратной связи с вывода Q присутствует 1) и состояние выхода нижнего элемента также не изменится. Таким образом, триггер сбрасывается при подаче положительного импульса на вход R. При нормальном функционировании (предполагается, что оба входа не могут быть активными в один и тот же момент времени^) оба выхода дополняют друг друга, что отражено на условном графическом изображении триггера (Рис. 2.14, б). Существует много различных реализаций бистабильных ячеек. Например, за- мена элементов ИЛИ-HE на элементы И-НЕ приведет к образованию RS-тригге- ра, в котором активным входным сигналом является лог. 0. В схеме, приведенной на Рис. 2.15, такой триггер используется для подавления дребезга контактов меха- нического переключателя. Переключатели часто используются для управления входами логических схем. Однако большинство металлических контактов не мо- гут замыкаться мгновенно, и при нажатии происходит их многократное размыка- ние/замыкание в течение нескольких десятков миллисекунд. То есть при исполь- зовании механического ключа, скажем, для прерывания работы компьюте- ра/микроконтроллера результат будет совершенно непредсказуем. В схеме на Рис. 2.15 установка триггера происходит при переводе ключа в верхнее положение. При размыкании контактов состояние триггера не меняется, благодаря чему пульсации на выходе схемы отсутствуют. При переводе ключа в нижнее положение схема работает аналогичным образом, только триггер при этом сбрасывается. Дальнейшим развитием RS-триггера является D-защелка. В этом элементе вы- ходной сигнал (Q) повторяет входной (D), если на входе управления С присутству- ет активный уровень (в данном случае — ВЫСОКИЙ), и сохраняет предыдущее значение при неактивном уровне на входе управления. Таким образом, D-защелку можно рассматривать как 1-битную ячейку памяти, запоминающую значение, ко- торое присутствует на ее входе на момент завершения импульса управления. На Рис. 2.16,6 взаимное влияние входов D и С обозначается символами «С1» и «1D». Префикс «1» у D указывает на то, что этот вход зависит от любого сигна- ! ) Если это произойдет, то оба выхода Q и Q должны будут переключиться в 0. После снятия с входов активных сигналов триггер останется в одном из стабильных состояний, определяемом последовательностью снятия сигналов. Реакция триггера на одновременную подачу сигналов установки и сброса не определена и зависит от его конкретной реализации. Если, скажем, поп- робовать одновременно включить и выключить выключатель, то он может просто разломиться на две части!
Глава 2. Логические схемы 45 RS Q R----- R Q 0 0 Q (не изменяется) 0 1 1 (установка) 1 0 0 (сброс) S---- S а) Таблица истинности RS-триггера б) Условное обозначение с прямым и инверсным выходами Рис. 2.14. RS-триггер Рис. 2.15. Использование RS-триггера для подавления дребезга контактов ла, в обозначении которого имеется суффикс «1», в данном случае — от входа С. То есть фиксация значения 1D происходит по сигналу С1. Триггер тоже представляет собой 1-битную ячейку памяти, однако в нем дан- ные передаются на выход только по активному фронту сигнала на управляющем (тактовом) входе. D-триггер, таблица истинности которого приведена на Рис. 2.16, в, переключается по нарастающему фронту (в таблице истинности это обозначается символом «?»), однако часто встречаются и триггеры, переключаю- щиеся по спадающему фронту. Импульсный вход на условном обозначении тригге- ра по стандарту ANSI/IEC обозначается символом >, как показано на Рис. 2.16, г.
46 Часть I. Основы CD Q 1 1 1 (пропуск) OX Q (защелкивание) 1D С1 а) Таблица истинности б) Условное обозначение D-защелки D-защелки С D ? О Т 1 Q О , , (выборка) OX Q 1 X Q (хранение) I X Q в) Таблица истинности D-триггера г) Условное обозначение D-триггера Рис. 2.16. D-защелка и D-триггер Микросхема малой степени интеграции 74LS741), показанная на Рис. 2.17, со- держит два D-триггера. Каждый триггер имеет входы сброса R и установки S, ко- торые являются асинхронными, т.е. их функционирование не зависит от тактово- го сигнала. Среди микросхем средней степени интеграции встречаются наборы из 4, 6 и даже 8 триггеров, имеющих общий тактовый вход. Микросхема 74LS3772), показанная на Рис. 2.18, состоит из восьми D-тригге- ров, тактируемых одним сигналом С, который, в свою очередь, управляется сиг- налом G. То есть 8 бит данных 8D,..., 1D защелкиваются по нарастающему фрон- ту на входе С при НИЗКОМ уровне на входе G. На условном обозначении микро- схемы по стандарту ANSI/IEC, приведенном на Рис. 2.18, б, эта зависимость обозначена как G1 -> 1С2 -> 2D, т.е. вход G разрешает работу тактового входа С, который, в свою очередь, воздействует на входы данных. Наборы D-триггеров обычно называются регистрами, т.е. устройствами памя- ти, хранящими одно слово данных. Полное название микросхемы 74LS377 — ре- гистр с параллельным входом и параллельным выходом (PIPO-регистр), посколь- ку данные загружаются в него и считываются из него параллельно (т.е. одновре- менно). Выпускаются также микросхемы, содержащие массив D-защелок. В качестве примера можно указать 8-битный регистр-защелку 74LS3733), показанный на Рис. 2.19, в котором вместо восьми D-триггеров используется восемь D-защелок. Кроме того, выходы защелок могут устанавливаться в третье состояние. Эта воз- Отечественный аналог — микросхема К555ТМ2. — Примеч. пер. 2) Отечественный аналог — микросхема К555ИР27. — Примеч. пер. 3) Отечественный аналог — микросхема К555ИР22. — Примеч. пер.
Глава 2. Логические схемы 47 а) Логическая схема б) Обозначение по ANSI/IEC Рис. 2.17. Микросхема сдвоенного D-триггера 74LS74 (К555ТМ2) б) Обозначение по ANSI/IEC Рис. 2.18. Микросхема 8-битного параллельного регистра 74LS377 (К555ИР27)
48 Часть I. Основы Рис. 2.19. Микросхема 8-битного параллельного регистра-защелки 74LS373 (К555ИР22) б) Обозначение noANSI/IEC можность используется в тех случаях, когда данные сначала защелкиваются в ре- гистре, а затем выставляются на общую шину для последующего их считывания компьютером. Подходящий пример использования PIPO-регистра приведен на Рис. 2.20. На этой схеме к входу 8-битного регистра подключен выход АЛУ. Выходы регистра, в свою очередь, подключены к одному из входов АЛУ. Этот регистр служит для на- копления результата последовательных операций и обычно называется аккумуля- тором, или рабочим регистром. Чтобы разобраться в работе этой схемы, рассмотрим процесс сложения двух слов — А и В. Если предположить, что АЛУ представляет собой две каскадно-соединенные микросхемы 74LS238, то последовательность операций может быть следующей: 1. Шаг программы • Режим = ООО (сброс). • По импульсу на входе «Исполнение» значение с выхода АЛУ (00000000) загружается в регистр. • Выходные данные — ноль (00000000).
Глава 2. Логические схемы 49 2. Шаг программы • Значение слова А подается на вход АЛУ. • Режим = 011 (сложить). • По импульсу на входе «Исполнение» значение с выхода АЛУ (слово А + + ноль) загружается в регистр. • Выходные данные — слово А. 3. Шаг программы • Значение слова В подается на вход АЛУ. • Режим = 011 (сложение). • По импульсу на входе «Исполнение» значение с выхода АЛУ (слово В + + слово А) загружается в регистр. • Выходные данные - сумма слов В и А. Рис. 2.20. 8-битный блок обработки (АЛУ/рабочий регистр)
50 Часть I. Основы Последовательность кодов операций (ООО — 100 — 100) и составляет программу. На практике каждая команда будет также содержать (при необходимости) адрес обрабатываемых данных; в данном случае — местонахождение слов А и В. Результат любой операции характеризуется некоторым набором свойств. К примеру, результат может быть равен нулю или же при его вычислении может произойти переполнение. Эти свойства могут потребоваться при дальнейшем вы- полнении программы. В рассматриваемой схеме для сбора такой информации ис- пользуются два D-триггера, тактируемые сигналом «Исполнение». В данном кон- тексте состояния этих триггеров называются флагами (реже — семафорами). Та- ким образом, у нас имеются флаг нуля Z и флаг переноса из 7-го бита С, образующие регистр состояния (STATUS). Как мы увидим далее, связка АЛУ/рабочий регистр является «сердцем» любо- го цифрового вычислительного устройства. Причем при использовании сложных систем, таких как компьютер или микроконтроллер, нам совершенно не нужно досконально знать их внутреннее устройство, а процессы, протекающие в систе- ме, скрыты от пользователя. К примеру, на Рис. 2.21 изображен тот же самый блок, но на более высоком уровне абстракции. В частности, группы линий дан- ных (шины) изображены в виде толстых линий, действительная их реализация не имеет никакого значения. Количество линий в шине не показано, но при необхо- димости оно указывается рядом с коротким штрихом, пересекающим изображе- ние шины по диагонали, например так . Рис. 2.21. 8-битный блок обработки (АЛУ/рабочий регистр) на системном уровне Центральным элементом нашей системы является АЛУ, изображение которо- го имеет сложную форму. Значения на его входах данных (операнды) обрабатыва- ются согласно сигналам на входах режима. Первый операнд поступает извне, тог-
Глава 2. Логические схемы 51 да как 2-й операнд считывается из рабочего регистра. В компьютерах коды, пода- ваемые на вход режима, обычно считываются из памяти программ, а 1-й операнд — из памяти данных. Значение с выхода АЛУ может быть загружено обратно в рабочий регистр W по сигналу «Исполнение» либо передано вовне по шине данных. Такая структура показана на Рис. 3.2 (стр. 60). Существуют также и другие разновидности регистров. Четырехбитный сдвиго- вый регистр, показанный на Рис. 2.22, а, является примером структуры с последо- вательным вводом и последовательным выводом (SISO). В данном случае бит данных, хранящийся в л-м D-триггере, поступает на вход следующего ((л + 1)-го) каскада. При подаче тактового импульса (или, в данном контексте, импульса сдвига) этот бит перегружается в (л + 1)-й триггер, т.е. сдвигается с л-й позиции в позицию л + 1. Поскольку все триггеры тактируются одним сигналом, по каждо- му импульсу сдвига все слово данных сдвигается вправо. Дополнительный параллельный выход Последовательный выход данных а) 4-битный сдвиговый регистр Вход импульсов»—^ сдвига ‘ Последовательный вход данных SRG4 1D Qo Qi Q2 Последовательный выход данных/Оз в) Обозначение по ANSI/IECрегистра с последовательным вводом и параллельным выводом Рис. 2.22. Сдвиговый регистр с последовательным вводом и выводом В примере, приведенном на Рис. 2.22, б, по тактовому сигналу в левую пози- цию побитно вдвигается 4-битное число. После 4-го импульса новое слово пол- ностью окажется в регистре. Для его считывания потребуется еще четыре импуль- са, во время которых произойдет побитная выдача содержимого сдвигового ре- гистра. Если обеспечить доступ к выходу каждого триггера, чтобы данные можно было считать за один раз, получим структуру с последовательным входом и па- раллельным выходом (SIPO).
52 Часть I. Основы На Рис. 2.22, в символ «->» в обозначении тактового входа используется для указания операции сдвига. Аббревиатура SRG4 означает «4-битный сдвиговый регистр». Пример 8-битного сдвигового регистра приведен на Рис. 12.2 (стр. 370). Существуют и другие разновидности структур, в том числе структура с парал- лельным входом и последовательным выходом (PISO), часто применяемая для преобразования параллельного кода в последовательный. Инкрементирование или декрементирование счетных регистров (счетчиков) производится по каждому импульсу тактового сигнала в соответствии с двоичной последовательностью. Обычно «-битный счетчик может отсчитывать 2п состояний. Некоторые счетчики можно загружать в параллельном режиме, т.е. использовать как память. Рассмотрим D-триггер, тактируемый по спадающему фронту (Рис. 2.23), ин- версный выход Q которого подключен к входу 1D. По каждому спадающему фронту на входе С1 данные с входа 1D будут защелкиваться и появляться на вы- ходе Q. Поскольку инверсный сигнал этого выхода подается обратно на вход, то в следующий раз триггер переключится в противоположное состояние. Это перио- дическое переключение между двумя состояниями помечено на временной диа- грамме символом «Т». В результате при подаче на вход триггера сигнала некото- рой частоты на его выходе будет сформирована последовательность импульсов, частота которых в 2 раза ниже. Если частота входного сигнала не изменяется, то выходной сигнал представляет собой точный прямоугольный сигнал (меандр). Иногда такой Т-триггер называют триггером счетного типа или делителем на два. Разумеется, Т-триггеры тоже можно каскадировать, как показано на Рис. 2.24, а. В данном случае 4 триггера с запуском по спадающему фронту соеди- нены таким образом, чтобы выход л-го разряда управлял тактовым входом разря- да п + 1. Соответственно, если частота сигнала на входе С равна 8 кГц, то на выхо- де Qa будет прямоугольный сигнал частотой 4 кГц, на выходе QB — 2 кГц, на Qc — 1 кГц и на Qd — 500 Гц. Сигнал QA на Рис. 2.24, б формируется так же, как и на Рис. 2.23. Выход QB переключается по каждому спадающему фронту сигнала QA.
Глава 2. Логические схемы 53 Аналогично функционируют и остальные выводы. Сопоставив ВЫСОКОМУ уровню лог. 1, а НИЗКОМУ — лог. О, получим 24 (16) двоичных комбинаций в по- ложительной логике, сдвинутых по фазе друг относительно друга. При достиже- нии максимального значения счет начинается с 0 и так до бесконечности. Каждая комбинация остается в регистре до появления активного фронта следующего так- тового импульса (в данном случае — спадающего фронта). Если взглянуть на формируемую последовательность, то можно увидеть, что она представляет собой последовательность натуральных двоичных чисел от Ь’0000’ до b’l 11 Г. Вообще говоря, такая схема называется двоичным счетчиком по модулю 16. При счете по модулю п используются только первые п формируемых значений1*. т2 т4 +8 +16 а) Каскадное соединение Т-триггеров с., зъъгшллшиииишллл. Qc 0 0 0 0 | 1111 | О О О О I 1 1 1 1 |_0 qd оооооооо 1 1 i i i i i Г|_о б) Формируемые сигналы Рис. 2.24. Счетчик со сквозным переносом по модулю 16 Теоретически нет никаких ограничений на количество каскадов, соединяе- мых указанным образом. То есть, используя 8 Т-триггеров, мы получим счетчик по модулю 256 (28). На практике же каждый триггер переключается с некоторой задержкой, что ограничивает максимально возможную частоту счетчика. К при- меру, у сдвоенного D-триггера, показанного на Рис. 2.17, максимальная задержка распространения сигнала от фронта тактового импульса до появления выходного С математической точки зрения любое число можно преобразовать в его эквивалент по модулю п путем деления этого числа на п. Остаток, или, иначе, модуль, будет представлять собой число от 0 до п — 1.
54 Часть I. Основы значения составляет 25 нс. Максимальная частота переключения одного каскада, например, такого как показан на Рис. 2.23, составляет 25 МГц. Соответственно, максимальная задержка в 8-битном счетчике составит 200 нс. Если такой счетчик со сквозным переносом будет тактироваться сигналом с частотой 5 МГц (равной —!—), то возникнет ситуация, при которой новое значение будет формировать- 200 нс ся до установления предыдущего. Это представляет серьезную проблему, если различные состояния счетчика декодируются и используются для управления другими схемами. Схема декодирования, например, такая как приведена на Рис. 2.25, может отреагировать на это кратковременное переходное состояние не- произвольным образом, что вызовет сбой в работе устройства. В таких случаях лучше использовать более сложный синхронный счетчик, в котором все триггеры переключаются одновременно. Рис. 2.25. Формирование временных диаграмм Рассмотренные схемы осуществляли прямой счет. Если в качестве выходов использовать инверсные (Q), то счет будет осуществляться в обратном направле- нии (обратный счет). Того же результата можно достичь, если в качестве элемента памяти использовать триггеры, переключающиеся по нарастающему фронту, та- кие как сдвоенный триггер 74LS74. С помощью простой логической схемы можно легко объединить эти две функ- ции и реализовать программируемый реверсивный счетчик. Еще можно добавить логику для параллельной загрузки триггеров любым значением, с последующим счетом от этого значения в заданном направлении. Такие структуры называются счетными регистрами с параллельной загрузкой. Наряду с наиболее очевидным использованием счетного регистра для накоп- ления числа событий, например, таких как количество консервных банок, про- шедших через конвейер, существуют и другие варианты его использования. Од- ним из таких применений является разнесение во времени некоторых операций. На Рис. 2.25 счетчик по модулю 4 используется для управления одной из секций дешифратора 2 на 4 в микросхеме 74LS139 (Рис. 2.5, а). Этот дешифратор детек- тирует 4 состояния счетчика и формирует четыре сигнала, сдвинутых во времени друг относительно друга, которые могут использоваться, скажем, для задания
Глава 2. Логические схемы 55 последовательности операций, выполняемых управляющей логикой компьютера. Для адресации дешифратора используется инверсный выход триггеров. Это сде- лано специально, поскольку в противном случае по нарастающему фронту такто- вого сигнала осуществлялся бы обратный счет. Счетчики с большей разряд- ностью могут использоваться для формирования более сложных последователь- ностей управляющих операций. Термин «регистр», как правило, используется применительно к элементу опе- ративной памяти, который может хранить одно двоичное слово, обычно разряд- ностью от 4 до 64 бит. Память большего объема можно реализовать, группируя п таких регистров и выбирая один из них. Подобная структура обычно называется регистровым файлом. Например, микросхема 74LS6701) представляет собой ре- гистровый файл 4 х 4 с раздельными входом и выходом 4-битных данных, а также отдельными входами 2-битного адреса для операций чтения и записи. Это озна- чает, что любой регистр этого файла может быть считан в любой момент времени независимо от одновременно осуществляемой записи. Память больших объемов называется оперативной памятью произвольного до- ступа или сокращенно ОЗУ. Словосочетание «произвольный доступ» означает, что для выбора любого слова памяти требуется одно и тоже время, не зависящее от расположения этого слова в матрице2). Этим ОЗУ отличается от памяти на магнит- ной ленте, в которой бобина должна прокрутиться до требуемого сектора. А если этот сектор находится в конце ленты... Для примера на Рис. 2.26 показана микросхема ОЗУ 62643). Она содержит мат- рицу из 65 536 (216) бистабильных ячеек, организованных в виде матрицы из 8192 (213) 8-битных слов. Слово п выбирается при подаче на линии адреса АО...А12 дво- ичного числа п. В режиме чтения (R/W= 1) на выходы I/O7...1/00 выдается я-е слово данных, определяемое я-й комбинацией битов адреса. Символ «А» в обозначении вхо- дов/выходов (как и на Рис. 2.12) указывает на эту взаимосвязь. Для включения вы- ходных буферов с тремя состояниями на входе ОЕ должен быть НИЗКИЙ уровень. Адресованное слово записывается в память при R/W= 0. Байт данных, кото- рый должен быть записан в я-ю ячейку, подается на входы 1/07...1/00. Такая дву- направленная передача данных является отличительной особенностью компью- терных шин. В обоих случаях микросхема ОЗУ должна быть выбрана подачей лог. О на вы- вод CS1 и лог. 1 — на вывод CS2. В зависимости от версии микросхемы интервал между подачей сигналов выборки и началом обращений к ней составляет от 100 до 150 нс. Если напряжение питания не пропадает, время хранения данных не ог- раничено. По этой причине микросхема 6264 называется статическим ОЗУ (SRAM). Вместо того чтобы использовать для хранения одного бита пару тран- зисторов, данные можно хранить в виде заряда емкости затвор-исток одного по- !) Отечественный аналог — микросхема К555ИР26. — Примеч. пер. 2) Строго говоря, ПЗУ тоже следует называть памятью с произвольным доступом, однако по традиции этот термин используется только для ОЗУ. 3) Отечественный аналог — микросхема К537РУ17. — Примеч. пер.
56 Часть I. Основы Е А12 [Т А7 [Т А6 [Т А5 [Т А4 []Г АЗ [Г А2 |Т А1 [V АО []£ l/OO [К 1/01 [iT 1/02 [кГ VSS [й~ VDD 27 | R/W 26]CE2 25 | A8 A9 23 | A11 22] 6Ё 2l] A10 CE1 19j |/07 is] 1/06 n] 1/05 Тб] 1/04 is] 1/03 а) Корпус DIP б) Обозначение no ANSI/IEC Puc. 2.26. Микросхема ОЗУ 6264 (8196 x 8 бит) левого транзистора. Время рассасывания подобного заряда составляет несколько миллисекунд, поэтому заряд необходимо периодически обновлять. Такая дина- мическая память (DRAM) дешевле в изготовлении, и микросхемы данного типа имеют большую емкость. Обычно память подобного типа используется там, где требуется очень большой объем памяти, например в персональных компьютерах. В этом случае стоимость схемы регенерации компенсируется дешевизной микро- схем памяти. Оба типа памяти являются энергозависимыми, т.е. они не сохраняют свое со- держимое после выключения питания. Однако некоторые микросхемы статичес- кого ОЗУ позволяют хранить данные при напряжении, которое ниже, чем рабо- чее, потребляя при этом очень маленький ток. В таких случаях для сохранения содержимого в течение нескольких месяцев можно использовать батарею.
ГЛАВА ОБРАБОТКА ХРАНИМОЙ ПРОГРАММЫ В предыдущей главе мы с вами разработали простейший процессор, состоя- щий из арифметико-логического устройства (АЛУ) и регистра с параллельным вводом/выводом данных. Собственно АЛУ выступает в роли «числодробилки», а рабочий регистр используется для хранения операндов, а также результатов всех операций. В нашем примере, описанном на стр. 33, мы складывали вместе два числа, накапливая результат в рабочем регистре. Если задавать код режима работы АЛУ перед каждым шагом, то мы в принципе можем заставить наше вычислитель- ное устройство выполнить любую задачу, которая может быть описана последова- тельностью арифметических и логических операций. Эта совокупность кодов ко- манд (например, «сложить», «вычесть», «логическое И», ...) может храниться во внешней памяти. Там же могут находиться различные операнды, передаваемые в АЛУ, а также результаты выполнения команд. Таким образом, эти коды включают в себя как собственно программу программируемого устройства, так и различные операнды, или данные. Извлекая (fetch) эти команды по очереди, мы можем выпол- нять заданную программу. Такая структура вместе с соответствующими каналами передачи данных, дешифраторами и логическими схемами обычно называется цифровым компьютером или цифровой вычислительной машиной. Как мы с вами вскоре убедимся, в основе архитектуры микроконтроллера ле- жит архитектура компьютера. С учетом этого обстоятельства в данной главе рас- сматривается архитектура и рабочий ритм некоего обобщенного компьютера. Не- смотря на то что этот компьютер является чисто гипотетическим устройством, при его «разработке» принимались во внимание именно те микроконтроллеры, которые рассматриваются в данной книге. Прочитав эту главу, вы: • Познакомитесь с фон-неймановской архитектурой и узнаете ее недостатки. • Познакомитесь с гарвардской архитектурой, с ее параллельно работающи- ми блоками выборки и дешифрации, а также раздельными адресными про- странствами. • Поймете, какая взаимосвязь существует между цифровым компьютером, микропроцессором и микроконтроллером. • Познакомитесь со структурой памяти программ, а также ее взаимодействи- ем со счетчиком команд и конвейером.
58 Часть I. Основы • Узнаете формат типичных команд. • Познакомитесь с назначением и структурой памяти данных. С исторической точки зрения электронные цифровые вычислительные ма- шины в том виде, в котором мы их сегодня знаем, являются косвенным результа- том Второй мировой войны. В то время были созданы различные опытные образ- цы компьютеров, причем некоторые из них действительно работали Ч Как прави- ло, эти вычислительные машины представляли собой специализированные устройства, предназначенные для выполнения какой-либо конкретной задачи при различных входных данных. Алгоритм функционирования некоторых из та- ких машин можно было менять, но при этом требовалась их частичная переделка. Поскольку принципиальный вопрос возможности создания таких вычисли- тельных систем был уже решен, основным достижением группы инженеров, ра- ботавших с Джоном фон Нейманом* 2), было осознание того факта, что программа может храниться в памяти вместе с данными. Основным преимуществом такого подхода является его гибкость, так как для изменения программы достаточно просто загрузить новый код в соответствующую область памяти. По существу, фон-неймановская3) архитектура, показанная на Рис. 3.1, состоит из центрально- го процессора (ЦПУ), памяти и общей шины (называемой также магистралью), по которой в обоих направлениях пересылаются данные. На практике ЦПУ также должен взаимодействовать и с окружающим миром. При этом данные к/от соот- ветствующих интерфейсных портов передаются по одной общей шине данных. Огромным преимуществом фон-неймановской архитектуры является ее про- стота, поэтому данная концепция легла в основу большинства компьютеров об- щего назначения. Однако использование общей шины означает, что в любой мо- мент времени может выполняться только одна операция. Соответственно, пере- сылка данных между ЦПУ и памятью данных не может осуществляться !) В качестве примера можно привести английский компьютер под названием «Колосс», кото- рый на протяжении нескольких лет использовался для расшифровки секретных кодов немецкой армии. Более подробную историческую и техническую информацию об этих первых компьютерах вы сможете узнать, посетив Web-сайт, посвященный оригинальному изданию данной книги. 2) Джон фон Нейман — венгерский математик, принимавший участие в Манхэттенском проекте (американская программа по созданию ядерной бомбы), осуществлявшемся во время Второй мировой войны. После войны был принят на должность консультанта в проекте по созданию машины EDVAC, проводившемся в Муровской школе электрических разработок (The Moore School of Electrical Engineering) Пенсильванского университета (The University of Pennsylvania). В этом компьютере предполагалось реализовать концепцию хранения программ и данных в общей памяти. Эти идеи фон Нейман опубликовал в 1946 году, однако EDVAC был запущен только в 1951 году. По иронии судьбы англичане смогли реализовать эти идеи гораздо раньше — компьютер «Марк 1», созданный в Манчестерском университете (The University of Manchester), выполнил свою первую программу уже в июне 1948 года! Совсем немного отстали от своих коллег разработчики из Кембриджского университета — их компьютер EDSAC был запущен в мае 1949 года, почти за два года до создания EDVAC. 3) Справедливости ради стоит упомянуть и об одной из первых отечественных ЭВМ «М-1», эксплуатация которой была начата весной 1952 года. В этой машине тоже была реализована концепция программы, хранимой в оперативной памяти, хотя отчет Принстонского универси- тета, в котором были сформулированы архитектурные принципы Дж. фон Неймана, в то время и не был известен разработчикам «М-1». — Примеч. пер.
Глава 3. Обработка хранимой программы 59 Окружающий мир Память Схема управления Блок обработки *<ч (АЛУ) Окружающий мир Центральный процессор (ЦПУ) Рис. 3.1. Элементарная фон-неймановская вычислительная машина (шина адреса не показана) одновременно с выборкой команды из памяти программ. Эту особенность иногда называют фон-неймановским узким местом. В первое послевоенное десятилетие в Гарвардском университете было создано несколько компьютеров семейства «Марк», от «Марк 1» до «Марк 4», в которых память программ была полностью отделена от памяти данных (в первых машинах «Марк 1» и «Марк 2» программа считывалась с бумажной перфоленты). Такая концепция была более эффективной, чем фон-неймановская (или, как ее иногда называют, принстонская1J) архитектура, поскольку код программы мог считы- ваться из памяти программ одновременно с обменом между ЦПУ и памятью дан- ных или с операциями ввода/вывода. Однако такие машины были намного слож- нее и дороже в изготовлении. А с учетом уровня технического развития 50-х го- дов, да еще и после проигрыша в конкурсе на создание компьютера для контроля сети континентальных радиолокационных станций, устроенного Министерством обороны США, они и вовсе не получили широкого распространения. Однако с развитием сложных интегральных схем эта гарвардская архитектура снова оказа- лась в центре внимания. На Рис. 3.2 показаны две физически разделенные шины, используемые для передачи информации между ЦПУ и этими неперекрывающимися областями памяти. Каждая память имеет собственную шину адреса, поэтому адрес ячейки памяти программ никоим образом не связан с адресом ячейки памяти данных. В таком случае говорят, что обе области памяти находятся в различных адресных пространствах. Память данных иногда называют файловой памятью, в этом слу- чае п-я ячейка обозначается как файл п. !) Причем это название более верно с исторической точки зрения, поскольку авторство принадлежит не фон Нейману, а группе разработчиков компьютера ENIAC, у которых фон Нейман проходил стажировку. — Примеч. пер.
60 Часть I. Основы Окружающий мир Окружающий мир Рис. 3.2. Элементарная гарвардская вычислительная машина (шины адреса не показаны) А теперь давайте познакомимся поближе с различными элементами компью- терной архитектуры. Центральный процессор Центральный процессор состоит из связки АЛУ/рабочий регистр и соответ- ствующей управляющей логики. По сигналам схемы управления команды про- граммы выбираются из памяти, дешифруются и исполняются. Данные, которые получаются или используются во время выполнения программы, также распола- гаются в памяти. Этот цикл «выборка — исполнение» образует рабочий ритм вы- числительной машины и повторяется непрерывно в течение всего времени, когда система находится в активном состоянии. Память Во всех вычислительных устройствах память используется для хранения как кода программы, так и данных. Память с произвольным доступом характеризуется содержимым, хранящимся в группе ячеек, и расположением (адресом) каждой ячейки. В случае фон-неймановской архитектуры и программа, и данные распо- лагаются в одной области памяти, тогда как при использовании гарвардской архи- тектуры эти объекты располагаются в совершенно разных областях. То есть адреса одной области памяти никоим образом не связаны с адресами другой области. В обоих случаях данные, хранящиеся в памяти, передаются в ЦПУ по шине дан-
Глава 3. Обработка хранимой программы 61 ных. При этом ЦПУ выставляет на шину адреса код адреса той ячейки, к которой он собирается обратиться. В системах с гарвардской архитектурой каждая область памяти имеет собственные шины адреса и данных (Рис. 3.4). В запоминающих ус- тройствах с произвольным доступом длительность операции чтения или записи любой из ячеек не зависит от положения этой ячейки в адресном пространстве. В большинстве компьютеров используется долговременная память больших объемов, обычно на магнитных или оптических дисках, в которой время доступа к ячейке зависит от ее физического расположения в памяти. Помимо этого недо- статка, присущего памяти с последовательным доступом, такие устройства, как правило, слишком медленны для использования их в качестве основной памяти. Поэтому они используются для резервного хранения больших объемов данных (например, ответов на экзаменационные билеты) или программ, которые перед выполнением необходимо подгружать в основную память. Память программ В памяти программ хранится двоичный код, составляющий программу, или программное обеспечение (software). Это слово созвучно термину hardware (аппа- ратные средства) и отражает тот факт, что данный код не связан с каким-либо фи- зическим изменением схемы устройства. В идеале память, в которой находится программа, должна быть такой же быстрой, как и ЦПУ, поэтому для данных це- лей обычно используется полупроводниковая память, созданная при помощи технологий, подобных рассмотренным в предыдущей главе1*. Память данных В памяти данных хранятся данные, используемые во время работы програм- мы. И опять же быстродействие этой памяти обычно сравнимо с быстродействи- ем ЦПУ. Также в адресном пространстве памяти данных могут располагаться спе- циальные регистры, например порты ввода/вывода. Интерфейсные порты Независимо от своего назначения компьютер должен иметь возможность вза- имодействовать с окружающим миром. Хотя обычно вспоминаются такие уст- ройства, как клавиатура и монитор, можно считывать и изменять состояние практически любого физического устройства. Так, данные об объеме топлива, впрыскиваемого в цилиндр двигателя, в совокупности со значением скорости вращения вала могут использоваться для управления моментом зажигания искры в камере сгорания бензинового двигателя. Однако это не всегда верно — самые первые быстродействующие устройства памяти про- грамм были построены на ферритовых кольцах, которые могли намагничиваться в одном из двух направлений. Память на магнитных сердечниках использовалась с 50-х годов вплоть до начала 70-х, но и до сих пор в литературе можно встретить термин core (сердечник), применяе- мый для обозначения памяти программ.
62 Часть I. Основы Шина данных Все элементы фон-неймановского компьютера соединяются между собой од- ной общей магистралью передачи данных, или шиной (понятие шины было вве- дено во 2-й главе, см. Рис. 2.4 на стр. 34). Вся информация передается по этим об- щим линиям в обоих направлениях, при этом ЦПУ играет роль главного контрол- лера. В компьютере с гарвардской архитектурой память программ имеет отдельную шину данных, что позволяет осуществлять выборку команд одновре- менно с действиями на шине данных памяти программ. Другие шины использу- ются для передачи адресов различным областям памяти, а также управляющей информации и информации о состоянии (см. Рис. 3.4). Микроконтроллеры, которым посвящена эта книга, имеют гарвардскую архи- тектуру, поэтому дальше мы будем рассматривать только ее. Взяв за основу ЦПУ, показанный на Рис. 2.20 (стр. 49), и добавив к нему память программ, память данных, а также схемы управления и дешифрации, мы получим примитивный компьютер с гарвардской архитектурой (Рис. 3.3). Серым цветом на рисунке вы- делены элементы исходной схемы с Рис. 2.21, приведенного на стр. 50. Благодаря подключению шины данных АЛУ к памяти данных, мы получа- ем возможность считывать из памяти первый операнд, а также при необходи- мости помещать в нее результат операции. Адрес этого операнда является частью кода команды, считанного из памяти программ и дешифрованного уст- ройством управления. Это же устройство управления формирует сигналы вы- бора режима АЛУ, который зависит от текущей команды. Результат, получае- мый на выходе АЛУ, может быть загружен либо в рабочий регистр (устройство управления формирует импульс на линии W), либо обратно в ту же ячейку па- мяти, откуда был считан операнд (устройство управления формирует импульс на линии F). Информация об адресате результата операции также содержится в коде команды. Команды (в виде кодовых слов) обычно располагаются в памяти программ последовательно. Для поочередной адресации каждой команды используется двоичный суммирующий счетчик (см. Рис. 2.24 на стр. 53). Если, предполо- жим, при сбросе компьютера счетчик команд (Program Counter — PC) обнуляет- ся, то первая команда будет расположена по адресу h’000’ памяти программ, вторая — по адресу h’001’ и т.д. (см. Рис. 3.4). Устройство управления просто инкрементирует счетчик после выборки каждой команды. Непосредственно за- гружая новый адрес в счетчик команд, можно осуществить переход к другому участку кода. Последовательность операций «выборка команды/ее дешифровка/исполне- ние», т.е. так называемый цикл выборки — исполнения команды, является фунда- ментальным понятием, необходимым для понимания работы компьютера. Чтобы проиллюстрировать этот рабочий ритм, рассмотрим простую программу, которая считывает переменную NUM_1, прибавляет к ней число 4 и записывает результат в
Глава 3. Обработка хранимой программы 63 Рис. 3.3. Структура элементарного гарвардского компьютера на системном уровне переменную NUM_2. На языке высокого уровня Си эту операцию можно записать следующим образом1*: NUM_2 = NUM_1 + 4; Несколько более подробно структура нашего компьютера, который я назвал BASIC (аббревиатура от Basic All-purpose Stored Instruction Computer — базовый универсальный компьютер с хранимой программой), изображена на Рис. 3.4. На этом рисунке показаны ЦПУ и обе области памяти со своими шинами данных и соответствующими шинами адреса. Центральный процессор можно условно разделить на две части. Узлы, распо- ложенные в левой части рисунка, осуществляют выборку кодов команд и пооче- редно передают их в дешифратор команд ID. Узлы, расположенные в правой час- ти, исполняют каждую из команд в соответствии с сигналами, формируемыми этим дешифратором. Сначала разберемся с процессом выборки команд. Счетчик команд Команды обычно располагаются в памяти программ последовательно, а счет- чик команд PC является обычным счетным регистром, определяющим местона- хождение текущей команды. Этот суммирующий счетчик иногда называют (мо- жет быть, даже более правильно) указателем команд. На языке Паскаль или Модула-2 это же выражение будет иметь вид NUM_2 : = NUM_1 + 4.
64 Часть I. Основы Память программ Память данных ООО movf 25,w 0825 001 addlw 04 ЗЕ 04 002 movwf 26 00А6 003 00F 010 012 022 020 032 030 РегистрИ команд IR1 У addlw 04 IR1 ЗЕ04 PC 001 013 023 033 BASIC 01F 02F 2С 21 23 24 TUM1& Счетчик команд movf 25,w IR2 0825 РегистрТ команд IR2 у Блок выборки Регистр адреса FAR 03F Центральный процессор|(ЦПУ) ATUM22C 27 Исполнительный FDR DATUM 1 Дешифратор команд управления । внутренними! W DATUM1 АЛУ Пропуск Рабочий регистр NUM_1 хранится /здесь Лмим_2 / хранится здесь Внутренняя шина передачи данных Рис. 3.4. Состояние ЦПУ в момент выполнения первой команды при одновременной выборке второй команды. Все адреса/данные представлены в шестнадцатеричной системе Поскольку счетчик команд подключен через внутреннюю шину данных к ис- полнительному блоку, мы можем использовать АЛУ для управления этим регист- ром и изменения предопределенной последовательности исполнения команд. Та- ким образом, можно реализовать различные команды перехода к другим частям программы, а также команды пропуска. Конвейер В двух регистрах команд содержатся коды команд, считанные из памяти про- грамм. В начало конвейера (в первый регистр команд, IR1) загружается код л-й команды и хранится там для обработки в следующем цикле. Это позволяет испол-
Глава 3. Обработка хранимой программы 65 нять командуя - 1, находящуюся в конце конвейера (во втором регистре команд, IR2) одновременно с выборкой л-й команды и загрузкой ее в конвейер. Работа конвейера показана на Рис. 3.7. Дешифратор команд Дешифратор команд ID является «мозгами» ЦПУ — он дешифрует код коман- ды, находящийся в регистре IR2, и формирует в определенной последовательнос- ти сигналы для исполнительного блока, необходимые последнему для определе- ния местоположения операнда (если таковой имеется) в памяти данных и для пе- реключения АЛУ в заданный режим. На Рис. 3.4 показано выполнение команды movf h ’ 2 5 ' , w (копирование содержимого регистра данных с адресом h’25’ в ра- бочий регистр). Исполнительный блок осуществляет обращения к памяти данных и конфигу- рирование АЛУ. Работой исполнительного блока управляет дешифратор команд, функционирование которого, в свою очередь, зависит от значения кода команды п — 1, находящегося в регистре команд IR2. Исполнительный блок обрабатывает все числа группами по восемь битов, данные во всех регистрах и в памяти данных также хранятся побайтно. Поэтому о таком компьютере обычно говорят как о 8-битном процессоре. Регистр адреса Когда ЦПУ собирается обратиться к ячейке (регистру) памяти данных, он по- мещает адрес этой ячейки в регистр адреса FAR. При этом производится непос- редственная адресация памяти данных по ее шине адреса. Как показано на Рис. 3.4, из памяти данных считывается регистр с адресом h’25’, и его содержимое защелкивается во внутреннем регистре данных FDR процессора. Регистр данных Этот «двунаправленный» регистр выполняет две функции: • Хранит содержимое адресованного регистра данных, если ЦПУ осуществ- ляет цикл чтения. Именно это происходит при выполнении 1-й команды (movf h'25* ,w), которая пересылает (копирует) содержимое регистра с адресом h’25’ в рабочий регистр. • Хранит данные, которые ЦПУ собирается записать в адресованный регистр данных. Такой цикл записи, в частности, имеет место при выполнении ко- манды movwf h1 2 6 ', которая пересылает (копирует) содержимое рабочего регистра в регистр с адресом h’26’.
66 Часть I. Основы Арифметико-логическое устройство АЛУ выполняет арифметические и логические операции, определяемые зна- чением кода режима (см. Рис. 2.10 на стр. 39), который извлекается из кода ко- манды дешифратором команд. Регистр состояния Этот регистр содержит флаги нуля Z и переноса С, которые устанавливаются соответственно, если результат операции равен нулю и если в результате сложе- ния возник перенос. Рабочий регистр В рабочем регистре АЛУ (W) обычно находится один из операндов команды — либо источник, либо адресат. Например, команда addwf h1 20 1 ,w складывает содержимое рабочего регистра с содержимым регистра h’20’ и помещает сумму обратно в рабочий регистр W. В некоторых компьютерах этот регистр называется также аккумулятором. Помимо ЦПУ, в нашем компьютере имеется две области памяти, предназна- ченные для хранения кода программы и данных. Память программ * Каждая позиция (или ячейка) памяти программ может содержать одну команду, которая кодируется 14-битным словом. Из Рис. 3.4 видно, что каждая из этих ячеек имеет свой адрес, выставляемый счетчиком команд на шину адреса памяти про- грамм. На этом рисунке содержимое PC равно h’001 ’ (или Ь’00000000000001 ’), что приводит к выдаче содержимого ячейки h’001 ’ на шину данных памяти программ и, следовательно, загрузке его в начало конвейера. В рассматриваемом примере счи- танное значение равно h’3E04’ (или Ь’11111000000100’), что является машинным кодом команды addlw 04. В конечном счете дешифратор команд интерпретирует этот код как операцию сложения константы «4» с рабочим регистром. Память данных Каждая ячейка (или регистр) памяти данных содержит один байт (восемь би- тов) данных. Адрес регистра формируется исполнительным блоком в регистре ад- реса FAR и выставляется на шину адреса памяти данных. Содержимое адресован- ного регистра либо считывается в регистр данных FDR, либо перезаписывается находящимся в нем значением.
Глава 3. Обработка хранимой программы 67 Шины адреса и данных памяти данных совершенно независимы от одно- именных шин памяти программ, что позволяет одновременно обращаться к обе- им областям памяти. Таким образом, адреса памяти программ и памяти данных имеют различный смысл, т.е. адрес h’25’ в памяти программ совсем не то же са- мое, что адрес h’25’ в памяти данных, который соответствует регистру h’25’. Теперь, когда у нашего ЦПУ появилась память программ и память данных, рассмотрим более подробно саму программу. Наша иллюстративная программа состоит всего из трех команд и, как уже упоминалось, предназначена для копиро- вания увеличенного на 4 значения однобайтной переменной, расположенной по адресу NUM_1, в ячейку с адресом NUM_2. Из Рис. 3.4 видно, что переменная num_1 представляет собой псевдоним для обозначения содержимого регистра данных h’25’. Аналогично, имя num_2 является символическим выражением для указания содержимого регистра данных h’26’. А теперь рассмотрим используемые в программе команды. movf Эта команда (MOVe File) копирует содержимое заданного регистра данных в ра- бочий регистр (как правило) или же обратно в тот же регистр данных (см. стр. 141). Таким образом, команда movf num_1,w загружает байт, расположен- ный по адресу h’25’ памяти данных, в рабочий регистр. Если содержимое указан- ного регистра равно нулю, то при выполнении команды устанавливается флаг Z, в противном случае этот флаг будет сброшен. addlw Эта команда (ADD Literal to Working register) прибавляет однобайтную константу к содержимому рабочего регистра. Таким образом, команда addlw 04 прибавля- ет число 4 к содержимому рабочего регистра и записывает результат обратно в этот же регистр. Если возникает переполнение, то устанавливается флаг С, а если результат равен нулю — флаг Z. movwf Эта команда (MOVe Working register to File) копирует содержимое рабочего регис- тра в заданный регистр данных. Таким образом, команда movwf NUM_2 сохраня- ет содержимое рабочего регистра по адресу h’26’ памяти данных. На состояние флагов данная команда не влияет. Описывая команды, мы использовали мнемонические обозначения, такие как addlw. Разумеется, реальные логические схемы, декодирующие эти команды, работают исключительно с двоичными кодами. Мнемонические обозначения просто играют роль своеобразных «памяток» для программиста. Хотя крайне ма- ловероятно, что кто-либо станет писать программы в машинных кодах, двоичный формат всех команд имеет логическую основу, и его знание будет полезно для по- нимания недостатков и ограничений набора команд и реальных аппаратных средств, которые мы будем обсуждать в следующих двух главах. Пока мы рассмотрим две категории команд.
68 Часть I. Основы КОП d fffffff Прямая адресация регистра данных Данный способ адресации используют команды, в которых указывается адрес регистра данных, являющийся их операндом. Например, в команде movf h' 25' , w операндом является регистр h’25’. Из Рис. 3.5 видно, что 14-битный код команды состоит из трех частей: • Шесть старших битов (13...8) называются кодом операции или, сокращенно, КОП. Каждая команда имеет уникальный КОП, и именно по его значению схема дешифратора определяет тип обрабатываемой команды. • Седьмой бит кода команды, обозначенный символом «d», определяет адре- сата результата операции. Например, команда addwf h' 30 ' , w означает «сложить содержимое рабочего регистра с регистром h’30’ и поместить ре- зультат обратно в рабочий регистр», тогда как команда addwf h1 3 0 1 , f оз- начает «сложить содержимое рабочего регистра с регистром данных h’30’ и поместить результат обратно в регистр h’30’». В первом случае адресатом операции является рабочий регистр W и бит d равен 0, а во втором случае адресатом является регистр данных и бит d равен 1. Мы еще вернемся к этой команде в пятой главе. В символической записи команды символы «, w» со- ответствуют сброшенному биту адресата d, а символы «, f» соответствуют установленному биту d. • Младшие семь битов (6...0) определяют адрес регистра данных. Так, в на- шем примере используется регистр h’25’, поэтому в указанном поле содер- жится значение Ь’0100101 ’. Так как размер поля адреса равен семи битам, то посредством прямой адресации можно адресовать только один банк памяти, вмещающий в себя 2б) 7 = 128 регистров, т.е. с регистра h’00’ по регистр h’7F’ (см. Рис. 4.7 на стр. 97). Адресат (W или регистр данных) хххххх d fffffff а) Формат кода команды § СО movf Из регистра h’25’ 000100 0 0100101 б) Код команды для movf h' 2 5 ' , w Рис. 3.5. Формат кода команд, использующих прямую адресацию
Глава 3. Обработка хранимой программы и 69 КОП LLLLLLL Операции с константами Команды, работающие с константами, кодируются немного иначе, как пока- зано на Рис. 3.6. В старших шести битах по-прежнему содержится код операции, а в младших восьми битах находится собственно значение константы. Результат выполнения таких команд всегда помещается в рабочий регистр, поэтому не тре- буется ни бит адресата, ни значение адреса в памяти данных. КОП Константа хххххх LLLLLLLL а) Формат кода команды addlw 04 111110 00000100 б) Код команды для addlw 04 Рис. 3.6. Формат слова команд операций с константами Код операции команды из нашего примера (addlw 04) равен Ь’111110’, а константа равна Ь’00000100’. Значение константы должно лежать в диапазоне b’OOOOOOOO’...b’ 11111111 ’ (h’00’...h’FF’ или, в десятичной системе, 0...255), что вполне логично, поскольку рабочий регистр, как и все внутренние регистры ис- полнительного блока, является восьмибитным1). Не только команды могут иметь мнемонические обозначения. Как мы виде- ли, символические имена можно присваивать и ячейкам памяти данных. Так, на Рис. 3.4 идентификатор NUM_1 используется для указания содержимого регист- ра данных h’25’, a NUM_2 — регистра данных h’26’. Таким образом, нашу про- грамму можно символически записать следующим образом: NUM_2 = NUM_1 + 4; Возвращаясь к собственно компьютеру, мы видим, что, начиная с адреса h’000’, наша программа имеет вид 00100000100101 11111000000100 00000010100110 Если только вы не киборг, то чтение такой программы — весьма сомнительное удовольствие2). 11 Одна из наиболее распространенных ошибок заключается в упущении из виду этого 8-бит- ного ограничения и в использовании в программе команд, подобных addlw h' 500 '. Результат этой операции аналогичен попытке заполнить литровую бутыль содержимым 4-литрового ведра! 2) Мне ли этого не знать! Я писал программы подобным образом в середине 70-х.
70 Часть I. Основы Если мы воспользуемся шестнадцатеричной системойто будет уже удобнее: 0825 ЗЕ04 00А6 но ненамного. К тому же ЦПУ все равно понимает только двоичные числа, поэтому нам в любом случае понадобится программа-транслятор для перевода шестнадцате- ричных значений в двоичные, запускаемая, скажем, на персональном компьютере. Раз уж мы все равно собираемся использовать компьютер для перевода нашей программы, называемой также исходным кодом, в двоичный машинный код, на- зываемый объектным кодом, то имеет смысл не мелочиться и записать ее пол- ностью в символическом виде. При этом различные команды будут представлены их мнемоническими обозначениями, а адреса переменных — своими именами. В результате наша программа примет вид: movf NUM_l,w ; Копируем содержимое NUM_1 в W addlw 4 ; Прибавляем к нему число 4 movwf NUM_2 ; Копируем NUM_1 + 4 в NUM_2 Текст после символов «;» называется комментариями, которые используются для облегчения понимания программы. Код, записанный таким образом, представляет собой программу на языке ассем- блера. Синтаксису этого языка, а также процессу преобразования написанных на нем программ в исполняемый двоичный код полностью посвящена восьмая глава книги. При написании программ с использованием языка ассемблера необходимо помнить, что каждая инструкция программы один в один соответствует исходной машинной команде и ее двоичному коду. В главе 9 мы увидим, что в языках высо- кого уровня это соотношение нарушается. В основе работы любого вычислительного устройства лежит периодическое выполнение цикла выборка — исполнение. При этом каждая команда поочередно выбирается из памяти программ, интерпретируется и исполняется. Поскольку память, к которой обращается программа в процессе выполнения, является па- мятью данных, а каждое устройство памяти имеет собственные шины, операции выборки и исполнения могут осуществляться параллельно. Таким образом, во время выборки л-й команды исполняется команда л — 1. На Рис. 3.4 показано, что коды обеих команд, как следующей, так и текущей, хранятся в двух внутрен- них регистрах команд — IR1 и IR2 соответственно. Команды, считанные из памя- ти программ, загружаются в начало этого конвейера и «выталкиваются» в дешиф- ратор команд с конца конвейера. На Рис. 3.7 изображен развернутый во времени процесс выполнения нашей команды, разбитый на машинные циклы. Во время каждого цикла, за исключением самого первого, выборка новой команды и ис- полнение предыдущей осуществляются одновременно. Не забывайте, что мы используем шестнадцатеричную нотацию исключительно для удо- бочитаемости. Если вы возьмете электронный микроскоп и посмотрите внутрь этих ячеек, то сможете «увидеть» только двоичную структуру.
Гшва 3. Обработка хранимой программы 71 Выборка, IR1 Исполнение, IR2 .......... । Время Рис. 3.7. Параллельные потоки выборки и исполнения команд А теперь, чтобы лучше понять работу конвейера, давайте пройдемся по нашей программе. Предположим, что компьютер (т.е. счетчик команд) был сброшен и только что завершилась операция выборки первого цикла. Выборка (Рис. 3.4).................................................Цикл 2 • Инкрементируется счетчик команд, чтобы указать на 2-ю команду. • Одновременно код 1-й команды перемещается по конвейеру (из регистра IR1 в регистр IR2). • Содержимое счетчика команд (h’001 ’) выставляется на шину адреса памяти программ. • После этого на шине данных памяти программ появляется код 2-й коман- ды, который загружается в регистр IR1. Исполнение (Рис. 3.4)..............................................Цикл 2 • Адрес операнда h’25’ (т.е. NUM_1) заносится в регистр адреса FAR и вы- ставляется на шину адреса памяти данных. • Искомое значение, находящееся по адресу NUM_1, выставляется на шину данных памяти данных, после чего загружается в регистр FDR. • АЛУ переключается в режим пропуска, в котором аргумент с входа АЛУ без изменений копируется в рабочий регистр. Выборка...........................................................Цикл 3 • Инкрементируется счетчик команд, чтобы указать на 3-ю команду. • Одновременно код 2-й команды перемещается по конвейеру (из регистра IR1 в регистр IR2). • Содержимое счетчика команд (h’002’) выставляется на шину адреса памяти программ. • После этого на шине данных памяти программ появляется код 3-й коман- ды, который загружается в регистр IR1. Исполнение........................................................Цикл 3 • АЛУ переключается в режим сложения, в котором константа, содержащая- ся в коде 2-й команды, прибавляется к содержимому рабочего регистра.
72 Часть I. Основы • Результат операции NUM_1 + 4 с выхода АЛУ помещается обратно в рабо- чий регистр. Выборка..........................................................Цикл 4 • Инкрементируется счетчик команд, чтобы указать на 4-ю команду. • Одновременно код 3-й команды перемещается по конвейеру (из регистра IR1 в регистр IR2). • Содержимое счетчика команд (h’003’) выставляется на шину адреса памяти программ. • После этого на шине данных памяти программ появляется код 4-й коман- ды, который загружается в регистр IR1. Исполнение.......................................................Цикл 4 • Адрес операнда h’26’ (т.е. NUM_2) заносится в регистр адреса FAR и вы- ставляется на шину адреса памяти данных. • АЛУ переключается в режим пропуска, в котором содержимое рабочего ре- гистра без изменений копируется в регистр FDR и выставляется на шину данных памяти данных. • Содержимое регистра FDR заносится в память данных по адресу, выстав- ленному на шину адреса, т.е. в регистр NUM_2. \ Обратите внимание на автоматическое изменение счетчика команд на этапе выборки каждого цикла. Такой последовательный характер изменения его содер- жимого будет сохраняться до тех пор, пока не встретится команда, напрямую мо- дифицирующая этот счетчик, например команда goto h' 2 00 '. При выполне- нии этой команды адрес h’200’ помещается в счетчик команд, нарушая обычный процесс инкрементирования, в результате чего ЦПУ перейдет к команде, распо- ложенной по адресу h’200’. Далее изменение счетчика команд снова примет ли- нейный характер. Хотя наша программа делает, прямо скажем, немного, на выполнение каждой команды затрачивается всего около 1 мкс. Миллион простейших операций в се- кунду — это сила! По существу, все компьютеры, какими бы «умными» они ни ка- зались, просто выполняют с очень большой скоростью множество относительно простых операций. Поэтому основной задачей программиста является принятие решения о том, в какой последовательности необходимо расположить команды и структуры данных для выполнения соответствующей задачи. До настоящего момента мы рассматривали одни компьютероподобные струк- туры. Для логического завершения главы нам осталось только провести связь между предметом обсуждения и собственно микроконтроллерами. Так что же это такое — микроконтроллер? Кратко говоря, микроконтроллер — это микропроцессор, который объединен с памятью и различными устройствами ввода/вывода в одной интегральной микросхеме. То есть по сути дела это микро-
Глава 3. Обработка хранимой программы 73 процессор со встроенными вспомогательными узлами. Так что нам придется со- вершить небольшое путешествие во времени к моменту рождения микропроцес- сора. Вся эта история началась в 1968 году, когда Роберт Нойс (один из изобрета- телей интегральных микросхем), Гордон Мур1 ** и Эндрю Грув уволились из компании Fairchild Corporation и основали свою собственную компанию, назвав ее Intel2). В течение трех лет новая компания освоила выпуск полупроводниковой памяти всех основных типов, используемых в настоящее время, — динамического и статического ОЗУ, а также микросхем EEPROM. Одним из неосновных направлений деятельности компании была разработка интегральных микросхем большой степени интеграции (БИС) по спецификациям заказчика. В 1970 году к Intel обратились представители японской корпорации Busicom с предложением изготовить подходящий набор микросхем (так называе- мый чипсет) для линейки калькуляторов. В то время рынок калькуляторов разви- вался очень динамично, поэтому любые микросхемы пришлось бы менять каждые несколько лет. Естественно, при этом снижалась рентабельность производства БИС и увеличивалась их стоимость. И вот инженеру Тэду Хоффу3* пришла в голову революционная идея: а почему бы не создать простой ЦПУ на кристалле? Его мож- но было бы запрограммировать на реализацию функций калькулятора, а для рас- ширения функциональности устройства по мере появления новых требований до- статочно будет просто усовершенствовать его программное обеспечение. Все это значительно увеличивало срок жизни такой микросхемы и ее рентабельность. Кро- ме того, не следует забывать, что основной сферой деятельности компании Intel было производство микросхем памяти, а компьютероподобные архитектуры требу- ют ее очень много! Это была поистине блестящая мысль. Разумеется, в конце 1969 года японские заказчики одобрили предложение компании Intel, поскольку оно было простым и гораздо более гибким, нежели традиционные решения. Весной 1970 года в Intel появился Федерико Фаггин4*, а уже к концу года были изготовлены рабочие образцы первого в мире чипсета. Эксклюзивными правами на его приобретение обладала компания Busicom. Однако к середине 1971 года у нее возникли серьезные финансовые затруднения, и компания Intel, возместив затраты на разработку этого чипсета в размере 65 000 долл., получила права на его продажу всем желающим. В то время рыночные перспективы этого изделия были достаточно туманны, но Intel все же решила рискнуть и опубликовала в ноябрь- ском номере журнала «Electronic News» за 1971 год рекламу своего «микропро- граммируемого компьютера на кристалле», получившего обозначение 4004 (тер- ’* Именно он высказал в 1965 году предположение, что число транзисторов на кристалле будет удваиваться каждые 18 месяцев (в то время типичная ИС состояла из 50 транзисторов). Эту зависимость, которая получила название первого закона Мура, он вывел путем экстраполя- ции роста емкости микросхем с 1959 года. Впоследствии эта зависимость пересматривалась каждые два года и все время с блеском подтверждалась. 2* Считается, что это название произошло от слов INTELligence (интеллект) или INTegrated ELectronics (интегрированная электроника). 3) Говорят, что эта идея возникла у него, когда он отдыхал на топлесс-пляже на Таити. 4) Именно он позже основал компанию Zilog, которая стала известна благодаря своему микропроцессору Z80 — основному конкуренту процессора Intel 8085.
74 Часть I. Основы мин микропроцессор вошел в обиход только после 1972 года). Появление процес- сора 4004 вызвало бурный интерес, поскольку он позволял внедрить «интеллект» в электронную технику. Микропроцессор 4004 имел фон-неймановскую архитектуру с 4-битной ши- ной данных и позволял напрямую адресовать до 512 байт памяти. Он работал на частоте 108 кГц и содержал 2300 транзисторов1 \ Годом позже был выпущен 8-бит- ный микропроцессор 8008, работавший на частоте 200 кГц и позволявший адре- совать 16 Кбайт памяти. Эта микросхема состояла уже из 3500 транзисторов. Если четырех битов было вполне достаточно для работы с BCD-числами, использую- щимися в калькуляторах, то 8-битная архитектура уже годилась для создания ин- теллектуальных терминалов (наподобие кассовых аппаратов), в которых требова- лась поддержка разнообразных алфавитно-цифровых символов. В 1974 году на смену 8008 пришел микропроцессор 8080* 2) 3, а в 1976 году появился слегка моди- фицированный вариант последнего — 8085. Нужно сказать, что 8-битный микро- процессор 8085 до сих пор выпускается компанией Intel. Идея микропроцессора оказалась настолько удачной, что множество других производителей электронных компонентов тоже поспешили застолбить место на этом рынке. Более того, многие бывшие разработчики открывали собственные компании, взять, к примеру, ту же Zilog. К 1976 году было выпущено или хотя бы анонсировано 54 различных моделей микропроцессоров. Так, родоначальником одного из семейств, имевших наибольший успех на рынке, был микропроцессор 6800, разработанный компанией Motorola31. Этот микропроцессор имел ясную и гибкую фон-неймановскую архитектуру, мог работать на частоте 2 МГц и адресо- вать до 64 Кбайт памяти. В модели 6802 (1977) была даже встроенная память объ- емом 128 байт и внутренний тактовый генератор. В 1979 году была выпущена усовершенствованная модель 6809, ставшая последним представителем этого се- мейства 8-битных микропроцессоров. Основными ее конкурентами были такие микропроцессоры, как 8085 компании Intel, Z80 компании Zilog и 6502 компании MOS Technology. Вообще говоря, изначально микропроцессоры не предназначались для исполь- зования в обычных компьютерах. Но в 1975 году небольшая компания — произво- дитель калькуляторов MITS4), оказавшись на грани банкротства, совершила риско- ванный ход и переориентировалась на изготовление и продажу компьютеров. При- митивная вычислительная машина, разработанная инженером Эдом Робертсом (Ed Roberts), была построена на базе микропроцессора 8080 компании Intel. Взаи- модействие с оператором осуществлялось посредством переключателей и лампо- чек, располагавшихся на передней панели, — никакой клавиатуры и монитора. В течение нескольких недель после начала рекламной акции компания получила ° Сравните с 5.5 млн транзисторов процессора Pentium Pro (известного также как Р6 или 80686). 2) Разработанный Масатоши Шима (Masatoshi Shima), который позже перешел в компа- нию Zilog и занялся там разработкой микропроцессора Z80, совместимого с 8080. 3) Компания Motorola была основана в 30-х годах как производитель автомобильных радио- приемников. Во время написания книги (2005 год) она занимала ведущую позицию на мировом рынке микроконтроллеров. 4) Располагавшаяся в Нью-Мексико по соседству с массажным кабинетом.
Глава 3. Обработка хранимой программы 75 около 650 предварительных заказов на этот компьютер, названный «Альтаир»1* (сумма каждого заказа составляла около 400 долл.). В результате вместо долга на сумму 400 000 долл, компания получила прибыль в размере 250 000 долл. Этот первый персональный компьютер (ПК) породил целое поколение ком- пьютерных фанатов. Так, однажды, в декабре 1975 года, никому доселе не извест- ный 19-летний студент факультета вычислительной техники Гарвардского уни- верситета Билл Гейтс (Bill Gates) и зашедший к нему в гости приятель Пол Аллен (Paul Allen) увидели фотографию «Альтаира»2* на обложке журнала «Popular Electronics» и решили заняться написанием программного обеспечения для этого ПК. Они связались по телефону с Эдом Робертсом и сообщили ему, что у них есть уже почти завершенный транслятор языка Бейсик для «Альтаира» (вообще-то это была, мягко говоря, неправда). Так на свет появилась корпорация Microsoft. Примерно двумя месяцами позже насколько десятков человек основали в Сан-Франциско своеобразный компьютерный клуб, приобретя в складчину один «Альтаир» на всех. В числе членов этого клуба были Стив Джобс (Steve Jobs) и Стив Возняк (Steve Wozniak). В качестве демонстрации работы клуба они собрали собственный ПК, назвав его «Apple»3*. К 1978 году объем продаж компьютеров «Apple II» составил 700 000 долл.; в течение 1979 года этих компьютеров было продано на сумму 7 млн долл., в следующем году — на сумму 48 млн долл... Компьютер «Apple II» был построен на базе недорогого микропроцессора 6502 производства компании MOS Technology. Разработчиком этого процессора (и од- ним из основателей компании) был Чак Педдл (Chuck Peddle), ранее служивший в компании Motorola и отвечавший там за разработку процессора 6800. Неудиви- тельно, что микропроцессор 6502 до боли напоминал это предыдущее детище Ча- ка. Компания Motorola даже подала в суд с требованием запретить продажу мик- ропроцессора 6501, тем более что его цоколевка полностью совпадала с цоколев- кой их процессора 6800. Вплоть до конца 70-х годов микропроцессор 6502 оставался одним из основных игроков на рынке ПК, будучи, помимо всего проче- го, «сердцем» таких известных в то время компьютеров, как «ВВС Micro»4* и «Commodore РЕТ»5*. Основным фактором, реально повлиявшим на популярность компьютера «Apple II», было наличие у последнего пакета программ «VisiCalc» для работы с электронными таблицами. Когда бизнес-сообщество осознало, что ПК — это не просто игрушка и что с его помощью можно решать «реальные» задачи, объем продаж этих компьютеров резко подскочил. То же самое произошло и с компью- терами IBM PC. Этот ПК, представленный компанией IBM в 1981 году, был пост- ’* По названию планеты (Altair) из популярного телесериала «Star Trek». 2* На фотографии была изображена всего лишь экспериментальная модель — к тому вре- мени компьютеры еще не были готовы. Так что первые экземпляры «Альтаиров» существовали только в воображении заказчиков! 3) От англ, слова «apple» — яблоко. Дело в том, что Джобс придерживался исключительно фруктовой диеты, кроме того, одно время он работал в садоводческом хозяйстве. 4) Один из первых домашних компьютеров. Был спроектирован и разработан компанией Acorn Computers Ltd. для корпорации ВВС. — Примеч. пер. 5* Разработка компании Commodore Business Machines — предшественник одного из самых популярных домашних компьютеров «Commodore 64». — Примеч. пер.
76 Часть I. Основы роен на базе микропроцессора 8088, работавшего на частоте 4.77 МГц. В компью- тере имелось ОЗУ объемом 128 Кбайт, два дисковода для дискет объемом 360 Кбайт и монохромный дисплей, работавший в текстовом режиме. В качестве операционной системы в нем использовалась ОС PC/MS-DOS версии 1.0 компа- нии Microsoft. В комплекте с ней поставлялся пакет программ для обработки электронных таблиц «Lotus 1-2-3». Уровень развития технологии изготовления кремниевых СБИС, достигнутый к концу 70-х, сделал возможным размещение на одном кристалле нескольких де- сятков и даже сотен тысяч транзисторов. И сразу же перед разработчиками мик- ропроцессоров встал вопрос: каким образом использовать эту возможность? Наи- более очевидным и, соответственно, наиболее популярным направлением совер- шенствования микроконтроллеров было увеличение разрядности АЛУ и емкости шин/памяти. По этому пути, в частности, пошла компания Intel, выпустив в 1978 году микропроцессор 8086 (16-битный вариант микропроцессора 8085!)), имев- ший в своем составе 29 000 транзисторов. При его разработке особое внимание было уделено программной и аппарат- ной совместимости с его 8-битным предшественником. С коммерческой точки зрения это было очень мудрым решением, поскольку позволяло удержать много- численных потребителей процессора 8085 от перехода к изделиям конкурентов. В то же время это решение было весьма неоднозначным с технической точки зре- ния. Так или иначе, но выпуск этого микропроцессора оказался несколько пре- ждевременным, поэтому в оригинальных компьютерах IBM PC компании IBM использовалась модифицированная версия 8086 — процессор 8088, имевший уре- занную 8-битную шину данных и 20-битную шину адреса* 2). В 1979 году компания Motorola представила свой вариант 16-битного микро- процессора, получившего название 68000, а также его модификацию 68008 с 8-бит- ной шиной данных. Однако внутренняя организация всех этих микропроцессоров была 32-битной, за счет чего удалось обеспечить их совместимость с более поздни- ми моделями, вплоть до выпущенного в 1995 году микропроцессора 68060, а также RISC-процессора ColdFire, появившегося в 1997 году. Микропроцессоры семей- ства 68000 представляли собой совершенно новую разработку и технически были более прогрессивными по сравнению со своими конкурентами семейства 80x86. Компания Apple решила использовать процессор 68000 в своих новых ПК «Macintosh». Однако, несмотря на все преимущества этого процессора, объем продаж компьютеров Apple Мас составил менее 5% от объема продаж IBM PC. Гораздо больших успехов компания Motorola добилась на рынке микропроцессо- ров для встраиваемых систем — начиная от яйцеварок и заканчивая системами управления самолетов. Конечно же, микропроцессоры изначально были разрабо- таны именно для этой области, поэтому количество микропроцессоров, продан- ]) На долю микропроцессоров с интеловской архитектурой 8086 приходится наибольшее число продаж. 2) При этом объем адресуемой памяти составлял 220 = 1 Мбайт. Именно по этой причине для обеспечения обратной совместимости в MS-DOS было введено ограничение на 1 Мбайт основной памяти. В среде Microsoft Windows эта область памяти называется реальной памятью.
Глава 3. Обработка хранимой программы 77 них для использования во встраиваемых системах, более чем на порядок превы- сило количество, проданное для нужд компьютерного рынка. В таких устройствах микропроцессор «спрятан» в недрах системы вместе с па- мятью и различными интерфейсными схемами ввода/вывода. То есть он выступает в роли центрального контроллера, управляя системой в соответствии с програм- мой, зашитой в его памяти программ. Ежегодно для использования во встраивае- мых системах продается свыше 3.5 млрд микропроцессоров и сопутствующих мик- росхем, что составляет более 95% всего рынка микропроцессоров. Другое направление совершенствования микроконтроллеров, ставшее воз- можным в конце 70-х, заключалось в сохранении относительно простого ЦПУ и использовании оставшихся ресурсов кристалла для реализации встроенной памя- ти и интерфейсов ввода/вывода. Это дало возможность создавать простые встра- иваемые системы управления на одной-единственной микросхеме, значительно уменьшая таким образом общее число микросхем, необходимое для реализации заданной функции. Для реализации подавляющего большинства задач управле- ния большой вычислительной мощности не требуется, а вот уменьшение размера готовых устройств и, соответственно, их стоимости крайне желательно. В качест- ве простого примера можно привести смарт-карту с интегрированным процессо- ром. Такие микропроцессорные устройства получили название микроконтролле- ров^. Например, в каждом доме, незаметно для нас, обитает несколько сот микроконтроллеров. Они есть повсюду — в бытовой технике, аудио- и видео- аппаратуре, персональных компьютерах, телекоммуникационных устройствах, смарт-картах и, в том числе, в автомобилях. Если микропроцессор с точки зрения архитектуры (см. Рис. 3.1 и Рис. 3.2) представляет собой только блок центрального процессора, то микроконтроллер уже является законченной самодостаточной компьютероподобной системой. Рассмотрим в качестве примера электронную часть системы контроля автомо- бильного одометра, которая отображает общий пробег автомобиля с момента из- готовления, а также дальность последней поездки (так называемый путевой одо- метр). Основным входным сигналом системы является сигнал от автомобильного тахометра, который формирует импульсы при каждом обороте маховика двигате- ля. Подсчитав суммарное количество этих импульсов, можно определить коли- чество оборотов двигателя, а по интервалу между импульсами можно вычислить скорость движения автомобиля. Разумеется, реальный путь, проходимый автомо- билем, зависит от передаточного числа коробки передач, поэтому нам необходи- мо знать о том, какая из пяти передач была включена водителем в каждый момент времени. Эта информация поступает из коробки передач по линиям G5, ..., G1 (обычно обозначаемым как G[5:1]). Включенной передаче соответствует напря- жение ВЫСОКОГО уровня на соответствующей линии (передача заднего хода не учитывается). Два дополнительных входа предназначены для задания единицы измерения отображаемых значений (мили или километры) и для обнуления путе- вого одометра. Изначально существовал еще один термин, микрокомпьютер, однако он вскоре исчез из употребления, поскольку точно так же назывались персональные компьютеры того времени.
78 Часть I. Основы Собственно дисплей одометра представляет собой семиразрядный 7-сегмент- ный индикатор (см. Рис. 6.8 на стр. 183), который может отображать значения до 999999.9 . Поскольку общее число сегментов довольно велико (целых 49), то для управления индикатором используется сдвиговый регистр (см. Рис. 2.22 на стр. 51), данные в который передаются по одной линии (Рис. 3.8). По второй ли- ни передаются тактовые импульсы — для полного обновления содержимого дис- плея необходимо 49 импульсов!). Микроконтроллер Энергонезависимая память Рис. 3.8. Пример микроконтроллерной системы путевого одометра Дисплей путевого одометра является 4-разрядным и позволяет отображать значения до 999.9 . Сдвиговый регистр этого дисплея тоже управляется по двум линиям, только в данном случае для вывода нового 4-разрядного значения необ- ходимо 28 тактовых импульсов. Для реализации этой системы нам потребуются следующие ресурсы (так на- зываемый бюджет ресурсов)'. • Вход, срабатывающий по фронту и подключенный к счетчику/таймеру для подсчета числа оборотов вала двигателя (на этот вход поступают импульсы от тахометра). • Семь цифровых входов для ввода текущего передаточного отношения, зада- ния единицы отображения (мили/км) и для сброса путевого одометра. Во многих индикаторах уже имеется встроенный сдвиговый регистр.
Глава 3. Обработка хранимой программы 79 • Четыре цифровых выхода для тактирования двух сдвиговых регистров и пе- редачи информации о сегментах. • Микропроцессор для выполнения вычислений, считывания входных сигна- лов и формирования выходных. • Память программ, обычно ПЗУ какого-либо типа. • Память данных для хранения рабочих переменных программы, обычно ста- тическое ОЗУ. • Энергонезависимая память для долговременного хранения информации, такой как суммарный путь, пройденный автомобилем, и расстояние, прой- денное с момента последнего сброса путевого одометра. Все эти функции могут быть реализованы в одной-единственной интеграль- ной микросхеме, называемой в данном случае микроконтроллером, т.е. микро- процессором, интегрированным на одном кристалле со вспомогательными схема- ми и выполняющим работу целого микрокомпьютера. Разумеется, перечислен- ные ресурсы имеют отношение только к нашему примеру. Хотя основные узлы (микропроцессор и память) являются общими для широкого круга приложений, интерфейс ввода/вывода необходимо подбирать под каждую конкретную задачу. Причем эти интерфейсные модули могут быть самыми разными, например: • Модули приема/передачи данных по последовательным каналам с исполь- зованием разнообразных синхронных и асинхронных протоколов. • Модули счетчиков/таймеров для подсчета числа внешних событий и фор- мирования цифровых сигналов с точными временными параметрами. • Модули аналого-цифрового преобразователя для считывания и оцифровки входных аналоговых сигналов. • Модули цифро-аналогового преобразователя для формирования выходных аналоговых сигналов. • Модули специализированного интерфейса для управления многоразрядны- ми жидкокристаллическими индикаторами (ЖКИ). Такое использование дополнительных ресурсов кристалла привело к появле- нию в конце 70-х годов первых микроконтроллеров. К примеру, микроконтроллер Motorola 6801 (35 000 транзисторов), разработанный специально для использова- ния в автомобилях, был построен на базе существующего микропроцессора 6800. Этот микроконтроллер имел ПЗУ программ объемом 2048 байт, ОЗУ данных объ- емом 128 байт, 29 линий ввода/вывода и 16-битный таймер. После того как микро- контроллеры доказали свою жизнеспособность, все ведущие производители мик- ропроцессоров выпустили на рынок различные семейства микроконтроллеров. Каждое из этих семейств базировалось на определенном ядре, при этом различные представители одного и того же семейства отличались набором периферийных устройств. Например, в семействе 68НС11 компании Motorola (дальнейшее разви- тие микроконтроллера 6801) было использовано слегка модернизированное ядро 6800. Семейства 68НС12 и 68НС16 имели уже 16-битные ядра, которые, однако, обеспечивали совместимость с предыдущим 8-битным семейством 68НС11. Вско- ре выяснилось, что во многих встраиваемых приложениях вовсе не требуются все вычислительные возможности древнего ядра 6800, поэтому было выпущено новое
80 Часть I. Основы семейство 68НС05!), представители которого имели значительно урезанное ядро и, соответственно, меньшую стоимость. Как это ни удивительно, но 4-битные микроконтроллеры, такие как TMS1000 компании Texas Instruments, лидировали по объему продаж среди всех остальных разновидностей процессоров вплоть до начала 90-х (и до сих пор продолжают пользоваться устойчивым спросом). Похо- же, что и 8-битным микроконтроллерам, ставшим в последнее время наиболее по- пулярными, в обозримом будущем уготована та же судьба. Кстати говоря, процес- сор 14500 компании Motorola вообще был однобитным! В основе всех этих микропроцессоров и микроконтроллеров лежала фон-ней- мановская архитектура, используемая в универсальных ЭВМ. Альтернативная гарвардская архитектура впервые возродилась в микропроцессоре 8X300 компа- нии Signetics, который в середине 70-х был приспособлен компанией General Instruments для работы в качестве периферийного интерфейсного контроллера (Peripheral Interface Controller — PIC). Компания собиралась использовать этот контроллер как программируемый порт ввода/выводадля своего 16-битного мик- ропроцессора СР 1600. После того как в 1988 году компания General Instruments продала свое подразделение интегральных микросхем молодой компании, на- званной Arizona Microchip Technology, это устройство вновь появилось на свет, но уже в виде самостоятельного микроконтроллера. Именно данному семейству микроконтроллеров и посвящена оставшаяся часть книги. Примеры Пример 3.1 Контроллер теплицы должен контролировать аналоговый сигнал от датчика влажности почвы и, если его величина ниже некоторого порогового значения, от- крывать водяной клапан на пять секунд с последующей 5-секундной паузой. В ре- зервуаре с водой имеется поплавковый датчик, который замыкает контакты при снижении уровня воды ниже порогового значения. В этом случае должен вклю- чаться звуковой сигнализатор для индикации тревоги. Можете ли вы придумать систему на базе микроконтроллера, реализующую указанные функции? Решение В решении, приведенном на Рис. 3.9, используется автомобильный одометр с Рис. 3.8. Единственным новым периферийным устройством является аналоговый порт, используемый для считывания и оцифровки аналогового сигнала отдатчика влажности почвы. В основе работы этого датчика лежит зависимость сопротивле- ния почвы от ее влажности. Электроды датчика, включенные последовательно с постоянным резистором, образуют делитель напряжения, выходное напряжение Микроконтроллеры семейства 68НС05 прочно обосновались в нише процессоров для смарт-карт, где производительность является не самым важным параметром.
Глава 3. Обработка хранимой программы 81 Поплавковый выключатель Рис. 3.9. Климатический контроллер теплицы -|( Сигнализатор Резервуар с водой К насосу оросительной системы Водяной клапан которого будет меняться в зависимости от влажности почвы. Микроконтроллер может преобразовать это аналоговое напряжение в соответствующий цифровой код, который затем будет сравниваться в программе с предустановленным значе- нием. Также порт ввода может представлять собой обыкновенный аналоговый компаратор, формирующий на выходе лог. 1 или лог. О, если входное напряжение превышает определенное значение, которое может задаваться программно. Глядя на Рис. 3.9, мы можем оценить требуемые ресурсы: • Вход для внешнего генератора, подключенный к счетчику/таймеру. Это не- обходимо для того, чтобы микроконтроллер мог отсчитывать временные интервалы. На практике такие таймеры очень часто работают от внутренне- го тактового сигнала микроконтроллера. • Один аналоговый вход для измерения уровня аналогового сигнала от датчи- ка влажности. • Один цифровой вход для контроля уровня воды в резервуаре. • Один цифровой выход для открытия и закрытия водяного клапана. • Один цифровой выход для управления звуковым сигнализатором. • Микропроцессор для вычислений, считывания входных и формирования выходных сигналов. • Память программ, обычно ПЗУ какого-либо типа. • Память данных для хранения рабочих переменных программы, обычно ста- тическое ОЗУ. Если учесть, что для выполнения указанных задач требуется не так уж и много времени, можно задействовать дополнительные входы микроконтроллера для
82 Часть I. Основы контроля других параметров, таких как температура и освещенность. В результате мы сможем осуществлять более комплексное управление климатической обста- новкой в теплице. Пример 3.2 Наиболее сложной проблемой, с которой приходится сталкиваться програм- мисту, часто является собственно постановка решаемой задачи. Для этого необхо- димо логическое мышление, которым обладает человек и которое отсутствует у машины. Именно способность принимать решения и является отличительной чертой хорошего программиста. Эта способность складывается из опыта, капель- ки таланта, а также хорошего понимания решаемой задачи. Чтобы проиллюстрировать процесс принятия решения, продумаем последо- вательность элементарных действий, которые должен будет выполнить робот с микроконтроллерным управлением для перехода через регулируемый пешеход- ный переход на улице с оживленным движением. Решение 1. Подойти к переходу и остановиться. 2. Посмотреть на светофор. 3. Принять решение — не горит ли в нашем направлении зеленый сигнал? 4. ЕСЛИ сигнал красный, ТО перейти к шагу 2, ИНАЧЕ продолжить. 5. Посмотреть налево. 6. Едут ли машины? 7. ЕСЛИ да, ТО перейти к шагу 5, ИНАЧЕ продолжить. 8. Посмотреть направо. 9. Едут ли машины (вообще-то все машины уже должны были остановиться, но кто знает!)? 10. ЕСЛИ да, ТО перейти к шагу 5, ИНАЧЕ продолжить. 11. Перейти через дорогу — задача решена! На Рис. 3.10 описанный алгоритм представлен в графическом виде. В этой блок-схеме прямоугольники используются для обозначения действий, ромбы — для обозначения условий, а прямоугольники со скругленными углами — для обозначения точек входа и выхода. Линии со стрелками указывают последова- тельность выполнения действий и дополнительно помечаются в точках принятия решений. В принципе в данном конкретном случае графическое представление алгоритма не имеет больших преимуществ по сравнению с текстовым. Однако в более сложных задачах, со множеством условий и вариантов выполнения, графи- ческое представление может оказаться гораздо удобнее для документирования поведения системы. А когда система становится очень сложной, то и простой пе- речень задач, и блок-схема становятся одинаково бесполезными. В этом случае
Глава 3. Обработка хранимой программы 83 Рис. 3.10. Блок-схема алгоритма перехода через дорогу описание системы необходимо строить по иерархическому принципу, начиная с самых общих вопросов и постепенно продвигаясь к более конкретным задачам. На первый взгляд этот пример может показаться довольно глупым и надуман- ным, но именно эти операции вам приходится совершать каждый раз при перехо- де загруженной улицы по регулируемому пешеходному переходу. И именно этот алгоритм вы должны заложить в робота, чтобы он смог сделать то же самое. Такая последовательность элементарных шагов, или инструкций, называется програм- мой. Со стороны все эти действия, предпринимаемые роботом для перехода через улицу, могут показаться проявлением интеллекта. Но это не интеллект — интел- лектом обладают люди. Это программист, запрограммировавший микроконтрол- лер робота, вложил в него необходимые знания.
84 Часть I. Основы Разумеется, робот не будет иметь ни малейшего понятия о том, что ему делать после перехода на другую сторону, если только мы не сообщим ему об этом. Что же касается человека, то он уже, образно говоря, «запрограммирован» — у него есть опыт! Заметьте, что этапы пронумерованы в том порядке, в котором они должны выполняться. Счетчик команд, в данном случае читатель, начинает выполнение с 1-й команды (состояние сброса) и заканчивает выполнением 11-й команды. В микроконтроллере после выполнения действий, предписываемых текущим этапом, счетчик команд автоматически инкрементируется, указывая на следую- щий этап, если только текущая команда не была командой пропуска или перехода. При выполнении команды пропуска счетчик команд «перепрыгивает» через сле- дующую команду, обычно при определенном условии или результате. А при вы- полнении команды перехода счетчик команд просто переходит к заданному эта- пу. Если бы таких команд не было, в программе нельзя было бы реализовать ветв- ления и циклы. Под циклом в данном случае понимается многократное повторение одних и тех же действий, например периодическая проверка наличия зеленого сигнала светофора до тех пор, пока он не включится. Вопросы для самопроверки 3.1. Можете ли вы предложить вариант инкрементирования и декрементирова- ния содержимого рабочего регистра, показанного на Рис. 3.4, с использова- нием трех команд, рассмотренных в этой главе? 3.2. Разработайте программу, которая позволит роботу с микроконтроллерным управлением из Примера 3.2 наполнить стакан водой из крана. 3.3. Компьютер BASIC, структура которого изображена на Рис. 3.4, может одно- временно осуществлять выборку одной команды и исполнять другую ко- манду. Объясните, за счет чего он может выполнять эти операции парал- лельно. 3.4. Составьте перечень задач, в соответствии с которым робот сможет пройти к ближайшему банкомату, снять со счета заданную сумму наличных, запро- сить баланс и вернуться на исходную позицию. Не забудьте про обработку запроса на печать баланса, а также продумайте действия робота при отсут- ствии на счете достаточной суммы денег! 3.5. Для подключения коробки передач к микроконтроллерной системе, пока- занной на Рис. 3.8, требуется пять выводов микросхемы. Многие микрокон- троллеры выпускаются в корпусах с малым числом выводов (см., например, Рис. 10.2 на стр. 304). Подумайте, как можно уменьшить требуемое число выводов, а также будет ли ваше решение экономически оправданным? Под- сказка: взгляните на Рис. 2.6 (стр. 36). 3.6. Подумайте, каким образом можно уменьшить на единицу количество выхо- дов микроконтроллера, требуемых для управления дисплеями обоих одо- метров, и насколько это будет удобно?
ЧАСТЬ II ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ Глава 4. Микроконтроллер PIC16F84 Глава 5. Набор команд Глава 6. Подпрограммы и модули Глава 7. Обработка прерываний Diaea 8. Инструментальные средства для работы с языком ассемблера Глава 9. Язык высокого уровня
86 Часть II. Программное обеспечение В 1-й части мы познакомились с понятием гарвардской архитектуры и разра- ботали собственный примитивный компьютер BASIC. Хотя этот компьютер су- ществует только в нашем воображении, он был разработан с оглядкой на реаль- ные микроконтроллеры, являющиеся предметом обсуждения данной книги. Вторая часть книги посвящена главным образом программным аспектам мик- роконтроллеров, выбранных нами для изучения, — микроконтроллеров PIC® среднего уровня компании Microchip. В этой части мы рассмотрим следующие вопросы: • Внутренняя структура микроконтроллеров PIC среднего уровня. • Набор команд. • Способы адресации команд. • Разработка программ с использованием интегрированной среды разработки MPLAB®. • Трансляция с языка ассемблера. • Подпрограммы и модульный принцип разработки программ. • Обработка прерываний. • Язык высокого уровня Си и компиляция написанных на нем программ.
ГЛАВА МИКРОКОНТРОЛЛЕР PIC16F84 В том же году, когда Microchip приобрела у компании General Instrument ин- теллектуальные права на микросхему периферийного интерфейсного контролле- ра (PIC), было разработано первое семейство 8-битных микроконтроллеров с гар- вардской архитектурой. Это начальное (или базовое) семейство PIC16C5XX, как и более современные семейства того же уровня PIC10FXXX и 12СХХХ, имело 33 команды, 12-битную память программ, параллельные порты ввода/вывода и один 8-битный таймер/счетчик. Как и во всех последующих семействах микроконт- роллеров PIC, исполнительный блок обрабатывал данные побайтно, что соот- ветствует 8-битной организации памяти данных. К 1992 году на свет появилось среднее семейство PIC16CXXX. Микроконт- роллеры этого семейства имели уже 14-битную память программ, что облегчало доступ к памяти данных больших объемов. По сравнению с младшими семей- ствами появились две новые команды. Был значительно расширен базовый набор периферии — добавились такие устройства, как 16-битные таймеры, модуль АЦП, последовательные порты. Также была добавлена поддержка прерываний. В оставшейся части книги, за исключением главы 16, мы будем рассматривать микроконтроллеры именно этого семейства. И только в 16-й главе мы коснемся старшего семейства PIC18XXXX, появившегося на рынке в 1999 году. Микроконт- роллеры данного семейства получили 16-битное ядро и 42 дополнительные ко- манды, большинство из которых направлено на поддержку компиляторов с язы- ков высокого уровня. В этой главе мы познакомимся с ядром микроконтроллеров семейства сред- него уровня с точки зрения его архитектуры. Прочитав эту главу, вы: • Познакомитесь с внутренней структурой микроконтроллеров PIC среднего уровня с гарвардской архитектурой. • Разберетесь в назначении, структуре и распределении памяти программ и памяти данных. • Поймете идею деления памяти данных на банки и их взаимосвязь с состоя- нием управляющего бита RP0 регистра STATUS. • Сможете интерпретировать биты регистра STATUS, которые управляют страницами памяти, а также содержат флаги С, DC и Z.
88 Часть II. Программное обеспечение • Поймете, как можно манипулировать содержимым счетчика команд и ка- кую роль в этом играет регистр специального назначения PCLATH. • Узнаете зависимость между фазами тактового сигнала и внутренней после- довательностью микроопераций. • Познакомитесь с основными периферийными модулями на примере моде- ли PIC16F84. С точки зрения программирования все устройства с одним и тем же ядром полностью идентичны. Более того, все ядра, используемые в микроконтроллерах PIC, имеют очень много общего. С аппаратной точки зрения блоки выборки и исполнения незначительно отличаются, в частности, поддерживаемым объемом памяти и построением схем сброса. Отдельные представители семейства имеют схожий базовый набор портов и модулей периферийных устройств, однако отличаются в части дополнительных возможностей ввода/вывода. Например, в микроконтроллере PIC16F73 имеется 5-канальный 8-битный аналого-цифровой преобразователь, в PIC16F874/7 — уже 8-канальный 10-битный аналого-цифровой преобразователь, а в PIC16F627/628 — аналоговый компаратор и 16-битный таймер/счетчик. Однако, несмотря на эти отличия, существует множество идентичных модулей, использу- ющихся как в моделях среднего уровня, так и в моделях с расширенным ядром. К рассмотрению этих модулей мы вернемся в 3-й части книги. В данной главе мы в основном будем рассматривать ядро микроконтроллеров семейства среднего уровня. Одним из первых представителей этого семейства (1994) был 18-выводной микроконтроллер PIC16C83/4. Он быстро завоевал попу- лярность, поскольку был первым PIC-микроконтроллером, в котором память программ была реализована в виде электрически стираемого ППЗУ (см. Рис. 2.13 на стр. 42). То есть для стирания кристалла не требовался источник ультрафиоле- тового излучения, и весь процесс перепрограммирования занимал всего несколь- ко секунд. Кроме того, эти модели имели блок энергонезависимой памяти данных объемом 16 байт для длительного хранения данных. В течение нескольких лет эта модель оставалась уникальной среди всей линейки микроконтроллеров PIC. В 1996 году на смену PIC16C83/4 пришла модель PIC16F83/4, которая имела FLASH-память программ и лучшие параметры, например более высокую рабо- чую тактовую частоту. Несмотря на то что для новых разработок рекомендуется использовать микро- контроллер PIC16F627/8, появившийся в 2002 году, основное его отличие от предшественника заключается в расширенном наборе периферийных модулей, к рассмотрению которых мы приступим в третьей части книги. Поэтому для про- стоты изложения большинство материалов данной части будет базироваться на модели PIC16F84. Однако при необходимости мы будем ссылаться и на другие модели семейства. В упрощенном виде архитектура микроконтроллера PIC16F84 показана на Рис. 4.1. Модель PIC16F83 отличается только уменьшенным объемом памяти программ (512 команд). На первый взгляд эти микроконтроллеры имеют очень сложную архитектуру, однако в действительности она ненамного сложнее архи- тектуры нашего компьютера BASIC (см. Рис. 3.4 на стр. 64). Основное отличие
Глава 4. Микроконтроллер PIC16F84 89 заключается в том, что к внутренней шине данных памяти данных, помимо ЦПУ, подключены периферийные модули. Чтобы лучше понять материал этой главы, рекомендую еще раз просмотреть материал, касающийся указанного компьюте- ра. Вообще говоря, в микроконтроллерах PIC реализована гарвардская архитек- тура с ее раздельными модулями памяти программ и данных, в которой перифе- рийные устройства отображены на адресное пространство памяти данных. То есть с точки зрения программы все эти устройства расположены в памяти данных. То же касается и различных служебных регистров, таких как регистры статуса и уп- равления, а также счетчика команд. RA4/TOCKI ' 3 - RA1 18 ' RA0 17 RA3 2/, RA2 Рис. 4.1. Архитектура микроконтроллера PIC16F84
90 Часть IL Программное обеспечение Блок выборки Блок выборки, показанный отдельно на Рис. 4.2, предназначен для выборки команд из памяти программ и загрузки их в конвейер. Адрес каждой извлекаемой команды задается счетчиком команд. В свою очередь код операции, выделенный из кода команды, подается на вход дешифратора команд, который активирует в требуемой последовательности соответствующие узлы исполнительного блока. Память программ Рис. 4.2. Блок выборки микроконтроллера PIC16F84 Память программ Основным элементом блока выборки является память программ. Программ- ное обеспечение во встраиваемых системах фиксировано, поскольку после вклю- чения питания микроконтроллер должен сразу же приступить к выполнению сво- их задач, не тратя время на загрузку программы. Поэтому память программ, как правило, реализуется в виде ПЗУ какого-либо типа. В большинстве микроконт- роллеров PIC используется многократно-программируемое ПЗУ; если в обозна-
Глава 4. Микроконтроллер PIC 16F84 91 чении модели присутствует буква «F», то это FLASH-память. В памяти программ микроконтроллера PIC16F84 может храниться до 1024 команд, размер каждой из которых составляет 14 бит (см. Рис. 3.5 на стр. 68). В новых микроконтроллерах семейства среднего уровня размер памяти программ колеблется от 1024 (напри- мер, PIC16F627) до 8192 (PIC 16F876/7) команд. Счетчик команд Счетчик команд (Program Counter — PC) используется для адресации, или указания, считываемой из памяти программ команды. Этот 13-битный регистр обычно инкрементируется после каждой выборки, функционируя как двоичный счетчик. Однако, как мы с вами увидим в следующей главе, существует ряд ко- манд, таких как команда goto, выполнение которых вызывает переход к другому месту в памяти программ. Соответственно, нормальное функционирование счет- чика команд нарушается. Кроме того, программист может напрямую обращаться к счетчику команд через память данных (см. Рис. 4.8). Несмотря на то что показанный на Рис. 4.3 счетчик команд является 13-бит- ным регистром, способным адресовать 213 = 8192 команды, в микроконтроллере PIC16F84 задействованы только младшие 10 бит (210 = 1024). Если программа по- 13 13 Память программ 0 Исполнение Команда л+1 Команда п Выборка Команда 2 Команда 3 Команда'4^ Команда 5 SSWTOS.,.. Команда/ Команда8 Команда_9 Команда 10 Команда 11 Команда 12 Команда 1017 Команда Ю18 Команда 1019 Команда 1020 Команда 1021 Команда 1022 Команда 1023 Команда 1024 Конвейер h'OOO1 Нют W Ир Еж ЕЖ Ж ЕЖ ТГооэ7 Ж Wa Wb h*3FC* l7‘3FD‘ h'3FE’ гзрГ |Сброс| Счетчик команд Указывает на команду, которая будет выбрана из памяти программ. Инкрементируется после каждой выборки Рис. 4.3. Память программ микроконтроллера PIC16F84
92 Часть II. Программное обеспечение пытается перейти по адресу, находящемуся вне этого диапазона, то адрес «свер- нется» по границе памяти программ. В любом случае счетчик команд очищается при сбросе микроконтроллера, т.е. первая выполняемая команда всегда распола- гается по адресу h’000’. Этот адрес называется вектором сброса. Конвейер Конвейер реализован в виде двух 14-битных регистров. В первом регистре хранится команда, только что считанная из памяти программ по адресу, опреде- ляемому значением счетчика команд. Второй регистр подключен к дешифратору команд и соответственно содержит исполняемую в данный момент команду. Та- кое решение позволяет осуществлять выборку команды одновременно с исполне- нием команды, выбранной в предыдущем цикле. Естественно, при этом предпо- лагается, что последовательность выполнения команд имеет линейный характер. Для команд, выполняющих переход к другому месту памяти программ, команда, находящаяся на вершине конвейера, должна быть заменена на другую. Этот про- цесс называется сбросом конвейера и увеличивает время исполнения команды на один цикл. Как видно из Рис. 4.4, обычно команда выполняется за один машин- ный цикл. Выборка fosc PC++ на шину адреса памяти программ Команду в конвейер Поток выборки | Выборка команды л Поток исполнения Q1 q2 Q3 04 Выборка команды л+1 Исполнение команды л Цикл п+1 Декодирование команды Чтение из памяти данных Обработка данных в АЛУ Запись в память данных Выборка команды л+2 | Исполнение | Выборка команды л+3 | j ^cnon^w^<OMa>w^+ij |испда^еком^^т^ Цикли Цикл п+2 Цикл п+3 ..— ... Время Рис. 4.4. Фазы машинного цикла Дешифратор команд Дешифратор команд представляет собой логическую схему, которая декоди- рует каждое поле 14-битного слова команды и управляет передачей соответствую- щих адресов и данных на требуемые входы исполнительного блока, а также кон- фигурирует АЛУ. Все модели микроконтроллеров PIC имеют встроенный генератор, задающий последовательность выполнения внутренних микроопераций в соответствии с сигналами, поступающими от дешифратора команд. В качестве времязадающего
Глава 4. Микроконтроллер PIC16F84 93 элемента обычно используется внешний кварцевый резонатор, подключаемый к выводам OSC1 и OSC2 микроконтроллера (Рис. 4.2). Именно этот резонатор оп- ределяет тактовую частоту микроконтроллера Tbsc- Более подробно о генераторе мы поговорим в главе 10. Максимальная тактовая частота большинства моделей среднего уровня составляет 20 МГц, однако в некоторых ранних моделях частота не могла превышать 10, а то и 4 МГц. Минимальная тактовая частота ничем не ог- раничена. Частота тактового генератора делится на четыре с помощью схемы, показан- ной на Рис. 2.25 (стр. 54), чтобы получить четыре не перекрывающихся тактовых сигнала. Каждый из этих сигналов используется схемой дешифратора для управ- ления внутренними узлами процессора в требуемой последовательности. Соот- ветственно длительность одного машинного цикла равна четырем периодам внеш- него сигнала/Osc (см. Рис. 4.4). Таким образом, при использовании резонатора с частотой 4 МГц частота машинных циклов составляетybsc/4, или один миллион циклов в секунду, что соответствует периоду машинного цикла, равному 1 мкс. В зависимости от текущей фазы машинного цикла блок выборки выполняет следующие операции: Qf. Инкрементирование счетчика команд и выставление его содержимого на шину адреса памяти программ. Q4: Считывание кода команды с шины данных памяти программ в регистр ко- манд IR1 и одновременно с этим пересылка предыдущей команды по кон- вейеру в регистр команд IR2, подключенный к дешифратору команд. Стек Стек представляет собой отдельный блок из восьми 13-битных регистров, ко- торый подключен к счетчику команд. В 6-й главе мы увидим, что стек использу- ется для хранения предыдущего значения счетчика команд, т.е. «запоминает» точ- ку возврата при вызове подпрограммы. Исполнительный блок Исполнительный блок (Рис. 4.5) осуществляет извлечение данных из памяти данных или непосредственно из кода команды и выполняет обработку этого зна- чения, используя АЛУ. Режим работы АЛУ определяется сигналами, формируе- мыми дешифратором команд. Результат помещается либо в рабочий регистр W, либо обратно в память данных, перезаписывая исходное значение. Арифметико-логическое устройство (АЛУ) Основным элементом исполнительного блока является АЛУ (см. Рис. 2.10 на стр. 39), которое осуществляет обработку данных, поступающих из двух источни- ков. Одним из этих источников является 8-битный рабочий регистр W. В качестве другого источника может выступать:
94 Часть II Программное обеспечение • Регистр данных, указанный в команде. Например, команда addwf h' 2 0 ' , f прибавляет содержимое рабочего регистра к содержимому регистра h’20’. • Константа, являющаяся частью слова команды (см. Рис. 3.6 на стр. 69). К примеру, команда addlw 5 прибавляет число 5 к содержимому рабочего регистра. В первом случае результат может быть помещен либо обратно в память дан- ных, если бит адресата равен 0 (см. Рис. 3.5 на стр. 68), либо в рабочий регистр, если этот бит равен 1, например в случае команд типа addwf h' 2 0 ' , w
Глава 4. Микроконтроллер PIC 16F84 95 Регистр STATUS Этот регистр, тесно связанный с АЛУ, содержит три бита-флага, которые ис- пользуются для информирования программы о результате выполнения команды. В частности, не было ли переноса при выполнении операции сложения. Флаг переноса Бит 0 регистра STATUS используется в качестве флага переноса С (от англ, carry — перенос). Основное его назначение — хранение бита переноса предыдущей операции сложения. При операциях вычитания значение этого бита соответствует инвертированному биту заема (см. Пример 4.2). Например, 24 — 12 = 1251, а 12 — 24 = 8850. Флаг С также используется в операциях сдвига, как показано на Рис. 5.13 (стр. 148). Пометка «R/W ?» на Рис. 4.6 отражает тот факт, что этот бит доступен как для чтения (R), так и для записи (W), а после сброса по питанию его состояние не определено (при любом другом сбросе состо- яние этого бита не изменяется). Регистр состояния (STATUS) 7 (R/WO) (R/W 0) Т RPO (R/W0) ТО (R D PD (R 1) Z (R/W?) "l1 DC (R/W?) С (R/W?) • • • Флаг переноса/заема .......Флаг десятичного переноса/заема .......Флаг нуля Бит выбора банка ОЗУ • • ; при прямой адресации БанкО (h’00’...h’7F') — 0 ! Банк 1 (h'80’...h,FF’) — 1 • Флаг тайм-аута сторожевого таймера......• После тайм-аута сторожевого таймера — 0 После сброса по питанию или выполнения команд clrwdt и sleep — 1 Флаг включения питания............... После выполнения команды sleep — 0 После сброса по питанию или выполнения команды clrwdt — 1 Рис. 4.6. Формат регистра STATUS микроконтроллера PIC16F84 Флаг десятичного переноса Бит 1 регистра STATUS используется в качестве флага десятичного переноса DC (от англ, digit carry). Этот флаг функционирует точно так же, как и обычный флаг С, только содержит бит переноса из младшего полубайта в старший, т.е. из 3-го бита в 4-й. Аналогично флагу С, флаг DC содержит дополнение бита заема из 3-го бита в 4-й. Информация о наличии или отсутствии переноса между полубайтами полезна при работе с данными, представленными в двоично-десятичном коде (посмотрите хотя бы на Пример 4.5). При использовании этого кода в каждом полубайте хра- нится 4-битное представление десятичного числа от 0 до 9 (см. стр. 20), и флаг де- сятичного переноса указывает на возникновение переноса между десятичными разрядами.
96 Часть II. Программное обеспечение Флаг нуля Бит 2 регистра STATUS используется в качестве флага нуля Z (от англ, zero — ноль). Этот бит устанавливается, если результат выполнения команды равен ну- лю, и сбрасывается в противном случае. В отличие от многих других микроконтроллеров, в микроконтроллерах PIC отсутствуют команды, специально предназначенные для сброса или установки флагов, подобные команде sec, имеющейся в микроконтроллерах семейств 6800/5/11 компании Motorola. Тем не менее, как видно из Рис. 4.7, к регистру STATUS можно обращаться как к регистру данных с адресом h’03’. Поэтому любая команда, которая изменяет содержимое регистра данных, может в принципе использоваться для изменения состояния флагов. Однако существует определенная проблема, заключающаяся в том, что многие из этих команд сами по себе влияют на состояние одного или не- скольких флагов (см., к примеру, Табл. 5.1 на стр. 129) и таким образом пере- определяют значение, являющееся результатом выполнения команды. Если мы, к примеру, попробуем сбросить все флаги с помощью команды очистки регистра с 1 г f 3 (см. Табл. 5.2 на стр. 131), то в результате флаг Z окажется установленным в 1, сообщая о том, что результат операции равен нулю! Для изменения отдельных битов регистра STATUS рекомендуется использовать команды сброса/установки бита регистра данных bcf и bsf (см. Табл. 5.2), поскольку сами по себе эти ко- манды не влияют на состояние флагов. Например, команда bsf 3,0 (установить 0-й бит регистра h’03’) эквивалентна команде sec, а команда bcf 3,2 (сбросить 2-й бит регистра h’03’) сбросит флаг Z. Назначение более специализированных флагов, размещающихся в 3-м и 4-м битах, будет описано в последующих главах. В общих чертах флаг PD (Power Down) сбрасывается при выполнении команды sleep, которая используется для выключения тактового генератора и перевода микроконтроллера в режим ожида- ния с малым потреблением (< 1 мкА). Флаг ТО (Time Out) сбрасывается при на- ступлении тайм-аута сторожевого таймера^. Оба этих флага доступны только для чтения — их состояние не может быть изменено программно. После сброса ука- занные флаги устанавливаются в 1. Все эти биты называются флагами или, иногда, семафорами, поскольку они сигнализируют о том или ином результате выполнения команды. Бит 5 этого ре- гистра несколько отличается от остальных, так как на его состояние не влияют происходящие события. Наоборот, флаг RP0 используется программистом для изменения состояния процессора. Чтобы проиллюстрировать назначение этого бита-переключателя, нам потребуется более подробно изучить структуру памяти данных микроконтроллера PIC16F84. На Рис. 4.7 приведена упрощенная модель памяти данных микроконтроллера PIC16F84. Эту память данных можно представить в виде картотечного шкафа, ко- торый в данном случае имеет два отделения (банка). Внутри каждого отделения имеется некоторое количество папок, или файлов, каждый из которых содержит ° Этот дополнительный счетчик, который рассматривается в главе 13, сбрасывает устрой- ство, если в течение заданного интервала времени не была выполнена команда clrwdt.
Глава 4. Микроконтроллер PIC16F84 97 БанкО S^0/> IN3F । регистр косвенной адресации) TMRO (счетный регистрТаймерэ 0) РСЦсчетмик команд, младший байт) STATUS (регистр состояния) FSR । Индексный регистр косвенной адресации) PORTA__________________________________ PORTE EEDATA 'регистр данных EEPROM I____________ EEADR (регистр адреса EEPROM; PCLATH (регистр-защелка старшего байта PC) IlffCON (регистр управления прерываниями) ±2 00 01 02 03 М 05* 06 07 08 09 0А 0В ОС 0D 0Е 0F 10 11 12 13 14 15 16 17 18 19 1А 1В 1С 1D 1Е 1F 20 21 22 23 24 25 26 27 28 29 Регистр STATUS Примечание: Бит 5 регистра STATUS, расположенного по адресу h’O3’, может быть установлен программистом с помощью команды установки бита: bsf 3,5 или сброшен с помощью команды сброса бита: bcf 3,5 Банк 1 INDF (регистр косвенной адресации) OPTIONREG (регистр опций)___________ РС1(снетчик команд, младший байт)_______ STATUS (регистр состояния) FSR (индексный регистр косвенной адресации) TRISA___________________________________ TPISB EFCON1 d-й регистр управления EEPROMi EECON2 (2-й регистр управления ЕЕРЯОМ) РС1АТН (регистр-защелка старшего байта PC) 2В 2С 2D 2Е 2F 30 31 32 33 34 35 36 37 38 39 ЗА ЗВ ЗС 3D ЗЕ 3F 40 41 42 43 44 45 46 47 48 49 4А 4В 4С 4D 4Е 4F РОН, отображенные на оба банка 80 81 82 83 84 85 86 87 88 89 8А 8В 8С 8D 8Е 8F 90 91 92 93 94 95 96 97 98 99 9А 9В 9С 9D 9Е 9F АО ..........— АВ АС AD ...................... ЛА А4 А5 А6 А7 AF ВО В1 В2 ВЗ В4 В5 В6 В7 В8 В9 ВА ВВ ВС BD BE BF СО С1 С2 СЗ С4 С5 С6 С7 С8 С9 СА СВ СС CD СЕ CF Рис. 4.7. Упрощенная структура памяти данных микроконтроллера PIC16F84
98 Часть II. Программное обеспечение восемь битов данных. Наравне с этим термином также используется термин ре- гистр^. Вообще говоря, в нашей картотеке присутствуют файлы двух типов. Некото- рые из этих файлов имеют названия и выполняют специальные, заранее задан- ные функции. Такие файлы называются регистрами специального назначения (РСН). РСН используются для управления и отслеживания состояния микрокон- троллера и его различных периферийных устройств. В частности, как мы уже ви- дели на Рис. 4.6, регистр с адресом Ь’ОЗ’ является регистром STATUS, а регистр с адресом h’06’ представляет собой регистр данных параллельного порта В, связан- ного с выводами RB7...RB0 (обычно обозначаемыми как RB[7:0]) микроконтрол- лера, как показано на Рис. 4.1. Остальные файлы, выделенные на рисунке серым цветом, программист мо- жет называть по своему усмотрению и использовать для хранения пользователь- ских данных общего назначения. В микроконтроллере PIC16F84 имеется 68 та- ких регистров общего назначения (РОН), адреса которых лежат в диапазоне h’0C’...h’4F’. Во всех микроконтроллерах PIC среднего уровня регистры с млад- шими адресами используются в качестве РСН, а регистры со старшими адресами — в качестве РОН. Однако в более новых представителях семейства, та- ких как PIC16F628, требуется больше РСН в связи с большим числом перифе- рийных устройств. При этом под РСН резервируется память с адресами до h’lF’. С учетом сказанного, во всех программах, приведенных в настоящей книге, пред- полагается, что для хранения переменных доступна область памяти, начиная с ад- реса h’20’. Во многих моделях объем памяти для хранения пользовательских данных больше, чем в микроконтроллере PIC16F84. И тем не менее даже в самой разви- той модели среднего уровня объем памяти не превышает 368 байт. Это не так уж и много, поэтому программы должны использовать эту ограниченную емкость очень эффективно* 2 3 *). Возвращаясь к Рис. 3.5 (стр. 68), можно заметить, что из 14 бит, составляющих код команды, семь зарезервировано под адрес операнда в памяти данных. Семь битов дают нам всего 27 = 128 адресов, т.е. страницу или банк, вмещающий в себя 128 регистров. Для преодоления этого ограничения необходимо где-то взять до- полнительные биты, чтобы расширить диапазон адресов. В микроконтроллере PIC16F84 для этой цели используется 5-й бит регистра STATUS, который называ- ется RPO (Register Page 03)). Этот бит выполняет роль 8-го бита адреса, позволяя использовать память данных, содержащую до 256 регистров. При RPO = 0 (после сброса по питанию) обращения производятся к О-му банку памяти данных (ре- гистры h’00’...h’7F’). При RPO = 1 разрешается доступ к 1-му банку, т.е. к регист- рам h’80’...h’FF’. *) В русскоязычной литературе в основном используется именно этот термин. — Примеч. пер. 2) Сравните с вашим ПК, имеющим сотни мегабайтов ОЗУ! 3) Возможно, потому, что более очевидное название Register Bank О (RB0) используется для обозначения вывода, подключенного к 0-му биту порта В.
Глава 4. Микроконтроллер PIC16F84 99 Большинство моделей среднего уровня имеют 4 банка ОЗУ и используют уже два бита регистра STATUS — RP1.RP0 (6-й и 5-й биты соответственно); см. Рис. 5.5 на стр. 122. В результате формируется 9-битный адрес памяти данных. Более подробно этот вопрос будет рассмотрен в следующей главе. Несмотря на то что использование банков памяти является довольно эффек- тивным вариантом преодоления 7-битного ограничения на размер адреса, их ис- пользование может вызвать трудности у неопытных программистов. В качестве примера рассмотрим фрагмент кода, в котором осуществляется запись числа b’0000111Г в регистр h’86’. Для выполнения этой операции мы воспользуемся ко- мандой movlw, загружающей 8-битную константу в рабочий регистр: movlw b'00001111' ; Загружаем константу h'OF' в W movwf h'86' ; и копируем его в регистр с адресом h'861 В коде ошибочно указан адрес h’86’, или Ь’70000110’, значение которого слишком велико для имеющегося размера поля. Большинство ассемблеров прос- то обрежут биты, выходящие за границы поля, в результате чего мы получим ад- рес h’06’, или Ь’0000110’. Таким образом, с точки зрения ассемблера адреса h’86’ и h’06’ являются одинаковыми, хотя большинство ассемблеров при этом выдадут программисту предупредительное сообщение. В частности, фирменный ассемб- лер Microchip (который мы будем рассматривать в главе 8) выдаст следующее со- общение: Message[302] Register in operand not in bank 0. Ensure that bank bits are correct. Другими словами, именно программист должен позаботиться о корректной установке битов RPx перед обращением к таким адресам. Чтобы понять, как это можно сделать, нам придется забежать немного вперед и рассмотреть команды манипуляций с битами, приведенные в Табл. 5.2 на стр. 131. Во всех микроконтроллерах требуется иметь возможность управления состоянием отдельных битов регистра как для задания опций в РСН, так и для «дрыгания» ножками портов ввода/вывода. В микроконтроллерах PIC для этих целей используются следующие команды bcf Команда сброса бита регистра данных (Bit Clear File) позволяет программисту сбросить любой бит в любом регистре. К примеру, команда bcf h' 2 0 ' , 7 сбрасы- вает 7-й бит регистра h’20’. Состояние остальных битов регистра при этом не из- меняется. bsf Команда установки бита регистра (Bit Set File) позволяет программисту устано- вить любой бит в любом регистре. К примеру, команда bs f h131', 3 устанавлива- ет 3-й бит регистра h’31’. Состояние остальных битов не изменяется. В старшем семействе появилась еще одна команда — btg (переключить бит в регистре данных).
100 Часть II. Программное обеспечение Возвращаясь к нашему примеру, теперь мы можем написать: bsf 3,5 ; Установка 5-го бита (RPO) регистра STATUS ; позволяет обращаться к 1-му банку памяти movlw b'00001111' ; Загружаем константу h'OF' в W movwf h' 86' ; и копируем его в регистр с адресом h' 86 ' bcf 3,5 ; Сбрасываем RP0 для возврата к 0-му банку На самом деле использование 1-го банка в микроконтроллере PIC16F84 све- дено к минимуму. Все 68 РОН отображены на оба банка памяти, т.е. по адресу h’n’ и по адресу h’w+80’ расположен один и тот же регистр. Например, если програм- мисту необходимо прочитать содержимое регистра h’20’, не имеет значения, ка- кой из банков при этом используется процессором, поскольку в регистре h’AO’ находятся те же самые данные, а не просто их копия! Такое встречается достаточ- но редко, поскольку микроконтроллеры PIC с памятью данных большего объема «разбрасывают» свои уникальные РОН (и РСН) по всем имеющимся банкам па- мяти. Тем не менее в более новых моделях, таких как PIC16F628, имеется неболь- шая группа (обычно 16) РОН, отображенных на все банки памяти. Такое решение позволяет максимально быстро сохранять и восстанавливать критические данные независимо от того, с каким из банков процессор работает в данный момент вре- мени (см. стр. 218). Большинство наиболее часто используемых РСН также отображаются на все банки памяти. Типичным примером может служить регистр STATUS, который одновременно расположен и по адресу h’03’, и по адресу h’83’. Это сделано из-за того, что к флагам и битам регистра STATUS приходится обращаться очень часто, и постоянное переключение банков было бы неэффективным. В самом деле, ког- да в коде, приведенном выше, мы сбрасываем бит RP0 для перехода от 1-го банка к 0-му, мы предполагаем, что регистр STATUS присутствует в обоих банках. В противном случае мы никогда бы не смогли переключить бит RP0 и сменить банк памяти. Большинство РСН микроконтроллера PIC16F84 встречаются во всех предста- вителях семейств среднего уровня, более того, они, как правило, и размещаются по тем же самым адресам. Так, регистр STATUS расположен по адресам h’03’/h’83’. Формально мы не будем в этой главе рассматривать никакие РСН, за исклю- чением регистра STATUS и регистров, имеющих отношение к счетчику команд. Однако будет удобнее, если все эти регистры будут кратко описаны в одном мес- те. Поэтому мы просто перечислим их здесь, а подробно об их назначении пого- ворим в соответствующих главах книги. Косвенная адресация При непосредственной адресации адрес операнда содержится в коде коман- ды. В микроконтроллерах среднего уровня для этого зарезервировано 7-битное поле, показанное на Рис. 3.5 (стр. 68). В сфере встраиваемых устройств, когда ко-
Глава 4. Микроконтроллер PIC 16F84 101 ды команд хранятся в ПЗУ какого-либо типа, такие адреса являются фиксирован- ными и, соответственно, не могут модифицироваться. Альтернативным способом, используемым в том или ином виде всеми вычис- лительными устройствами, является хранение адреса операнда в каком-либо ре- гистре. В случае PIC этот адрес содержится в регистре FSR (File Select Register), располагающемся по адресу h’04’ памяти данных. Для переключения в режим косвенной адресации внутренняя логика отслеживает обращение по нулевому ад- ресу памяти данных. Когда в команде указывается этот нулевой адрес, на шину адреса памяти данных выставляется содержимое регистра FSR, как показано на Рис. 5.6 (стр. 124). При косвенной адресации местоположение операндов является не констан- той в памяти программ, а переменной в регистре FSR. То есть положение операнда может изменяться в процессе выполнения программы. В качестве примера мож- но взглянуть на Программу 5.2, приведенную на стр. 125. К режиму косвенной адресации имеют отношение следующие регистры: INDF (h’00’) Нулевой адрес памяти данных обозначается как INDF (INDirect File). Поскольку этот регистр используется исключительно для включения режима косвенной ад- ресации, то его не стали реализовывать как настоящий регистр. То есть вы не мо- жете хранить данные в регистре INDF, поскольку он физически не существует! FSR (h’04’> В индексном регистре FSR содержится 8-битный адрес, который используется при обращении команды по нулевому адресу (к регистру INDF). Таймер Большинство микроконтроллеров имеют возможность измерения временных интервалов и/или формирования прямоугольных импульсов заданной длитель- ности. Как правило, для этого используется один или более счетчиков, инкре- ментирование которых происходит либо по внешним импульсам, либо по внут- реннему тактовому сигналу Например, если в автоматической упаковочной ма- шине необходимо осуществлять подсчет консервных банок, движущихся по конвейеру, то в качестве входного сигнала таймера может использоваться сигнал от фотоэлектрического датчика. Если в одну коробку помещается 24 банки, то во внутренний 8-битный счетчик необходимо загрузить значение h’E8’ (-24). При переполнении счетчика с h’FF’ до h’00’ будет сгенерировано прерывание (см. главу 7) и микроконтроллер начнет выполнять соответствующие действия. Во всех микроконтроллерах PIC имеется, по крайней мере, один базовый тай- мер/счетчик — Таймер 0. Счетный регистр таймера TMR0 (Ь’ОГ), доступный для чтения и записи, может тактироваться внешним сигналом, подаваемым на вход микроконтроллера TOCKI (TimerO ClocK. In), который совмещен с линией RA4 порта А. Кроме того, инкрементирование счетчика может происходить по внут- реннему тактовому сигналу Q4 (Рис. 4.4), частота которого составляет 1 /4 частоты кварцевого резонатора. Частота обоих сигналов (и внешнего, и внутреннего) мо-
102 Часть II. Программное обеспечение жетбыть снижена при помощи внутреннего 8-битного предделителя. Коэффици- ент деления предделителя задается тремя младшими битами регистра OPTION_REG, расположенного по адресу h’81 ’ (см. Рис. 13.2 на стр. 452), кото- рые называются PS2:PS1:PSO. Соответственно, коэффициент деления будет ра- вен 2PS+1. Например, если PS[2:0] = 111, то счетный регистр таймера будет инкре- ментироваться с частотой f 256 где f — частота источника тактового сигнала. Предделитель может быть отключен от таймера установкой бита PSA (OPTION_REG[3]) в 1. При этом импульсы будут поступать непосредственно на счетчик. Кроме того, при записи в счетный регистр таймера регистр предделителя также сбрасывается (к примеру, последовательность команд movlw h'E8', movwf 1 вызовет сброс предделителя), позволяя отсчитывать временной интер- вал от точно заданного момента. Когда бит PSA регистра OPTION_REG установлен в 1, то предделитель ис- пользуется в качестве постделителя сторожевого таймера (см. Рис. 13.1 на стр. 451). Сторожевой таймер предназначен для сброса микроконтроллера в слу- чае, если он не будет периодически переустанавливаться командой сброса сторо- жевого таймера clrwdt (Clear WatchDog Timer). Это гарантирует сброс микро- контроллера в случае его некорректной работы, вызванной внешними помехами или ошибкой в программе, возможно, из-за перехода к незапрограммированной области памяти программ. При этом сторожевой таймер перестанет периодичес- ки переустанавливаться. Если предделитель подключен к Таймеру 0 (PSA = 0), то период тайм-аута сторожевого таймера будет примерно равен 18 мс. При установ- ленном бите PSA период гарантированного сброса процессора будет составлять (2PS х 18) мс. Таким образом, для предотвращения сброса микроконтроллера ин- тервал между выполнением команд clrwdt должен быть меньше указанного пе- риода. Кроме того, эта команда сбрасывает счетчик предделителя. При наступле- нии тайм-аута бит ТО регистра STATUS сбрасывается. Если это необходимо, сто- рожевой таймер можно отключить при программировании микроконтроллера (во время занесения кода программы в память программ). Различные конфигураци- онные биты (fuses) располагаются в ячейке памяти программ по адресу h’2007’ (см. Рис. 10.6 на стр. 312), которая недоступна, когда микроконтроллер работает в нормальном режиме. Все эти детали обычно скрыты от оператора программным обеспечением программатора. С Таймером 0 связаны следующие регистры: TMRO (h’01 ’) Этот 8-битный суммирующий счетчик, иногда называемый таймером/счетчиком реального времени, осуществляет счет импульсов, поступающих на вход таймера. Данный регистр в любой момент времени доступен как для чтения, так и для за- писи. При переполнении счетчика (при смене значения с h’FF’ на h’00’) он уста- навливает бит TOIF (Timer 0 Interrupt Flag) регистра управления прерываниями INTCON (см. Рис. 7.3 на стр. 213). Этот флаг может использоваться для генера- ции прерывания.
Глава 4. Микроконтроллер PIC16F84 103 OPTION_REG (h’81 ’) Для управления Таймером 0 используются шесть битов этого регистра, располо- женного по адресу h’81 ’ (см. Рис. 13.2 на стр. 452): • PS2, PSI, PSO (биты 2, 1 и 0 соответственно) определяют коэффициент де- ления предделителя (2PS+1) Таймера 0 или постделителя (2PS) сторожевого таймера. • T0SE (бит 4) позволяет программисту задать фронт импульсов на входе T0CKI, по которому будет осуществляться инкрементирование счетчика: 0 — нарастающий фронт, 1 — спадающий фронт. • TOCS (бит 5) используется для выбора источника тактового сигнала тайме- ра: 0 — системный тактовый сигнал, 1 — импульсы со входа T0CKI. Остальные два бита регистра используются для выбора активного фронта внешнего прерывания и конфигурирования входов порта В. Счетчик команд Мы уже говорили (см. Рис. 4.2), что микроконтроллеры PIC среднего уровня имеют 13-битный счетчик команд (Program Counter — PC), выполняющий функ- цию указателя на команды в пределах 8 Кбайт. Сколько именно битов счетчика используется в каждой конкретной модели, зависит от имеющегося объема памя- ти программ. Так, в микроконтроллере PIC16F84 используется 10 бит (210 = 1 Кбайт), в PIC16F628 - 11 бит (2И = 2 Кбайт), в PIC16F874 - 12бит (212 = 4 Кбайт), а в PIC16F877 задействованы все 13 бит. Иногда может потребоваться изменить состояние счетчика команд из про- граммы. Для этого младший байт PC напрямую доступен через регистр специ- ального назначения PCL (Program Counter Low). А для изменения всех 13 бит требуется дополнительный регистр. Регистр-защелка старшего байта PCLATH не является в действительности старшим байтом PC, а служит в качестве буфе- ра. Изменение содержимого регистра PCLATH не влияет на старший байт счет- чика команд, однако одновременно с записью в регистр PCL новое значение PCLATH загружается в старший байт 13-битного счетчика команд. Таким обра- зом, как показано на Рис. 4.8, все 13 бит счетчика команд обновляются одновре- PCL П’ОА’ Результат с выхода АЛУ заносится в регистр PCL. Например,movwf PCL Рис. 4.8. Изменение всех 13 битов счетчика команд при записи в регистр PCL
104 Часть II. Программное обеспечение менно. Запомните эту особенность, она потребуется вам при ответе на Вопрос для самопроверки 4.2. Для манипулирования счетчиком команд используются следующие регистры: PCL (h’02’) Регистр PCL физически является младшим байтом счетчика команд. Этот ре- гистр доступен как для чтения, так и для записи. PCLATH (h’OA’) Регистр PCLATH является регистром-защелкой для хранения данных, которые должны быть загружены в старший байт счетчика команд. Загрузка старшего бай- та происходит при записи в регистр PCL, что обеспечивает одновременное об- новление всех 13 бит счетчика. Не забывайте, что регистр PCLATH используется только в качестве буфера, поэтому значение, считанное из него, не будет соответствовать текущему состоя- нию старшего байта счетчика команд1 \ Параллельные порты ввода/вывода Способность одновременно изменять или контролировать состояние не- скольких цифровых линий представляет собой воистину универсальную возмож- ность систем на базе микроконтроллеров. В зависимости от типа корпуса микро- контроллеры среднего уровня имеют от 4 до 52 таких линий ввода/вывода. К при- меру, в 40-выводном микроконтроллере PIC16F877 имеется в общей сложности 33 линии ввода/вывода. Микроконтроллер PIC16F84 имеет 13 линий ввода/вывода, разделенных на два порта. ПортА имеет 5 линий ввода/вывода, отображенных на адресное про- странство памяти данных по адресу h’05’. Остальные 8 линий относятся к порту В, размещенному по адресу h’06’. Эти порты можно считать своеобразны- ми «окнами» в памяти данных, поскольку значения, записываемые в регистры с адресами h’05’ и h’06’, появляются на выводах микроконтроллера RA4...RA0 и RB7...RB0 соответственно (см. Рис. 10.2 на стр. 304). Однако физически и логи- чески эти порты гораздо сложнее, чем обычные внутренние регистры. Мы еще вернемся к этому вопросу в главе 11, пока же скажем только, что линия порта мо- жет быть сконфигурирована как выход (при этом ЦПУ может управлять состоя- нием соответствующего вывода) или как вход (при этом ЦПУ может считывать состояние данного вывода). Для этого предназначены регистры направления данных TRISA и TRISB (для порта А и В соответственно), расположенные по ад- ресам h’85’ и h’86’. Название TRIS образовано от слова TRIState21 (см. Рис. 11.3 на стр. 333). Эти регистры находятся в 1-м банке, поскольку они обычно конфи- гурируются в начале программы и впоследствии не изменяются. ° В моделях старшего семейства PIC18XXXX при чтении регистра PCLATH возвращается значение старшего байта счетчика команд. 2) Tristate — тристабильный (англ.). — Примеч. пер.
Глава 4. Микроконтроллер PIC 16F84 105 В качестве примера рассмотрим следующую ситуацию. Предположим, что нам необходимо сделать выводы RB[6:0] порта В входами, а вывод RB7 — выхо- дом. Тогда код для конфигурирования порта будет выглядеть следующим обра- зом: bsf 5,3 ; Переходим к 1-му банку movlw h'7F' ; Двоичному числу 0111 1111 соответствует movwf h’86’ ; RB7 - выход, RB6...0 - входы bcf 5,3 ; Возвращаемся к 0-му банку Несмотря на то что этот код совершенно корректен и благодаря комментари- ям его назначение в общем-то понятно, он все же не очень удобен для чтения. Второй вариант того же куска кода имеет куда более дружественный вид, но с точ- ки зрения ассемблера совершенно идентичен первому (см. стр. 70). STATUS equ 03 ; Регистр STATUS расположен по адресу h’03' RP0 equ 05 ; Бит переключения банков - 5-й TRISB equ h’86' ; Регистр направления расположен по адресу h’86' PORTB equ 06 ; Регистр данных порта расположен по адресу h'06' bsf STATUS,RP0 ; Переходим к 1-му банку movlw b' 01111111' ; Двоичному числу 0111 1111 соответствует: movwf TRISB ; RB7 - выход, RB6...0 - входы bcf STATUS,RP0 ; Возвращаемся к 0-му банку Очевидно, что последний вариант более предпочтителен. Может показаться, что сделанные нами изменения носят исключительно косметический характер. Однако более ясный текст снижает вероятность возникновения ошибок, а также облегчает отладку и внесение последующих изменений в программу. В реальных программах, в отличие от приведенного фрагмента кода, используется множество разнообразных переменных и битов регистров, поэтому они должны быть ясны- ми и понятными. Четыре первые строки приведенного фрагмента показывают один из спосо- бов, посредством которого программист может сообщить транслятору с языка ас- семблера о необходимости подстановки вместо символьного имени числового значения. В частности, строка STATUS equ 03 говорит о том, что при использовании в качестве операнда имени STATUS оно должно заменяться числом 3 (т.е. регистр h’03’). Директива equ является сокра- щением от «EQUivalent to»1*. Директивой называется псевдокоманда, которая, как правило, не генерирует реальный машинный код, а используется для переда- чи информации транслятору. Начиная с этого момента, мы будем для ясности присваивать нашим регистрам и битам имена. В качестве примера напишем код, который формирует на выводе RB7 поло- жительный импульс (предполагается, что ЦПУ работает с 0-м банком): ° Be equivalent to — быть равноценным чему-либо (англ.). — Примеч. пер.
106 Часть И. Программное обеспечение bsf PORTB,7 ; Выставляем на RB7 ВЫСОКИЙ уровень (устанавливаем 7-й бит) bcf PORTB.7 ; Выставляем на RB7 НИЗКИЙ уровень (сбрасываем 7-й бит) С параллельными портами ввода/вывода связаны следующие регистры: PORTA (h’05’) В этом регистре задействовано только 5 младших битов, подключенных к выво- дам RA4...RA0 микроконтроллера. Вывод RA4 используется также модулем Таймера 0. Фантомные три старших бита читаются как 0. В некоторых моделях семейства, например в PIC16F628, могут быть реализованы все 8 линий порта А. TRISA (h’85’) Этот регистр предназначен для конфигурирования линий порта А в качестве вхо- дов или выходов. Установка бита TRISA[«] в 1 делает вывод RA[«] входом, а сброс в 0 — выходом1*. При любом сбросе все биты регистра TRISA устанавливаются в 1, и все выводы порта соответственно становятся входами. PORTB (h’06’) Двунаправленный 8-битный порт ввода/вывода, подключенный к выводам RB0...RB7 микроконтроллера. Вывод RB0 может использоваться также в качестве входа аппаратного прерывания. TRISB (h’86’) Этот регистр используется для конфигурирования линий порта В в качестве вхо- дов или выходов. Более подробно — см. описание регистра TRISA. EEPROM-память данных В большинстве моделей среднего и старшего семейства имеется блок памяти объемом до 256 (в PIC16F84 — 64) байт, для хранения содержимого которого не требуется питания. Эта энергонезависимая память не является частью (энергоза- висимой) памяти данных, а обращения к ней производятся посредством опреде- ленных РСН, как к обычному периферийному устройству. Любой байт этой па- мяти можно считать или записать посредством регистра EEDATA. Адрес байта задается регистром EEADR, а управление процессом чтения/записи осуществ- ляется с помощью регистров EECON1 и EECON2. Срок службы большинства модулей EEPROM составляет не менее 10 млн циклов перезаписи, а период со- хранности данных — не менее 40 лет. Типичными примерами использования энергонезависимой памяти является хранение количества страниц, отпечатан- ных лазерным принтером, или суммарный путь, пройденный автомобилем. Подробно процессы чтения и записи EEPROM будут рассмотрены в главе 15, здесь же мы просто приведем последовательность действий для выполнения опе- раций чтения/записи. Г) Простой мнемотехнический прием для знающих английский: 0 — Output (выход), а 1 — Input (вход).
Глава 4. Микроконтроллер PIC 16F84 107 Чтение 1. Поместить адрес (h’00’...h’FF’) в EEADR. 2. Установить бит RD (0-й бит регистра EECON1) в 1 для переключения в ре- жим чтения. 3. Считать адресованные данные из EEDATA. Запись 1. Поместить адрес в EEADR. 2. Поместить данные в EEDATA. 3. Установить бит WREN (2-й бит регистра EECON1) в 1 для переключения в режим записи. 4. Записать число h’55’ в EECON2. 5. Записать число h’AA’ в EECON2. 6. Начать цикл записи установкой бита WR (1-й бит регистра EECON1) в 1. Операция записи, которая, как правило, является достаточно редким событи- ем, специально сделана такой запутанной, чтобы исключить случайное измене- ние EEPROM. На самом деле регистра EECON2 не существует, однако последова- тельная запись по его адресу значений Ь’55’и h’AA’ необходима для разблокирова- ния EEPROM. Прерывания могут нарушить эту последовательность, поэтому, если они используются, их следует запретить. Длительность операции записи со- ставляет около 50 мс, после ее завершения устанавливается 4-й бит регистра EECON1 (флаг EEIF), который может использоваться для прерывания работы процессора. Флаг WRERR (3-й бит регистра EECON1) устанавливается, если цикл записи был прерван, скажем, в результате внешнего сброса. К EEPROM-памяти данных относятся следующие регистры (адреса указаны для модели PIC16F84): EEDATA (h’08’> Этот регистр содержит данные адресованной ячейки EEPROM после операции чтения или же данные, которые будут записаны в EEPROM во время операции записи. EEADR (h’09’> Этот регистр содержит адрес байта, к которому производится обращение, загру- жаемый перед началом операции чтения или записи. EECON1 (h’88’) Этот регистр содержит следующие биты управления и состояния: • Бит запуска операции чтения EEPROM. • Бит разрешения операции записи. • Бит запуска операции записи в EEPROM. • Бит признака преждевременного завершения цикла записи. • Бит признака нормального завершения цикла записи.
108 Часть II. Программное обеспечение Более полную информацию можно получить из Рис. 15.2, приведенного на стр. 545. EECON2 (h’89’) Этот управляющий регистр физически не существует, и при его чтении всегда возвращается нулевое значение. Указанный адрес используется для загрузки пос- ледовательности, разрешающей цикл записи. Последовательность состоит из двух чисел h’55’ и h’AA’, записываемых друг за другом. Прерывания Регистр управления прерываниями INTCON, расположенный по адресу h’OB’1), содержит биты маски и статуса, управляющие реакцией микроконтролле- ра на прерывания. Использование этого регистра описывается в главе 7. Боль- шинство периферийных устройств имеют собственные биты, относящиеся к пре- рываниям и расположенные в других управляющих регистрах (см., например, Рис. 7.5 на стр. 223). Примеры Пример 4.1 Объясните, каким образом внедрение в схему блока выборки команд кон- вейера увеличивает производительность микроконтроллеров PIC. Предвидите ли вы какие-либо проблемы, связанные с поддержкой команд перехода (таких как goto), относительно структуры конвейера? Решение Наличие конвейера является обязательным условием для организации парал- лельной работы блока выборки и исполнительного блока. То есть для того, чтобы иметь возможность исполнять команду п одновременно с выборкой из памяти программ команды п + 1, требуется внутренний элемент с памятью, который пе- редавал бы код команды в дешифратор команд. Поскольку все команды имеют одинаковый размер (14 бит), структуру регистров конвейера и управление ими можно значительно упростить. Большинство традиционных CISC-процессоров имеют команды, длина которых может быть различной. К примеру, размер ко- манд микроконтроллера 68НС11 колеблется от 1 до 4 байт, т.е. длительность фазы выборки составляет от 1 до 4 транзакций на шине. Некоторые более развитые процессоры имеют многоступенчатый конвейер, каждый этап которого связан с определенной частью исполнительного блока. За счет этого можно реализовать несколько одновременных потоков исполняемых команд. ° И отображенный на все банки памяти.
Глава 4. Микроконтроллер PIC 16F84 109 Проблема, связанная с конвейером, вытекает из предположения, что коман- ды программы будут исполняться последовательно, т.е. так, как они расположены в памяти программ. При этом команды, не удовлетворяющие этому условию и изменяющие содержимое счетчика команд, требуют очистки конвейера, с тем чтобы код адресованной команды оказался на вершине конвейера. Например, ес- ли командой к является команда goto п, то к тому моменту, когда процессор уз- нает, что в действительности на следующем шаге необходимо будет выполнить команду п, команда к + 1 будет уже загружена в конвейер. Поэтому необходимо выполнить холостой цикл, во время которого код команды п будет напрямую за- гружен в конвейер (разумеется, команда к + 1, код которой находится на вершине конвейера, не выполняется). Иногда эту операцию называют очисткой конвейера. Соответственно, такие команды, как goto, выполняются за два машинных цик- ла. Команды условного пропуска (см. главу 5) выполняются за два цикла в случае пропуска и за один цикл в противном случае. Все остальные команды выполня- ются за один машинный цикл. Пример 4.2 Можете ли вы определить, почему после операции вычитания или сложения с отрицательным числом значение флага С является дополнением к биту заема? Подсказка: вспомните правила двоичной арифметики в дополнительных ко- дах (стр. 22). Решение Операция вычитания во всех микроконтроллерах PIC реализована одинако- во: байт данных переводится в дополнительный код, а затем выполняется сложе- ние, как показано на Рис. 2.9 на стр. 39. В этой ситуации итоговый бит переноса равен 0, если результат сложения получается отрицательным, и 1, если положи- тельным. Например: 1.06 - ОА -4 00000110 + 11110110 = (0) 11111100 ил и -4 (нет переноса). 2. 0А - 06 -> 00001010 + 11111010 = (1) 00000100 или +4 (есть перенос). В обоих случаях флаг переноса соответствует инвертированному биту заема. Такое поведение соответствует философии RISC PIC-микроконтроллеров — про- цессор должен быть максимально простым и понятным. Точно такая же инверсия происходит при использовании отрицательного опе- ранда в командах сложения, например в команде addlw -6. Это выражение бу- дет преобразовано транслятором в addlw h' FC ', где h' FC ', конечно же, пред- ставляет собой дополнительный код числа 6. Пример 4.3 Один умник решил скопировать содержимое регистра STATUS в регистр h’40’, с тем чтобы использовать его в дальнейшем. Однако бит 2 регистра STATUS оказался сброшен в 0. Почему?
110и Часть II. Программное обеспечение Решение Из текста на стр. 67 мы узнали, что команда movf устанавливает флаг Z, если содержимое регистра-адресата равно нулю, в противном случае флаг Z сбрасыва- ется. Так что следующий фрагмент программы movf STATUS,w ; Скопировать содержимое регистра h'03'(STATUS) в W, movwf h’40' ; а потом в регистр h1401 действительно скопирует содержимое регистра h’03’ в регистр h’40’. Но до тех пор, пока все биты регистра STATUS не будут равны нулю, флаг Z будет постоян- но сбрасываться. При нормальной работе флаги PD и ТО равны 1, поэтому итого- вым значением флага Z всегда будет 0, независимо от его исходного состояния. Разумеется, это ограничение можно обойти. Одно из таких решений показано на стр. 217. Пример 4.4 Как бы вы задали следующую конфигурацию некоторых РСН из 1-го банка: • OPTION_REG b’10101111’ • TRISA b’00011110’ • TRISB b’llllllll’ Решение Поскольку все три регистра находятся в 1-м банке, нам необходимо будет пе- реключить банки перед записью данных и переключиться на 0-й банк после кон- фигурирования регистров. STATUS equ 3 ; Регистр STATUS расположен по адресу h'03' RPO equ 5 ; Бит RPO - 5-й OPTION_REG equ h'811 ; Регистр OPTION_REG расположен по адресу h'81 TRISA equ h'8 51 ; Регистр направления порта A TRISB equ h'8 6' ; Регистр направления порта В bsf STATUS,RPO ; Переходим к 1-му банку movlw b'lOlOllir ; Первую константу movwf OPTION_REG ; в регистр OPTION_REG movlw b’00011110’ ) Вторую константу movwf TRISA ; в TRISA movlw b' 11111111' ; Третью константу movwf TRISB ; в TRISB bcf STATUS,RPO ; Возвращаемся к 0-му банку
Глава 4. Микроконтроллер PIC 16F84 111 Пример 4.5 Напишите программу для инкрементирования упакованного BCD-числа, на- ходящегося в памяти данных по адресу h’20’. Решение Два двоично-десятичных (BCD) разряда можно упаковать в один байт, т.е. он может использоваться для хранения чисел от Q до 99. Например, значение 0100100lh’2O’ соответствует числу 49. Инкрементирование числа, хранящегося в таком хитром виде, с использованием обычных правил двоичного сложения мо- жет привести к некорректному результату. Например, Ь’01001001 + 1’ (49 + 1) даст нам b’01001010’ (h’4A’), тогда как нам необходимо получить число Ь’01010000’ (h’50’). Аналогично, Ь’ 10011001 + Г (99 + 1) даст нам Ь’10011010’ (h’9A’) вместо Ь’00000000’ + ЬТ (h’l 00’). Из приведенных примеров можно увидеть, что если после инкрементирова- ния какого-либо разряда BCD-числа он равен 10, то необходимо его обнулить, а к старшему разряду прибавить единицу. Воспользовавшись этим рассуждением, сформулируем перечень задач, которые должна выполнять наша программа: 1. Инкрементировать BCD-число по правилам обычной двоичной арифметики. 2. Если младший полубайт результата равен 10, прибавить к результату число 6. 3. Если старший полубайт результата равен 10, прибавить к нему число 6. В Программе 4.1 приведена эффективная реализация описанного алгоритма. После инкрементирования по правилам обычной двоичной арифметики к ре- зультату прибавляется число 6 и проверяется состояние флага DC. Этот флаг уста- новится только в том случае, если исходное значение полубайта было равно деся- ти (h’OA’ + h’06’ = h’10’). В этом случае сумма сохраняется как необходимая кор- рекция, иначе производится вычитание для возврата к исходному значению. Старший полубайт (BCD-разряд) проверяется и корректируется аналогичным образом, только при этом используется уже флаг полного переноса С. Если он ус- тановлен, то результат сложения с числом h’60’ сохраняется, в противном случае это число вычитается. При необходимости флаг переноса может использоваться для установки разряда сотен, чтобы показать переполнения с 99 до 100. Программа 4.1. Инкрементирование упакованного BCD-числа .************************************************************** ;* ФУНКЦИЯ : Инкрементирует число в BCD-формате * ;* ВХОД : BCD в регистре h'20' ★ ; * ВЫХОД BCD+1 в регистре h'20' ★ ; * ПРИМЕР : 10011000 (98) + 1 = 10011001 (99) ★ *i++i*+*++++*+*+*tt***t*tttt****t*+*++*t+^ STATUS equ 3 ; Регистр STATUS С equ 0 ; Флаг переноса - бит 0 DC equ 1 ; Флаг десятичного переноса - бит 1 BCD equ h'20' ; Исходное BCD-число - в регистре h'20'
112 Часть II. Программное обеспечение BCD_INC incf BCD, w ; Инкрементируем число и помещаем в W addlw 6 ; Прибавляем шесть btf ss STATUS,DC ; Это было нужно, ЕСЛИ был десятичный перенос, addlw -6 ; ИНАЧЕ не нужно ; Теперь проверим старший разряд, прибавляя к нему б и проверяя флаг переноса addlw h' 60' ; Прибавим h'601 (т.е. шесть к старшему разряду) btf ss STATUS,C ; Это было нужно, ЕСЛИ был перенос, addlw -h’60• ; ИНАЧЕ отменяем коррекцию ; Инкрементированное и скорректированное BCD-число теперь в W movwf BCD ; Помещаем его в память В качестве альтернативного варианта можно было бы вычитать перед инкре- ментированием число девять и, если в результате инкрементирования флаг Z ока- жется установленным, инкрементировать старший разряд, в противном случае прибавить 10. Ту же операцию следует повторить для старшего разряда. Вопросы для самопроверки 4.1. Когда микропроцессор используется в вычислительном устройстве общего назначения, программа обычно загружается в ОЗУ, доступное как для чте- ния, так и для записи, и выполняется уже оттуда. Это означает, что в один момент времени в системе может выполняться текстовый редактор, а в дру- гой — программа работы с электронными таблицами. Разумеется, это не- применимо к встраиваемым приложениям, в которых программа хранится в энергонезависимом ПЗУ какого-либо типа. Объясните, для чего так сдела- но, и укажите преимущества различных вариантов исполнения энергонеза- висимой памяти - ПЗУ (ROM), СППЗУ (EPROM) и ЭСППЗУ (EEPROM). 4.2. Микроконтроллер среднего уровня PIC16F877 имеет память программ объ- емом 8 Кбайт, в которой может храниться до 8192 14-битных команд, распо- ложенных в диапазоне адресов h’0000’...h’lFFF’. Как, не прибегая к помо- щи команды goto, которая имеет определенные ограничения (см. Рис. 5.1 на стр. 117), можно выполнить переход к команде, расположенной в памяти программ по адресу h’ 1234’, из любого места программы? 4.3. Учитывая, что команда movf воздействует на флаг Z (см. Пример 4.3), как можно использовать эту команду для проверки на ноль содержимого любого регистра данных? 4.4. Из Табл. 1.1, приведенной на стр. 18, можно увидеть, что коды заглавных букв A...Z отличаются от кодов соответствующих строчных букв только зна- чением 5-го бита, который равен 0 в случае заглавных и 1 — в случае строч- ных букв. Можете ли вы, используя команды, которые фактически были представлены в этой главе, написать процедуру перевода символа ASCII, находящегося в регистре h’20’, из нижнего регистра в верхний?
Глава 4. Микроконтроллер PIC 16F84 113 4.5. Используя конфигурационные значения из Примера 4.4, напишите про- грамму, формирующую положительный импульс на выводе RA0 длитель- ностью 4 мкс. Предполагается, что тактовая частота равна 4 МГц. 4.6. Можете ли вы написать последовательность команд, которая выдаст на вы- вод RA1 ВЫСОКИЙ уровень, затем сформирует на выводе RA0 четыре им- пульса и в завершение выставит на вывод RA1 НИЗКИЙ уровень? Не за- будьте сконфигурировать регистр TRISA. 4.7. В большинстве электронных часов используется кварцевый резонатор час- тотой 32.768 кГц, часто называемый «часовым». Из-за больших объемов вы- пуска эти резонаторы имеют низкую стоимость. Хотя использование такого резонатора и снизит скорость выполнения программы, из Рис. 10.3 на стр. 306 можно увидеть, что мощность, рассеиваемая микроконтроллером, прямо пропорциональна тактовой частоте. Поэтому «часовой» резонатор является достаточно привлекательным выбором для многих экономичных приложений. Можете ли вы вычислить длительность машинного цикла при использова- нии такого резонатора? Какой смысл имеет значение 32 768 для времязада- ющих узлов?
ГЛАВА НАБОР КОМАНД Написание программы в чем-то сродни постройке дома. Имея в наличии оп- ределенные стройматериалы, строитель просто укладывает их вместе в нужном порядке. Разумеется, для этого он должен иметь соответствующие знания и навы- ки, ведь нарушение технологии может привести к тому, что крыша дома будет протекать, а сам дом будет продуваться всеми ветрами и даже может развалиться! Можно проектировать дом одновременно с его постройкой. В принципе, если вы строите простую хижину, это вполне допустимо. Однако очевидно, что при та- ком подходе к строительству полученный в результате дом не сможет очень долго защищать владельца от дождя, а также не будет экономичным, ремонтопригод- ным, эргономичным, да и просто красивым. Гораздо лучше нанять архитектора, чтобы он спроектировал здание до того, как начнется строительство. Разумеется, этот проект будет в достаточной степени абстрактным, хотя лучше, если проекти- ровщик имеет представление о технических характеристиках и стоимости имею- щихся строительных материалов. К сожалению, очень многие программируют «на бегу», практически не заду- мываясь о составлении сколько-нибудь подробного проекта. В области программ- ного обеспечения термин «проектирование» означает написание алгоритма и раз- работку необходимых структур данных. И опять же лучше, если разработчик алго- ритма будет учитывать «кирпичики», из которых будет построена программа. В нашем случае такими кирпичиками являются машинные команды. В примерах, приведенных в данной главе, большей частью затрагиваются именно вопросы кодирования (строительства). В последующих главах мы позна- комимся с более развитыми структурами, которые облегчат нам жизнь на этом этапе разработки программы. Кроме того, мы сможем попрактиковаться в разра- ботке алгоритмов и структур данных. Можно провести параллель между написанием программы и приготовлени- ем кулинарного блюда. Для любой плиты, будь то микроволновая печь или электроплита, имеется свой набор операций. Эти операции — к примеру, выпа- ривание, обжаривание, кипячение — по своей сути аналогичны набору команд, которые могут быть выполнены центральным процессором. Различные ингре- диенты, которые могут использоваться во время этих операций, являются дан- ными команд. Эти данные могут находиться как во внутренних регистрах про-
Глава 5. Набор команд 115 цессора, так и во внешней памяти. Причем исполнительный адрес операнда мо- жет задаваться несколькими различными способами. Эти способы называются режимами адресации. В соответствии с RISC-подобной философией микроконтроллеров PIC ядро среднего уровня имеет всего 33 команды плюс две устаревшие команды, достав- шиеся в наследство от младшего семейства, которые мы не будем рассматривать. Каждая команда представляет собой 14-битное слово, в котором содержится собственно код операции (КОП), адрес или значение операнда, а также бит адре- сата результата операции. Некоторые из этих команд и режимов адресации мы уже рассмотрели в главе 3 при обсуждении нашего компьютера BASIC. Теперь же пришла пора полностью разобраться с этим материалом. Так что в этой главе мы подробно рассмотрим различные режимы адресации и все имеющиеся команды. Прочитав эту главу, вы: • Узнаете, что режим адресации предназначен для точного указания местона- хождения данных команды. • Поймете, каким образом адресуется операнд команды при различных режи- мах адресации. • Поймете, каким образом формат слова команды влияет на использование этих команд. • Разберетесь, каким образом бит IRP регистра STATUS позволяет процессо- ру обращаться ко всей памяти данных с использованием косвенной адреса- ции. • Узнаете, что наиболее часто используемыми командами являются команды пересылки данных, предназначенные для копирования данных между рабо- чим регистром и памятью данных. • Узнаете, что процессор может выполнять базовые арифметические опера- ции, такие как сложение, вычитание, инкрементирование, декрементиро- вание и изменение битов. • Научитесь выполнять сравнение данных и их проверку на определенное значение с выполнением требуемых действий в зависимости от результата. • Узнаете, что данные в памяти данных можно циклически сдвигать через флаг переноса С. • Научитесь использовать четыре базовых логических операции для того, что- бы выполнять инвертирование, установку, сброс, смену значения, проверку состояния битов и сличение данных. • Поймете, как можно управлять ходом программы в зависимости от состоя- ния любого бита или нулевого значения в регистре данных. Подавляющее большинство команд оперирует данными, находящимися либо во внутренних регистрах ЦПУ, либо вне его (в памяти данных или памяти про- грамм). Так что в 14-битном слове команды должно быть поле, информирующее дешифратор команд о том, где расположены эти данные. Исключение из указан- ного правила составляют несколько команд с адресацией кодом самой команды, такие как пор (нет операции) и return (возврат из подпрограммы). Прежде чем перейти к изучению собственно команд, мы рассмотрим различные методы, ис- пользуемые для указания местоположения любых операндов.
116 Часть II. Программное обеспечение В общем случае все команды записываются следующим образом: мнемоника <операнд А>,<операнд В> где операнд-А — исходные данные или их местоположение, а операнд В — адресат команды. Например, команда movf h'20' ,w (копировать регистр данных) ко- пирует содержимое источника (регистр h’20’) в приемник (рабочий регистр). Существует несколько вариантов такой записи. Наиболее часто встречаются команды с 2.5 операндами. Например, команда addwf [регистр] , d прибавля- ет содержимое рабочего регистра W к содержимому указанного регистра данных и помещает результат либо в W, либо обратно в регистр данных. Так, команда addwf h'20' , w означает «прибавить содержимое W к содержимому регистра h’20’ и записать результат в регистр h’20’». Коротко это можно записать как [f20] <- W + [f20], где квадратные скобки означают «содержимое», а стрелка — «становится». Такой тип нотации называется языком регистровых передач (Register Transfer Language — RTL). Разумеется, эта команда не является трехоперандной в полном смысле этого слова, поскольку в качестве адресата может использоваться только один из источ- ников, т.е. либо рабочий регистр, либо регистр данных. В некоторых командах указывается только один операнд-адресат, например clrf h'20', ay команд с самоадресацией вообще нет явных операндов. Все команды можно разделить по используемому способу адресации. Адресация кодом команды Такие команды, как clrwdt (сброс сторожевого таймера), retf ie (возврат из прерывания), пор (нет операции), return (возврат из подпрограммы) и sleep (переход в «спящий» режим), не используют операнды из памяти. У всех этих команд в старших семи битах слова команды присутствуют нули. Например, команда clrwdt имеет машинный код Ь’00000000000100’. Адресация константы В командах, работающих с константами, младшие 8 бит кода команды ис- пользуются для указания операнда-источника, являющегося в данном случае константой, а не байтом в регистре данных. Например, команда addlw 0 6 коди- руется как Ь’77111000000770’. Операндом-адресатом в командах такого типа всег- да является рабочий регистр, что и отражено в мнемоническом обозначении. Так, в нашем примере сумма W + 6 копируется обратно в рабочий регистр W. На языке регистровых передач эта операция выражается как W <- W + #6, где символ «#» (диез, или решетка) указывает, что стоящее после него число является констан- той, а не адресом регистра данных. В некоторых процессорах такая разновидность адресации называется непос- редственной, поскольку значение данных доступно непосредственно, без обра- щения к памяти. 0000000 ??????? 11 ???? LLLLLLLL
Глава 5. Набор команд 117 10 ? ААААААААААА Абсолютная адресация памяти программ В микроконтроллерах PIC предусмотрены две команды, которые позволяют программе перейти к другой команде, находящейся в любом месте памяти про- грамм. Этими командами являются команды goto и call (вызов подпрограммы, см. главу 6). В микроконтроллерах с 14-битным ядром под этот адрес1) в коде ко- манды выделяется 11 бит. Так что машинный код команды goto h' 400 1 будет равен Ь’ 10X10000000000'. Аналогично call h ' 530 ' — b’ 10^10100110000'. Используя этот 11-битный адрес, можно непосредственно адресовать любую команду в памяти программ объемом до 211 = 2 Кбайт. Однако в микроконтролле- рах среднего уровня реализован 13-битный счетчик, который может адресовать па- мять данных объемом до 8 Кбайт (память такого объема имеется, например, в мо- дели PIC16F877). Для разрешения этой ситуации при выполнении команд goto и call абсолютный 11-битный адрес объединяется с битами 4:3 регистра защелки PCLATH, формируя таким образом полный 13-битный адрес, загружаемый в счет- чик команд. Этот процесс показан на Рис. 5.1 (см. также Рис. 4.8 на стр. 103). h’OA’ Рис. 5.1. Формирование 13-битного адреса памяти программ из 11-битного абсолютного адреса, передаваемого при вызове команд goto и call При сбросе по включению питания регистр PCLATH сбрасывается, так что непосредственная область действия команды goto составляет h’000’...h’7FF’. Это соответствует диапазону адресов памяти программ объемом 2 Кбайт, имеющейся, например, в микроконтроллере PIC16F628. В моделях с большим объемом памя- ти программ необходимо использовать дальние переходы и вызовы (т.е. за преде- лы h’7FF’), используя биты PCLATH[4:3]. Например, в микроконтроллере PIC16F877 переход к адресу h’FOO’ должен быть реализован следующим образом: bsf PCALTH,3 bsf PCLATH,4 goto h'FOO' ; Запишем в PCALTH[4:3] = 11 ; Перейдем к требуемому адресу! ° Не путайте этот адрес с адресом регистра из памяти данных. В гарвардской архитектуре эти две области памяти физически разделены и находятся в различных адресных пространствах.
118 и Часть II. Программное обеспечение 00 ???? d fffffff Обратите внимание, что после изменения этих битов они остаются в данном состоянии до следующего изменения, поэтому необходимо быть внимательным при переносе такого кода на процессор с памятью программ больше 2 Кбайт1). Прямая адресация памяти данных Большинство данных, используемых программой, размещаются в памяти данных. Соответственно, этот режим адресации используют команды, в которых источник и/или адресат находятся в регистрах памяти данных. Адрес регистра со- держится в семи младших битах кода команды (обозначенных как ffffffl). Напри- мер, код команды addwf h' 2 б' , f (сложить содержимое рабочего регистра с ре- гистром данных h’26’ и поместить результат обратно в регистр данных, или, ко- ротко, [fl <- [fl + [W]) выглядит какЬ’0001 \\ 10100110’. Большинство команд, использующих прямую адресацию, могут пересылать результат либо в рабочий регистр, либо обратно в регистр данных. Бит 7 кода ко- манды, помеченный как d (см. также Рис. 3.5 на стр. 68), используется для указа- ния адресата, как в следующем примере: addwf h'26',w ; Код команды - 00 0111 0 0100110 addwf h'26', f ; Код команды - 00 0111 1 0100110 В обоих случаях содержимое регистра с адресом h’26’ прибавляется к содер- жимому рабочего регистра. В первом случае, приведенном на Рис. 5.2, а, резуль- тат помещается в рабочий регистр, оставляя содержимое регистра данных неиз- менным (d = 0), тогда как во втором случае, показанном на Рис. 5.2, б, исходное содержимое регистра данных замещается (d = 1) итоговой суммой. Как мы увидим далее (см., например, Табл. 5.2), большинство команд исполь- зуют непосредственную адресацию. Однако у этого метода адресации есть два ог- раничения, которые программист должен иметь в виду. Всего 7 бит Под адрес регистра данных в коде команды среднего семейства отведено всего семь битов, соответственно, используя прямую адресацию, можно обращаться только к регистрам из диапазона h’00’...h’7F’. Из Рис. 4.7 (стр. 97), а также Рис. 5.3 видно, что для обхода этого ограничения в микроконтроллере PIC16F84 в качестве суррогатного старшего бита адреса используется 5-й бит (RP0) регистра STATUS. В результате память разбивается на два банка регистров, каждый объ- емом до 27 = 128 регистров. Для переключения между 0-м (RP0 = 0) и 1-м (RP0 = 1) банками этот бит управления страницами, расположенный в 5-м бите регистра STATUS (Рис. 4.6 на стр. 95), можно менять точно так же, как и любой другой бит регистра данных. Например, если осуществляется перенос работающего кода из микроконтроллера с небольшим объемом памяти программ, например PIC16F84, на более емкую модель, скажем, PIC16F877. Поэтому для обеспечения переносимости программ рекомендуется всегда сбрасы- вать регистр PCLATH, даже если это в принципе и не нужно.
Глава 5. Набор команд 119 Рис. 5.2. Выбор операнда-результата в команде addwf h' 2 6 ' Особенность модели PIC16F84 заключается в том, что в ней имеется всего два банка памяти. Большинство микроконтроллеров среднего уровня имеют 4 банка памяти. В качестве примера можно назвать микроконтроллер PIC 16F627/8 (усо- вершенствованный PIC16F84), структура памяти данных которого приведена на Рис. 5.4. Чтобы иметь возможность переключаться на любой банк памяти, требу- ется уже два бита управления страницами, показанных на Рис. 5.5. Эти два бита RP1:RPO, выделенные на рисунке серым цветом, обнуляются при сбросе любого типа, т.е. после сброса мы всегда работаем с 0-м банком памяти. Поэтому про- граммист должен соответствующим образом изменить эти биты, если он хочет обратиться к регистру, находящемуся в другом банке. Например, если необходи- мо скопировать содержимое регистра h’ 120’, расположенного во втором банке, в рабочий регистр и переключиться обратно на 0-й банк, мы должны написать: bsf bcf STATUS,6 STATUS,5 ; Устанавливаем RP1 (6-й бит) в 1 ; Сбрасываем RPO (5-й бит) в 0 movf h'120',w ; Копируем содержимое регистра h'120' в W bcf STATUS,6 ; Сбрасываем RP1 (возвращаемся к 0-му банку)
120 Часть II. Программное обеспечение do RPO Адрес регистр h’00’ h’01’ h’02’ h’03’ h’04’ h’05’ h’06’ h’07’ h’O8’ h’09’ h’OA’ h’OB’ h’OC h’4F’ h’50’ Обращение к регистру h’00’ \ (INDF) \h’O4’ h’7F’ о EN IND EN IND , TMR0 OPTION PCL PCL STATUS STATUS FSR FSR PORTA TRISA PORTB TRISB He реализовано He реализовано EEDATA EEC0N1 EEADR EECON2 PCLATH PCLATH INTCON . INTCON 68 регистров общего назначения Отображение 68 регистров общего назначения He реализовано EN Не реализовано EN h’80’ h’81’ h’82’ h’83’ h’84’ h’85’ h’86’ h’87’ h’88’ h’89’ h’8A’ h’8B’ h’8C’ h’CF’ h’DO’ h’FF' ф ф Й7 do ф 5 s Рис. 5.3. Память данных микроконтроллера PIC16F84 Примером такой интенсивной работы с банками памяти может служить Программа 15.4, приведенная на стр. 552. Если программист забудет изменить биты RP1:O перед выполнением команды movf h112 0 ', то в рабочий регистр будет скопировано содержимое из регистра данных h’020’ (полагая, что процессор находится в нулевом банке), поскольку в коде команды будет записано только семь младших битов адреса b’(01 )0100000’ (h’120’)! Ассемблер, однако, выдаст предупреждение, вид которого показан на стр. 99. Чтобы избежать слишком частого переключения банков памяти, вс£регистры общего назначения (РОН) микроконтроллера PIC16F84 отображены на оба банка,
Глава 5. Набор команд 121 (НР1 :НР0 = 11) —М ... 1 \ 1 j 1 L. t ENO |ND ООО EN, |ND 080 EN2 (ND ,00 ЕЮ (ND ,30 >g s ! 1 TMR0 001 OPTION 081 TMR0 101 OPTION 181 1 * PCL 002 PCL 082 PCL 102 PCL п -—н- STATUS 003 STATUS 083 STATUS 103 STATUS 183 ю g V FSR 004 FSR 084 - FSR ’°4 FSR 184 PORTA 005 TRISA 085 105 185 PORTB 006 TRISB 086 PORTB 106 TRISB 186 007 087 107 187 008 088 108 188 освенная адресация Прямая адресаци! 009 089 109 189 PCLATH °°A PCLATH 08A PCLATH ,0A PCLATH 18A INTCON 008 INTCON 388 INTCON ,<ie INTCON 188 PIR1 °°c PIE1 °8C 10C 18C 00D 08D 10D 180 -TMR1L" 00E POON 08E > \ , ЮЕ ' lf’ 18E TMR1H 00F 08F ' ' 10F ' 18F T1CON 010 090 110 190 TMR2 " °" 091 ". Ill ni L, 191 T2CON 0,2 PR2 092 112 ' 192 013 093 113 193 z , M f, 014 , . 094 ,z 114 , x ; 194 CCPR1L 015 095 , , ” 115 „„i 195 CGPR1H016 096 ,116 ъ196 GCP1CON0’7 097 117 197 RCSTA 0,8 TXSTA 098 i f i118 $ . 198 !TXREG 0,9 SPBRG 099 „„ 119 199 RCREG 01A EEDATA °9A 11A 19A 01B EEADR 09B 11B 19B 01C EECON1 09C 11C 19C г 01D EECON2 090 11D 19D 01E 09E 11E 19E CMCON 01F VRCON 09F 11F 19F 020 80 регистров общего назначения ОАО 80 регистров общего назначения 48 120 регистров общего назначения з 1 4 ’ Г - ш _ * * ю ст . - 2 LL г i 150 з i " ! Е О — ь- т 16 общих 070 ENO РОН 07F 16 общих wo EN1 РОН off 16 общих 170 El® РОН 16 общих ,F° EN3 POH ,FF *9 й 1 СО СО ’ 1 " 1 IRP U I IRP=1 Рис. 5.4. Память данных микроконтроллеров PIC 16F627/8
122 Часть II. Программное обеспечение как показано на Рис. 5.3. Подобное зеркалирование всех регистров встречается достаточно редко — чаще отображают небольшую группу регистров. Например, в моделях PIC16F627/8 предусмотрена общая область из 16 РОН, отображенных на все четыре банка (Рис. 5.4). Например, регистры данных с адресами h’070’, h’OFO’, h’ 170’ и h’lFO’ являются одним и тем же регистром. Переменные, которые могут потребоваться при работе с различными банками, по возможности следует размещать в этом общем пуле регистров. В общей же сложности в данных моде- лях имеется 224 уникальных РОН. Некоторые из наиболее часто используемых регистров специального назначения (РСН) тоже отображены на все банки, например регистр STATUS. Поэтому в приведенном выше примере мы могли изменять биты RP1:O и возвра- щаться в 0-й банк, даже находясь во 2-м банке. Биты выбора банка ОЗУ при прямой адресации Бит выбора банка • ' при косвенной адресации Банк 0/1 (h'000’...h’0FF’)-0 Банк2/3 (h’100’...h’1FF’> -1 '• Флаг переноса/заема Флаг десятичного переноса/заем Флаг нуля Флаг тайм-аута сторожевого таймера После тайм-аута сторожевого таймера — 0 После сброса по питанию или выполнения команд clrwdt и sleep — 1 Флаг включения питания После выполнения команды sleep — 0 После сброса по питанию или выполнения команды clrwdt — 1 Рис. 5.5. Обобщенный формат регистра STATUS микроконтроллеров с 14-битным ядром Фиксированные адреса Будучи составной частью кода команды, 7-битный адрес операнда является фиксированным и поэтому не может быть изменен во время выполнения програм- мы. Хотя явное задание этих адресов может показаться очевидным способом для указания местоположения объекта в памяти данных, существует ряд ситуаций, в которых такое ограничение слишком неудобно. В качестве примера, иллюстрирующего эту недостаточную гибкость, предпо- ложим, что мы хотим очистить содержимое всех регистров данных 0-го банка мо- дели PIC16F627/8, т.е. регистров h’20’...h’7F’. Очевидным решением этой задачи будет многократное (96 раз) использование команды clrf (очистка регистра), как показано в Программе 5.1.
Глава 5. Набор команд 123 Программа 5.1. Очистка группы регистров с использованием прямой адресации CLEAR_ARRAY clrf h’20' ; Очищаем регистр 32 clrf h'21' ; и 33 clrf h' 22 1 ; Каждая команда clrf clrf h'23 ' ; занимает одну ячейку clrf h' 24 ' ; в памяти программ clrf h’25' ; Очищаем регистр 37 clrf h’26’ ; и так далее clrf h'7E' ; Очищаем регистр 126; еще чуть-чуть clrf h' 7F' ; Очищаем регистр 127; уф-ф! Несмотря на то что этот код вполне работоспособен, он чрезвычайно неэф- фективен. Каждая из 96 команд выполняет одну и ту же операцию, хотя и для дру- гого адреса. Если нам потребуется очистить все 244 РОН, то придется выполнить 224 команды сlr f, и все для того, чтобы выполнить эту простейшую задачу. Пос- кольку в памяти программ микроконтроллера PIC16F627 имеется всего 1024 ячейки, такое решение займет более 20% памяти. Должен быть лучший способ! 00 ???? d 0000000 Косвенная адресация памяти данных В любом процессоре имеется одна из разновидностей косвенной адресации, при которой один или более внутренних регистров используются для хранения адреса операнда в памяти данных. Такие адресные или индексные регистры ис- пользуются в качестве указателя на данные. Основное отличие от прямой адреса- ции заключается в том, что содержимое регистра-указателя может изменяться в процессе выполнения программы. То есть искомый адрес уже не зафиксирован в виде двоичного кода в памяти программ (обычно ПЗУ), а является переменной величиной. Например, для очистки массива данных из Программы 5.1 можно ис- пользовать цикл, инкрементируя в каждом проходе цикла регистр, указывающий на очищаемый регистр. В микроконтроллерах PIC реализован достаточно простой вариант такого ти- па адресации — в полном соответствии с их философией. В младшем и среднем семействах1) имеется отдельный элемент ИЛИ-HE, который детектирует обраще- ние по прямому 7-битному адресу Ь’0000000’ и, как показано на Рис. 5.6, просто выставляет на шину адреса памяти данных содержимое регистра h’04’, называе- мого индексным регистром (FSR). Это происходит, если в качестве адресата ко- манды используется нулевой адрес, по которому располагается регистр косвен- ной адресации INDF. Этот регистр является виртуальным, т.е. физически не су- ществует. Он используется исключительно для выставления содержимого регистра !) В старшем семействе косвенная адресация реализована похожим образом, просто там имеется несколько регистров-указателей и вариантов их использования.
124 Часть II. Программное обеспечение FSR в качестве адреса операнда. Хотя такая реализация косвенной адресации мо- жет показаться довольно ущербной, она использует очень простую дополнитель- ную логику и не требует для работы дополнительных тактов, в отличие от альтер- нативных способов, реализованных в других процессорах и микроконтроллерах. В качестве простого примера предположим, что содержимое регистра FSR равно Ь’86’.Тогда команда clrf 0 (или clrf INDF) очистит регистр, располо- женный по адресу h’86’, а не по адресу h’00’! Разумеется, содержимое регистра FSR можно изменить в любой момент времени, например, его можно инкремен- тировать в каждом проходе цикла, как в Программе 5.2. Давайте в качестве примера перепишем Программу 5.1, заменив линейную структуру циклом, как показано на Рис. 5.7. Теперь наша программа будет рабо- тать по следующему алгоритму, представляющему собой перечень задач: 1. Установить указатель FSR на начало массива. 2. Очистить адресуемый регистр данных, указав в качестве адресата регистр данных h’00’. 3. Инкрементировать указатель FSR. 4. Проверить, не достигли указатель конца массива, в нашем случае — адреса h’80’. Если нет, то перейти к пункту 2. 5. Продолжить выполнение программы. Визуально этот процесс представлен на Рис. 5.8. Код, соответствующий этому алгоритму, приведен в Программе 5.2. Линейная структура предыдущей программы была преобразована в цикл, тело которого вы- делено серым цветом. Очистку регистров по-прежнему выполняет команда clrf, которая «проходит» по массиву, начинающемуся с адреса h’20’. При каждом прохо- де цикла указатель в регистре данных h’04’ инкрементируется. В конце концов со- держимое регистра FSR выйдет за границу заданного диапазона, в результате чего программа выйдет из цикла и продолжит выполнение следующей секции кода.
Глава 5. Набор команд 125 Рис. 5.7. Использование цикла для очистки массива данных Программа 5.2. Очистка группы регистров с использованием косвенной адресации ; Присвоим имена регистрам и битам для удобочитаемости FSR equ 4 ; Регистр FSR расположен по адресу h’04’ STATUS equ 3 ; Регистр STATUS расположен по адресу h'03' Z equ 2 ; Флаг нуля - бит 2 регистра STATUS ; Собственно программа CLEAR ARRAY Переходим сюда, если FSR не равно h’80' movlw h’20’ ; Помещаем начальный адрес в W movwf FSR ; и копируем его в FSR в качестве указателя -Hiiiil iiiilliiiii iiijii clrf 0 ; Очищаем регистр, на который указывает FSR incf FSR,f ; Инкрементируем указатель в FSR ; Теперь проверяем, не достиг ли указатель верхней границы массива? movf FSR,w ; Копируем указатель в W о addlw —h'80’ ; Сравниваем с адресом последнего регистра (h’801) 1: г- btfss STATUS,Z ; ЕСЛИ флаг нуля установлен (равно), ТО завершаем цикл ------ £ goto CLOOP ; ИНАЧЕ выполняем следующую итерацию (2 • • • • .... ; Следующая часть программы ф со
126 Часть II. Программное обеспечение "'h’20’ h’21’ h’22’ h’23’ h24’ h’25’ h’26’ h’27’ h’7E’ h’7F’ tttttttt ccccoccrcrcccccc СЛСЛСЛСЛСЛСЛСЛСЛ Время Рис. 5.8. Проход массива В Программе 5.2 имеется много других особенностей, так что нам еще при- дется вернуться к рассмотрению набора команд. Задача 1 Регистр FSR инициализируется адресом первого очищаемого регистра дан- ных путем записи константы h’20’ в рабочий регистр W (movlw h' 2 0 1) с после- дующим копированием W в регистр h’04’ (movwf FSR). Как видно, в наборе команд отсутствует отдельная команда непосредственного копирования константы в регистр данных. Практически все циклы требуют инициализации перед входом в них. Задача 2 Основная команда очистки регистра использует косвенную адресацию, указывая в качестве адресата фантомный регистр h’00’ (INDF) — clrf INDF. Эта строка помечена меткой CLOOP. Ассемблер понимает, что это именно метка, а не команда, поскольку она начинается с самой левой позиции строки исходного файла. Строки без меток должны начинаться с отступа хотя бы в один пробел. Задача 3 При каждом проходе цикла указатель увеличивается на единицу. Эта опера- ция осуществляется командой incf FSR, f. Обратите внимание, что в качестве адресата указан сам регистр памяти данных, а не рабочий регистр W. Задача 4 Если вы не собираетесь крутиться в этом цикле бесконечно, то вам потребует- ся механизм для выхода из него. В нашем случае для этого используется сравне- ние содержимого регистра FSR с константой h’80’, т.е. адресом первого регистра, находящегося вне заданного диапазона. Сравнение осуществляется копировани- ем содержимого регистра FSR в W (movf FSR,w) и последующим вычитанием рабочего регистра из константы h’80’ с использованием команды addlw -h' 8 0 ' (прибавление отрицательного числа). Если эти числа равны, то флаг Z будет уста- новлен, в результате чего команда bt f ss STATUS, z (см. стр. 133) пропустит сле- дующую за ней команду goto CLOOP. До наступления этого события команда goto будет передавать управление на начало цикла, и процесс будет повторяться с FSR, указывающим наследующий сбрасываемый регистр данных.
Глава 5. Набор команд 127 В итоге вариант программы с циклом состоит из 8 команд против 96 в линей- ном варианте, т.е. размер программы уменьшился в 12 раз. Однако наша новая программа выполняется в 7 раз дольше из-за наличия различных команд, необхо- димых для организации цикла и выполняющихся 96 раз! Обычно затраты на на- кладные расходы не так велики, как в данном примере. Наличие регистра FSR, хранящего адрес операнда, означает, что у нас теперь есть 8-битный изменяемый адрес для обращения к памяти данных вместо фикси- рованного 7-битного. В свою очередь, из этого следует, что при работе с памятью данных, имеющей два банка (аналогичной приведенной на Рис. 5.3), к любому регистру можно обратиться откуда угодно. Например, если мы хотим записать число Ь’01111111’ в регистр данных h’86’ (регистр специального назначения TRISB, расположенный в 1-м банке), то вместо кода, приведенного на стр. 105, мы можем написать: movlw h' 86' ; Настроим FSR для работы movwf FSR ; с регистром h'86'(TRISB) movlw b'01111111’ ; Маска movwf 0 ; Записываем ее в указываемый регистр При этом нам не придется возиться с битом переключения страниц RP0. Если не- обходимо часто обращаться к какому-либо из регистров первого банка, то можно записать в FSR адрес этого регистра и больше регистр FSR не трогать. Разумеет- ся, предполагается, что он не требуется для других целей1). В моделях с четырьмя банками памяти требуется дополнительный бит для об- разования 9-битного адреса. Бит IRP регистра STATUS, формат которого показан на Рис. 5.5, позволяет косвенно адресовать банки 0/1 (IRP= 0, состояние по умолчанию) и банки 2/3 (IRP = 1). Например, ранее написанный код для копиро- вания содержимого регистра h’ 120’ банка 2 (PIC16F627/8) в рабочий регистр W, приведенный на стр. 119, можно переписать следующим образом: bsf STATUS,7 ; Установим бит IRP (банки 2/3) movlw h'120' ; Инициализируем указатель в FSR movwf FSR / movf 0 ,w ; Копируем содержимое регистра, указываемого FSR, в W bcf STATUS,7 ; Сбрасываем IRP (банки 0/1) Поскольку в регистре W могут находиться только 8-битные значения, стар- ший бит адреса при выполнении команды movlw h' 12 0 ' будет отброшен, т.е. в регистр W будет записано число h’20’. Роль отсутствующего девятого бита выпол- п В микроконтроллерах старшего семейства имеется три регистра FSR (в действительности реализованные как пара РСН для хранения 12-битного адреса — см. Рис. 16.5 на стр. 579), что упрощает подобное использование этих регистров.
128 Часть II. Программное обеспечение 01 ?? NNN FFFFFFF няет бит IRP, установленный в 1, поэтому обращение произойдет к регистру h’ 120’, что и требовалось. Ассемблер, возможно, выдаст предупреждение, что в регистр W записывается слишком большое значение. Это предупреждение можно игнорировать. Битовая адресация Четыре команды (на что указывают два бита, помеченные знаками «??») предназначены либо для изменения, либо для проверки состояния отдельных битов в регистре данных. В этом случае в коде команды имеется 3-битное поле NNN, предназначенное для хранения позиции бита (0...7), тогда как адрес ре- гистра кодируется обычным образом. Так, машинный код команды bcf h' 20 ' ,7 (сбросить бит 7 в регистре h’20’) выглядит как Ь’0100117010000’. Остальными командами этой группы являются команда bsf (установить бит ре- гистра данных, код 01), btf sc (проверить состояние бита и пропустить следую- щую команду, если он сброшен, код 10) и btfss (проверить состояние бита и пропустить следующую команду, если он установлен, код 11). Последнюю из пе- речисленных команд мы уже использовали в Программе 5.2 для проверки 2-го бита регистра h’03’ (т.е. флага Z регистра STATUS) и выходили из цикла, если условие было истинно. Пока что мы классифицировали команды по способу, которым они определя- ют местоположение своих операндов. Однако чаще используется деление команд по выполняемым функциям. С этой точки зрения все 33 команды микроконтрол- леров PIC с 14-битным ядром можно разбить на 6 групп, четыре из которых будут рассмотрены в этой главе. Команды, относящиеся к подпрограммам и прерыва- ниям, будут описаны в 6-й и 7-й главах, а управляющим командам, связанным с функционированием микроконтроллера, посвящена глава 10. В таблицах команд, приводимых далее, в левом столбце приводятся мнемони- ческие обозначения команд. Затем указывается влияние данной команды на три флага регистра STATUS, причем символ «•» соответствует отсутствию какого-ли- бо изменения, а символ «л/» — нормальному воздействию. В последнем столбце приводится краткое описание операций, выполняемых командой. Полностью набор команд приведен в Приложении Г. Если вам потребуется более подробное описание, его можно найти в документации на любой микроконтроллер PIC со- ответствующего семейства (см. сайт, посвященный оригинальному изданию дан- ной книги). Однако, в связи с тем что микроконтроллеры PIC имеют RISC-архи- тектуру, команд достаточно мало, и они простые. Команды пересылки данных Почти треть всех команд в любой компьютерной программе, независимо от оборудования, на котором она выполняется, используются для простой пересыл- ки данных между памятью и внутренними регистрами. С учетом этого в Табл. 5.1 приведены наиболее часто используемые команды PIC.
Глава 5. Набор команд 129 Таблица 5.1. Команды пересылки данных Операция Мнемоника Флаги Описание Z DC c Пересылка Копирует байт данных Константы в W movlw к • • • [W] <- #кк Регистра movf f,d • • [d] <- [f] W в регистр movwf f • • • [f] <- [w] Перестановка Переставляет полубайты регистра Регистра swapf f,d • • • [d] <- [f (3:0)] [f (7:4) ] Условные обозначения: • He воздействует на флаг И Содержимое V Воздействует на флаг d Адресат, W или регистр данных w Рабочий регистр #кк 8-битная константа f Регистр данных < - Становится Все три команды пересылки используются либо для простого копирования од- нобайтного значения между рабочим регистром и указанным регистром данных, либо для загрузки константы в рабочий регистр. При этом исходные данные не изменяются, они просто копируются в регистр-адресат. Команда swap тоже ко- пирует содержимое регистра данных в W, однако при этом меняет местами млад- ший и старший полубайты. movlw Эта команда заносит указанную 8-битную константу в рабочий регистр W. На- пример, команда movlw h' 80 ' инициализируетWзначениемb’10000000’. #b’10000000’ movlw h ' 8 0 ' ..........i....।.......i. .iliuil.llll . .li| 1 0 0 0 0 0 0 0 8 w Заметьте, адресатом в этой команде всегда является рабочий регистр, поэтому для инициализации регистра данных какой-либо константой требуется дополни- тельная операция — см. ниже. movwf Эта команда предназначена для копирования (сохранения) содержимого рабочего регистра в регистре данных. Например, команда movwf h' 2 3 ' скопирует байт из W в регистр h’23’. h’23’ W
130 Часть II. Программное обеспечение Таким образом, для инициализации регистра h’23’, скажем, числом Ь’10000000’, необходимо выполнить следующие операции: movlw h’80’ ; Заносим в W число Ь'10000000' movwf h’23’ ; и копируем его в регистр h'23' movf Эта команда предназначена для копирования (загрузки) содержимого любого ре- гистра данных в рабочий регистр W. Например, команда movf h1 2 2 1 , w загрузит в Wсодержимое регистра h’22’. 11’22’ W Вообще говоря, в качестве адресата данной команды можно указать сам ре- гистр данных, в результате чего мы выполним, как может показаться, бессмыс- ленную операцию. В нашем случае это будет команда movf h' 22 ' , f, которая скопирует содержимое регистра h’22’ в него же! Однако команда movf воздей- ствует на флаг нуля Z (это единственная команда из Табл. 5.1, воздействующая хоть на какой-то флаг регистра состояния), который установится, если содержи- мое регистра равно нулю. Команда movf [File] , f не изменяет содержимое указанного регистра, поэтому ее можно использовать для проверки регистра на ну- левое значение, т.е. в качестве отсутствующей команды tstf [File],f, имею- щейся во многих других микроконтроллерах и микропроцессорах. Таким обра- зом, мы можем проверить содержимое любого регистра данных с помощью од- ной-единственной команды. Вариант проверки рабочего регистра на нулевое значение приведен на стр. 141. Принимая во внимание тот факт, что большинство команд, оперирующих ре- гистрами в памяти данных, позволяют задавать в качестве адресата либо тот же регистр, либо рабочий регистр W, операцию пересылки можно рассматривать как неявного члена этой группы команд. Например, в зависимости от ситуации опе- рация инкрементирования регистра данных с последующим копированием ново- го значения в рабочий регистр W может быть записана следующим образом: incf h'22',f movf h'22',w либо так: incf h'22',w ; Инкрементируем содержимое регистра h’22' ;'и копируем его в W ; Копируем инкрементированное содержимое ; регистра h'22' в W Разумеется, в последнем случае содержимое регистра данных не изменяется.
Глава 5. Набор команд 131 swapf Команда swapf переставляет местами старший и младший полубайты содержи- мого регистра данных и помещает результат либо в тот же регистр данных, либо в рабочий регистр. Например, команда swapf h1 22 1 , w выполнит операцию: h’22’ ХХХХYYYY swapf h'22',w W Команда swapf полезна в тех случаях, когда полубайты регистра используют- ся для хранения BCD-чисел, однако может использоваться и для копирования содержимого регистра данных в W. В отличие от более понятной команды movf [File] ,w, состояние флага Z при этом не изменяется. Недостатком, ко- нечно же, будет перестановка 'полубайтов местами при копировании. В Программе 7.2 на стр. 226 команда swapf используется именно с этой целью. Команды арифметических операций Процессоры микроконтроллеров PIC младшего и среднего уровня помимо сложения и вычитания могут выполнять и другие арифметические операции. В качестве примера можно отметить операции сброса, инкрементирования и де- крементирования. В Табл. 5.2 также перечислены команды, устанавливающие или сбрасывающие заданный бит в указанном регистре данных. Таблица 5.2. Команды арифметических операций Операция Мнемоника Флаги Описание Z DC c Сложение Двоичное сложение Константы с W addlw k yl у/ у/ [W] <- [W] + #kk W с регистром addwf f ,d >/ [d] <- [W] + [f] Очистка Обнуляет адресованный регистр или бит Регистра clrf f у/ • • [f] <- #00 Рабочего регистра clrw у/ • • [W] <- #00 Бита bcf f ,n • • • [fn] <- #0 Декремент Вычитает единицу, признак заема не формируется Регистра decf f ,d у/ • • [d] <- [f] - #01 Инкремент Прибавляет единицу, признак переноса не формируется Регистра incf f ,d у/ • • [d] <- [f] + #01 Установка Устанавливает заданный бит регистра в 1 Бита bsf f ,n • • • [fn] <- #1
132 Часть II Программное обеспечение Таблица 5.2. Команды арифметических операций (продолжение) Операция Мнемоника Флаги Описание Z DC с Вычитание Двоичное вычитание W из константы sublw к л/ л/ л/ [W] <- #кк - [W] W из регистра subwf f,d у! у! [d] <- [f] - [W] Условные обозначения: • Не воздействует на флаг V Воздействует на флаг W Рабочий регистр f Регистр данных [] Содержимое d Адресат, W или регистр данных #кк 8-битная константа < — Становится #0 Нулевой бит #1 Единичный бит #00 Нулевой байт #01 Байт h’Ol’ п 3-битная позиция изменяемого бита fn n-й бит регистра Сложение и вычитание В микроконтроллерах PIC имеется две команды сложения. addlw Эта команда позволяет прибавить 8-битную константу к рабочему регистру W. Например, команда addlw Ь' 10101010': addwf Эта команда прибавляет переменную из памяти данных к содержимому рабочего регистра W В отличие от команды addlw, в качестве адресата может использо- ваться как W, так и исходный регистр данных. Например, addwf h’26’ , f: Очистка Как рабочий регистр, так и любой регистр данных могут быть сброшены. В обоих случаях при выполнении этой операции будет установлен флаг Z, инди- цирующий нулевой результат операции.
Глава 5. Набор команд 133 clrw Эта команда очищает рабочий регистр. По своему действию она эквивалентна команде movlw 0. clrf С помощью этой команды можно очистить содержимое любого регистра данных. Например, clrf h'26’: Обе команды сложения, да и вообще все команды, оперируют 8-битными операндами. Тем не менее можно обрабатывать операнды любой длины, если де- лать это побайтно. В случае сложения, например, нам потребуется складывать попарно соответствующие байты операндов (от младшего байта до старшего) с добавлением бита переноса, полученного при сложении я-х байтов, к (п + 1)-й сумме. Входной перенос при сложении младшего байта равен нулю, а перенос из старшего байта становится старшим битом результата. Например, h’FFFF’ + + h’FF’ = h’ 1 00FE’ (65 535 + 255 = 65 790). Чтобы проиллюстрировать этот процесс, напишем программу, складывающую 8-битное число с 16-битным и получающую в результате 17-битную сумму. Первое слагаемое, как показано на Рис. 5.9, размещается в двух ячейках памяти данных с адресами h’20 (старший байт) и h’21 ’ (младший байт). Сумма сохраняется в трех ячейках с адресами h’30’ (старший байт), h’31 ’ (средний байт) и h’32’ (младший). h’20’ AUGEND_H I h’21 ’ AUGENDJ. I h’30’ SUM U I _________h’22’ ADDENDJ. I Puc. 5.9. Сложение 16-битного числа с 8-битным Составим перечень задач (алгоритм), учитывая, что нам нужно реализовать этот процесс в виде последовательности операций, выполняемых 1-байтными ко- мандами: 1. Прибавить младший байт первого слагаемого к младшему байту второго слагаемого — получим младший байт суммы и бит переноса С1 (Рис. 5.10, а).
134 и Часть П. Программное обеспечение 2. Прибавить бит переноса С1 к старшему как байту первого слагаемого — по- лучим средний байт суммы и новый бит переноса С2 (Рис. 5.10, б). 3. Старшим байтом суммы является последний бит переноса С2 — 0 или 1 (Рис. 5.10, в). Поскольку это будет первая программа главы, все операции подробно показа- ны на Рис. 5.10. Во многих случаях подобная детализация совершенно бесполез- на, а алгоритм в виде списка задач может быть дополнен более абстрактной блок- схемой. _______h’20’______h’21’ | AUGEND H | AUGEND L ]----x h’22’ I [ AUGSMDJL |----*K+)---**-C1 _______h’30’______h’31 ’_____h’32’ I | SUMJJ I SUM_H I SUMX *T*------------' а) Сложение младших байтов _______h’20’______h’21’ s------1 AUGENDJ-l | AUGEN D L | I _________h’22’ C2 —---©—---------C1 | AUGENDJ-l _______h’30’ и h’31’_________h’32’ | SUMJJ | SUMJ-I | SUM L ~| б) И старших байтов h’20’ h’21’ | AUGENDJ-l | AUGENDJ~| C2 I-----------1 I AUGEND L I ____'' h’30’______h’31 ’______h’32’ | SUMJJ | SUM H | SUM L ~~| в) Старшим байтом суммы становится последний бит переноса Рис. 5.10. Визуализация процесса сложения Прежде чем перейти к написанию программы, нам необходимо познако- миться с двумя командами (подробно мы их рассмотрим чуть позже). Команда incf позволяет нам непосредственно прибавлять единицу к содержимому лю- бого регистра данных, а команда btf sc проверяет состояние конкретного бита заданного регистра данных и, если этот бит сброшен, выполняет пропуск следу- ющей команды (см. Табл. 5.4). В нашем случае таким регистром является ре-
Глава 5. Набор команд 135 гистр h’03’ (регистр STATUS), а проверяемым битом — бит 0 (флаг переноса), т.е. команда будет записана как btfsc 5,0 или, более понятно, как btf sc STATUS, С. Мы уже использовали аналогичную команду bt f ss для про- верки флага Z в Программе 5.2. Все три указанные задачи помечены в листинге соответствующими коммен- тариями. Вводная часть Всем регистрам с данными присвоены символические имена с помощью ди- рективы equ. Как уже говорилось на стр. 105, использование осмысленных имен вместо голых адресов регистров дает в итоге более удобочитаемую программу. При этом уменьшается вероятность возникновения ошибок и облегчается отлад- ка программы. Задача 1 Младший байт 1-го слагаемого загружается в W, складывается со 2-м слагае- мым, и результат сохраняется в памяти в качестве младшего байта суммы. При этом команда addwf изменяет соответствующим образом состояние флага С. К счастью, на его состояние не влияют последующие команды пересылки. Задача 2 Старший байт 1-го слагаемого загружается в W. Если бит переноса С1 из пре- дыдущей задачи равен 0, то команда прибавления единицы (addlw 1) пропуска- ется, в противном случае производится инкрементирование содержимого W. За- тем результат копируется в средний байт суммы. Задача 3 Если бит переноса С2 из предыдущей задачи равен 1, то предварительно сбро- шенный старший байт суммы увеличивается до Ь’ОГ. Обратите внимание, что ко- манда clrf SUM_u не воздействует на флаг переноса. Если С2 равен 0, то коман- да incf SUM_1, f пропускается и старший байт суммы остается нулевым. Программа 5.3. Выполнение сложения с двойной точностью AUGEND.H equ h' 20' ; Два регистра 1-го слагаемого AUGEND_L equ h' 21' ADDEND.L equ h’22’ ; Второе слагаемое SUM_U equ h’30* ; Три регистра суммы SUM-H equ h’ 31' SUM_L equ h'32' STATUS equ 3 ; Регистр STATUS расположен по адресу h’03 C equ 0 ; Флаг переноса - 0-й бит регистра STATUS ; Задача 1 ------------------------------------------------------ DP_ADD movf AUGEND_L,w ; Берем младший байт 1-го слагаемого
136 Часть II. Программное обеспечение addwf movwf ADDEND_L,w SUM_L ; Прибавляем 2-е слагаемое, результат - в W ; Помещаем результат в младший байт суммы Задача 2 - — movf AUGEND_H,w ; Берем старший байт 1-го слагаемого btfsc STATUS,C ; Был ли перенос при предыдущем сложении? addlw 1 ; ЕСЛИ да, ТО прибавляем единицу movwf SUM_H ; Помещаем в средний байт суммы Задача 3 - — clrf SUM-U ; Обнуляем старший байт суммы (не влияя на флаг С) btfsc STATUS,C ; Был ли перенос при предыдущем сложении? incf SUM_U,f ; ЕСЛИ да, ТО старший байт суммы равен 01 В Программе 5.3 следует обратить внимание на два момента: 1. Ни одна из команд программы, за исключением команд сложения, не вли- яет на состояние флага С. Благодаря этому флаг С можно проверить с по- мощью команды btf sc даже через две команды после выполнения опера- ции сложения. 2. Команды, следующие после каждой команды bt f sc, имеют отступ на один пробел больше, чем остальные. Увеличенный отступ просто подчеркивает, что выполнение этого блока необязательно, т.е. он может быть пропущен. Ассемблер все эти украшательства игнорирует! Команды инкрементирования и декрементирования Содержимое любого регистра данных можно увеличить или уменьшить на единицу. incf Эта команда увеличивает на единицу содержимое заданного регистра данных, по- мещая результат либо обратно в исходный регистр, либо в рабочий регистр W. decf Эта команда уменьшает на единицу содержимое заданного регистра данных, по- мещая результат либо обратно в исходный регистр, либо в рабочий регистр W. На- пример, если в регистре h’26’ было записано число h’64’, то после выполнения команды decf h1 2 6 ' , f в неУ окажется число h’63’. #01 h’26* Л h’26* 01 1 0 0 1 0 0 ZC decf h’26',f
Глава 5. Набор команд 137 Если в качестве адресата указать рабочий регистр (dec f h' 2 6 1 , w), то содер- жимое регистра h’26’ останется равным h’64’, а содержимое рабочего регистра станет равным h’63’. Хочу обратить ваше внимание на то, что обе эти команды не влияют на флаг переноса С в отличие от эквивалентных команд прибавления или вычита- ния единицы1). В частности, это означает, что если вы собираетесь инкременти- h’20’ h’21’ h’22’ ровать 3-байтное число, хранящееся в формате | ~pper | middle | lower |»т0 просто инкрементировать младший байт и проконтролировать переносы в стар- шие байты не получится. В следующем фрагменте программы используется ко- манда btf ss, которая пропускает команду, если 2-й бит регистра STATUS (флаг нуля Z) установлен в 1. incf btfss goto LOWER,f STATUS,Z NEXT ; Прибавим единицу ; Результат равен нулю? ; ЕСЛИ нет (Z == 0), ТО выходим incf MIDDLE,f ; ИНАЧЕ инкрементируем следующий байт goto NEXT ; ЕСЛИ нет (Z == 0), ТО выходим incf UPPER,f ; ИНАЧЕ инкрементируем следующий байт NEXT ; Прочий код В приведенном фрагменте инкрементируется самый младший байт, и если он становится равным нулю (h’FF’ -> h’00’), то инкрементируется следующий байт и так далее для всех байтов. Эта последовательность прерывается, если при инкрементировании регистра получается ненулевое значение, например h’06 FF FE’ h’06 FF FF’ -4 h’07 00 00’. Бит-ориентированные команды Возможность сброса либо установки отдельного бита в любом регистре памяти данных очень важна, особенно при изменении различных РСН, управляющих процессором и его периферийными устройствами. Изменяемый регистр можно указать с помощью как прямой, так и косвенной адресации. bcf Эта команда позволяет программисту сбросить в 0 любой из восьми битов указан- ного регистра памяти данных. ') В микроконтроллерах старшего семейства команды incf и decf влияют на флаг С.
138 Часть II. Программное обеспечение bsf Эта команда аналогична bcf, только указанный бит не сбрасывается, а устанав- ливается в 1. Например, при установке 5-го бита регистра h’26’ мы имеем zc Одним из назначений данных команд является управление различными фла- гами и переключателями регистра STATUS. В коде, приведенном на стр. 119, мы уже использовали эти две команды для изменения состояния битов RPx (для пе- реключения банков памяти данных). Ни одна из этих команд не воздействует на биты регистра STATUS. Однако важно понимать, что все команды, непосред- ственно изменяющие содержимое регистров данных, на самом деле считывают этот байт во временный регистр, выполняют соответствующую операцию (incf, bcf и т.д.), используя АЛУ, после чего помещают результат обратно в память дан- ных. Такое поведение называется принципом чтение — модификация — запись и выполняется за один машинный цикл. Иногда такое функционирование может привести к неожиданным побочным эффектам (см., например, стр. 335). Вычитание В системе команд имеется две команды вычитания, операнды которых анало- гичны командам сложения. subwf Эта команда вычитает содержимое рабочего регистра из переменной, хранящейся в памяти данных. Как обычно, результат операции может быть помещен либо в рабочий регистр, либо обратно в исходный регистр данных. Например, при вы- полнении команды subwf h' 2 6 ' , f происходит следующее: Как мы уже обсуждали на стр. 95 и в Примере 4.2, приведенном на стр. 109, состояние флага переноса С равно дополнению к биту заема, возникающего при выполнении команд вычитания. Упущение из виду этого факта является одним из основных источников ошибок при написании программы!
Глава 5. Набор команд 139 sublw Команда sublw представляет собой еще один из источников ошибок, поскольку она вычитает содержимое рабочего регистра W из константы, а не наоборот, как можно подумать. Например, если в регистре W было, скажем, h’64’ (d’ 100’), то в результате выполнения команды sublw 1 вместо вычитания единицы получим 1 — h’64’ = h’9D’, что равно десятичному 157 (вообще говоря, это число —h’63’ в дополнительном коде). Лично я считаю, что из-за такой перевернутой реализа- ции использование этой команды является неоправданным риском. В качестве альтернативы давайте посмотрим на команду addlw h' FF ‘. В нашем случае мы получим h’64’ + h’FF’ = h’( 1)63’ (десятичное 99). Если игнорировать перенос, то получается, что 8-битный результат в W на единицу меньше исходного значения. Конечно же, зная о том, что h’FF’ является числом —1 в дополнительном коде, можно записать команду как addlw -1, что будет более понятно. Более того, флаг С в данном случае равен 1 и, интерпретируя его как дополнение к биту за- ема, получаем, что заема не было. В дальнейшем мы будем игнорировать команду sublw и использовать вместо нее addlw. Вообще-то мы уже так делали в Программе 5.2, где нам было нужно вычесть константу h’80’ из W. Ассемблер просто преобразует отрицательное число в его эквивалент в дополнительном коде, например, вместо addlw -6 будет addlw h'FA'. Одной из наиболее важных операций является операция сравнения двух чисел. С математической точки зрения это можно сделать при помощи вычитания байта (обозначаемого ниже как [f] и для регистра данных, и для константы) из содержи- мого рабочего регистра [W]. Результат [W] — [f] представляет реальную разность величин операндов. Однако в большинстве случаев достаточно определить отно- шение между величинами, т.е. узнать, не больше ли W, чем байт данных? Для это- го необходимо контролировать состояния флагов С и Z регистра STATUS. Рабочий регистр больше, чем байт данных.....нет заема, не ноль Рабочий регистр равен байту данных..........ноль Рабочий регистр меньше, чем байт данных.....заем, не ноль В нашем процессоре флаг С является дополнением к биту переноса, а флаг Z устанавливается при нулевом результате. Таким образом: [W] больше, чем или равно [f]: [W] - [f] дает отсутствие заема (С = 1). [W] равно [fj: [W] — [fj дает ноль (Z = 1). [W] меньше, чем [f]: [W] - [f] дает заем (С = 0). Эти варианты приведены на Рис. 5.11, где показано сравнение значения, нахо- дящегося в W, с содержимым регистра h’36’. Команда subwf h1 3 61 , w формирует разность и изменяет флаги Z и С, как показано на рисунке. Собственно, разность двух чисел, находящаяся в W, нас не интересует, однако она перезаписывает исход- ное содержимое, которое может потребоваться сохранить перед сравнением. Рассмотрим следующий пример. Имеется топливная цистерна объемом 255 л, на дне которой установлен датчик, показывающий оставшееся количество топли-
140 Часть II. Программное обеспечение a) Wменьше чем [Регистр] Разность 7 р Ш Нетзаема Ноль Разность 7 р ИЕсть заем Не ноль б) Wравно [Регистр] в) Wбольше чем [Регистр] Рис. 5.11. Сравнение содержимого W и регистра данных командой subwf h' 2 6 ' ва как линейную функцию от давления. Предположим, что значение выходного сигнала датчика представляется в виде байта, считываемого с порта В (см. стр. 105), который мы назовем FUEL. Нам нужно написать процедуру, которая будет включать световой сигнал «Пусто» (бит 0 порта А), если в цистерне осталось меньше 20 л, и включать звуковой излучатель (бит 1 порта А), если осталось мень- ше 5 л (см. Рис. 5.12). Активный уровень на обоих выходах — ВЫСОКИЙ. Эта за- дача может быть реализована следующим образом: STATUS equ 3 ; Регистр STATUS расположен по адресу h'03' С equ 0 ; Флаг переноса - 0-й бит Z equ 2 ; Флаг нуля - 2-й бит FUEL equ 6 ; Уровень топлива можно считать из регистра h'Об 1 (порт В) DISPLAY equ 5 ; Порт А - регистр h'05' LAMP equ 0 ; Сигнальная лампочка управляется 0-м битом BUZZ equ 1 ; Звуковой излучатель управляется 1-м битом ALARM bcf DISPLAY,BUZZ ; Выключим пищалку bcf DISPLAY,LAMP ; Выключим лампочку movf FUEL,W ; Считываем значение уровня топлива в W addlw -5 ; FUEL - 5. ЕСЛИ БОЛЬШЕ ИЛИ РАВНО, btfss STATUS,C ; ТО заема не будет (С == 1), так что пропускаем bsf DISPLAY,BUZZ ; ИНАЧЕ включаем пищалку movf FUEL,w ;'Снова считываем значение уровня топлива в W addlw -d'20’ ; FUEL - 20. ЕСЛИ БОЛЬШЕ ИЛИ РАВНО, btfss STATUS,C ; ТО заема не будет (С == 1), так что пропускаем bsf NEXT: DISPLAY,LAMP ; ИНАЧЕ включаем лампочку После каждого вычитания флаг переноса будет равен 1 (т.е. нет заема), если число в рабочем регистре (количество топлива) больше или равно значению кон- станты, с которым оно сравнивается посредством вычитания. Обратите внимание
Глава 5. Набор команд 141 Рис. 5.12. Операции сравнения в системе контроля уровня топлива на использование команды bsf для установки соответствующих битов порта А (предполагается, что эти линии уже сконфигурированы как выходы). Точно так же команда bcf используется для выключения светового сигнала и звукового из- лучателя в самом начале процедуры. К операциям сравнения можно отнести и операцию проверки, во время кото- рой байт данных проверяется на равенство нулю. Мы уже видели (см. стр. 67), что содержимое любого регистра данных можно проверить на нулевое значение простым копированием его в себя самого, например movf h1 36 ' , f. Если в ре- гистре находится нулевое значение, то флаг Z установится в 1!). Аналогичная про- верка рабочего регистра может быть выполнена прибавлением к нему нуля, т.е. addlw 0. Эта команда установит флаг Z при нулевом значении в рабочем регист- ре, не изменяя его содержимого. Команды логических операций и операций сдвига Микроконтроллеры PIC могут выполнять все четыре базовые логические операции — НЕ, И, ИЛИ и Исключающее ИЛИ, как показано в Табл. 5.3. Во многих микропроцессорах/микроконтроллерах (например, PIC18XXXX) имеется спе- циальная команда проверки на ноль.
142 Часть И. Программное обеспечение Таблица 5.3. Команды логических операций и операций сдвига Операция Мнемоника Флаги Описание Z DC c И Константы с W W с регистром andlw к andwf f,d a/ V • • • • Побитовое логическое И [W] <- [W] #kk [d] <- [W] • [f] Дополнение Регистра comf f V • • Инвертирование или операция HE (обратный код) [d] <- [f] ИЛИ Константы с W W с регистром iorlw к iorwf f,d V • • Побитовое логическое ИЛИ [W] <- [W] + #kk [d] <- [W] + [f] Исключающее ИЛИ Константы с W W с регистром xorlw к xorwf f,d V • • Побитовое логическое Исключающее ИЛИ [W] <- [W] Ф #kk [d] <- [W] Ф [f] Циклический сдвиг регистра Влево Вправо rlf f,d rrf f,d • • • • b? bo Циклический сдвиг через перенос ^—| С |-~| 7 Регистр 0 7 Регистр 0 С |— Условные обозначения: Не воздействует на флаг V Воздействует на флаг W Рабочий регистр f Регистр данных [] Содержимое d Адресат, W или регистр данных #kk 8-битная константа < — Становится • Побитовая операция И + Побитовая операция ИЛИ Ф Побитовая операция Исключающее ИЛИ [Т] Побитовая инверсия содержимого регистра Операция НЕ Логическая функция НЕ, показанная на Рис. 1.1 (стр. 26), инвертирует (фор- мирует обратный код) логическое состояние входа. comf С помощью этой команды можно инвертировать содержимое любого заданного регистра данных. Так, команда comf h1 2 6 ' , f вычисляет обратный код содер- жимого регистра h’26’:
Глава 5. Набор команд 143 Как обычно, результат может быть помещен либо в исходный регистр данных, либо в W (в последнем случае исходное содержимое остается неизменным), на- пример: L comf h 1 26 ' ,w 10001110 1 ____ г—————i h’26’ —* 01110001 I----------1 w В микроконтроллерах PIC отсутствует команда типа comw для инвертирова- ния содержимого рабочего регистра, однако эту операцию можно выполнить за один машинный цикл посредством вычитания W из числа b’ 11 111 111 ’, что дает в результате тот самый обратный код, т.е. sublw h' FF'. Например (см. также стр. 147): 11111111 Константа h’FF’ — 10001110 Рабочий регистр 01110001 Дополнительный код содержимого W Операция И Из Рис. 1.2 (стр. 27) можно увидеть следующие соотношения: • Логическое И любого бита и 0 всегда дает в результате 0. • Логическое И любого бита и 1 дает в результате исходный бит. Используя эти свойства, мы можем обнулять группы битов в байте данных посредством логического умножения этого байта на соответствующую битовую маску. Операция И между байтом данных и тестовым шаблоном, используемая для сброса всех ненужных битов, может также применяться для проверки на ноль за- данной группы битов. Если эти биты равны нулю, то общий результат тоже будет равен нулю, и флаг Z установится в 1. andwf Команда andwf выполняет операцию побитового И между содержимым рабочего регистра W и любого регистра данных, помещая результат либо в исходный ре- гистр данных, либо в W. Например, при логическом умножении каждого бита W на соответствующий бит регистра h’26’ и помещении результата обратно в h’26’, имеем
144 Часть II. Программное обеспечение К примеру, если нам нужно сбросить шесть старших битов регистра h’26’, то мы можем написать следующее: movlw b'00000011' ; Маска andwf h'26', f ; Логически умножается на содержимое регистра h'26' Эту же операцию можно было бы выполнить, повторив шесть раз команду bcf. Чтобы разобраться, как можно использовать функцию И для проверки на ноль группы битов, представим себе контроллер стиральной машины, который считывает состояние восьми переключателей передней панели через порт В, т.е. через регистр h’06’. Нам нужно, чтобы при нулевом значении битов 7 и 6 (одно- временно нажаты кнопки «СТАРТ» и «БЫСТРО») включалась программа быст- рой стирки. Вот как можно это сделать: movlw b111000000' ; Маска andwf h'06',w ; Операция И с регистром PORTB btfss STATUS,Z ; Пропуск, если Z == 0 (т.е. результат не равен 0) goto FAST_WASH ; ИНАЧЕ перейти к процедуре FAST_WASH ...................... ; Следующая проверка В результате операции логическое И между содержимым регистра h’06’ и кон- стантой h’11000000’ младшие 6 битов сбрасываются. Результат будет равен нулю, если оба бита 6 и 7 порта В были сброшены перед выполнением команды. При этом будет установлен флаг Z, в результате чего программа перейдет к команде, помеченной меткой fast_wash. Не забудьте, что для проверки нулевого значе- ния одного бита регистра данных можно использовать команду bt f sc. andlw Эта команда выполняет операцию побитового И между содержимым рабочего ре- гистра и однобайтной константой. Например: 10001110 andlw h'OF' W * 00001110 ---------w В результате операции, показанной на рисунке, старший полубайт содержимого W обнуляется, а младший — остается неизменным. Операция ИЛИ Из Рис. 1.3 (стр. 28) можно увидеть следующие соотношения: • Логическое ИЛИ любого бита с 0 всегда дает в результате исходный бит. • Логическое ИЛИ любого бита с 1 всегда дает в результате 1. Используя эти свойства, мы можем устанавливать группы битов в байте дан- ных, выполняя логическое сложение с соответствующей битовой маской.
Глава 5. Набор команд 145 iorwf По аналогии с командой andwf, эта команда выполняет операцию побитового ИЛИ между любым регистром данных и содержимым рабочего регистра W. Так, при логическом сложении каждого бита W с соответствующим битом регистра h’26’ и помещении результата обратно в h’26’ имеем Например, для установки в 1 старших семи битов регистра данных h’36’ мы можем написать: movlw b'11111110' ; Маска iorwf h'3 61,f ; Устанавливаем старшие 7 битов, младший бит не ; изменяется iorlw Эта команда выполняет операцию побитового ИЛИ содержимого W с однобайт- ной константой. Например, для установки младших двух битов рабочего регистра в 1: 10001110 | 1О^ 03 | 10001111 ----------1 w ---------- Операция Исключающее ИЛИ Из Рис. 1.4 на стр. 28 можно увидеть следующее: • В результате операции Исключающее ИЛИ между битом и нулем возвраща- ется исходный бит. • В результате операции Исключающее ИЛИ между битом и 1 возвращается инвертированное значение исходного бита. Другим полезным свойством оператора XOR является его использование в ка- честве логического дифференциатора. При более внимательном рассмотрении таб- лицы истинности можно заметить, что на выходе элемента Исключающее ИЛИ бу- дет 1, если на его входах присутствуют различные логические уровни, и 0, если эти уровни одинаковы. Соответственно, в результате побитовой операции Исключаю- щее ИЛИ между двумя байтами мы получим байт с 0 в тех позициях, где биты вход- ных переменных были одинаковыми, и с 1 в тех позициях, где они были различными.
146 Часть II. Программное обеспечение xorwf Эта команда выполняет побитовую операцию Исключающее ИЛИ между любым регистром данных и содержимым рабочего регистра W. Так, при выполнении опе- рации Исключающее ИЛИ между каждым битом W и соответствующим битом регистра h’26’ и записи результата обратно в h’26’ имеем Например, для переключения состояния старшего бита регистра h’36’ мы мо- жем написать: movlw b'10000000' ; Маска xorwf h'3 б 1,f ; Переключаем только старший бит регистра В качестве примера продемонстрируем использование операции Исключаю- щее ИЛИ для определения отличий между двумя группами битов. Рассмотрим процедуру, непрерывно опрашивающую состояние порта В микроконтроллера, к которому подключены восемь переключателей с передней панели стиральной ма- шины. Процедура ожидает изменения состояния переключателей: START movf movwf PORTB,w h' 20 ’ LOOP movf PORTB,w xorwf h' 20' , w btf sc STATUS,Z goto S_LOOP ; Считываем начальное состояние переключателей ; Сохраняем его в регистре h’20' ; Считываем текущее состояние переключателей ; Ищем отличия от исходного состояния ; Пропускаем, если результат проверки не равен ;_ нулю ; ИНАЧЕ проверяем снова При этом возможны два варианта: I 10011110 h xorwf h'2 О 1,w WmmJ h’20’ —- I 10001110 h xorwf h'20’,w h’20’ —” I 10011110 I = I 00000000 I Z = 1 I---------1 w I----------1W I 10011110 I = I 00010000 I z=o I---------1 w 1----------1 w
Глава 5. Набор команд 147 Результат, получаемый в рабочем регистре, отражает любые изменения состо- яния передней панели. В первом случае между исходным состоянием переключа- телей, сохраненным в регистре h’20’, и текущим нет никаких отличий. Во втором случае 4-й переключатель был переключен из 1 в 0. Чтобы определить, какой именно бит изменился, можно сдвигать результат вправо с подсчетом количества сдвигов до тех пор, пока оставшееся значение не будет равно 0 (см. Рис. 5.14). А характер изменения (0 -> 1 или 1 -> 0) можно определить посредством логического умножения итогового байта на байт исходного состояния переклю- чателей, находящийся в регистре h’20’, т.е. с помощью команды andwf h12 0' , w. Если 4-й бит результата равен нулю, то исходное значение тоже было равным 0 и, соответственно, состояние бита изменилось с 0 на 1 и наоборот. xorlw Эта команда выполняет побитовую операцию Исключающее ИЛИ содержимого W с однобайтной константой. Например, для инвертирования всех битов в регистре W, т.е. для вычисления обратного кода'. 10001110 xorlw h'FF' W * I 01110001 lw Операции сдвига Сдвиг данных влево или вправо является базовой операцией, реализованной во всех цифровых системах. Мы уже видели на Рис. 2.22 (стр. 51), как это можно сделать аппаратно. АЛУ всех без исключения микроконтроллеров и микропро- цессоров позволяют реализовать различные комбинации команд сдвига вправо и влево. Во всех микроконтроллерах PIC имеется две команды для циклического сдви- га содержимого любого регистра данных, по одной команде для каждого направ- ления1). rrf Эта команда сдвигает содержимое указанного регистра данных на один бит впра- во, при этом вдвигаемый бит считывается из флага С, значение которого затем ус- танавливается в соответствии с выдвинутым битом. Эта операция показана на Рис. 5.13. Учитывая эту особенность команды, программист может выполнить нормаль- ный сдвиг вправо с загрузкой в старший бит нуля (как на Рис. 2.22), если он сбро- сит бит С перед выполнением команды сдвига. bcf STATUS,С ; Обнуляем бит переноса в регистре STATUS rrf h'30',f ; Сдвигаем регистр вправо 11 В старшем семействе микроконтроллеров для операций циклического сдвига через пере- нос были введены мнемоники rlcf/rrcf. Две новые команды, сдвигающие содержимое регис- тра без учета бита переноса, были названы rlnc и ггпс.
148 Часть II. Программное обеспечение После операции $rrf h'20' ,f а) Циклический сдвиг регистра вправо Рис. 5.13. Циклический сдвиг содержимого регистра данных на один бит вправо Одним из использований операций сдвига является побитовая проверка дан- ных. Предположим, для примера, что состояние 8 кнопок мобильного телефона было сохранено в регистре данных h’26’. Вам требуется определить самую левую разомкнутую кнопку, при этом считаем, что разомкнутой кнопке соответствует 1, а замкнутой — 0. Так, если были считаны следующие состояния: j q SW8 | Q SW7 | 1 SW6 | q SW5 [ -| SW4 | -| SW3 [ -| SW2 | -| SW1~| то в W должно получиться число 6 (Ь’00000110’). Рабочий регистр в Программе 5.4 используется в качестве счетчика. Посколь- ку флаг переноса сбрасывается перед каждым сдвигом, вдвигается всегда лог. 0Ч В какой-то момент остаток становится равным нулю, и процесс завершается. Так, 00010111 (1) -> 00001011 (2) -> 00000101 (3) -> 00000010 (4) -> 00000001 (5) -> 00000000 (6). Список действий, необходимых для решения поставленной задачи, показан- ный также в виде блок-схемы на Рис. 5.14, будет следующим: 1. Обнулить KEY_COUNT. 2. ПОКА SWITCH-PATTERN не равно нулю, ВЫПОЛНЯТЬ: а) ЕСЛИ остаток равен нулю, ТО выйти из цикла. б) Сдвинуть SHIFT-PATTERN на один бит влево. в) Инкрементировать KEY_COUNT. 3. Значение в KEY_COUNT равно позиции самой левой разомкнутой кнопки. При выполнении команд логического сдвига, имеющихся во многих других микропро- цессорах и микроконтроллерах, всегда вдвигается 0, независимо от состояния флага С.
Глава 5. Набор команд 149 Рис. 5.14. Процедура определения позиции самого левого установленного бита При сдвиге вправо во флаг переноса выдвигается самый правый (младший) бит. Заменив команду btfsc status,Z командой btfsc STATUS,С, мы смо- жем определить позицию самого правого бита. Во многих случаях циклическое выдвигание бита во флаг переноса может использоваться для побитовой провер- ки данных. Например, мы можем модифицировать свою программу таким обра- зом, чтобы она подсчитывала число установленных битов в байте (см. Программу 5.6). Программа 5.4. Поиск самого старшего единичного бита в регистре SWITCH_PATTERN equ h'26’ ; Состояние кнопок в регистре h'26' STATUS equ 3 ; Регистр STATUS расположен по адресу h'03 С equ 0 ; Бит 0 - флаг переноса Z equ 2 ; Бит 2 - флаг нуля ; Задача 1 HIGH_BIT clrw ; Обнуляем счетчик ; Задача 2: Сдвигаем вправо и инкрементируем счетчик до тех пор, ; пока проверяемый байт не равен нулю ; Задача 2а ------------------------------------------------------------ LOOP movf h'26', f ; Остаток равен нулю?
150 Часть И. Программное обеспечение btfsc STATUS,Z ; ЕСЛИ нет, ТО пропускаем команду goto FINI ; ИНАЧЕ выходим из цикла ; Задача 26 ----------------------------------------------------- bcf STATUS,С ; Сбрасываем флаг переноса rrf SWITCH_PATTERN,f ; Сдвигаем регистр вправо ; Задача 2в ----------------------------------------------------- addlw 1 ; Увеличиваем счетчик на 1 goto LOOP ; и выполняем следующий сдвиг ; Задача 3 ------------------------------------------------------ FINI ......................... ; KEY_C0UNT в W Заметьте, если все кнопки были замкнуты, то в Программе 5.4 возвращается ноль. Поскольку после сдвига производится проверка на ноль, то нельзя будет раз- личить ситуацию «нет разомкнутых кнопок» и «разомкнута только 1-я кнопка». При разработке программ необходимо уделять особое внимание вопросу их «жи- вучести» при возникновении ограничивающих условий, подобных указанным. rlf Команда rlf похожа на команду rrf, только она, как показано на Рис. 5.15, вы- полняет сдвиг влево. После операции fiprlf h'20' , f а) Циклический сдвиг регистра влево Рис. 5.15. Циклический сдвиг содержимого регистра данных на один бит влево В качестве примера использования команды rlf вспомним (см. стр. 25), что сдвиг влево можно использовать для умножения числа на степень двойки. На- пример: 00000110 (6) « 00001100 (12) « 00011000 (24) « 00110000 (48) « ит.д.
Глава 5. Набор команд 151 где оператор языка Си «<<» используется для обозначения сдвига влево. Чтобы проиллюстрировать этот процесс, предположим, что у нас имеется 16- битное число Ь’00000111 11010000’ (равное десятичному 1024 + 512 + 256 + 128 + + 64 + 16 = 2000), хранящееся в двух регистрах данных, например: I 00000111 h 110Ю000 b h’30’ После сдвига на один бит влево получим число-: 00001111 1 10100000 1 h’30’ h’31 ’ h’31 ’ равное десятичному числу 4000 (2048 + 1024 + 512 + 256 + 128 + 32 = 4000). Проблема в том, что команда rlf может сдвигать только один бит. Поэтому нам необходимо разбить эту операцию на три этапа, как показано на Рис. 5.16: 1. Сбросить флаг переноса, чтобы при сдвиге вдвигался 0. 2. Сдвинуть влево младший байт, в результате чего значение флага переноса станет равным значению бита by. 3. Сдвинуть влево старший байт, при этом на месте младшего бита окажется бит, загруженный во флаг переноса при предыдущей операции. 100001111 rlf h'30',f _______h’30’ h’31’ I 000001111 111010000 | rlf h'31',f $ Puc. 5.16. Сдвиг 2-байтного числа на один бит влево для умножения на 2 Из рисунка видно, что этот процесс совершенно прозрачен — выходной бит переноса первого регистра данных становится входным для второго. Фрагмент кода, выполняющий эту операцию, выглядит следующим образом: bcf STATUS,С ; Сбрасываем флаг С, в котором содержится ; вдвигаемый бит rlf h' 31*,f ; Сдвигаем младший байт, MSB оказывается во флаге С rlf h’30’,f ; Сдвигаем старший байт
152 Часть II. Программное обеспечение Команды передачи управления Все команды, перечисленные в Табл. 5.4, тем или иным образом модифициру- ют состояние счетчика команд PC. Таблица 5.4. Команды передачи управления Операция Мнемоника Флаги Описание Z DC c Абсолютный переход Перейти к адресу goto ааа • • • Передает управление по фиксированному адресу [РС] <- ааа Нет операции пор • • • Пустая команда [PC] <- [РС] + 1 Проверка бита и пропуск Проверка бита регистра и пропуск, если сброшен Проверка бита регистра и пропуск, если установлен btfsc f,n btfss f,n • • • Проверяет бит регистра и пропускает команду, если условие истинно РС++, ЕСЛИ fn == 0 РС++, ЕСЛИ fn == 1 Декрементирование и пропуск, если ноль Регистра decfsz f,d • • • Декрементирует регистр и пропускает команду, если результат равен нулю d <- f--, РС++, ЕСЛИ [f] == #00 Инкрементирование и пропуск, если ноль Регистра incfsz f,d • • • Инкрементирует регистр и пропускает команду, если результат равен нулю d <- f++, РС++, ЕСЛИ [f] == #00 Условные обначения: Не воздействует на флаг f Регистр данных [ ] Содержимое d Адресат, W или регистр данных ааа Абсолютный 11-битный адрес Становится + + Инкрементирование содержимого Декрементирование содержимого #00 Нулевой байт fn л-й бит регистра п 3-битная позиция изменяемого бита пор Команда «нет операции» не изменяет состояние системы, однако при ее выпол- нении инкрементируется PC, поскольку при этом производится выборка следую- щей команды из памяти программ. Таким образом, единственным результатом выполнения команды пор будет изменение значения счетчика команд. Эта команда выполняется за один машинный цикл, так что ее основное на- значение — реализация коротких задержек (с дискретностью 1 мкс при частоте тактового сигнала 4 МГц). Например, чтобы выдать на 0-й вывод порта А отрица- тельный импульс длительностью 2 мкс, мы можем написать: bcf PORTA,0 ; Выставляем на RA0 НИЗКИЙ уровень пор ; Ждем 2 мкс
Глава 5. Набор команд 153 пор ; bsf PORTA,0 ; Выставляем на RA0 ВЫСОКИЙ уровень предполагая, что 0-й бит КЪрта А сконфигурирован как выход (см. стр. 105) и пе- ред выполнением указанных команд на этом выходе был ВЫСОКИЙ уровень. goto Эта команда позволяет выполнить переход к любой требуемой команде в преде- лах всей памяти программ. В примере, показанном на Рис. 5.17, командагдосо h' 3F9 ' размещена в па- мяти программ по адресу h’005’. В процессе выполнения программы счетчик ко- манд инкрементируется до h’006’, а команда, расположенная по этому адресу, из- влекается в конвейер для исполнения в следующем цикле. Однако при выполне- нии команды goto h' 3F9 ' в счетчик команд помещается адрес h’3F9’. То есть следующей исполняемой командой будет команда, расположенная по указанно- му адресу. Для этого команда №1018 должна быть загружена в конвейер, поверх Память программ PC FRED: PC Команда 1017 Ж Команда 1018 3F9 Команда 1019 3FA Команда 1020 3FB Команда 1021 ЗГС Команда 1022 ЗЙЭ Команда 1023 3FE Команда 1024 3FF
154 Часть II. Программное обеспечение ненужного уже кода 7-й команды. Этот процесс называется сбросом конвейера, и для него требуется дополнительный машинный цикл. Поэтому команда goto вы- полняется за два машинных цикла. На рисунке ячейка с адресом h’3F9’ помечена меткой FRED (завершающее двоеточие необязательно). Настоятельно рекомендуется использовать метки, а не абсолютные адреса (см. также стр. 106), поскольку программисту не так-то легко узнать, по какому адресу будет размещаться та или иная команда, и в любом слу- чае ее положение может измениться в процессе разработки программы. btfsc Команды btfsc и btfss играют очень важную роль при программировании микроконтроллеров PIC — это видно хотя бы из того, что они встречались прак- тически в каждой программе данной главы. Эти команды использовались для ор- ганизации операции выбора на основе состояний различных флагов регистра STATUS, обозначаемого фразами «ЕСЛИ...ТО» в текстовых описаниях алгорит- мов или символом О на блок-схемах. В частности, в Программе 5.4 команда btfsc STATUS, z (или, что менее понятно, команда btfsc 3,2) позволяет реа- лизовать цикл, выполняющийся до тех пор, пока байт данных не станет равным нулю, пропуская команду выхода из цикла goto при Z = 0. На самом деле, команда btfsc может использоваться не только для проверки флагов регистра STATUS. Проверить можно любой бит в любом регистре данных и пропустить следующую команду, если этот бит сброшен (см. стр. 128). На Рис. 5.18 Память программ Команда 1 Команда 2 Команда 3 Команда 4 Команда 5 btfac Ь'ЗО*,? Команда 7 Команда 8 Команда 9 Команда 10 Команда 11 Команда 12 ООО ооТ "002 "ооз 004 005 “006 007 "008 "009 "00А "оов Память данных ЕСЛИ 7-й бит регистра h’20’ равен 0, ТО пропустить команду, ИНАЧЕ продолжать h’20’ (Не пропускать) Нет ОПУСТИТЬ pg Рис. 5.18. «Перескакивание» через команду при сброшенном бите регистра h’20’ 7-я команда Блок-схема 7-й бит > Выбор равен 0? Да (Пропустить) 8-я команда
Глава 5. Набор команд я 155 шестой командой является команда btfsc h'20',7. Она проверяет состояние 7-го бита регистра h’20’ и, в зависимости от его состояния, выполняет одно из двух действий: 1. ЕСЛИ 7-й бит равен О, ТО команда 7 пропускается и выполняется команда 8. 2. ЕСЛИ 7-й бит равен 1, то выполняется команда 7. Часто в качестве такой 7-й команды используется команда goto, что позволя- ет программе реагировать на изменение состояния любого бита в памяти данных переходом к соответствующему блоку При пропуске команды необходимо очищать конвейер, так же как и в случае команды goto, поскольку нарушается линейный характер выполнения програм- мы. Это означает, что команда btfsc выполняется за один машинный цикл, если пропуск не осуществляется, и за два цикла — в противном случае. btfss Данная команда выполняет пропуск следующей команды, если указанный бит равен 1. За исключением этого ее функционирование полностью аналогично ко- манде btfsc. decfsz Команда decfsz представляет альтернативный вариант реализации операции выбора. Подобно комбинации команды decf и следующей за ней команды btfss status , z, эта команда позволяет декрементировать содержимое любого регистра данных и в случае равенства его нулю пропускать следующую команду. Типичным примером использования этой команды является подсчет числа проходов цикла. Например, предположим, что нам требуется сформировать на выводе RAO 20 импульсов, длительность каждого из которых будет не менее 2 мкс. Ниже приведен фрагмент программы, выполняющий указанные действия, а его блок-схема — на Рис. 5.19 (предполагается, что частота используемого резо- натора равна 4 МГц). movlw d' 20' ; Запишем 20 в W movwf h'3F' ; и скопируем его в регистр h’3F' - счетчик цикла LOOP bcf PORTA,0 ; Выставим на RA0 НИЗКИЙ уровень nop ; Ждем один машинный цикл bsf PORTA,0 ; Выставим на RA0 ВЫСОКИЙ уровень / decfsz h' 3F’ ,f ; Считаем в обратном направлении goto LOOP ; Повторить тело цикла, если не ноль .................... ; ИНАЧЕ выйти из цикла Первоначальный код, обрамленный комментариями в виде пунктирной ли- нии, завершается командой декрементирования с проверкой, которая обеспечи- вает выход из цикла при достижении регистром h’3F’ нулевого значения. Обрати- те внимание на запись d’20’ — так в ассемблере явно указывается десятичное чис- ло (см. стр. 267). Эта запись эквивалентна записи h’14’, однако гораздо понятнее
156 Часть II. Программное обеспечение программисту. В теле цикла используется только одна команда пор, поскольку дополнительная задержка длительностью в один машинный цикл формируется в результате выполнения команд bcf и bsf. decfsz NUMBER,f Рис. 5.19. Формирование 20 импульсов на выводе RA0 incfsz Команда инкрементирования регистра данных и пропуска следующей команды при нулевом результате инкрементирует, а не декрементирует содержимое ука- занного регистра данных. При переходе содержимого через ноль, т.е. в ситуации h’FC’ -> h’FD’ -> h’FE’ -> h’FF’ -> h’00’, будет пропущена следующая команда. Вернемся к нашему примеру, блок-схема которого показана на Рис. 5.19. Если мы предварительно загрузим в регистр h’3F’ число -20 (h’FC’) и заменим команду decfsz h'3F' , f командой incfsz h'3F' , f, то получим тот же самый ре- зультат. Только в этом случае счет будет осуществляться в прямом направлении, а не в обратном. Примеры Пример 5.1 Напишите программу для декрементирования 2-байтной переменной, распо- ложенной в памяти данных по адресам h’26’ (старший байт) и h’27’ (младший байт). Помните, что команда decf не влияет на состояние флага переноса/заема.
Глава 5. Набор команд 157 Решение Сначала напишем алгоритм: 1. ЕСЛИ младший байт в регистре h’27’ равен нулю, то декрементировать старший байт. 2. ВСЕГДА декрементировать младший байт. Одна из возможных реализаций этого алгоритма приведена в Программе 5.5. При увеличении разрядности исходного значения до п байт оно будет обрабаты- ваться точно так же — от младшего байта к старшему, с декрементированием как (п + 1)-го, так и и-го байта при равенстве нулю и-го байта. Программа 5.5. Декрементирование 2-байтного числа STATUS Z MSB LSB equ 3 equ 2 equ h'2 6 ' equ h'2 7' ; Регистр STATUS ; Бит 2 - флаг нуля ; Старший байт ; Младший байт movf LSB,f ; Младший байт равен нулю? btfsc : STATUS,Z ; ЕСЛИ нет, ТО пропускаем декрементирование ; старшего байта decf MSB,f ; ИНАЧЕ декрементируем старший байт decf LSB,f ; Всегда декрементируем младший байт Пример 5.2 В некоторых ранних моделях компьютеров для представления двоично-деся- тичных чисел использовался сдвоенный пятизначный код (bi-quinary). Этот код представляет собой 7-битный код, в котором при любой комбинации битов будут установлены только два из них: 01 00001 0 01 00010 1 01 00100 2 01 01000 3 01 10000 4 10 00001 5 10 00010 6 10 00100 7 10 01000 8 10 10000 9 Хотя такое представление чрезвычайно неэффективно (используется только 10 из 128 возможных комбинаций), его преимуществом является чрезвычайная простота обнаружения ошибок. Напишите программу для проверки корректнос- ти числа, представленного в сдвоенном пятизначном коде и находящегося в ре- гистре h’20’ (полагаем, что старший бит равен нулю). В случае ошибки в рабочий регистр необходимо записать h’FF’, иначе — h’00’.
158 Часть II. Программное обеспечение Решение Все, что нам нужно сделать, — это распознать ситуацию, когда число установ- ленных битов будет больше или меньше двух. Исходя из этого, составим перечень задач: 1. Подсчитать количество единичных битов в числе. 2. Обнулить W. 3. Если полученное число не равно двум, загрузить h’FF’ в W для индициро- вания ошибки. Одна из возможных реализаций этого алгоритма приведена в Программе 5.6. В программе исходный байт сдвигается влево до тех пор, пока остаток не станет равным нулю. Если в результате сдвига устанавливается флаг переноса, то инкре- ментируется счетчик единичных битов. При выходе из счетчика битов вычитается двойка. Если результат вычитания равен нулю, процедура завершается с нулевым значением в W, индицирующим корректность числа. В противном случае в W за- гружается число h’FF’ для индикации ошибки. Это значение соответствует числу — 1 и традиционно используется для сообщения об ошибочных ситуациях. Суще- ствует всего 20 комбинаций с двумя установленными битами, из которых только 10 являются корректными. Можете ли вы доработать программу таким образом, чтобы исключить из рассмотрения эти дополнительные комбинации? Программа 5.6. Обнаружение ошибок в сдвоенном пятизначном коде STATUS equ 3 ; : Регистр STATUS расположен по адресу h'03' С equ 0 ; ; Бит 0 - флаг переноса Z equ 2 ; ; Бит 2 - флаг нуля BI_QUIN equ 20h ; ; Проверяемый байт COUNT equ 21h ; : Счетчик битов BI_QUINARY clrf COUNT ; Обнуляем счетчик битов ; Задача 1 LOOP bcf STATUS,C , ; Сбрасываем флаг переноса rlf BI_QUIN,f , ; Сдвигаем байт влево btfsc STATUS,C , ; ЕСЛИ нет переноса, ТО пропускаем команду incf COUNT,f ; Инкрементируем счетчик movf BI_QUIN,f , ; Проверяем остаток btfss STATUS,Z ; ЕСЛИ ноль, ТО выходим из цикла goto LOOP ИНАЧЕ повторяем цикл ; Задачи 2 и 3 movf COUNT,w ; Берем подсчитанное значение sublw 2 ; Сравниваем его с двумя btfss STATUS,Z ; ЕСЛИ ноль, завершаем программу (W = 0) movlw h’FF' ; ИНАЧЕ помещаем h’FF' (-1) в W ; и выходим
Глава 5. Набор команд 159 Пример 5.3 Микроконтроллеры PIC младшего и среднего уровней не имеют команд для непосредственного умножения или деления1*. Однако для реализации этих важ- ных арифметических операций можно использовать сложение и вычитание. Например, для деления числа на 10 можно подсчитать, сколько раз можно вычесть из исходного числа десять без формирования бита заема. Подсчитанное таким образом значение будет частным, а оставшееся после вычитаний значение — остатком отделе- ния. Используя этот способ, напишите программу для преобразования двоичного чис- ла, меньшего или равного h’63’ (десятичное 99), находящегося в регистре h’20’, в два BCD-числа, помещаемые в регистры h’21 ’ (десятки) и h’22’ (единицы); см. стр. 20. Решение При делении числа на 10 формируется частное от 0 до 9 (напоминаю, что мак- симальное значение по условиям задачи равно 99) и остаток. Частное представля- ет собой число десятков, а остаток — число единиц. Самым простым решением этой задачи, блок-схема которого изображена на Рис. 5.20, является циклическое вычитание десяти (addlw —d’ 10 ’ или addlw -h' 0А'). В регистре TENS будет подсчитываться количество операций вычитания, выполненных до момента генерации заема, — искомое число десят- ков на единицу меньше подсчитанного значения. Прибавив к оставшемуся значе- нию число 10, получим остаток отделения, т.е. число единиц. Программа 5.7. Преобразование двоичного числа в двоично-десятичное STATUS equ 3 Регистр STATUS расположен по адресу h’03’ С equ o Флаг переноса - бит 0 BINARY equ h'20’ ; Исходное число TENS equ h'21' ; Частное (число десятков) UNITS equ h’22’ ; Остаток (число единиц) ; Сначала делим на 10 BIN_2_BCD clrf TENS ; Обнуляем счетчик цикла movf BINARY,w ; Копируем исходный байт в W ; Вычитаем 10 и считаем i <ол-во вычитаний до генерации заема LOOP incf TENS,f ; Запомнили очередную операцию addlw -d'10' ; Вычли десять btfsc STATUS,C ; ЕСЛИ заем (С == 0), ТО выходим из цикла goto LOOP ; ИНАЧЕ вычитаем- еще раз ; Корректируем лишнее вычитание и определяем число единиц decf TENS,f ; Последняя операция вычитания - лишняя addlw d’10’ ; Прибавляем 10 к оставшемуся значению movwf UNITS ; Получаем остаток от деления (число единиц) ; Следующая процедура ° В наборе команд старшего семейства имеется команда умножения беззнаковых 8-битных чисел, формирующая 16-битное произведение, — и все это за один машинный цикл!
160 я Часть II. Программное обеспечение Рис. 5.20. Преобразование десятичного числа (0...99) в BCD-число Пример 5.4 Другим подходом к делению является представление делителя в виде суммы чисел, являющихся дробными степенями двойки. К примеру, дробь % можно приближенно выразить следующим образом: 1 = 1 _ 1 + 1 _J_.J___L._L 3 2 4 8 16 32 64 129” На основе этого ряда напишите программу, которая будет делить число N, на- ходящееся в рабочем регистре, на три, помещая частное в тот же регистр. В качестве временных переменных для хранения частного и количества сдви- гов можно использовать соответственно регистры h’20’ и h’21 ’. Решение Сначала в Программе 5.8 обнуляется байт частного, а число из W копируется в регистр h’21’. После этого исходное число сдвигается вправо для получения раз- личных дробей, которые либо прибавляются, либо вычитаются из регистра h’20’, постепенно формируя искомое частное. 1 129’ При последнем члене ряда, равном результат равен 0.3359375, т.е. откло- нение от точного значения составляет 0.78%. При работе с 8-битными числами включать в ряд остальные члены не имеет смысла. Если же необходима большая точность, то исходное значение следует расши- рить до 16 бит, добавив младший нулевой байт. Используя при этом 2-байтные арифметические операции и операции сдвига, можно будет увеличить число чле- 1 нов ряда и получить точность ВПЛОТЬ ДО . 32768
Глава 5. Набор команд 161 Программа 5.8. Процедура деления на три QUOTIENT equ TEMP equ STATUS equ C equ h’20' ; Временная переменная для хранения частного h'21' ; Временная переменная для операций сдвига 3 ; Регистр STATUS 0 ; Бит 0 - флаг переноса DIV_3 clrf movwf bcf rrf movf movwf QUOTIENT ; Обнуляем результат TEMP ; Помещаем N во временный регистр STATUS,С ; Сбрасываем флаг переноса TEMP,f ; Сдвигаем вправо, получаем N/2 TEMP,w ; Копируем в W QUOTIENT ; и в QUOTIENT, получаем Q = N/2 bcf rrf movf subwf STATUS,С ; Сбрасываем флаг переноса TEMP,f ; Сдвигаем вправо, получаем N/4 TEMP,w ; Копируем в W QUOTIENT,f; Вычитаем, получаем Q = N*(l/2-l/4) bcf rrf movf addwf STATUS,С ; Сбрасываем флаг переноса TEMP,f ; Сдвигаем вправо, получаем N/8 TEMP,w ; Копируем в W QUOTIENT,f; Складываем, получаем Q = N* (1/2-1/4 + 1/8) bcf rrf movf subwf STATUS,С ; Сбрасываем флаг переноса TEMP,f ; Сдвигаем вправо, получаем N/16 TEMP,w ; Копируем в W QUOTIENT,f; Вычитаем, получаем Q = N* (1/2-1/4+1/8-1/16) bcf rrf movf addwf STATUS,С ; Сбрасываем флаг переноса TEMP,f ; Сдвигаем вправо, получаем N/32 TEMP,w ; Копируем в W QUOTIENT,f; Складываем, получаем Q = N*(1/2-1/4+1/8-1/16+1/32) bcf rrf movf subwf STATUS,С ; Сбрасываем флаг переноса TEMP,f ; Сдвигаем вправо, получаем N/64 TEMP,w ; Копируем в W QUOTIENT,!; Вычитаем, получаем Q = N*(1/2-1/4+1/8—1/16+1/32- ; -1/64) bcf rrf movf addwf STATUS,С ; Сбрасываем флаг переноса TEMP,f ; Сдвигаем вправо, получаем N/128 TEMP,w ; Копируем в W QUOTIENT,w; Складываем, получаем N*(1/2-1/4+1/8-1/16+1/32- ; -1/64+1/128)
162 Часть II. Программное обеспечение Пример 5.5 Одной из операций, выполняемой процедурой перевода температуры из шка- лы Цельсия в шкалу Фаренгейта, является умножение числа, находящегося в ре- гистре h’22’, на девять. Итоговое 16-битное произведение должно находиться в регистрах h’21 ’ (старший байт) и h’22’ (младший байт). Решение Задачу умножения числа на девять можно разбить на две подзадачи: умноже- ние исходного числа на восемь и прибавление к полученному произведению ис- ходного числа. Соответственно, в Программе 5.9 реализован следующий алго- ритм: 1. Умножить число на восемь (сдвинуть 3 раза влево). 2. Добавить исходное число к частичному 16-битному произведению. Однобайтный множитель копируется в младший байт будущего произведе- ния. Расширение до 16 бит производится обнулением старшего байта произведе- ния. Сбросив флаг переноса и выполнив 3 раза операцию сдвига, получаем час- тичное произведение исходного числа на 8. И наконец, прибавив однобайтный множитель к двухбайтному частичному произведению, получаем окончательный результат. Принцип «сдвиг и сложение» (см. стр. 25) может использоваться для реализа- ции умножения любых чисел. Например, умножение на 10 можно реализовать как х8 + х2. Эту операцию запрограммировать немного сложнее, поскольку необ- ходимо оперировать 2-байтными временными переменными. Однако это все рав- но гораздо быстрее, нежели простое сложение в цикле. Программа 5.9. Процедура умножения на девять STATUS equ 3 ; Регистр STATUS расположен по адресу h'03 MULTIPLICAND equ h'22' ; Множимое PRODUCT_H equ h’23' ; Старший байт произведения PRODUCT_L equ h1 24' ; Младший байт произведения С equ 0 ; Флаг переноса - 0-й бит регистра STATUS ; Задача 1: Умножить множимое на восемь MUL_9 movf MULTIPLICANDS ; : Берем множимое, которое movwf PRODUCT_L . ; ; становится младшим байтом произведения, clrf PRODUCT_H ; ; расширенным до 16 бит bcf STATUS,C ; ; Сбрасываем флаг переноса rlf PRODUCT_L,f ; ; Теперь сдвигаем 16-битное значение rlf PRODUCT_H,f ; на три разряда влево rlf PRODUCT_L,f rlf PRODUCT_H,f rlf PRODUCT_L,f rlf PRODUCT_H,f
Глава 5. Набор команд 163 ; Задача 2: Сложить Х8 и XI addwf PRODUCT_L,f ; Прибавим множимое (еще в W!) к младшему байту ; произведения btfsc STATUS,С ; ЕСЛИ нет переноса, ТО пропускаем команду incf PRODUCT_H,f ; ИНАЧЕ увеличиваем старший байт произведения ; на 1 ................... ; Следующая процедура Пример 5.6 Некий температурный регистратор считывает значение температуры каждый час, и к концу дня в памяти данных накапливается 24 значения, расположенные по адресам h’30’...h’47’. Напишите программу, просматривающую этот массив и вычисляющую среднесуточную температуру. Решение Для вычисления среднего значения необходимо просмотреть весь массив, аналогично тому, как это было показано на Рис. 5.8, добавляя каждый его элемент к 2-байтной сумме. После прохода массива эта сумма делится на 24 для вычисле- ния среднего значения: ТетРШ. 24 Исходя из сказанного, составим перечень задач: 1. Обнулить среднее. 2. Установить указатель на Temp[0] (i = 0). 3. ВЫПОЛНЯТЬ: а) Прибавить Temp[i] к общей 2-байтной сумме. б) Инкрементировать i. в) Повторять, ПОКА i < 24. 4. Разделить на 24. Этот алгоритм реализован в Программе 5.10. Сумма элементов массива на- капливается в регистрах h’48’:h’47’, которые перед входом в цикл сбрасывают- ся. Деление реализовано циклическим вычитанием числа 24 из общей суммы. Это похоже на процедуру деления на 10, реализованную в Программе 5.7, толь- ко в данном случае однобайтная константа вычитается из двухбайтного значе- ния. Число успешных вычитаний представляет собой частное, т.е. в нашем слу- чае усеченное среднее значение. Разумеется, более правильно было бы округ- лять результат до ближайшего большего целого, если остаток больше половины делителя.
164 Часть II. Программное обеспечение Программа 5.10. Вычисление среднесуточной температуры INDF equ 0 Регистр косвенной адресации STATUS equ 3 Регистр STATUS FSR equ 4 Индексный регистр ТЕМР_0 equ h'30' Начальный элемент массива SUM equ h'48' Общая сумма накапливается в регистрах h'48':h'49' AVERAGE equ h' 4A1 Среднее Z equ 2 Флаг нуля - 2-й бит регистра STATUS С equ 0 Флаг переноса - 0-й бит регистра STATUS ; Задача 1: Обнулить общую сумму и среднее AV_DAILY clrf SUM ; Обнуляем старший байт суммы clrf SUM+1 ; Обнуляем младший байт суммы ; Задача 2: Установить указатель на Тетр[0] movlw TEMP_0 ; Помещаем адрес первого элемента массива movwf FSR ; в регистр указателя ; Задача 3: Основной цикл ; Задача 3,а: Прибавить Temp[i] к 2-байтной сумме LOOP1 movf INDF,w Считываем Temp[i] addwf SUM+1,f Добавляем к младшему байту суммы btfsc STATUS,C ; ЕСЛИ нет переноса, ТО не инкрементируем старший байт incf SUM,f ИНАЧЕ учитываем перенос ; Задача 3,6: Инкрементирование i NEXT incf FSR,f ; i++ ; Задача 3,в: Повторять вычисления, пока i < 24 movf FSR, w ; Считываем значение указателя sublw TEMP_0+h'18 '; Вычитаем адрес конечного элемента массива ; (Тетр[24]) btfss STATUS,Z ; ЕСЛИ равно, то выходим из цикла goto LOOP1 ; ИНАЧЕ повторяем ; Задача 4: Разделить на 24 для получения среднего clrf AVERAGE ; Обнуляем регистр среднего ; Вычитаем 24 и накапливаем количество вычитаний до формирования бита заема LOOP2 movlw d' 24' ; Заносим константу 24 в W incf AVERAGE,f ; Запоминаем очередную операцию вычитания subwf SUM+1,f Вычитаем 24 из младшего байта суммы btfsc STATUS,C ; ЕСЛИ заем, ТО переходим к старшему байту goto LOOP2 ; ИНАЧЕ повторяем вычитание movlw 1 ; Вычитаем единицу из старшего байта subwf SUM, f btfsc STATUS,C ; ЕСЛИ заем (С==0), ТО выходим из цикла goto LOOP2 ; ИНАЧЕ повторяем вычитание decf AVERAGE, f ; Компенсируем лишнюю операцию вычитания ; Следующая процедура
Глава 5. Набор команд 165 Вопросы для самопроверки 5.1. Можете ли вы сказать, какую операцию выполняют следующие команды над байтом данных, находящимся в рабочем регистре W? addwf FILE,w subwf FILE,w 5.2. Как можно проще всего с помощью одной команды поменять значение 0-го бита любого регистра данных? Допускается затрагивать и другие биты. 5.3. Напишите программу, которая складывает два 16-битных числа, получая 17- битную сумму. Первое слагаемое размещается в регистрах памяти данных h’20’ (старший байт) и h’21’ (младший байт). Второе слагаемое размещается в регистрах h’22’ (старший байт) и h’23’ (младший байт). Сумма запомина- ется в трех регистрах: h’24’ (старший байт), h’25’ (средний байт) и h’26’ (младший байт). 5.4. Напишите программу для вычитания двухбайтного числа NUM_2, находя- щегося в регистрах h’22’:h’23’, из числа NUM_1, находящегося в регистрах h’20’:h’21’. Двухбайтная разность должна запоминаться в регистрах h’24’:h’25’. Не забудьте, что в случае возникновения заема при вычитании младших байтов, необходимо при вычитании старших байтов дополнитель- но вычесть единицу из NUM_1. Считается, что NUM_2 меньше или равно NUM_1. Как можно после завершения программы определить, что это ус- ловие не было выполнено? 5.5. Как можно доработать программу из Примера 5.3, чтобы результат ее вы- полнения представлял собой однобайтное значение в формате TENS:UNITS, сохраняемое в регистре h’21’? Такое представление числа на- зывается упакованным двоично-десятичным форматом, при котором в каж- дом байте хранится значение двух декад (по одной на каждый полубайт). Подсказка: подумайте об использовании команды swapf. 5.6. Доработайте программу из Примера 5.3 для получения трехразрядного BCD-числа, удалив ограничение на максимальный размер исходного деся- тичного числа. Результат должен сохраняться в регистрах h’21’, h’22’ и h’23’ (сотни, десятки и единицы соответственно). 5.7. В качестве фрагмента процедуры тестирования памяти данных в каждый ре- гистр диапазона h’20’...h’4F’ необходимо записать значение Ь’01010101’ (h’55’). Используя в качестве заготовки Программу 5.2, напишите эту про- цедуру. 5.8. Доработайте программу из Примера 5.1 таким образом, чтобы она могла де- крементировать 32-битное число, расположенное в регистрах h’26’...h’29’ (первым расположен старший байт). 5.9. Данные из массива, расположенного в памяти данных по адресам h’30’...h’4F’, необходимо передать побайтно в удаленный компьютер по се- ти Интернет. Чтобы приемник мог проверить корректность принимаемых данных, предлагается добавлять один байт, представляющий собой допол-
166 Часть II. Программное обеспечение нительный код 8-битной суммы всех переданных байтов данных. Если сло- жить все принятые байты данных и эту контрольную сумму, то при отсут- ствии ошибок сумма должна быть равна нулю. Напишите процедуру, просматривающую весь массив данных и помещающую эту контрольную сумму в регистр h’20’. 5.10. Взяв за основу программу регистратора из Примера 5.6, напишите програм- му, вычисляющую максимальную суточную температуру. При выходе из процедуры это значение должно находиться в регистре h’48’. 5.11. В Примере 5.6 среднее значение массива отсчетов температуры вычисляется путем суммирования всех байтов и последовательного вычитания из суммы числа 24 до тех пор, пока частное не станет меньше нуля. Доработайте про- грамму таким образом, чтобы среднее значение округлялось до ближайшего целого, т.е. при остатке, большем 12, округление производилось бы в большую сторону. 5.12. Напишите процедуру умножения однобайтного числа, находящегося в регист- ре h’23’, на 13. Двухбайтное произведение следует поместить в регистры h’23’:h’24’. Распределение памяти данных для этой процедуры выглядит следу- ющим образом: |OVERFLOW | h>2i-| MULTIPLICAND] х 13 = |PROD h| PROD L~] h-24> (регистр h’21’ служит для расширения однобайтного множимого до 16 байт). Обратите внимание, что для выполнения задачи потребуется три операции сдвига и сложения. 5.13. Одним из простейших методов шифрования данных является изменение порядка битов. Например, Ь’10111100’ -> Ь’00111101’. Напишите процеду- ру, выполняющую эту операцию над числом, находящимся в регистре h’20’. Зашифрованное значение должно остаться в рабочем регистре. Вы можете использовать регистр h’21’ в качестве временной переменной и W — в ка- честве счетчика цикла. Подсказка: используйте 8 раз команду сдвига влево и вправо. 5.14. Простейший цифровой фильтр нижних частот может быть реализован с ис- пользованием алгоритма: Array[i] = — + + , 4 2 4 где S„ — и-й отсчет 8-битного АЦП, подключенного к порту В. Напишите процедуру, реализующую такой фильтр, при условии, что три от- счета Sn.2, Sn.\ и Sn хранятся в памяти данных по адресам h’20’, h’21’ и h’22’ соответственно. Итоговое значение Array[i] должно сохраняться в регистре h’48’. 5.15. Некое 3-байтное число размещено в памяти данных следующим образом: |24 h’30’ ieli5 h’31’ sb h’32’ Напишите процедуру, подсчитываю- щую количество единичных битов в этом числе.
Глава 5. Набор команд 167 5.16. В телевизионном шоу имеется 8 участников, разделенных на две команды: А и В. У каждого участника есть кнопка, формирующая при нажатии сигнал лог. 1. Состояние всех этих кнопок можно одновременно считать с порта В микроконтроллера. Кнопки команды А подключены к младшим четырем линиям порта. Напишите процедуру, которая будет: • Определять момент ответа на вопрос (нажата любая из кнопок). • Определять ответившую команду (если команда А, то регистр h’20’ обнуляется, если команда В, то в него записывается ненулевое значение). • Определять, кто из членов команды нажал на кнопку (номер участника помещается в регистр h’21’). 5.17. Контроль четности является простейшим методом защиты цифровых данных от помех. При проверке на нечетность (odd parity) к биту данных до- бавляется такой дополнительный бит, чтобы итоговое количество единич- ных битов получилось нечетным. Напишите процедуру, которая считывает 8-битное число, находящееся в регистре h’20’, и изменяет его старший бит в соответствии с описанным принципом. Можно допустить, что перед входом в процедуру 7-й бит исходного байта всегда сброшен. Подсказка: подсчитай- те количество единичных битов как в Примере 5.2, а затем проверьте млад- ший бит полученного значения. Любая степень двойки — четная, кроме ну- левой (2° = 1). Соответственно, если 0-й бит равен 1, то число нечетное.
ГЛАВА ПОДПРОГРАММЫ И МОДУЛИ Хорошо написанное программное обеспечение должно представлять собой совокупность взаимодействующих модулей, а не одну большую программу, вы- полняющую все задачи от начала и до конца. У модульного принципа программи- рования имеется множество достоинств, без использования которых практически нельзя обойтись, когда размер кода становится больше нескольких сот строк или когда проект разрабатывается командой программистов. Что же должны представлять собой эти модули? Чтобы ответить на этот воп- рос, рассмотрим применение программных структур, которые предназначены для упрощения использования такого модульного подхода, и команды процессо- ра, связанные с этими структурами. Прочитав эту главу, вы: • Убедитесь в необходимости применения модульного принципа программи- рования. • Поймете структуру стека и его использование в механизме вызова подпро- грамм и возврата из них. • Поймете термин «вложенная подпрограмма». • Узнаете, как можно передать параметры в подпрограмму и возвратить ре- зультат в вызывающую программу. • Сможете писать подпрограммы, оказывающие минимальное влияние на свое окружение. • Сможете создавать программный стек для открытия и закрытия кадра в па- мяти данных, служащего для передачи параметров и обеспечения времен- ной рабочей области. Давайте заглянем внутрь вашего персонального компьютера. Скорее всего он будет похож на тот, фотография которого приведена на Рис. 6.1. На материнской плате типичного компьютера размещаются микропроцессор, различного рода па- мять и другие сопутствующие ИС, а также некоторое количество слотов расшире- ния. К этим слотам можно подключить плату дискового контроллера и видеокар- ту. Существуют и другие платы, например звуковая плата, модем, сетевой конт- роллер. Каждая из этих плат выполняет собственные и совершенно независимые задачи, но все они взаимодействуют через сервисы, предоставляемые главной платой — материнской.
Глава 6. Подпрограммы и модули 169 Рис. 6.1. Модульная конструкция на примере ПК Преимущества такой модульной конструкции очевидны: • Гибкость, т.е. относительная легкость модернизации или изменения конфи- гурации путем добавления или смены карт расширения. • Возможность повторного использования компонентов от предыдущей сис- темы. • ВОЗМОЖНОСТЬ покупки стандартных плат или разработки собственных спе- циализированных плат. • Легкость обслуживания. Разумеется, у такого подхода есть и некоторые недостатки. Материнская пла- та, в которую интегрированы все устройства, имеет меньший размер и, теорети- чески, меньшую стоимость, чем эквивалентная совокупность простой материн- ской платы и карт расширения. Скорее всего она даже будет более надежной, поскольку входные и выходные сигналы не проходят через разъемы. Однако, если в такой плате возникнут какие-либо неполадки, их, как правило, гораздо труднее отследить и исправить. В модульном программировании используется тот же самый принцип построе- ния «программных узлов», т.е. программ. Вот формальное определение принципа модульного программирования, которое дается в научно-технической литера- туре:
170 Часть II. Программное обеспечение Способ разработки программ, при котором программа разбивается на логи- чески завершенные единицы — модули. При этом каждый модуль может раз- рабатываться, программироваться, транслироваться и тестироваться незави- симо от других1*. Таким образом, для написания программы в соответствии с принципом мо- дульного программирования нам необходимо разбить общую задачу на несколько отдельных процедур, каждая из которых будет выполнять четко очерченную зада- чу. Такого рода модуль должен быть достаточно маленького размера, хорошо до- кументирован и легок для понимания, причем не только для написавшего его программиста. Преимущества модульного программирования аналогичны преимуществам модульного проектирования, но еще более убедительны: • Модули можно тестировать, отлаживать и поддерживать по отдельности; это обеспечивает общую надежность. • Можно повторно использовать модули из других проектов или купить их у сторонних производителей. • Легче модернизировать программу (простой заменой модулей). Решение о том, каким образом следует разбить программу на отдельные неза- висимые задачи, принимается на основе опыта. Собственно кодирование этих за- дач в виде подпрограмм ничем не отличается от написания примеров, которые мы рассматривали в предыдущих главах (см., например, Программу 5.9 на стр. 162). Для реализации указанных подпрограмм имеется несколько дополни- тельных команд, которые перечислены в Табл. 6.1. Далее в настоящей главе мы рассмотрим эти команды, а также некоторые методики, применяемые при разра- ботке программного обеспечения. Таблица 6.1. Команды поддержки подпрограмм и прерываний Операция Мнемоника Описание Вызов Вызов подпрограммы call ааа Передает управление в подпрограмму TOS <- PC, PC <- ааа Возврат из подпрограммы из прерывания return retlw retfie Возврат в вызвавшую программу РС <- TOS W <- #kk, PC <- TOS GIE <- #1, PC <- TOS Условные обозначения: W Рабочий регистр #kk 8-битная константа ааа Адрес <- Становится PC Счетчик команд #1 Единичный бит GIE Бит глобального разрешения прерываний TOS Вершина стека 11 Chamber Science and Technology Dictionary, Cambridge Chiversity Press, 1988.
Глава 6. Подпрограммы и модули 171 Вход в программный модуль можно выполнить посредством вызова этого мо- дуля из другой части программы или по некоторому аппаратному событию, внеш- нему по отношению к ЦПУ. Этим событием может быть напряжение заданного уровня на одном из выводов процессора или же сигнал от внутреннего перифе- рийного устройства, например признак переполнения в модуле таймера. В первом случае программный модуль называется подпрограммой, как и во многих языках высокого уровня, таких как FORTRAN и BASIC1). Во втором случае речь идет о подпрограмме обработки прерывания, или просто обработчике прерывания. Прин- ципы написания этих модулей, а также процедурьгвхода и выхода из них настоль- ко отличаются от обычных подпрограмм, что им посвящена отдельная глава (глава 7). Пока же мы займемся подпрограммами. Аналогом подпрограмм в аппаратуре являются платы расширения. Предполо- жим, что нам необходимо реализовать задержку длительностью 1 мс. Эта задержка может потребоваться при генерации тонального сигнала частотой 500 Гц, чтобы пилот самолета обратил внимание на предупреждающие сигналы панели управле- ния, например о низком уровне топлива или перегреве двигателей. В модульной программе эта задержка может быть реализована отдельной подпрограммой, ко- торая будет вызываться из основной программы по мере необходимости, скажем, для периодического переключения состояния вывода порта с ВЫСОКОГО на НИЗКОЕ на время длительностью 1 мс. Эта ситуация показана на Рис. 6.2. Задержка 1 мс Основной ход выполнения программы Рис. 6.2. Вызов подпрограммы Вообще говоря, операция вызова подпрограммы заключается в простой запи- си адреса первой команды подпрограммы в счетчик команд (PC), как это дела- лось и в команде goto. Так, если наша подпрограмма расположена, начиная с ад- реса h’400’, то может показаться, что команда goto h' 4 0 0 ' выполнит требуемое действие. Если предположить, что точка входа в подпрограмму обозначена мет- кой DELAY_1MS, как в Программе 6.1, мы получим команду goto DELAY_1MS. Теперь перед нами встает проблема — как вернуться обратно? Каким-то обра- зом микроконтроллер должен запомнить то место в программе, откуда он пере- шел к подпрограмме, чтобы вернуться к следующей команде вызывающей про- граммы. Эта ситуация показана на Рис. 6.2, причем вызов подпрограммы может осуществляться из любого места основной программы или даже из другой под- программы (см. Рис. 6.4). ° В других языках высокого уровня используется термин функция (Си и Паскаль) или про- цедура (Паскаль).
172 Часть II. Программное обеспечение Один из вариантов решения указанной проблемы заключается в запоминании этого адреса возврата в специальном регистре или ячейке памяти данных перед переходом к* подпрограмме. А для возврата это значение может быть загружено обратно в счетчик команд при завершении подпрограммы. Указанный способ пе- рестает работать в случае вызова одной подпрограммы из другой. В результате вторая подпрограмма перезапишет адрес, сохраненный первой подпрограммой, и возврата в основную программу никогда не произойдет. Этого можно избежать, если задействовать под стек адресов возврата более одного регистра или ячейки памяти. Структура такого стека типа LIFO (последним вошел — первым вышел) показана на Рис. 6.3, а. Аппаратный стек ЬООО SP б) Вызов (call DELAY_1MS) Рис. 6.3. Использование аппаратного стека для хранения адресов возврата В микроконтроллерах PIC с 14-битным ядром стек реализован в виде восьми 13-битных регистров, которые используются исключительно для хранения адре- сов возврата из подпрограмм1*. Структура, показанная на Рис. 6.3, называется также аппаратным стеком. Этот стек расположен вне адресного пространства па- мяти микроконтроллера, поэтому его содержимое не может быть изменено про- граммно2*. 11 Микроконтроллеры младшего семейства с 12-битным ядром имеют стек, состоящий всего из двух 12-битных регистров. А микроконтроллеры старшего семейства с 16-битным ядром имеют 31-уровневый 20-битный стек. 2) Большинство микроконтроллеров и микропроцессоров используют для реализации стека часть обычного ОЗУ данных совместно со специальным адресным регистром. Такое решение более гибкое, нежели аппаратный стек, однако требует усложнения набора команд: в нем должны присутствовать команды для манипулирования указателем стека, а также команды занесения данных в стек и извлечения их из стека.
Глава 6. Подпрограммы и модули 173 С данным стеком связан 3-битный счетчик, который указывает на следующий свободный регистр в стеке. Этот регистр указателя стека (SP) не может быть яв- ным образом изменен с помощью какой-либо команды, а автоматически инкре- ментируется при каждом исполнении команды call. Эта команда выполняется аналогично команде goto и, кроме того, перед записью заданного адреса в счет- чик команд заносит его текущее значение в стек. Это значение является адресом команды, следующей за командой call, пос- кольку PC уже был инкрементирован, и эта следующая команда была загружена в конвейер одновременно с выполнением команды c'all (см. Рис. 4.4 на стр. 92). На Рис. 6.3, б показано состояние, возникающее после вызова подпрограм- мы, обозначенной меткой DELAY_1MS. Процесс исполнения этой команды call DELAY_1MS выглядит следующим образом: 1. Содержимое счетчика команд загружается в ячейку стека, на которую ука- зывает указатель стека SP. Это сохраненное значение является адресом ко- манды, следующей за командой call. 2. Инкрементируется указатель стека. 3. Адрес назначения delay_1ms, представляющий собой адрес точки входа в подпрограмму, перезаписывает исходное содержимое PC. Это приводит к передаче управления в подпрограмму. За исключением операции записи адреса возврата (стадии 1 и 2), команда call функционирует точно также, как и команда goto. Соответственно, она тоже вы- полняется за два машинных цикла в связи с необходимостью сброса стека для уда- ления команды, расположенной вслед за командой call и уже загруженной на вершину конвейера. Схожи эти команды и тем, что абсолютный 11-битный адрес в коде команды call расширяется в 13-битный адрес памяти программ с помощью 3-го и 4-го битов регистра PCLATH, как показано на Рис. 5.17 (стр. 153). Дальние вызовы подпрограмм, расположенных в диапазоне адресов h’07FF’...h’lFFF’, тре- буются только в тех микроконтроллерах с 14-битным ядром, размер памяти про- грамм которых составляет более 2048 слов, например в PIC16F877. Командой, завершающей подпрограмму, должна быть команда return. Эта команда извлекает адрес возврата из стека и помещает его в счетчик команд, как показано на Рис. 6.3, в. Исполнение команды return происходит следующим образом: 1. Декрементируется указатель стека. 2. 13-битный адрес, адресуемый указателем стека, копируется из стека в счет- чик команд. Таким образом, независимо от того, откуда была вызвана подпрограмма, сра- зу же после ее завершения выполнение вернется к команде, следующей за коман- дой call. Команда retlw!) похожа на обычную команду return, за исключением того, что помещает заданное число в рабочий регистр. Так, чтобы после возврата из В микроконтроллерах PIC с 12-битным ядром имеется только эта команда возврата из подпрограммы.
174 Часть И. Программное обеспечение подпрограммы в W оказалось число h’FF’ (—1), скажем, для индикации ошибки, можно использовать команду retlw -1. Обе команды возврата сбрасывают кон- вейер и соответственно выполняются за два машинных цикла. Прелесть стекового механизма в том, что он поддерживает вложенные подпро- граммы. Рассмотрим ситуацию, показанную на Рис. 6.4, где основная программа вызывает подпрограмму первого уровня SR1, которая, в свою очередь, вызывает подпрограмму второго уровня SR2. Чтобы в конечном счете вернуться обратно в основную программу, последовательность действий при возврате должна в точ- ности соответствовать последовательности действий при входе. Это обеспечива- ется LIFO-структурой стека, который автоматически поддерживает произволь- ные вложенные последовательности, причем глубина вложенности ограничена только размером стека. То есть в микроконтроллерах среднего семейства число уровней вложенности равно восьми. Стек может даже обрабатывать ситуацию, когда подпрограмма вызывает саму себя! Такая подпрограмма называется рекур- сивной. Как мы увидим в главе 7, стековый механизм также используется и для об- работки прерываний. Поэтому в системах, использующих как подпрограммы, так и прерывания, глубина вложенности будет немного меньше. Этот способ на- столько удобен, что практически все микропроцессоры и микроконтроллеры осуществляют поддержку подпрограмм подобным образом. Рис. 6.4. Вложенные подпрограммы Поскольку стек совместно со своим указателем является частью «железа» микроконтроллера и не требует инициализации, программист должен учитывать только следующие моменты: • Вызов подпрограмм должен осуществляться с помощью команды call. • Точка входа в подпрограмму должна быть помечена (эта метка станет име- нем подпрограммы). • Последней командой в подпрограмме должна быть команда return или retlw, причем последняя используется для загрузки в рабочий регистр за- данной константы при возврате из подпрограммы (см. Программу 6.6).
Глава 6. Подпрограммы и модули 175 В качестве упражнения давайте напишем подпрограмму формирования за- держки длительностью 1 мс, которая указывалась на Рис. 6.2. Программное фор- мирование задержки заключается в простом «ничегонеделании» в течение требуе- мого времени. Обычно это реализуется с помощью цикла, в котором заданная константа декрементируется до нуля, как показано на Рис. 6.5. Выбирая соот- ветствующее значение константы, можно сформировать задержку требуемой дли- тельности. Понятно, что эта задержка будет зависеть от частоты тактового сигна- ла микроконтроллера. В примерах данной главы предполагается, что тактовая частота равна 4 МГц, что соответствует длительности машинного цикла 1 мкс (см. также Программу 12.8 на стр. 401). Рис. 6.5. Формирование задержки при помощи цикла Рассмотрим подпрограмму, блок-схема которой приведена на Рис. 6.5. В дан- ной подпрограмме в рабочий регистр помещается константа N, и это число инк- рементируется до достижения нулевого значения в цикле, тело которого состоит из трех команд. После завершения цикла осуществляется выход из подпрограм- мы с использованием команды return. Программа 6.1. Подпрограмма формирования 1-мс задержки ***************************************************************** ; * ФУНКЦИЯ • * : Формирует задержку длительностью 1 мс * при частоте резонатора 4 МГц * ; * вход ; * ВЫХОД : Нет * : Изменяются флаги nW * N equ d'2491 ; Параметр задержки, см. текст DELAY_1MS movlw • TTMTfTT N ; Инициализируем цикл 1- D_LOOP addlw -1 ; Декрементируем счетчик N- btfss STATUS,Z ; Проверяем: равен нулю? N+1-
176 Часть И. Программное обеспечение goto D_LOOP ; ЕСЛИ нет, ТО повторяем 2*(N-1)~ return Чтобы вычислить общее число машинных циклов, которое тратится на вы- полнение подпрограммы, и, таким образом, определить величину N, нужно оце- нить, сколько времени выполняется та или иная команда подпрограммы: 1. Команда call DELAY_1MS, используемая для перехода к подпрограмме, выполняется за 2 машинных цикла. 2. Команда movlw, предшествующая входу в цикл, выполняется за один ма- шинный цикл. 3. Команды addlw, декрементирующие содержимое рабочего регистра, за- трачивают в общей сложности N циклов (N проходов цикла). 4. Команда btfsc STATUS, Z, проверяющая состояние флага Z (не стал ли W равен нулю после предыдущего декрементирования?), также выполняется Араз. Однако при последнем проходе происходит выход из цикла за счет пропуска команды перехода, что добавляет один цикл из-за сброса кон- вейера. Таким образом, общая задержка, вносимая этой командой, состав- ляет N+ 1 циклов. 5. Поскольку выход из цикла происходит за счет пропуска команды goto, она выполняется только N- 1 раз; каждое ее выполнение занимает 2 цикла. Ее вклад в общую задержку составляет, таким образом, (N— 1) х 2. 6. Заключительная команда return выполняется за 2 цикла. Таким образом, общее число циклов равно 2 (call) + 1 (movlw) + TV(addlw) + (N + 1) (btf ss) + +2 x (A — 1) (goto) + 2 (return) Приравняв это выражение числу 1000, получим 2 + 1 + N+ (N+ 1) + 2 х (N— 1) + 2 = 1000 4 + (4х #) = 1000 4x7V= 996 #=249 Наша подпрограмма задержки в значительной степени ограничена тем, что рабочий регистр, как и все регистры данных микроконтроллеров PIC, являет- ся 8-битным, т.е. максимальное значение N равно Ь’11111111’, или десятичному 255. На самом деле значение N = 0 в нашей подпрограмме даст наибольшую за- держку! Это происходит потому, что W декрементируется до проверки на ноль, т.е. его содержимое будет изменяться следующим образом: h’00’ -> h’FF’ -> h’FE’ Ь’ОГ -> h’00’. To есть запись нуля аналогична записи числа d’256’. Та- ким образом, максимальная задержка, формируемая нашей подпрограммой, со- ставляет 4 + (4 х 256) = 1028 циклов, или 1.028 мс при частоте резонатора 4 МГц. Задержку можно немного увеличить, добавляя в тело цикла команды пор (нет операции). Каждая команда пор добавляет один машинный цикл, не влияя при этом на флаги регистра STATUS. Таким образом, вставка после команды addlw -1 четырех команд пор, как показано в Программе 6.2, даст суммарную
Глава 6. Подпрограммы и модули 177 задержку длительностью 4 + 8 х 7V машинных циклов. Для N= 249 мы теперь по- лучим 4 + 1992 = 1996 циклов, или примерно 2 мс при длительности машинного цикла 1 мкс. Подумайте, как можно использовать дополнительные команды пор для достижения точного значения в 2000 циклов? Программа 6.2. Подпрограмма формирования 2-мс задержки ; * ФУНКЦИЯ : Формирует задержку длительностью 2 мс ; * при частоте резонатора 4 МГц ; * ВХОД : Нет ; * ВЫХОД : Изменяются флаги и W equ d'249' ; Параметр задержки, см. текст DELAY_2MS movlw N ; Инициализируем цикл 1- D_LOOP addlw -1 ; Декрементируем счетчик N- nop ; Добавляем четыре дополнительных N- nop ; цикла с помощью команд N- nop ; «нет операции» N- nop / N- btfss STATUS,Z ; Проверяем: равен нулю? N+1- goto D_LOOP ; ЕСЛИ нет, ТО повторяем 2*(N-1)~ return Добавляя подобным образом команды пор, можно создавать подпрограммы задержки, работающие при различных тактовых частотах. Например, при частоте кварцевого резонатора 8 МГц подпрограмма из Программы 6.2 сформирует за- держку длительностью 1 мс. Так что вставка соответствующего количества ко- манд пор позволит программисту «подстроить» нашу подпрограмму для исполь- зования совместно с резонаторами частотой от 4 до 20 МГц (см. также Программу 12.8 на стр. 401). Подумайте, сколько потребуется команд пор для по- лучения 1-мс задержки при резонаторе на частоту 20 МГц? Этот метод не очень подходит в тех случаях, когда необходимы достаточно большие задержки. Для их реализации можно использовать дополнительный цикл, в теле которого будет выполняться наш базовый цикл, формирующий 1-мс задержку (выделено на Рис. 6.6 серым цветом). Если этот базовый цикл будет вы- полнен 100 раз, то мы получим 100-мс задержку. Код подпрограммы, реализующей 100-мс задержку, приведен в Программе 6.3. При входе в подпрограмму регистр, называемый COUNT 1, ини- циализируется значением d’100’. Затем выполняется внутренний цикл формиро- вания 1-мс задержки. Когда W становится равным нулю и внутренний цикл за- вершается, регистр COUNT 1 декрементируется при помощи команды decfsz COUNT1, f. Выход из внешнего цикла произойдет только при достиже-
178 Часть II. Программное обеспечение Рис. 6.6. Формирование задержки с использованием вложенных циклов нии нуля в счетном регистре, т.е. после выполнения 100 внутренних циклов. Пока содержимое этого счетного регистра не равно нулю, внутренний цикл выполня- ется вновь и вновь. Программа 6.3. Подпрограмма формирования 100-мс задержки ; * ФУНКЦИЯ : Формирует задержку длительностью 100 мс ; * при частоте резонатора 4 МГц ; * ВХОД : Нет ; * ВЫХОД : Изменяются флаги и W. Регистр h'30' обнуляется COUNT1 equ h'30' ; Регистр h'30' - счетчик цикла equ d'249' ; -Параметр задержки, см. текст DELAY_100MS movlw d'100' ; Инициализируем счетчик внешнего цикла movwf COUNT1 ; ; Внешний цикл --------------------------------------------------- DELAY_1MS movlw N ; Инициализируем внутренний цикл ; Внутренний цикл ------------------------------------------------ D_LOOP
Глава 6. Подпрограммы и модули 179 addlw btfss goto -1 STATUS,Z D_LOOP ; Декрементируем счетчик внутреннего цикла ; Проверяем: равен нулю? ; ЕСЛИ нет, ТО повторяем decfsz COUNT1,f ; Декрементируем счетчик внешнего цикла goto DELAY_1MS ; и повторяем до достижения им нуля return Разумеется, отсчет заданного времени в Программе 6.3 осуществляется не очень точно, поскольку мы игнорируем время, которое занимают команды внешнего цикла, такие как decf sz. Отчасти это компенсируется тем, что коли- чество машинных циклов, затрачиваемых при одном проходе внутреннего цик- ла, уменьшилось до 4 х N, давая в общей сложности 100 х 4 цикла, поскольку ко- манды goto и return теперь относятся к внешнему циклу. Реальная задержка, формируемая нашей подпрограммой, будет равна 99.905 мс, т.е. всего на 95 мкс меньше требуемого значения, что соответствует точности не хуже 0.1%. Добавив одну команду пор во внешний цикл, мы получим задержку длительностью 100.005 мс, т.е. на каждые 100 000 мкс погрешность составит 5 мкс. Максимальная задержка, формируемая этой подпрограммой, составляет 256 000 машинных циклов, что соответствует длительности 100 мс при исполь- зовании резонатора 10 МГц или 256 мс при использовании резонатора 4 МГц. Для формирования задержек большей длительности нам потребуется три вло- женных цикла, что позволит получать задержки более одной минуты (см. Пример 6.3). Наша процедура формирования 100-мс задержки является примером подпро- граммы, у которой отсутствуют входные параметры (аппаратным аналогом кото- рых являются входные сигналы карты расширения) и которая ничего не возвра- щает. Эта подпрограмма просто выполняет свою задачу, заключающуюся в фор- мировании задержки (а также изменяет регистры данных, рабочий регистр и некоторые флаги регистра STATUS). Однако большинство подпрограмм исполь- зуют данные, передаваемые им при вызове, а также предоставляют некоторые данные при возврате. В качестве простого примера доработаем Программу 6.3 таким образом, что- бы она формировала задержки длительностью К* 100 мс, где К — однобайтный параметр, «передаваемый» вызывающей программой. Системное представление такой функции приведено на Рис. 6.7. Здесь имеется один входной сигнал диапа- Внутренняя рабочая область COUNT1 h’30’ К h’31’ K(1...256)bW DELAY_K1OOMS Рис. 6.7. Системное представление подпрограммы формирования задержки длительностью К х 100 мс
180 Часть И. Программное обеспечение зона 1...256 и полностью отсутствуют выходные сигналы. Также на этом рисунке отмечено размещение всех локальных переменных, используемых внутри подпро- граммы. Последнее полезно для контроля многократного использования регист- ра данных различными подпрограммами и вызывающими функциями. Обратите внимание на двойные вертикальные границы прямоугольника — так на блок-схе- мах обычно обозначаются модули или подпрограммы. Поскольку в данном случае имеется всего один однобайтный параметр, наи- более удобным местом для размещения в вызывающей программе значения К яв- ляется рабочий регистр. Таким образом, для формирования 5-с задержки, в вызы- вающей программе можно написать: movlw d'50' ; 50 х 0.1 с даст нам 5-секундную задержку call DELAY_K100MS ; Сформируем ее! Сама подпрограмма, код которой приведен в Программе 6.4, реализует следу- ющий алгоритм: 1. ВЫПОЛНЯТЬ, ПОКА А'>0: а) Сформировать задержку 100 мс. б) Декрементировать К. 2. Конец. Программа 6.4. Подпрограмма формирования задержки длительностью К х 100 мс ; * ФУНКЦИЯ : Формирует задержку длительностью около К х 100 мс ; * при частоте резонатора 4 МГц ; * ПРИМЕР : К = 100, задержка 10 с ; * ВХОД : К в W, от 1 до 256 ; * ВЫХОД : Изменяются флаги и W. ; * Регистры h'301 и h'31'обнуляются ***************************************************************** COUNT1 equ h’30' ; Счетчик 100-мс цикла К equ h' 31' ; Временная переменная для К N equ d’249’ ; Параметр задержки DELAY_ KIOOMS movwf К ; Сохраняем К в регистре ; ФОРМИРУЕМ DELAY_100MS movlw movwf 100-мс задержку - d'lOO’ ; COUNT1 ; Инициализируем счетчик 100-мс цикла DELAY_1MS movlw N ; ; Инициализируем внутренний цикл D_LOOP addlw -1 i ; Декрементируем счетчик
Глава 6. Подпрограммы и модули 181 btfss goto STATUS,Z D_LOOP ; Проверяем: равен нулю? ; ЕСЛИ нет, ТО повторяем decfsz COUNT1,f ; Декрементируем счетчик 100-мс цикла goto DELAY_1MS ; и повторяем, пока он не будет равен 0 ; Декрементируем К ---------------------------------------------- decfsz K,f ; ПОКА К > 0 ---------------------------------------------------- goto DELAY_100MS ; Повторяем 100-мс задержку, ПОКА К > О FINI return Программа просто копирует значение параметра из W в регистр h’31’, пре- жде чем приступить к выполнению уже знакомого нам участка кода (он выделен комментариями в виде пунктирной линии), который идентичен коду Программы 6.3 и предназначен для формирования одной задержки длитель- ностью 100 мс. После формирования указанной задержки регистр, содержащий значение К, декрементируется, этот блок выполняется снова, и так до тех пор, пока К не станет равно нулю. Таким образом, код, формирующий 100-мс за- держку, будет выполнен К раз. Поскольку проверка К на ноль производится после формирования 100-мс за- держки^, то значение К= 0 будет интерпретироваться как К= 256. Таким обра- зом, диапазон задержек, формируемых подпрограммой, составит 0.1...25.6 с. Проверка перед циклом* 2) даст нам диапазон задержек 0...25.5 с. И опять же время задержки вычисляется приближенно, поскольку мы игнорируем время, которое затрачивается на выполнение команд внешних циклов. Поскольку рабочий регистр требуется для инициализации регистра COUNT 1 и организации внутреннего 1-мс цикла, мы не можем использовать его для хранения величины К во время выполнения подпрограммы. Вообще го- воря, если бы вызывающая программа знала, что регистр h’31’ используется подпрограммой для хранения значения К, то она могла бы передать это значе- ние, записав его непосредственно в данный регистр. Однако, чем меньше вызы- вающая программа знает о «внутренностях» вызываемой подпрограммы, тем лучше, поскольку подпрограмма должна как можно меньше затрагивать свое окружение. В этом отношении подпрограмма DELAY_K100MS не слишком хо- роша, поскольку использует два регистра памяти данных и изменяет содержи- мое рабочего регистра. В качестве примера рассмотрим Программу 6.5, в которой реализован тот же самый алгоритм, только блок формирования 100-мс задержки вызывается как су- ществующая подпрограмма (код которой приведен в Программе 6.3), т.е. являет- ся вложенной подпрограммой. Предположим, что для хранения параметра К был выбран регистр h’30’, который также используется подпрограммой !) В языке Си такая конструкция называется циклом DO...WHILE или циклом с постусловием. 2) В языке Си такая конструкция называется циклом WHILE или циклом с предусловием.
182 Часть II. Программное обеспечение DELAY_100MS в качестве счетчика цикла. В результате после возврата из подпро- граммы DELAY_100MS переменная К всегда была бы равна нулю, а последующее декрементирование всегда бы давало ненулевой результат. Таким образом, за- держка окажется бесконечной и система зависнет! Эта проблема решается прос- тым изменением строки «К equ h1 3 0 1» на строку «К equ h1 31'». Однако если программист, отвечающий за разработку подпрограммы DELAY_100MS, изменит ее внутреннее распределение памяти, не уведомив об этом остальных членов ко- манды, то может произойти настоящая катастрофа! Так что, даже если все под- программы прошли тестирование, определенная комбинация их вызовов может вызвать сбой. Мы еще вернемся к этой проблеме. Программа 6.5. Альтернативный вариант подпрограммы формирования задержки длительностью К х 100 мс • ***************************************************************** / ; * ФУНКЦИЯ : Формирует задержку длительностью около К х 100 мс ; * при частоте резонатора 4 МГц ; * ПРИМЕР : К = 100, задержка 10 с ; * ВХОД : К в W, от 1 до 256 ; * ВЫХОД : Изменяются флаги и W. ; * Регистры h'30' и h'31'обнуляются equ h'31' ; Временная переменная для К DELAY_K100MS movwf К ; Сохраняем К в регистре ; Задача 1: ФОРМИРУЕМ 100-мс задержку DK_LOOP call DELAY_100MS ; Задача 2: Декрементируем К decfsz K,f ; Декрементируем К ; Задача 3: ПОКА К > 0 goto DK_LOOP ; ПОВТОРЯЕМ, ПОКА К > 0 return Подпрограмма, код которой приведен в Программе 6.4, все еще имеет тип void, т.е. не возвращает никаких значений в вызвавшую программу. В качестве следующего примера мы напишем подпрограмму, результатом работы которой будет однобайтное значение. Эта подпрограмма будет использоваться совместно с цифровым индикатором. Большинство таких индикаторов работают по прин- ципу выборочного включения требуемых сегментов, как показано на Рис. 6.8. Обычно эти сегменты представляют собой светодиоды (см. Рис. 11.15 на стр. 361) или электроды элемента на жидких кристаллах.
Глава 6. Подпрограммы и модули 183 N (0...9) в W S (7-сегментный код) в W а) Системное представление gfedcba б) Знаки 7-сегментного индикатора Рис. 6.8. 7-сегментный индикатор Системное представление нашей подпрограммы приведено на Рис. 6.8, а. Входным сигналом в данном случае является 4-битный двоичный код, находя- щийся в рабочем регистре. Этот код представляет собой десять десятичных цифр в виде Ь’ОООО’...Ь’ 1001 ’. Выходным значением, также возвращаемым в W, является соответствующий 7-сегментный код, необходимый для отображения соответству- ющей цифры (см. Табл. 6.2). Причем предполагается, что включение сегмента происходит при подаче на него 1, а выключение — соответственно при подаче 0. При необходимости можно реализовать и обратную полярность. В большинстве микроконтроллеров и микропроцессоров таблицы преобразо- вания реализуются в виде набора кодов, хранящихся в памяти программ, а ре- зультатом отображающей функции f(N) является N-й байт таблицы. В микро- контроллерах PIC с 12- и 14-битным ядром гарвардская архитектура делает не- возможным использование значений памяти программ в виде данных (исключения — см. Программу 15.5 на стр. 553). Вместо этого таблицы преобра- зования реализуются в виде наборов команд retlw, каждая из которых возвра- щает однобайтную константу. Такая структура показана в Табл. 6.2. Поскольку каждая команда retlw помещает в W 8-битное значение, я сбросил неиспользу- емый 7-й бит в 0. При использовании таких таблиц извлечение k-ro элемента таблицы заклю- чается в выполнении N-й команды. При этом константа, находящаяся в коде команды, будет помещена в рабочий регистр, после чего произойдет нормаль- ный возврат в вызывающую программу. В следующем примере к = 6, поэтому выполнится 6-я команда retlw, возвращающая в W код Ь’ОН 11000’ для символа 0.
184 Часть II. Программное обеспечение Таблица 6.2. Таблица преобразования 7-сегментного кода PC Table [я] Изображение +0 retlw Ь'ООШНГ + 1 retlw b'00000110' ; a +2 retlw b'01011011’ ; В +3 retlw b'0100111Г ; a +4 retlw b'01100110' ; H +5 retlw b'01101101' ; a W=> +6 retlw b'0111110Г ; В = Table[6] +7 retlw b’0000011Г ; 3 +8 retlwb’OOllllir ; В +9 retlw Ь'ОНОШГ ; В Подпрограмма, код которой приведен в Программе 6.6, осуществляет выбор- ку элемента таблицы, прибавляя число N, передаваемое через рабочий регистр, к младшему байту счетчика команд (регистр PCL, расположенный по адресу h’02’). Поскольку PC уже указывает на 1-ю команду retlw, то после прибавления tVoh будет указывать на N-ю команду, что нам и требуется. Программа 6.6. Программная реализация дешифратора 7-сегментного индикатора ; * ФУНКЦИЯ : Возвращает N-й элемент таблицы, ; * : где N - содержимое W ; * ПРИМЕР : При W = б возвращается код Ь'01111101' ; * ВХОД : N (в диапазоне 0...9) в W ; * ВЫХОД : N-й элемент таблицы в W PCL equ 2 ; Младший байт PC - в регистре h'02 SVN_ _SEG addwf PCL, f ; Прибавим W к PCL, получая PC + N / retlw xgfedcba b'OOllllll1 ; Код для 0; Возвращается при N = 0 retlw b'00000110' ; Код для 1; Возвращается при N = 1 retlw b'01011011' ; Код для 2; Возвращается при N = 2 retlw b'01001111' ; Код для 3; Возвращается при N - 3 retlw b'OllOOHO’ ; Код для 4; Возвращается при N = 4 retlw b'01101101' ; Код для 5; Возвращается при N = 5 retlw b'01111101' ; Код для б; Возвращается при N = б
Глава 6. Подпрограммы и модули 185 retlw b'00000111' ; Код retlw b'01111111’ ; Код retlw b'01101111' ; Код для 7; Возвращается при N - 7 для 8; Возвращается при N = 8 для 9; Возвращается при N = 9 В Программе 6.6 не учитывается возможность того, что входное значение в W может быть больше h’09’. Разумеется, такого быть не должно, однако надежный код должен предусматривать все непредвиденные ситуации, даже если они оши- бочны с точки зрения программы. Это особенно справедливо в том случае, если модуль предполагается повторно использовать в других приложениях. Что же случится, если такое произойдет, и как можно усовершенствовать программу для возврата в этом случае кода ошибки, скажем —1? Кажущаяся простота метода прибавления байта, находящегося в W, к младше- му байту счетчика команд (PCL) для выбора одной из N команд возврата обман- чива. Несмотря на то что этот способ работает в большинстве случаев, когда раз- мер таблицы невелик, у неопытного программиста он может привести к краху системы в самой, казалось бы, безобидной ситуации. Проблема возникает из-за того, что изменение регистра PCL командой addwf PCL, f затрагивает только 8 младших битов 13-битного счетчика команд. Если при сложении произойдет переполнение, то в итоге счетчик команд изме- нится в обратном направлении! Например, если подпрограмма из Программы 6.6 будет расположена по адресу h’lF8’ (т.е. метка SVN_SEG будет соответствовать константе h’lF8’) и если в регистре W будет записано число h’08’, то в результате выполнения команды addwf PCL, f в счетчике команд вместо значения h’200’ окажется значение h’(l)F8’ + h’08’ = h’(l)00’. Весьма сомнительно, чтобы коман- да, расположенная по адресу h’ 100’, оказалась командой возврата из подпрограм- мы, поэтому выход из подпрограммы будет произведен некорректно и состояние стека останется несбалансированным. Точное положение подпрограммы в памя- ти программ предсказать нелегко, поскольку вряд ли программист может заранее сказать, в каком месте памяти программ будет расположена подпрограмма, т.е. какое значение будет в РС при входе в подпрограмму. Даже если он узнает значе- ние SVN_SEG, просмотрев ассемблерный листинг (см. Листинг 8.2 на стр. 247), оно может впоследствии измениться в результате корректировки других частей программы. Немного усложнив программу, ее можно сделать нечувствительной к пересечению этой 256-байтной границы (см. Программу 6.7). Хранение данных с использованием последовательности команд retlw довольно неэффективно, поскольку 14-битное слово используется для хранения 8-битного значения. В микроконтроллерах линейки PIC16F87X реализована воз- можность чтения 14-битных данных непосредственно из памяти программ, прав- да, достаточно «криво» (см. Программу 15.5 на стр. 553). Микроконтроллеры старшего семейства имеют специальные команды, такие как third, которые позволяют обращаться к отдельному байту любого 16-битного слова памяти про- грамм (см. Табл. 16.1 на стр. 585). Использование W для передачи данных в/из подпрограмм ограничено одним байтом в каждом направлении. Если необходимо передать несколько однобайт- ных значений или значение большей разрядности, то для этой цели придется за- действовать регистры данных. В качестве примера рассмотрим подпрограмму,
186 Часть II. Программное обеспечение код которой приведен в Программе 6.7. Эта подпрограмма выполняет перемно- жение двух однобайтных значений, обозначенных как multiplicand и multiplier, и возвращает 16-битное значение product_l : product_h (Рис. 6.9). Внутренняя рабочая область I MULTIPLICAND Н |h’3O’ MULTIPLIER (0...255) в регистре h'20’_ II II . ' 1 --- ------ .ии II PRODUCT_L:PRODUCT_H MULTIPLICAND (0...255) в регистре h’21’ . W* II 0...65535 в регистрах h'2E’:h’2F’ Рис. 6.9. Системное представление подпрограммы умножения однобайтных чисел Алгоритм умножения, реализованный в Программе 6.7, представляет собой обобщенный вариант алгоритма, использованный нами в предыдущих процеду- рах умножения, например в Программе 5.9, приведенной на стр. 163. В указан- ном примере значение множителя, равное 9, представлялось в виде суммы (1 + 8). Аналогичным образом, умножение на 10 можно выполнить путем однократного (х2) и троекратного (х8) сдвига исходного значения влево с последующим сложе- нием полученных частичных произведений. В общем случае множимое цикли- чески сдвигается влево и значение, полученное в результате л-го сдвига, прибав- ляется к произведению, если л-й бит множителя равен 1. Выполнив эту операцию 8 раз, получим 7 Произведение = (множимое « л) х бит л , л=0 где символы ««» обозначают операцию сдвига влево. Таким образом, в Программе 6.7 реализован следующий алгоритм: 1. Обнулить 2-байтное произведение. 2. Расширить множимое до 16 бит. 3. ВЫПОЛНЯТЬ, ПОКА множитель не станет равным нулю: а) Сдвинуть множитель вправо. б) Если есть перенос, то прибавить число, полученное в результате сдвига множимого к 2-байтному частичному произведению. в) Сдвинуть множимое вправо. 4. Вернуть 16-битное произведение. Программа 6.7. Подпрограмма умножения 8-битных чисел ; Глобальные объявления STATUS equ 3 ; Регистр STATUS С equ 0 ; Флаг переноса - бит 0 Z equ 2 ; Флаг нуля - бит 2 MULTIPLIER equ h’201 ; Множитель MULTIPLICAND equ h' 211 ; Множимое PRODUCT_L equ h'2E' ; Произведение, младший байт
Глава 6. Подпрограммы и модули 187 PRODUCT_H equ h'2F' ; Произведение, старший байт Подпрограмма MUL * ФУНКЦИЯ : Перемножает два байта и возвращает 2-байтное произведение * ПРИМЕР : MULTIPLICAND = h'10*, MULTIPLIER = h’FF’ PRODUCTS:PRODUCTS = h'OFFO' (d'16 x 255 = 4080' * ВХОД * выход : MULTIPLIER = per. h’20', MULTIPLICAND = per. h'21' : PRODUCTS = per. h'2E', PRODUCTS = per. h'2F' : MULTIPLIER, MULTIPLICAND изменяются : W, STATUS и MULTIPLICANDS = per. h* 30’ изменяются ; Локальные объявления MULTIPLICANDS equ h'30' ; Байт для расширения множимого ; Задача 1: Обнулить произведение MUL clrf PRODUCT_L Clrf PRODUCTS ; Задача 2: Расширить множимое до 16-битного числа Clrf MULTIPLICANDS ; Задача 3: ВЫПОЛНЯТЬ ; Задача За: Сдвинуть множитель на один бит вправо MUL_LOOP bcf STATUS,С ; Сбрасываем флаг переноса rrf MULTIPLIER,f Задача 36: ЕСЛИ С == 1, TO прибавить множимое к произведению btfss goto STATUS,C MUL_CONT • ; ЕСЛИ C == 1, TO складываем ; ИНАЧЕ пропускаем эту задачу movf MULTIPLICANDS ; Выполняем сложение addwf PRODUCTS, f ; Сначала младшие байты btfsc STATUS,C ; ЕСЛИ нет переноса, ТО переходим к старшим incf PRODUCTS, f ; ИНАЧЕ учитываем перенос movf MULTIPLICANDS ,w; Теперь старшие байты addwf PRODUCTS, f ; Задача Зв: Сдвинуть множимое на один бит влево (х2) *UL_CONT bcf STATUS,С ; Обнуляем бит переноса rlf MULTIPLICAND,f rlf MULTIPLICANDS, f ; ПОКА множитель не станет равным нулю movf MULTIPLIER,f ; Проверяем множитель на ноль btfss STATUS,Z goto MUL_LOOP ; ЕСЛИ не ноль, ТО повторяем return ; ИНАЧЕ выходим из подпрограммы
188 Часть II Программное обеспечение В самом начале Программы 6.7 объявлены переменные, которые передаются в/из подпрограммы. Размещение всех этих глобальных объявлений в одной части программы и использование отдельного регистра для каждой из этих переменных снижает вероятность их переопределения, но за счет довольно неэкономного ис- пользования скудных ресурсов, каковыми является память данных. Временные локальные переменные объявляются в каждой подпрограмме, поскольку их необ- ходимо будет «уничтожать» после завершения подпрограммы. Однако это все же не исключает переопределения локальных переменных при использовании вло- женных подпрограмм. Код программы в точности соответствует приведенному алгоритму. Принятие решения о том, прибавлять или нет сдвинутое влево 2-байтное множимое к час- тичному произведению, основывается на состоянии флага переноса после сдвига множителя вправо. Таким образом, реализуется условное сложение Произведение = произведение + (множимое « п) х битл. Чтобы не выполнять эту операцию 8 раз, суммирование завершается, когда множитель становится равным нулю. Отсюда следует, что время выполнения под- программы является переменной величиной, зависящей от значения множителя. Наихудшему случаю соответствует значение множителя, равное 255 (b’ 11111111 ’). При этом выполнение подпрограммы занимает 142 машинных цикла, включая и 2 машинных цикла, затрачиваемых на исполнение команды call1*. При использовании этой подпрограммы вызывающая программа копирует множимое в регистр h’20’, а множитель — в регистр h’21’. При возврате из под- программы 16-битное произведение можно прочитать из регистров h’2E’:h’2F’. Предположим, для примера, что нам необходимо перемножить байты, находя- щиеся в регистрах h’42’ и h’46’. movf h' 42 ’ ,w ; Берем 1-е число movwf h’20’ ; и копируем в MULTIPLIER movf h' 4 6',w ; Берем 2-е число movwf h’21* ; и копируем в MULTIPLICAND call MUL ; Перемножаем! ; После возврата результат - в регистрах h'2E*:h'2F* Большинство микроконтроллеров и микропроцессоров имеют программный стек, который, помимо сохранения адресов возврата из подпрограмм, позволяет программисту помещать и извлекать данные в/из памяти для передачи информа- ции между вызывающей программой и подпрограммой. Поскольку стек является динамическим объектом, увеличивающимся в соответствии с размером передава- емых и временных переменных и уменьшающимся после завершения подпро- граммы, он представляет собой очень эффективный метод распределения памя- ти. Более того, при каждом последующем вложенном вызове формируется новый стековый фрейм в дополнение к уже существующим. При этом вероятность пере- В старшем семействе имеются команды mulwf и mullw, которые выполняют перемно- жение 8-битных операндов за один машинный цикл.
Глава 6. Подпрограммы и модули 189 крытия переменных при использовании вложенных подпрограмм практически исключается. Языки высокого уровня, такие как Си (см. главу 9), обычно реализуют имен- но такую модель стека. При этом объем создаваемых и передаваемых переменных ограничивается только объемом памяти данных, которая может быть выделена под этот стек. Обратной стороной такого решения является необходимость использования дополнительных ресурсов ЦПУ для создания стека и управления им. Обычно ис- пользуется один или более отдельных регистров адреса или указателей стека, а для эффективной работы со стеком необходимы режимы адресации, облегчаю- щие доступ к переменным в стеке. И даже в этом случае результат обычно мед- леннее, а размер кода больше, чем в моделях, использующих фиксированное рас- пределение памяти. Ядро микроконтроллеров PIC среднего уровня в явном виде программный стек не поддерживает^. Однако такую структуру можно эмулировать, используя косвенную адресацию на базе регистров FSR и INDF (см. стр. 123). Поскольку регистр указателя стека, как таковой, отсутствует, в приведенном ниже коде для этих целей мы задействовали регистр данных h’40’, назвав его PSP. Программист должен также зарезервировать участок памяти данных для хранения различных стековых фреймов. Мы решили, что вершина стека (Top Of Stack — TOS) будет располагаться по адресу h’50’. Если не использовать регист- ры из диапазона адресов h’50’...h’70’, то для нашего стека будет доступно 48 байт. В микроконтроллерах PIC16F62X этот блок памяти отображен на все бан- ки. Поскольку адреса возврата из подпрограмм сохраняются в аппаратном сте- ке, наш программный стек может целиком использоваться для передачи пара- метров и хранения локальных переменных подпрограмм. Инициализация стека осуществляется записью константы h’50’, названной TOS, в регистр указателя PSP. В качестве примера разберем вариант подпрограммы умножения, ориентиро- ванный на использование стека (Программа 6.7). Структура программного стека для данного случая показана на Рис. 6.10. В соответствии с рисунком, для вызова этой подпрограммы необходимо выполнить следующие действия: 1. Поместить множимое и множитель в стековый фрейм и вызвать подпро- грамму. 2. Обнулить следующий байт фрейма, который будет использоваться в качест- ве дополнительного байта множимого. 3. Обнулить два следующих байта для инициализации будущего 2-байтного произведения. В приведенном ниже (на следующей странице) фрагменте кода показана реа- лизация 1-го пункта: Старшее семейство в какой-то степени приблизилось к этой модели, поскольку в нем имеется более глубокий 31-уровневый стек, а также указатель стека, доступный программно.
190 и Часть II Программное обеспечение а) Передать содержимое регистра PSP в FSR. В результате FSR будет указы- вать на вершину нового стекового фрейма. Если это подпрограмма пер- вого уровня (т.е. не вложенная в другую подпрограмму), то в этом регист- ре в нашем случае будет значение h’50’. б) Скопировать множимое из памяти (предполагаем, что, как и в предыду- щем примере, оно находится в регистре h’46’) в W, а затем во фрейм, ис- пользуя в качестве указателя регистр FSR. Операция занесения множи- мого в стек завершается декрементированием регистра FSR. в) Аналогичным образом поместить в стек множитель. г) Вызвать подпрограмму. Рис. 6.10. Стековый фрейм при работе с подпрограммой MUL_s Код подпрограммы MUL_S приведен в Программе 6.8. В этой программе реа- лизованы этапы 2...4, показанные на Рис. 6.10. Вначале обнуляется переменная multiplicand_h, используемая для расширения множимого, после чего обну- ляются следующие две ячейки фрейма для инициализации произведения. Затем в регистр PSP заносится адрес следующей свободной ячейки, расположенной пос- ле фрейма. Таким образом, если из подпрограммы будет вызвана другая подпро- грамма, то для следующего уровня вложенности будет задействован свой фрейм, вершина которого будет располагаться сразу же после старого фрейма. В нашем случае эти две команды можно опустить, поскольку вложенные вызовы отсут- ствуют, правда, при этом потребуется изменить код, реализующий в программе этап Зв. Именно так и сделано в Примере 6.6 (см. далее). ; Глобальные объявления PSP equ h' 40' ; Указатель псевдостека TOS equ h' 50' ; Исходная вершина стека INDF equ 0 ; Регистр косвенной адресации FSR equ 04 ; Индексный регистр STATUS equ 3 ; Регистр STATUS С equ 0 ; Флаг переноса - бит 0 Z equ 2 ; Флаг нуля - бит 2 MULTIPLIER equ h’ 46 ’ ; Множитель MULTIPLICAND equ h' 42 ’ ; Множимое
Глава 6. Подпрограммы и модули 191 MAIN Сначала инициализируем вершину стека, movlw movwf TOS PSP ; адрес которой равен h'40' Несколько позже, когда необходимо вызвать подпрограмму (а) movf PSP,w ; Заносим текущий адрес вершины movwf FSR ; в индексный регистр (б) movf MULTIPLICAND^ ; Помещаем множимое в стек, movwf INDF ; копируя содержимое регистра decf FSR,f ; и декрементируя FSR (в) movf MULTIPLIER,W ; Помещаем множитель в стек, movwf INDF ; копируя содержимое регистра decf FSR,f ; и декрементируя FSR (r) call MUL_S ; Вызываем подпрограмму стека Основная часть подпрограммы, т.е. реализация 3-го пункта, аналогична Программе 6.7, за исключением того, что для доступа к различным элементам стека необходимо манипулировать содержимым регистра FSR. Единственное место, где использование FSR может быть не очевидным, — реализация этапа Зв. Поскольку переход к этому блоку может осуществляться различным образом, в зависимости от того, прибавлялось ли множимое к произведению или нет, то со- держимое регистра FSR при входе в этот блок не определено. Однако его можно повторно инициализировать значением из регистра PSP, который на данном эта- пе выполнения программы указывает на регистр, расположенный сразу же после фрейма. Если мы увеличим это значение PSP на 5, то получим адрес параметра MULTIPLICAND. И в завершение подпрограмма «очищает» стек, записывая в регистр PSP его предыдущее значение. В данном случае для этого оно увеличивается на 5, а в об- щем случае — прибавляется размер фрейма п. Для реализации Программы 6.8 нам потребовалось 45 команд в отличие от 20 команд Программы 6.7. В наихудшем случае на ее выполнение будет затрачено 274 машинных цикла, что также является гораздо худшим результатом по сравне- нию со 142 циклами Программы 6.7. Таким образом, с какой стороны ни посмот- реть, такая стековая модель явно хуже, если не принимать во внимание возмож- ность повторного использования кода и его надежность. Использование стековой модели будет более оправданно в случае написания больших программ при огра- ниченных ресурсах памяти. Однако программы, выполняющиеся на микроконт- роллерах PIC младшего и среднего уровней, как правило, не очень сложны. Более того, маленький объем памяти программ может наложить дополнительные огра-
192 Часть II. Программное обеспечение ничения на использование такого довольно экстравагантного решения. Если вре- мя выполнения программы критично, то дополнительные накладные расходы на поддержание стека не стоят полученных результатов. Программа 6.8. Подпрограмма умножения 1-байтных чисел, использующая стековую модель / ; * ФУНКЦИЯ : Перемножает два байта и возвращает * ; * 2-байтное произведение * ; * ПРИМЕР : MULTIPLICAND = h’10’, MULTIPLIER = h'FF' * ; * PRODUCT_H:PRODUCT_L = h'OFFO’ (d’16 x 255 = 4080') * ; * ВХОД : MULTIPLICAND = PSP, MULTIPLIER = PSP-1 * ; * : FSR указывает на следующий после MULTIPLIER регистр * ; * ВЫХОД : PRODUCT_H = PSP-3, PRODUCT_L = PSP-4 * ; * ВЫХОД : Изменяются W и STATUS * . *ii*iiii*ii*i*i*i************************************************ / ; При вызове FSR —> MULTIPLICANDS (старший байт множимого) ; Задачи 1 и 2 : Расширить множимое и обнулить произведение MUL_S clrf INDF decf FSR,f ; FSR > PRODUCTS clrf INDF decf FSR,f ; FSR —> PRODUCTS clrf INDF decf FSR,w ; Теперь устанавливаем указатель стека PSP movwf PSP ; на нижнюю границу фрейма ; Задача 3: ВЫПОЛНЯТЬ ; Задача За: Сдвинуть множитель на один бит вправо incf FSR,f incf FSR, f incf FSR,f ; FSR ---> MULTIPLIER MUL_LOOP bcf STATUS,С ; Сбрасываем флаг переноса rrf INDF,f ; Задача 36: ЕСЛИ С == 1, TO прибавить множимое к произведению btfss STATUS,С ; ЕСЛИ С -= 1, ТО выполняем сложение goto MUL_CONT ; ИНАЧЕ пропускаем эту операцию incf FSR,f ; JSR ---> MULTIPLICAND movf INDF,w ; Выполняем сложение decf FSR, f decf FSR,f decf FSR,f ; FSR ---> PRODUCTS addwf INDF,f ; Сначала младшие байты decf FSR,f ; FSR ---> PRODUCTS btfsc STATUS,С ; ЕСЛИ нет переноса, переходим к старшим байтам incf INDF,f ; ИНАЧЕ учитываем перенос incf FSR,f
Глава 6. Подпрограммы и модули 193 incf FSR,f ; FSR ---> MULTIPLICAND_H movf INDF,w ; Теперь старшие байты decf FSR,f decf FSR,f ; FSR > PRODUCTS addwf INDF,f ; Задача Зв: Сдвинуть множимое на один бит влево MUL_CONT movf PSP,w ; Устанавливаем FSR на нижнюю границу фрейма addlw 5 movwf FSR ; FSR ---> MULTIPLICAND bcf STATUS,C ; Сбрасываем бит переноса rlf INDF,f decf FSR,f decf FSR,f ; FSR ---> MULTIPLICANDS rlf INDF,f ; ПОКА множитель не равен нулю incf FSR,f ; FSR ---> MULTIPLIER movf INDF,f ; Проверяем множитель на равенство нулю btfss STATUS,Z goto MUL_LOOP ; ЕСЛИ не ноль, ТО повторяем вычисления ; Задача 4: : Очистка стека movlw 5 ; Устанавливаем FSR на верхнюю границу фрейма addwf PSP,f ; прибавляя 5 к указателю PSP return ; Выходим из подпрограммы Примеры Пример 6.1 Напишите подпрограмму, формирующую фиксированную задержку длитель- ностью 208 мкс. Частота тактового сигнала процессора составляет 4 МГц. Решение Для коротких временных интервалов, сравнимых с заданным, наилучшим ре- шением будет код, приведенный в Программе 6.1. При частоте 4 МГц длительность машинного цикла равна 1 мкс, соответ- ственно нам потребуется 208 машинных циклов. Воспользовавшись формулой со стр. 176, получим 4 + 4 х N = 208 циклов 4 х N = 204 цикла N =51 Чему будет равно Nв случае использования 20-МГц резонатора?
194 Часть II. Программное обеспечение Программа 6.9. Подпрограмма формирования задержки длительностью 208 мкс N equ d'51' ; Параметр задержки DELAY_208 movlw N ; Берем параметр задержки, 1- D_LOOP addlw -1 ; ; Декрементируем счетчик, N- btfss STATUS,Z ; ; Пропускаем, ЕСЛИ ноль, N+1- goto D_LOOP ; ; ИНАЧЕ повторяем, 2*(N-1)- return ; Выходим, 2- Пример 6.2 В Программе 6.3 мы познакомились с подпрограммой, формирующей за- держку номинальной длительностью 100 мс. Причем длительность этой задержки была подсчитана довольно приблизительно, так как мы просто умножили вели- чину задержки, формируемую основным блоком (1 мс), на число проходов внеш- него цикла, равного 100. Вычислите точную задержку при использовании 4-МГц резонатора и определите величину ошибки (в процентах). Решение Просматривая приведенный ниже текст подпрограммы, мы можем вычислить общее количество машинных циклов, основываясь на времени выполнения каж- дой команды и ее положении относительно тела цикла. ; Два цикла на переход к подпрограмме 2- DELAY_100MS movlw movwf ; Внешний ци: DELAY_1MS movlw ; Внутренний D_LOOP addlw btfss goto d'100' COUNT1 f ! 1- 1~ 249 ; Эта команда выполняется 100 раз, 100*1- -1 STATUS,Z D_LOOP ; 249 раз по 100 ; плюс один раз при пропуске ; 248 раз по 2- и по 100 раз 249*100- 250*100- 248*2*100- / decfsz goto COUNT1,f DELAY_1MS ; 10 плюс один при пропуске ; 99 раз 100+1- 2*99- / return 1 2- В результате мы получим 99 905 циклов. Это на 95 меньше требуемого. Таким 95 образом, ошибка составляет______—___х 100 = -0 95% • 100000
Глава 6. Подпрограммы и модули 195 Одна команда пор, размещенная перед командой decf sz COUNT1, f, даст нам дополнительные 100 циклов. В результате длительность задержки будет равна 100.05 мкс, что соответствует ошибке +0.005%. Пример 6.3 Для полноты картины необходимо написать подпрограмму, формирующую минутную задержку. Решение Шестидесятисекундную задержку можно реализовать как 240 х 255 мс. Наше решение, код которого приведен в Программе 6.10, будет иметь точно такую же структуру, как и подпрограмма формирования задержки длительностью Кх 100 мс (Программа 6.4). Максимальное значение К равно 255, что дает нам всего 25.5 с, однако мы можем увеличить время выполнения прохода среднего цикла до 250 мс, получая в результате дискретность задания задержки, равную 0.25 с. Задав теперь количество повторений внешнего цикла, равное 240, мы по- лучим требуемые 60 с задержки. Программа 6.10. Подпрограмма формирования задержки длительностью 1 мин COUNT1 equ h'30' ; Счетчики в регистре h'301 COUNT2 equ h’31’ ; и h’31' ; * ФУНКЦИЯ : Формирует задержку длительностью - 1 мин * ; * при частоте резонатора 4 МГц * ; * ВХОД : Нет * ; * ВЫХОД : W и STATUS изменяются * ; * Регистры h'34:35:36' обнуляются * DELAY_1_MIN movlw d'240' movwf COUNT2 ; Инициализируем внешний цикл 1- 1- DELAY_250MS movlw movwf d'2501 COUNT1 ; Внутренний цикл (1 мс) DELAY—IMS ; Инициализируем'средний цикл ; для задержки 250 мс 1~ 1- movlw d'249' 250*240- D—LOOP addlw btfss goto -1 STATUS,Z D—LOOP 249*250*240- (249+1)*250*240- (2* (249-1)*250*240)- decfsz COUNT1,f (250+1)*240-
196 Часть II. Программное обеспечение goto DELAY_1MS 2* (250-1)*240- decfsz COUNT2,f 240+1- goto DELAY_250MS ; 2* (240-1)- return / 2- Из комментариев, приведенных в листинге, можно понять логику формиро- вания задержки, которая составляет 59.821088 с, обеспечивая точность около 0.3%. И опять же подпрограмму можно дополнить командами пор. Каждая ко- манда пор, помещенная после первого decfsz, добавляет 250 х 240 = 60 000 цик- лов, так что, вставив три таких команды, мы вместо недостачи получим перебор на 1088 циклов, что даст нам точность не хуже +0.02%. Пример 6.4 Напишите подпрограмму для преобразования однобайтного значения, пере- даваемого через рабочий регистр, в BCD-число, разряды которого будут нахо- диться в регистрах HUNDRED (h’30’), TENS (h’315) и UNITS (h’325). Решение Мы уже встречались с процедурой преобразования двоичных чисел в двоич- но-десятичные в Примере 5.3, приведенном на стр. 159. Однако эта подпрограм- ма могла преобразовывать только числа из диапазона 0...99, т.е. имеющие два раз- ряда. Тем не менее мы можем воспользоваться примененной в той подпрограмме методикой, вычитая сначала сотни и подсчитывая их число. После этой операции остаток будет меньше 100, и остальная часть подпрограммы будет в точности со- ответствовать исходной. Полный текст новой подпрограммы, приведенный в Программе 6.11, реализует следующий алгоритм: 1. Разделить на 100; остаток — число сотен. 2. Разделить частное на 10; остаток — число десятков. 3. Частное — число единиц. Программа 6.11. Подпрограмма преобразования двоичного числа в 3-разрядное BCD-число . ************************************************************ ; * ФУНКЦИЯ : Преобразовывает число из W в три BCD-разряда * ; * ПРИМЕР : Вход = h’FF' (d'255'), HUNDREDS = h'02' * ; * TENS = h'02', UNITS = h'051 * ; * ВХОД : W - исходное число * ; * ВЫХОД : HUNDREDS = число сотен, TENS - число десятков * ; * UNITS = число единиц. W - также число единиц * / * * * * ; Сначала делим на 100 BIN_2_BCD clrf HUNDREDS ; Обнуляем счетчик сотен LOOP100 incf HUNDREDS,f ; Запоминаем очередное вычитание
Глава 6. Подпрограммы и модули 197 addlw -d'100' ; btfsc STATUS,С ; goto LOOPIOO ; Вычитаем сотню ЕСЛИ заем (С -- 0), ТО выходим из цикла ИНАЧЕ вычитаем дальше ; Затем decf HUNDREDS,f ; addlw d'10 01 ; делим на 10 clrf TENS Корректируем лишнее вычитание, прибавляя 100 к остатку Обнуляем счетчик десятков LOOP10 incf TENS,f addlw -d'10' ; Запоминаем очередное вычитание Вычитаем десять btfsc STATUS,C goto LOOPIO ; ЕСЛИ заем (С == 0), ТО выходим из цикла ИНАЧЕ вычитаем дальше ; Берем остаток - число единиц decf TENS,f ; Корректируем лишнее вычитание, addlw d'10' ; прибавляя 10 к остатку movwf UNITS ; return ; Получая в результате число единиц, выходим из подпрограммы Пример 6.5 Напишите подпрограмму для вычисления квадратного корня из 16-битного целого числа, размещенного в регистрах h’26’:h’27’. Результат должен возвра- щаться в рабочем регистре. Решение Самый примитивный способ решения этой задачи будет заключаться в про- стом переборе всех целых чисел к, вычислении к2 посредством умножения и про- верке, что результат не превышает заданного значения. Эквивалентный, но не- много более замысловатый способ основывается на вычитании последователь- ности чисел 1, 3, 5, 7, 9, 11,... из исходного числа до возникновения заема. Число вычитаний и будет искомым ближайшим значением квадратного корня. Эту пос- ледовательность можно записать в следующем виде: к /=0 Таким образом, возможная структура нашей подпрограммы будет следующей: 1. Сбросить счетчик цикла. 2. Задать переменную I (магическое число) равной 1. 3. ВЫПОЛНЯТЬ бесконечно: а) Вычесть I из Number. б) ЕСЛИ результат меньше нуля, ТО выйти из цикла.
198 Часть II. Программное обеспечение в) ИНАЧЕ инкрементировать счетчик цикла. г) Прибавить 2 к /. 4. Вернуть значение счетчика цикла, равное v Number. На Рис. 6.11, а показано вычисление значения Тб5 описанным способом. Блок-схема этого алгоритма показана на Рис. 6.11, б, а код подпрограммы приве- ден в Программе 6.12. Максимальное значение счетчика цикла равно h’FF’, пос- кольку >/б5535 ® 255 . Поэтому под данную локальную переменную резервирует- ся всего один регистр h’35’. Аналогично, максимально возможное значение маги- ческого числа I равно 511 (h’lFF’), поэтому под эту локальную переменную резервируется уже два регистра h’36’:h’37’. Отсюда следует, что на этапе За выпол- няется двухбайтное вычитание. При возникновении заема из младшего байта, к копии старшего байта I (1_Н) перед вычитанием добавляется 1. Поскольку 1_Н никогда не будет больше h’01 ’, указанная операция никогда не вызовет перепол- нения. Если заем генерируется при вычитании из этого старшего байта, ЭТО озна- чает, что результат стал меньше нуля и цикл завершается. В противном случае COUNT инкрементируется, а / умножается на 2. На самом деле значение счетчи- ка цикла всегда равно III — 1, так что переменная COUNT не нужна. Вместо это- го при возврате из подпрограммы можно просто сдвинуть 16-битное значение / на один разряд вправо. При этом произойдет деление на 2, а вычитание единицы производится посредством отбрасывания бита, выдвинутого в флаг переноса (/всегда нечетное, поэтому младший бит этого числа всегда равен 1). Попробуйте реализовать этот альтернативный алгоритм. 65 count = О - 1 64 count = 1 - 3 61 count = 2 - 5 56 count = 3 - 7 49 count = 4 - 9 40 count = 5 -11 29 count = 6 -13 16 count = 7 -15 1 count = 8 -17 -16 Прекращаем вычисление, корень квадратный равен 8 а) Пример б) Блок-схема алгоритма Рис. 6.11. Нахождение корня квадратного из целого числа
Глава 6. Подпрограммы и модули и 199 Программа 6.12. Подпрограмма вычисления квадратного корня ; Глобальные объявления STATUS equ 3 ; Регистр STATUS С equ 0 ; Флаг переноса - бит 0 NUM_H equ h' 26 ’ ; Исходное значение, старший байт NUM_L equ h' 27 ’ ; Исходное значение, младший байт . ******************************************************** ** ; * ФУНКЦИЯ : Вычисляет корень квадратный из 16-битного целого * ; * ПРИМЕР : Число = h'FFFF' (65,535), Корень = h'FF' (d'255’)* ; * ВХОД : Число в регистрах h’26':h'27‘ * ; * ВЫХОД : Корень в W. Регистры h' 2 6 ' : 1т ’ 2 7 ' и * ; * h'35':h'36':h'37* изменяются * / ; Локальные объявления COUNT equ h13 5' ; Счетчик цикла I_H equ h'36' ; Магическое число, старший байт I_L equ h'37' ; Магическое число, младший байт ; Задача 1: Обнулить счетчик цикла SQR_ROOT clrf COUNT ; Задача 2: Инициализация магического числа единицей clrf I_L clrf I_H incf I_L,f ; Задача 3: ВЫПОЛНЯТЬ ; Задача За: Number - I SQR_LOOP movf I_L,w ; Берем младший байт магического числа subwf NUM_L,f ; Вычитаем из младшего байта исходного числа movf I_H,w ; Берем старший байт магического числа btfss STATUS,C ; ЕСЛИ не было заема (С==1), ТО пропускаем addlw 1 ; Учитываем заем subwf NUM.H,f ; Вычитаем старшие байты ; Задача 36: ЕСЛИ потеря значимости, ТО выйти btfss STATUS,С ; ЕСЛИ нет заема (С==1), ТО продолжаем goto SQR_END ; ИНАЧЕ вычисление завершено ; Задача Зв: ИНАЧЕ инкрементировать счетчик цикла incf COUNT,f ; Задача Зг: Увеличить магическое число на 2 movf I_L,w addlw 2 btfsc STATUS,С ; Если нет переноса, ТО пропускаем incf I_H,f ; ИНАЧЕ корректируем старший байт
200 Часть II. Программное обеспечение movwf I_L goto SQR_LOOP ; Задача 4: Вернуть счетчик цикла в качестве значения корня SQR_END movf COUNT,w ; Копируем результат в W return Пример 6.6 Напишите программу умножения содержимого регистра h’46’ на десять (х2 + х8). Для хранения данных и передачи параметров воспользуйтесь программ- ным стеком. Решение Объявления глобальных переменных для подпрограммы, код которой приве- ден в Программе 6.13, и вызывающей процедуры следующие: PSP equ h'40' ; Указатель псевдостека TOS equ h' 50' ; Исходная вершина стека INDF equ 0 ; Регистр косвенной адресации FSR equ 04 ; Индексный регистр XCAND equ h’46’ ; Множимое STATUS equ 3 ; Регистр STATUS С equ 0 ; Флаг переноса - бит 0 ; Основная процедура инициализирует указатель стека PSP MAIN movlw TOS ; Устанавливаем PSP movwf PSP ; на исходную вершину стека ; .......................... и т.д. ; Подготовка к вызову подпрограммы ХЮ movf PSP,w ; Устанавливаем FSR на текущую movwf FSR ; позицию в стеке ; Теперь заносим множимое в стек movf XCAND,w ; Копируем множимое в W, movwf INDF ; а затем помещаем его в стек call ХЮ ; Теперь вызываем подпрограмму ; При возврате из подпрограммы PSP возвращается в исходную позицию ; а произведение располагается по адресу PSP+3:PSP+2 NEXT_MAIN ......... ; Продолжение основной программы В Программе 6.13 сначала производится сдвиг множимого влево на один бит (умножение на два), а затем еще на два бита (умножение на 8). Два получившихся 16-битных числа затем складываются, образуя искомое произведение. Точно так же, как и в Программе 6.8, производится манипулирование регистром FSR для доступа к соответствующим данным. Двухбайтное произведение может быть счи-
Глава 6. Подпрограммы и модули 201 тано вызывающей программой по смещению относительно указателя псевдосте- ка. В отличие от Программы 6.8, в данном случае PSP не затрагивается ни когда в стек заносится множимое, ни в самой подпрограмме. Так сделано потому, что эта подпрограмма не вызывает других подпрограмм, т.е. не требуется формирования нового стекового фрейма. Программа 6.13. Использование программного стека для передачи параметров и организации рабочей области ФУНКЦИЯ : ПРИМЕР : ВХОД : ВЫХОД : Умножает 1-байтное число на 10 h'64 х 0А = ЗЕ8' (d'100 х 10 = 1000') Множимое помещается в стек по адресу PSP Произведение по адресу PSP-3:PSP-2 в формате (старший байт:младший байт) Х10 movf movwf decf clrf PSP,w FSR FSR,f INDF ; Устанавливаем FSR на ; текущую позицию стека ; Указываем на байт расширения XCAND ; Обнуляем его ; Теперь умножим на 2, сдвинув XCAND на один бит влево bcf STATUS,C ; Сбрасываем бит переноса incf FSR,f ; Указываем на младший байт XCAND rlf INDF,f ; Сдвигаем влево младший байт decf FSR, f ; Указываем на старший байт rlf INDF,f ; Сдвигаем влево старший байт ; Прибавляем к 16-битному частичному произведению incf FSR,f ; Указываем на младший байт XCANDx2 movf INDF,w ; Считываем его decf FSR,f ; Указываем на младший байт произведения decf FSR,f movwf INDF ; Копируем туда младший байт XCANDx2 incf FSR,f ; Указываем на старший байт XCANDx2 movf INDF,w ; Считываем его decf FSR,f ; Указываем на старший байт произведения decf FSR,f movwf INDF ; Копируем туда старший байт XCANDx2 ; Теперь надо < сдвинуть еще на два бита, чтобы умножить на 8 incf FSR,f ; Указываем на младший байт XCANDx2 incf FSR,f incf FSR,f bcf STATUS,C ; Сбрасываем бит переноса rlf INDF,f ; Сдвигаем влево младший байт decf FSR, f ; Указываем на старший байт XCANDx2 rlf INDF,f ; Сдвигаем влево старший байт incf FSR,f
202 Часть II. Программное обеспечение rlf INDF,f ; Сдвигаем влево младший байт decf FSR,f ; Указываем на старший байт rlf INDF,f ; Сдвигаем влево старший байт Прибавляем к 16-битному частичному произведению incf FSR,f ; Указываем на младший байт XCANDx8 movf INDF,w ; Считываем его decf FSR,f ; Указываем на младший байт произведения decf FSR,f addwf INDF,f ; Прибавляем младший байт incf FSR,f ; Указываем на старший байт XCANDx8 btfsc STATUS,c ; ЕСЛИ перенос, ТО инкрементируем старший байт incf INDF,f movf INDF,w ; ИНАЧЕ просто считываем его decf FSR, f ; Указываем на старший байт произведения decf FSR, f addwf INDF,f ; Прибавляем старший байт return Пример 6.7 Для гарантии того, что в подпрограмме дешифратора 7-сегментного кода (Программа 6.6) не возникнет переполнения регистра PCL при прибавлении к нему смещения, программист воспользовался директивой org (ORiGin; см. стр. 244), которая указывает ассемблеру разместить подпрограмму по некото- рому абсолютному адресу (h’700’ в Программе 6.14). При тестировании подпро- граммы посредством вызова ее из другой части программы по адресу, меньшему h’700’, система «падает» и ее поведение становится непредсказуемым. Что было сделано неправильно? Решение Система сходит с ума из-за того, что при сбросе регистр PCLATH обнуляется. При вызове подпрограммы командой call h' 7 00 ' счетчик команд становится равным h’700’, однако содержимое регистра PCLATH не меняется. Позже, при выполнении команды addwf PCL,f, все содержимое 13-битного счетчика ко- манд обновляется, причем младшие восемь битов берутся из регистра PCL, а старшие пять — из регистра PCLATH, как показано на Рис. 4.8 (стр. 103). В ре- зультате вместо перехода к одной из команд retlw происходит переход к произ- вольному адресу памяти программ в диапазоне h’0000’...h’00FF’! Это произойдет даже при отсутствии переполнения во время добавления к регистру PCL смеще- ния из рабочего регистра. Программа 6.14. Доработанный программный дешифратор 7-сегментного индикатора org h’700' ; Подпрограмма начинается с адреса h'700' SVN_SEG addwf PCL,f ; Прибавим W к PCL, получая PC + N
Глава 6. Подпрограммы и модули 203 retlw b'OOllllll' ; Код для 0; Возвращается при N = 0 retlw b’00000110' ; Код для 1; Возвращается при N = 1 retlw b'OlOllOll' ; Код для 2; Возвращается при N = 2 retlw b’OlOOllll’ ; Код для 3; Возвращается при N - 3 retlw b'01100110’ ; Код для 4; Возвращается при N = 4 retlw b'OllOllOl’ ; Код для 5; Возвращается при N = 5 retlw b'OlllllOl' ; Код для 6; Возвращается при N = 6 retlw b’00000111' ; Код для 7; Возвращается при N = 7 retlw b’01111111’ ; Код для 8; Возвращается при N = 8 retlw b'01101111' ; Код для 9; Возвращается при N = 9 Этой ошибки можно избежать, записав в регистр PCLATH число h’07’ (7-я страница) перед вызовом подпрограммы. В результате содержимое счетчика ко- манд вместо h’OONN’ изменится на h’07NN’, что и требовалось. movlw h ’ 07' ; Подготавливаем PCL movwf PCLATH ; к работе с 7-й страницей памяти программ movf NN,w ; Заносим десятичное число NN в W call SVN_SEG ; Вызываем подпрограмму Но даже при наличии такой заплатки размер таблицы ограничен 255 элемента- ми (это максимальное значение, добавление которого к регистру PCL не вызовет переполнения, приводящего к неверному функционированию программы). В любом случае в программировании считается дурным тоном задавать абсолют- ное положение секций кода программы, поскольку при этом можно перезаписать код, автоматически размещаемый самим ассемблером. В случае больших программ попытки определения и отслеживания положений несметного числа модулей чре- ваты ошибками. В качестве одного из вариантов решения проблемы больших таб- лиц, одновременно гарантирующего правильную установку регистра PCLATH, можно назвать вычисление смещения, которое необходимо прибавить к адресу на- чала подпрограммы, непосредственно в программе и помещение старшего байта суммы в регистр PCLATH. Разумеется, микроконтроллеры PIC поддерживают только 8-битную арифметику, поэтому нам придется отдельно вычислить значения старшего и младшего байтов адреса начала подпрограммы. К счастью, в ассемблере Microchip имеется две директивы, high и low, которые можно использовать для разбиения 13-битного адреса на 8-битные составляющие. movlw high SVN_SEG , ; Берем старший байт адреса начала таблицы, movwf PCLATH ; который является номером страницы памяти программ movlw low SVN_SEG+1 ; Берем младший байт адреса начала таблицы addwf NN,w ; Прибавляем к нему смещение из регистра NN btfsc STATUS,C ; Есть перенос? incf PCLATH,f ; Если да, значит, перешли границу страницы movf NN,w ; Берем смещение call SVN.SEG ; Вызываем подпрограмму В приведенном выше фрагменте кода используется адрес начала таблицы (SVN_SEG+1), поскольку именно это значение будет в счетчике команд после вы- борки команды addwf PC, f. Разумеется, в этом случае можно обойтись без инст- рукции org h1 7 0 0 1, использованной нами в Программе 6.14.
204 Часть II. Программное обеспечение Данный фрагмент кода можно легко доработать для вычисления 2-байтного смещения, выполняя при обновлении PCL 2-байтное сложение. Как и прежде, в подпрограмму будет передаваться только младший байт смещения. Используя эту методику, можно реализовать таблицы любого размера, располагающиеся в лю- бом месте памяти программ (эти параметры ограничены только размером памяти программ). Более подробно обо всем этом можно прочитать в фирменном руко- водстве по применению AN556 «Implementing a Table Read». Вопросы для самопроверки 6.1. Один студент написал подпрограмму формирования 1-мс задержки следую- щим образом: DELAY_1MS movlw d'249' ; Инициализируем счетчик цикла D_LOOP addlw -1 ; Декрементируем счетчик btfss STATUS,Z ; Проверяем: равен нулю? goto D_LOOP ; ЕСЛИ нет, ТО повторяем return Что получится в результате? 6.2. Напишите подпрограмму, которая будет считывать значение порта В каж- дый час. Вы можете воспользоваться модифицированным для 60-минутного интервала вариантом Программы 6.10. Подумайте, почему данное решение является не слишком хорошим примером использования ресурсов микро- контроллера. 6.3. Напишите подпрограмму по следующим исходным данным: • Разделить 2-байтное число на 1-байтное. • Делимое передается в подпрограмму в регистрах h’2E’:h’2F’ (DIVIDEND_H:DIVIDEND_L). • Делитель передается в подпрограмму в рабочем регистре. • Частное от деления возвращается в регистрах h’29’:h’2A’ (QUOTIENT_H:QUOTIENT_L). • Остаток от деления возвращается в рабочем регистре. Реализуйте деление методом вычитания до возникновения потери значи- мости (underflow). Похожую задачу выполняет Программа 5.10 на стр. 164. Прокомментируйте проблему, возникающую при выполнении деления ука- занным способом. 6.4. Доработайте Программу 6.6 таким образом, чтобы она могла отображать символы ‘A’...’F’. Необходимо предусмотреть обработку как заглавных, так и строчных букв. Также ваша программа должна быть надежной. 6.5. Программа 6.15 предназначена для формирования 30-секундной задержки. Подсчитайте время выполнения подпрограммы и, таким образом, реальную длительность формируемой задержки.
Глава 6. Подпрограммы и модули 205 Программа 6.15. Подпрограмма формирования задержки длительностью 30 с ; * ФУНКЦИЯ : ; * ВХОД : ; * ВЫХОД : / *********** Формирует з при частоте Нет W и STATUS Регистры h' ************************************** адержку длительностью 1 мин * резонатора 4 МГц * изменяются * 34:35:36' обнуляются * ; Локальные объявления COUNTO equ h ’ 34' ; 3-байтный счетчик в регистрах h'34 ' COUNT1 equ h • 35 ’ ; и h' 3 5 ' COUNT2 equ h' 36 ' ; и h ' 3 6 ' Н equ d' 153' ; Параметр задержки DELAY_30S movlw H ; Заносим 153 в старший байт счетчика movwf COUNT2 / clrf COUNT1 / clrf COUNTO / D_LOOP decfsz COUNTO,f ; Декрементируем младший байт goto D_LOOP ; до нуля decfsz COUNT1,f ; Затем декрементируем средний байт goto D_LOOP ; до нуля и повторяем decfsz COUNT2,f ; Затем декрементируем старший байт goto return D_LOOP ; до нуля и повторяем / 6.6. Результат считывания состояния механического переключателя может быть неверным, поскольку при замыкании контактов происходит их «дребезг» в течение нескольких миллисекунд, проявляющийся в формировании после- довательности нулей и единиц. Аналогично ведут себя и некоторые элект- ронные устройства, например фототранзистор при попадании в зону с по- ниженной освещенностью и выходе из нее. Хотя данная проблема может быть решена аппаратно, более экономичным решением будет использова- ние программных методов. Напишите подпрограмму, которая будет возвращать в 7-м бите рабочего ре- гистра установившееся состояние переключателя, подключенного к выводу RB7 порта В. Состояние будет считаться установившимся, если при 5000 (h’ 1388’) последовательных операциях считывания возвращается одно и то же значение. Состояние остальных битов рабочего регистра при возврате из подпрограммы не имеет значения. 6.7. К порту В подключен аналого-цифровой преобразователь. Напишите про- грамму, аналогичную программе из предыдущего вопроса, только на этот раз решение о стабильности считываемого значения будет приниматься в результате 1000 одинаковых считываний, а в рабочем регистре будет возвра- щаться код, соответствующий аналоговому напряжению.
206 Часть II. Программное обеспечение 6.8. В подпрограмме, являющейся ответом на предыдущий вопрос, возвращает- ся стабильное значение зашумленного оцифрованного сигнала после счи- тывания 1000 одинаковых значений. Используя эту подпрограмму, напиши- те основную процедуру, которая будет определять, насколько текущий результат отличается от предыдущего, и записывать этот признак в регистр h’40’. В позиции каждого отличающегося бита должна быть записана 1. Но- мер самого правого изменившегося бита следует поместить в регистр h’41’. 6.9. Подпрограмма, написанная в качестве ответа на вопрос 6.7, не вернет ника- кого значения, если в аналоговом сигнале будет присутствовать относитель- но высокочастотный шум, поскольку в результате «дрожания» сигнала появ- ление 1000 одинаковых отсчетов будет весьма маловероятным событием. Для снижения шума можно воспользоваться усреднением множества отсче- тов. Если шум является случайным, то считывание п значений приведет к снижению шума в 4п раз. Напишите подпрограмму, которая будет 256 раз считывать значение порта В и возвращать 8-битное среднее значение В ра- бочем регистре W (при этом отношение сигнал/шум увеличится в 16 раз). 6.10. Схема, приведенная на Рис. 6.12, представляет собой 7-битный генератор псев- дослучайных чисел, построенный на базе сдвигового регистра с элементом Ис- ключающее ИЛИ в цепи обратной связи. Напишите подпрограмму, последова- тельно выдающую в порт В 127 таких двоичных случайных чисел. Подпрограмма должна инициализироваться любым ненулевым значением. Например, если на- чальное значение будет равно 01, то первые 32 числа будут следующими: 02 04 08 10 20 41 83 06 ОС 18 30 61 С2 85 ОА 14 28 51 АЗ 47 8F 1Е ЗС 79 F2 Е4 С8 91 22 45 8В 16 ... Последовательность повторится после формирования 127 значений. Что произойдет, если в качестве начального значения будет взят ноль? 6.11. Преобразование значения температуры из шкалы Цельсия в шкалу Фарен- гейта осуществляется по формуле 9 Г = Сх- + 32- 5 Напишите подпрограмму, в которую передается значение температуры по шкале Цельсия (от 0 до 100°С) и которая возвращает соответствующее зна- чение температуры по шкале Фаренгейта.
ГЛАВА ОБРАБОТКА ПРЕРЫВАНИЙ Подпрограммы, которые мы с вами обсуждали в главе 6, можно назвать «пред- сказуемыми» событиями, поскольку они вызываются в соответствии с логикой программы. Ситуации же реального времени, наступающие в результате взаимо- действия процессора с внешними физическими воздействиями, далеко не так про- сты. Очень часто вне ядра ЦПУ происходят различные события, требующие немед- ленной реакции процессора. Подавляющее большинство контроллеров способны реагировать на самые разнообразные события такого рода, нарушающие их нор- мальное функционирование. Что же касается микроконтроллеров, то запросы на обслуживание могут исходить как от встроенных периферийных устройств, напри- мер при переполнении таймера, так и извне от источника, совершенно не связан- ного с микроконтроллером. По меньшей мере, по сигналу внешнего сброса (одно из внешних событий) микроконтроллер должен перейти к первой команде про- граммы. Аналогичным образом, в качестве реакции на внешний запрос на обслу- живание, или прерывание, микроконтроллер должен перейти к специальной под- программе, называемой подпрограммой обработки прерывания. Несмотря на то что представители семейства среднего уровня имеют различ- ные наборы встроенных периферийных устройств, таких как аналоговые уст- ройства, последовательные порты, таймеры, все они реагируют на прерывания одинаковым образом. В этой главе мы рассмотрим основные источники таких за- просов реального времени, общие для всех моделей микроконтроллеров, и, час- тично, внешние прерывания. Также мы коснемся вопроса взаимодействия встро- енных периферийных устройств с системой прерываний (прерывания, соответ- ствующие конкретным устройствам, будут рассмотрены в соответствующих главах третьей части книги). Прочитав эту главу, вы: • Осознаете необходимость обработки прерываний. • Разберетесь в концепции таблицы векторов как отправной точки при реак- ции на события сброса и прерываний. • Ознакомитесь с последовательностью событий, происходящих при обнару- жении PIC-микроконтроллером запроса на прерывание. • Поймете причины возникновения задержки. • Разберетесь в назначении бита глобального разрешения прерываний.
208 Часть II. Программное обеспечение • Поймете назначение попарно объединенных битов локальной маски пре- рывания и локального флага прерываний, соответствующих различным ис- точникам прерываний. • Сможете писать простые обработчики прерываний, реализующие: — Переключение контекста. — Определение источника прерывания. — Возврат по команде г е t f i е. Простой пример, демонстрирующий необходимость быстрой реакции на со- бытие, показан на Рис. 7.1. В данном случае нам необходимо измерить время между точками R сигнала электрокардиограммы (ЭКГ), который, по определе- нию, является внешним событием реального времени. Временное разрешение должно быть не менее 0.1 мс, а наибольший интервал между максимальными зна- чениями сигнала скорее всего не превысит 1.5 с. Для измерения этого интервала с заданными параметрами можно было бы использовать независимый 16-битный счетчик, работающий на частоте 10 кГц. Как мы увидим в главе 13, все микрокон- троллеры среднего уровня имеют 8-битный счетчик, счетный регистр которого расположен по адресу Ь’ОГ. На Рис. 7.1 показано, как можно с помощью регистра h’3F’ организовать 16-битный счетчик. Этой конфигурации соответствует Программа 13.2, приведенная на стр. 462. Пока же предположим, что состояние счетчика можно считать из двух указанных регистров в любой момент времени. Если состояние счетчика, соответствующее последней точке R, было сохранено в двух временных регистрах, то, вычитая состояние счетчика, соответствующее те- кущей точке R, получим требуемую длительность. Подсистема обработки события ________L Рис. 7.1. Обнаружение и обработка внешнего события Следующей задачей является обнаружение максимального уровня сигнала, поскольку сердце пациента, по определению, не синхронизировано с микроконт- роллером! Один из возможных способов определения точки R заключается в не- прерывном считывании этого сигнала и обработки его по алгоритму выделения максимума. В данном случае для обеспечения заданного временного разрешения применение метода последовательного опроса (polling) потребует проведения из-
Глава 7. Обработка прерываний 209 мерений 10 000 раз в секунду. Учитывая, что частота сердцебиения обычно со- ставляет около 60 ударов в минуту, 99.99% времени будет затрачено впустую. Бо- лее того, это означает, что большая часть вычислительной мощности процессора будет затрачена на обнаружение одного события из 10 000. В качестве альтернативы можно воспользоваться внешним устройством, зада- чей которого будет обнаружение максимального уровня сигнала. Это устройство может быть как полностью аналоговым, так и построенным на базе микроконт- роллера с аналого-цифровым преобразователем (см. Пример 14.2 на стр. 529). Независимо от реализации, этот блок будет посылать сигнал основному процес- сору при обнаружении точки R. Этот сигнал используется для прерывания работы микроконтроллера, который должен приостановить выполнение текущей задачи и изменить состояние счетчика не позже чем через 100 мкс. В таких ситуациях, когда внешние процессы происходят сами по себе и нико- им образом не синхронизированы с работой процессора, необходимо найти спо- соб, посредством которого определенные события смогли бы прерывать выполне- ние программы и направлять процессор на выполнение требуемых действий. Оп- рос внешних событий применим при достаточно редком их возникновении и/или малом количестве отслеживаемых параметров и небольшом объеме вычислений. Вероятность пропуска какого-либо важного события можно уменьшить, увеличи- вая частоту опроса, однако в конце концов наступает такой момент, когда процес- сор уже не сможет ничего делать, кроме как читать данные от периферийных уст- ройств. Вопрос недостаточности ресурсов особенно остро встает при необходи- мости опроса большого количества сигналов за короткий промежуток времени. Обратной стороной мониторинга сигналов в реальном времени с использова- нием прерываний является усложнение аппаратных средств и аппаратно-про- граммного интерфейса. Если вы совсем запутались, вообразите себе телефонную сеть. Можно построить такую сеть, при которой абонент снимал бы трубку, ска- жем, каждые 5 мин и спрашивал: «Эй! Есть тут кто-нибудь?» Не говоря уже о не- удобствах (накладные расходы), связанных с выполнением этой операции1), зво- нящему может просто надоесть ждать, и он повесит трубку. Разумеется, можно снизить вероятность такого события, увеличивая частоту опроса до, скажем, од- ного раза в минуту. Однако в этом случае вам придется проводить все свое время у телефона, принимая при этом всего несколько звонков в день. То есть 99% ваших усилий будет затрачено впустую. Данный пример достаточно нелеп, и на практике используется метод, осно- ванный на прерываниях, когда вы поднимаете трубку, только услышав сигнал вы- зова. Такое решение гораздо эффективнее, но эта эффективность достигается за счет усложнения аппаратуры для телефонной компании. Кроме того, существует и другая проблема, заключающаяся в том, что вы (сравните с процессором) не име- ете никакого понятия о том, когда зазвонит телефон. Ну а он, по закону подлости, зазвонит в самое неподходящее время. Так что вам (если только вы не железный) придется оторваться от текущих дел. К примеру, если вы в этот момент решали ка- 11 Проще всего, конечно, просто не обращать внимание на этот телефон!
210 Часть II. Программное обеспечение кую-либо задачу, вам придется потратить некоторое время на сохранение проме- жуточных результатов, с тем чтобы после разговора вы могли вернуться к ней. Микроконтроллеры могут реагировать на запросы прерывания от самых раз- ных источников, находящихся вне микроконтроллера, либо от различных портов и периферийных устройств, имеющихся в составе конкретного представителя се- мейства. Например, микроконтроллеры PIC16F874/7 поддерживают до 13 раз- личных прерываний от этих периферийных устройств, а также одно внешнее пре- рывание, подаваемое через вывод INT (вывод 6 на Рис. 4.1, стр. 89). Вход внешне- го прерывания использует ту же ножку микроконтроллера, к которой подключена 0-я линия порта В, т.е. вывод RB0. Программист может в индивидуальном поряд- ке запретить или разрешить прерывания от этих источников, а также полностью запретить работу всей системы прерываний. Поскольку процесс реакции на пре- рывание практически не зависит от его источника, в этой главе мы главным обра- зом будем вести речь именно об этом внешнем прерывании. С учетом случайного характера внешних событий отклик процессора на за- прос прерывания будет выглядеть следующим образом: 1. Завершение исполнения текущей команды. 2. Автоматическое сохранение, по меньшей мере, состояния счетчика команд (PC) — это необходимо для возврата из обработчика прерывания. Некото- рые процессоры (такие как микроконтроллеры PIC старшего семейства) могут также автоматически сохранять содержимое регистра STATUS и дру- гих внутренних регистров. 3. Переход к соответствующей процедуре обработки прерывания. 4. Выполнение требуемых действий. 5. Восстановление состояния процессора и возврат к тому месту основной программы, в котором произошло прерывание. Короче говоря, возникновение сигнала прерывания приводит к тому, что мик- роконтроллер прекращает выполнение текущей задачи, сохраняет свое состояние в прерываемой фоновой программе в стеке и переходит к выполнению специальной подпрограммы, называемой процедурой обработки прерывания (Interrupt Service Routine — ISR). Эта высокоприоритетная процедура представляет собой обычную подпрограмму, которая выполняется при наступлении заданного события. Конкретные детали отклика на запрос прерывания в некоторой степени отлича- ются от процессора к процессору. В микроконтроллерах семейства среднего уровня реакция на прерывание осуществляется следующим образом (см. Рис. 7.2)1); 1. При выполнении каждой команды процессор проверяет наличие запроса прерывания от разрешенного источника. Независимо от наличия такого запроса он дожидается завершения исполнения команды, т.е. выполнение команды не прерывается даже в случае команд, выполняющихся за 2 ма- шинных цикла. В микроконтроллерах младшего семейства вообще отсутствует поддержка прерываний, зато в старшем семействе реализована 2-приоритетная система прерываний, которая практи- чески идентична системе прерываний среднего семейства.
Глава 7. Обработка прерываний 211 2. Если такой запрос отсутствует, микроконтроллер просто переходит к вы- полнению следующей команды, и описанный процесс повторяется. 3. При наличии запроса следующие три машинных цикла затрачиваются на передачу управления процедуре обработки прерывания. Из этих цик- лов первый является холостым1), а во время двух оставшихся производит- ся сброс конвейера. Эта задержка длительностью от 3 до 4 машинных цик- лов между подачей внешнего сигнала на вывод INT и моментом выполнения 1-й команды обработчика называется задержкой обработки прерывания (interrupt latency). Большую точность получить нельзя в связи со случайной природой сигнала внешнего прерывания, который может появиться на любом этапе машинного цикла. 4. Во время этой задержки микроконтроллеры PIC выполняют следующие операции: а) Запрещается вся система прерываний, что гарантирует блокирование всех прерываний на время обработки текущего. Это осуществляется сбросом 7-го бита регистра управления прерываниями INTCON, кото- рый на Рис. 7.3 помечен как флаг общего разрешения прерываний (GIE). Указанный бит является маской прерывания, поскольку он ис- пользуется для маскирования активности прерываний. При сбросе мик- роконтроллера бит GIE всегда сбрасывается, так что по умолчанию пре- рывания запрещены. б) Состояние 13-битного счетчика команд заносится в стек точно так же, как и при выполнении команды call (см. Рис. 6.3 на стр. 172). Как и в случае подпрограмм, эта операция позволяет процессору после выхода из процедуры обработки прерывания вернуться к выполнению прерванной фоновой программы. Поскольку в PIC-микроконтроллерах среднего уровня реализован 8-уровневый аппаратный стек, из обработчика пре- рывания можно вызывать до семи вложенных друг в друга подпрограмм. в) Первая команда обработчика прерывания всегда размещается по адресу h’OO4’ памяти программ. Так что завершающий этап рассматриваемой последовательности состоит в занесении в PC указанного адреса, называ- емого вектором прерывания. Разумеется, если код обработчика прерыва- ния находится в каком-либо другом месте памяти программ, то первой ко- мандой будет команда goto, как показано в Программе 7.1. 5. Как и все подпрограммы, процедура обработки прерывания должна завер- шаться командой возврата. Однако при прерывании необходимо не только извлечь из стека сохраненное значение PC, но и установить бит GIE регистра INTCON для разрешения последующих прерываний. Напоминаю, что ука- занный бит был сброшен на этапе 4а при переходе к обработчику прерыва- ния. Для этого используется команда возврата из прерывания ret fie (см. Табл. 6.1 на стр. 170). Таким образом, после возврата в фоновую программу можно будет обработать все отложенные или будущие прерывания. ') Он же может быть последним циклом 2-цикловой команды, например команды пропуска.
212 Часть II. Программное обеспечение К следующей команде .....................retf ie Рис. 7.2. Отклик на запрос прерывания Однако отличие процедуры обработки прерывания от подпрограмм заключа- ется не только в использовании команды retf ie. Одни отличия связаны с логи- кой работы системы прерывания, а другие — с псевдослучайным характером пре- рываний. Сначала поговорим о первой ситуации, для чего рассмотрим логичес- кие узлы, относящиеся к системе прерываний. Хотя большинство представителей микроконтроллеров PIC среднего уровня поддерживают прерывания от различных источников, три из этих источников во всех без исключения устройствах связаны с регистром INTCON, как показано на Рис. 7.3. Этими основными источниками являются: • Внешний сигнал, подаваемый на вывод INT. Это внешнее прерывание мо- жет генерироваться либо по нарастающему _/~, либо по спадающему ”\_ фронту входного сигнала, что определяется состоянием бита INTEDG регистра OPTION_REG. Этот сигнал проходит через вентиль Исключаю- щее ИЛИ, выполняющий в данном случае роль программируемого инвер- тора, как было описано на стр. 28. • Изменение состояния любого из четырех старших выводов порта В (регистр h’06’) с момента последнего чтения из этого порта. • Переполнение счетного регистра таймера/счетчика TMR0 (регистр h’01 ’) с h’FF’ до h’00’. Формат регистра INTCON микроконтроллера PIC16F84 приведен на Рис. 7.3. С каждым из четырех источников прерываний связан соответствующий бит флага прерывания. Например, при появлении на 6-м выводе микроконтроллера отрица- тельного перепада сигнала будет установлен флаг INTF (бит 1). Это про- изойдет независимо от того, разрешена работа системы прерываний или нет.
Глава 7. Обработка прерываний 213 Сигнал прерывания к ЦПУ Рис. 7.3. Логика системы прерываний микроконтроллера PIC16F84 В тех случаях, когда разрешены прерывания более чем от одного источника, со- стояние этих флагов можно контролировать программно для определения конк- ретного источника; см. листинг на стр. 220. Вы можете опрашивать эти биты даже при выключенной системе прерываний. Несмотря на то что флаг устанавливается внешним (по отношению к ЦПУ) событием, сбрасываться он должен програм- мно. При обработке прерывания жизненно важно сбрасывать этот флаг в процеду- ре обработки прерывания перед возвратом из обработчика, т.е. следует выполнить команду be f INTCON, INTF. Каждый флаг прерывания имеет соответствующий бит разрешения прерыва- ния. Так, флагу INTF соответствует бит INTE. Это позволяет программисту про- извольным образом маскировать источники прерывания в любом сочетании.
214 Часть IL Программное обеспечение На самом деле каждый из флагов прерывания логически умножается (AND) на соответствующий бит маски прерывания. На Рис. 7.3 2-й элемент И показывает этот механизм для внешнего прерывания. Например, если требуется разрешить прерывания как от вывода INT, так и от Таймера 0, то придется установить биты 7, 5 и 4, т.е. выполнить команды movlw b'10110000' movwf INTCON Биты разрешения прерывания можно изменять точно так же, как и обычные биты регистров. При сбросе микроконтроллера все маскирующие биты обнуля- ются, тем самым запрещая прерывания от соответствующих источников. Что же касается именно PIC16F84, то в этой модели прерывание может генери- роваться также при завершении цикла записи во внутреннюю EEPROM-память данных. В регистре INTCON для флага EEIF не хватило места, поэтому этот флаг разместили в 4-м бите регистра управления EEPROM — регистре EECON1. Чуть позже в этой главе (Рис. 7.5) мы с вами увидим, каким образом осуществляется под- держка дополнительных прерываний в более развитых представителях семейства. Поскольку имеется четыре источника прерываний, выходы всех элементов И, на вход каждого из которых подаются сигналы флага и маски, необходимо объ- единить по ИЛИ для получения итогового сигнала запроса прерывания, который, собственно, и инициирует реакцию ЦПУ на прерывание. Из Рис. 7.3 видно, что сигнал с выхода этого элемента ИЛИ управляется (с помощью еще одного эле- мента И) битом общего разрешения прерываний GIE, который расположен в 7-м бите регистра INTCON. Однако для «пробуждения» процессора, если он находит- ся в режиме пониженного потребления, используется именно исходный сигнал с выхода элемента ИЛИ. Как мы увидим далее в главе 10, при останове программы и переводе микроконтроллера в режим пониженного потребления можно значи- тельно снизить ток, потребляемый устройством (до значения не более 1 мкА). На- пример, при контроле температуры на дне озера в течение года с интервалом в 1 час с помощью регистратора данных с батарейным питанием собственно вы- числения занимают ничтожную долю от общего времени работы. Перевод микро- контроллера в режим Power Down после считывания и сохранения каждого отсче- та уменьшает емкость батареи, необходимую для обеспечения работы прибора в течение столь длительного времени. Перевод в этот режим осуществляется ко- мандой sleep. Для «пробуждения» микроконтроллера используется прерывание от внешнего источника, в данном случае от экономичного генератора с периодом сигнала, равным одному часу. Как говорилось выше, процесс вывода микроконт- роллера из «спящего» режима не зависит от состояния бита GIE. Чтобы проиллюстрировать программные аспекты обработки прерываний, рас- смотрим задачу подсчета числа посетителей в небольшом магазине. Одним из воз- можных решений было бы использование пары маломощный лазер — фотоэле- мент, расположенных по бокам от входной двери. При пересечении луча покупате- лем на управляющий микроконтроллер поступит сигнал запроса в виде прямоугольного импульса _/—как показано на Рис. 7.4. В этот момент микро- контроллер может быть занят выполнением своей основной задачи, например ор-
Глава 7. Обработка прерываний 215 Q Лазер Покупатель Рис. 7.4. Контроль числа посетителей магазина PIC ганизацией обмена данными между торговым терминалом и основным компью- тером склада. Предположим, что при каждом проходе покупателя в магазин инкрементиру- ется регистр, который мы назовем EVENT. Разумеется, покупатели будут еще и выходить из магазина, однако, если проход достаточно узкий, мы можем просто разделить общее количество проходов на два, чтобы получить реальное число по- сетителей. Это накладывает ограничения на максимальное число посетителей, однако указанное ограничение можно легко преодолеть, задействовав дополни- тельные регистры. Предположим также, что в начале рабочего дня наша система сбрасывается. Таким образом, максимальное число посетителей, регистрируемое нашей системой, составит 126. Программа 7.1. Подсчет числа посетителей STATUS equ 3 ; Регистр STATUS INTCON equ h'OB' ; Регистр управления прерываниями INTF equ 1 ; Флаг внешнего прерывания - бит 1 INTE equ 4 ; Бит маски внешнего прерывания - бит 4 GIE equ 7 ; Бит глобального разрешения прерываний - бит 7 _status equ h'4F' ; Ячейка для сохранения регистра STATUS EVENT equ h'20' ; Счетчик общего числа посетителей ; Вектор сброса org ООО ; При сбросе в РС заносится число h'ООО' goto MAIN ; Переходим к началу фоновой программы Вектор прерывания — org 004 ; При прерывании PIC переходит к адресу h'0041 goto PERS_COUNT ; Переходим к обработчику прерывания ; Фоновая программа начинается с инициализации --------- MAIN bsf INTCON,INTE ; Разрешаем внешнее прерывание
216 Часть II. Программное обеспечение bsf INTCON,GIE ; Разрешаем работу системы прерываний clrf EVENT ; Обнуляем счетчик посетителей ; Бесконечный цикл основной программы ------------------------------- M_LOOP ; Выполняем то ; Выполняем это ; Выполняем еще что-нибудь goto M_LOOP • •k-k-k-k-k-k-k-k-k-k-k-k'k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-kir-k-k-k-k-k-k-k-k-k-k'k-k-k-k-k-k-k-k-k-k-k-k-k-k t • * ФУНКЦИЯ: Обработчик инкрементирует счетчик EVENT * . ************************************************************* PERS_COUNT movwf _work ; Сохраняем W в памяти данных swapf STATUS,w ; Считываем регистр STATUS, не меняя флагов movwf -Status ; Сохраняем его в памяти данных bc£ INTCON,INTF ; Сбрасываем флаг прерывания incf EVENT,f ; Регистрируем событие swapf _status,w ; Восстанавливаем исходное состояние movwf STATUS ; регистра STATUS swapf _work,f ; Теперь восстанавливаем исходное состояние W, swapf _work,w ; не воздействуя на флаги retfie ; и возвращаемся в фоновую программу В самом начале Программы 7.1 указано два вектора. По адресу h’000’, кото- рый является вектором сброса, размещена команда goto MAIN. Аналогично, по адресу h’004’ размещена команда перехода к обработчику прерывания goto pers_COUNT. Размещение команд по конкретным адресам осуществляется с помощью директивы org (см. стр. 244). Таким образом, при реагировании мик- роконтроллера на прерывание мы получаем следующую последовательность: прерывание -> h’004’ -> PERS_COUNT. В основной программе просто устанавливаются биты маски INTE и GIE для разрешения внешних прерываний, а также обнуляется счетчик посетителей EVENT. Расположенный далее бесконечный цикл представляет собой фоновые задачи процессора. С точки зрения программы прерывания генерируются произвольно, и, соот- ветственно, если они не запрещены, то они могут возникнуть при выполнении любого участка фонового цикла, в том числе и во время выполнения какой-либо подпрограммы. Процедура обработки прерывания использует внутренние регист- ры процессора точно так же, как и все остальные программные модули, что может привести к конфликтам при доступе к ресурсам микроконтроллера. Например, может случиться так, что прерывание возникнет в тот момент, когда фоновая про- грамма приступит к проверке содержимого какого-либо регистра. Выполнение следующей за операцией проверки команды пропуска может зависеть, скажем, от состояния флага нуля в регистре STATUS. Однако в процедуре обработки преры-
Глава 7. Обработка прерываний 217 вания состояние флага Z скорее всего изменится, в результате чего при возврате в фоновую программу будет выполнен пропуск команды, т.е. управление будет пе- редано совсем не туда, куда нужно. Любое изменение флага Z может привести к неправильной передаче управления в фоновой программе. Отследить возникно- вение такой ситуации практически невозможно, поскольку эффект от такого пре- рывания проявляется от случая к случаю, так как возникновение ошибки зависит от прерывания, возникающего в неправильное время и в неправильном месте (иногда это может происходить всего раз в неделю), и поэтому ее трудно воспро- извести. Повреждение содержимого регистра STATUS в таких случаях может иметь го- раздо более серьезные последствия, например, в цикле опроса (см. листинг на стр. 219). В данном случае при входе в обработчик прерывания был установлен бит RP0 регистра STATUS (см. Рис. 4.7 на стр. 97) для обращения к регистрам, расположенным в 1-м банке памяти. Эта операция была необходима, поскольку регистры управления EEPROM имеются только в данном банке, тогда как РСН STATUS и INTCON отражены на оба банка. При выходе из подпрограммы бит RP0 сбрасывается для возврата к 0-му банку памяти, т.е. предполагается, что в мо- мент возникновения прерывания фоновая программа работала с 0-м банком. Очевидно, что если прерывание возникнет во время работы с банком 1, то даль- нейшая программа будет работать неправильно. Отсюда становится ясно, что независимо от сложности обработчика прерыва- ния нам необходимо сохранить, по меньшей мере, содержимое рабочего регистра и регистра STATUS. Для работы в качестве временного хранилища резервируют несколько регистров данных, которые больше ни для чего не используются. Обычно названия этих переменных начинаются с символа подчеркивания, пока- зывающего, что эти регистры используются для системных нужд и не должны ис- пользоваться прикладной программой. В соответствии с данным соглашением в Программе 7.1 регистр h’4E’ обозначен как _work, а регистр h’4F’ — Как _status. Учитывая вышесказанное, любую процедуру обработки прерывания можно условно разделить на три отдельные части. Сохранение контекста Сначала копия рабочего регистра сохраняется в регистре _work. Напоминаю, что команда movwf не влияет на биты регистра STATUS. Затем регистр STATUS сохраняется в регистре данных _status (h’4F’). Казалось бы, что может быть проще — скопировать регистр STATUS в W, а затем сохранить рабочий регистр в регистре _status. Однако команда movf изменяет состояние флага Z. Поэтому для копирования данных в рабочий регистр мы вместо команды movf воспользу- емся командой swapf. Команда swapf не изменяет состояние флагов, однако переставляет местами старший и младший полубайты. Но мы можем восстано- вить их нормальное положение при восстановлении регистра. Процесс сохранения и восстановления состояния внутренних регистров (это внутреннее состояние называется контекстом программы) при входе и выходе из
218 и Часть II. Программное обеспечение обработчика прерывания называется переключением контекста. Разумеется, не- обходимо следить за тем, чтобы эти ячейки не использовались в обработчике для других целей. Основной код Сбрасывается флаг прерывания INTF регистра INTCON, чтобы избежать повторного перехода (т.е. по сути дела бесконечного возврата) к обработке пре- рывания после возврата в основную программу. При наличии нескольких источ- ников прерываний на этом этапе обработчика производится проверка соответ- ствующих флагов прерываний (см. стр. 219), каждый из которых впоследствии может сбрасываться в начале соответствующей процедуры обработки. В рабочей секции основного кода просто инкрементируется содержимое регист- ра EVENT. Естественно, это основная задача процедуры обработки прерывания. Восстановление контекста При завершении обработчика первым делом в рабочий регистр с помощью команды swapf заносится исходное состояние регистра STATUS, которое затем копируется оттуда в STATUS. Исходное значение W восстанавливается из временной переменной _work с использованием двух последовательных команд swapf. При этом состояние ре- гистра STATUS не изменяется. И наконец, выполняется команда возврата ret fie, которая также не влияет на состояние флагов регистра STATUS. Хотя в нашем примере мы сохраняли только рабочий регистр и регистр STATUS^, в других случаях может потребоваться сохранение и других РСН. Так, в Примере 7.3 сохраняется регистр FSR, поскольку он используется как в основной программе, так и в обработчике прерывания. Одним словом, если в процедуре обработки прерывания изменяются какие-либо РСН, то при выходе из обработчика должно быть восстанов- лено их исходное состояние. В любом случае первым необходимо сохранить содержи- мое рабочего регистра, поскольку он будет использоваться в качестве промежуточно- го хранилища при сохранении остальных регистров. Соответственно восстанавли- ваться рабочий регистр должен в самую последнюю очередь. По мере возможности регистры, в которых сохраняется контекст программы, следует выбирать таким образом, чтобы они не зависели от банка памяти, исполь- зуемого процессором в момент .прерывания. В микроконтроллере PIC16F84 все РОН отображены на оба банка, поэтому можно выбирать любые. Тем не менее это не слишком типичная ситуация, особенно если в модели используется боль- шее число банков для поддержки большого количества уникальных РСН. В более новых моделях часто предусмотрена небольшая область памяти, отображенная на все банки, например, старшие 16 байт в модели PIC16F627/8, как показано на Рис. 5.4 (стр. 121). Более старые модели, такие как PIC16C74, вообще не имеют ” В старшем семействе эти два регистра могут быть сохранены автоматически, см. стр. 584.
Глава 7. Обработка прерываний 219 общих РОН. В этих случаях программист должен либо гарантировать, что преры- вания не возникнут в те моменты, когда процессор работает с банком, отличным от используемого для сохранения, либо проверять состояние битов RP при входе в обработчик прерывания и переключаться на системный банк (обычно банк 0) перед сохранением регистра STATUS. Затем состояние битов RP в _status из- меняется таким образом, чтобы оно соответствовало их исходному значению. В нашем, намеренно упрощенном примере предполагается, что разрешено обслуживание только внешнего прерывания. В большинстве же случаев могут быть разрешены прерывания от нескольких источников. А поскольку в микро- контроллерах PIC имеется только один вектор прерывания (h’004’), то одной из первых задач обработчика будет проверка, какое из периферийных устройств вы- звало прерывание. Все флаги прерываний доступны для чтения, поэтому их мож- но поочередно проверять, пока не найдется флаг, который установлен. В самом сложном случае, когда активны все четыре источника прерываний микроконт- роллера PIC16F84, и учитывая, что регистр EECON1 находится в 1-м банке, код для проверки флагов будет выглядеть следующим образом: bsf STATUS,RPO ; Переключаемся на 1-й банк btfsc INTCON,! ; Проверяем флаг внешнего прерывания goto EXTERNAL ; ЕСЛИ установлен, переходим к соотв. обработчику btfsc INTCON,2 ; Проверяем флаг прерывания от Таймера 0 goto TIMERO ; ЕСЛИ установлен, переходим к соотв. обработчику btfsc INTCON,0 ; Проверяем флаг прерывания по изменению порта В goto CHANGE_B ; ЕСЛИ установлен, переходим к соотв. обработчику btfsc EECON1,4 ; Проверяем флаг прерывания от EEPROM goto EEPROM_WR ; ЕСЛИ установлен, переходим к соотв. обработчику IRQ_EXIT bcf STATUS,RPO ; Возвращаемся в 0-й банк retfie ; и выходим из обработчика Порядок опроса определяет уровень приоритета прерывания для случая одно- временной генерации нескольких прерываний. Так что, если активно и внешнее прерывание, и прерывание от Таймера 0, то сначала будет обработано первое. В этом случае наличие отложенного запроса прерывания от Таймера 0 при воз- врате в основную программу приведет к повторному вызову процедуры обработ- ки прерывания, где оно будет обработано (при условии, что в этот промежуток времени не возникло новых запросов прерывания с более высоким приорите- том). В любом случае соответствующий флаг должен сбрасываться, иначе преры- вание будет генерироваться бесконечно! Сброс флага производится в соответ- ствующей секции обработчика. При сброшенном бите маски аналогичная методика опроса может приме- няться для контроля событий без использования прерываний. Например, при за- писи байта в EEPROM (см. Программу 15.2 на стр. 547) программа обычно ожи- дает установки флага EEIF (4-й бит регистра EECON1), после чего сбрасывает его и продолжает выполнение.
220 Часть IL Программное обеспечение W_LOOP btfss EECON1,EEIF ; Проверяем состояние флага EEIF goto W_LOOP ; ЕСЛИ сброшен, проверяем снова ; ИНАЧЕ продолжаем выполнение программы после сброса флага EEIF bcf EECON1,EEIF Вообще говоря, во всех системах, управляемых прерываниями, необходимо предпринимать некоторые меры предосторожности в случае обработки прерыва- ний от нескольких источников. Для примера рассмотрим некоторую систему, по- лучающую запрос прерывания от таймера, скажем, 1000 раз в секунду, а также за- прос внешнего прерывания с вывода INT с нерегулярной частотой. Если обработ- чик внешнего прерывания выполняется, например, за 4 мс, то к моменту выхода из него будут потеряны три запроса прерывания от таймера! В некоторых процес- сорах^ имеется схема назначения приоритетов прерываний, благодаря которой запросы с более высоким приоритетом (в данном случае прерывание от таймера) могут прерывать процессы с более низким приоритетом (обработчик внешнего прерывания). В нашем же случае единственным вариантом будет введение огра- ничения времени выполнения обработчика внешнего прерывания — не более 1 мс* 2). При наличии прерываний от нескольких источников необходимо рассчи- тать время выполнения (включая задержки) и частоту возникновения прерыва- ний для наихудшего случая. Поскольку некоторые из этих параметров связаны с внешними событиями, не контролируемыми процессором, это может оказаться достаточно нетривиальной задачей. Другая часто возникающая проблема связана с обработкой таких событий, при которых многобайтные значения контролируются и изменяются как в фоно- вой программе, так и в обработчике прерывания. Возьмем, к примеру, часы ре- ального времени (RTC), в которых обновляется четыре регистра (HOURS, MINUTES, SECONDS и JIFFY), содержащих время в формате «часы : минуты : секунды : десятые доли секунд» (см. Пример 7.3). Предположим, что внешний генератор с частотой 10 Гц прерывает работу микроконтроллера 10 раз в секунду и обработчик прерывания обновляет указанные регистры. Теперь предположим, что эти RTC являются составной частью системы цент- рального отопления. Фоновая программа должна переключать насос из включен- ного состояния в выключенное в 09:00:00:00. И в указанное время это произошло. Но вот наступило время 09:59:59:09 того же дня. Фоновая программа, основная задача которой состоит в отслеживании текущего времени, считывает значение часов, равное 09. После этого она собирается считывать значение минут, когда происходит прерывание от генератора. Работа фоновой программы прерывается, и состояние RTC изменяется на 10:00:00:00. При возврате в фоновую программу соответственно считываются значения 00:00:00. Считая, что сейчас 09:00:00:00, программа переключает насос, вследствие чего периоды, когда насос включен и выключен, меняются местами неограниченное число раз! !) Например, используемых в старшем семействе микроконтроллеров PIC. 2) Менее элегантным решением был бы периодический опрос флага прерывания T0IF.
Глава 7. Обработка прерываний 221 Разумеется, использовать в программе именно переключение состояния яв- ляется плохим стилем программирования; то есть в 9 утра насос следовало имен- но включить, а не просто переключить его состояние. По крайней мере, в послед- нем случае система будет работать неверно лишь в течение ограниченного време- ни. Вообще говоря, в случае обработки подобных многобайтных данных в фоновой программе, необходимо на время запрещать обработчик прерывания сбросом соответствующего бита маски. Все прерывания, возникающие в этот пе- риод, будут обработаны позже, после установки этого бита маски. Хотя если ин- тервал времени, в течение которого прерывание было замаскировано, окажется слишком большим, некоторые события могут быть пропущены. Итак, подведем итог. Процедуры обработки прерываний аналогичны обыч- ным подпрограммам, однако необходимо помнить следующее: • Обработчик прерывания должен завершаться командой ret fie вместо return. • Рабочий регистр, а также все РСН, изменяемые в обработчике прерывания, должны быть сохранены при входе в обработчик и восстановлены при вы- ходе из него, если они также используются в фоновой программе. • Параметры не могут передаваться в/из обработчика прерывания через рабо- чий регистр. Вместо этого, при необходимости, следует использовать гло- бальные переменные (данные, расположенные в памяти по известному ад- ресу). • Обработчики прерывания должны быть как можно короче и должны вы- полнять минимальный набор операций. Это полезно при отладке, а также позволяет гарантировать, что ни одно из других событий не будет пропу- щено. • Если в обработчике прерывания обрабатываются многобайтные данные, то при любом обращении фоновой программы к этим переменным необходи- мо запрещать прерывания (сбросом бита GIE). Регистр INTCON, формат которого показан на Рис. 7.3, содержит только бит глобального разрешения прерываний, а также используется для управления тре- мя основными источниками прерываний, а именно внешним прерыванием, пре- рыванием по переполнению Таймера 0, а также прерыванием по изменению со- стояния выводов порта В. Последний оставшийся бит EEIE (INTCON[6]) в мо- дели PIC16F84 используется для хранения бита маски прерывания модуля EEPROM. По причине нехватки места соответствующий ему бит флага прерыва- ния EEIF размещен в другом регистре, а именно в регистре EECON1[4] (4-й бит). В 18-выводных моделях среднего уровня того же поколения часто используется такой подход. Например, бит маски прерывания от модуля АЦП ADIE в модели PIC16C71 располагается в INTCON [6], а соответствующий флаг прерывания ADIF находится в регистре управления АЦП ADCONO[1]. Однако в общем случае система прерываний должна быть способна работать со множеством периферийных модулей, каждый из которых, в свою очередь, мо- жет иметь одно или несколько прерываний. Одним из вариантов решения такой задачи было бы перемещение битов флагов и масок прерываний в локальные ре- гистры управления и состояния самого модуля, как это было сделано для модуля
222 Часть II. Программное обеспечение EEPROM модели PIC16F84. Более логичным, однако, было бы считать систему прерываний самостоятельным модулем и ввести группу дополнительных регист- ров, содержащих биты масок и флагов всех прерываний. На Рис. 7.5 показана логика системы прерываний в моделях PIC16F627/8. Эти модели имеют помимо основных еще пять периферийных модулей, формирую- щих в общей сложности семь отдельных запросов на прерывание. Правая часть схемы, изображенной на рисунке, практически идентична приведенной на Рис. 7.3. Единственное отличие заключается в том, что в данном случае 6-й бит регистра INTCON называется PEIE (разрешение прерывания от периферийных устройств)Ч Серым цветом в левой части Рис. 7.5 выделены семь дополнитель- ных источников прерываний и логические элементы, используемые для разреше- ния прерываний от этих источников. Выходы указанных элементов объединены по ИЛИ для формирования одного-единственного сигнала, который, в свою оче- редь, управляется битом маски PEIE. Так что в этих моделях 6-й бит регистра INTCON выполняет функцию разрешения прерываний от всех дополнительных периферийных модулей. Устройства среднего уровня имеют различные наборы периферийных уст- ройств, однако все они используют бит PEIE в качестве дополнительного бита маски для разрешения/запрещения прерываний от этих модулей. Если данный бит установлен, то любое из этих периферийных устройств может быть использо- вано для вывода микроконтроллера из спящего режима независимо от состояния бита глобального разрешения прерываний GIE. На Рис. 7.6 изображены два дополнительных регистра. Регистр флагов преры- ваний от периферийных устройств PIR1, расположенный в 0-м банке, который содержит семь флагов прерываний, и регистр разрешения прерываний от пери- ферийных устройств PIE1, содержащий соответствующие биты маскирования прерываний. Для примера на рисунке также показано формирование сигнала прерывания, генерируемого при приеме символа со входа последовательного порта (см. Рис. 12.20 на стр. 419), который устанавливает флаг прерывания по приему символа RCIF, расположенный в 5-м бите регистра PIR1. Для разреше- ния генерации прерывания по этому событию и перехода процессора к обработ- чику прерывания нам необходимо установить три бита маски. bsf STATUS,RPO ; Переключаемся на 1-й банк, чтобы bsf STATUS,RP1 ; обратиться к регистру PIE1 bsf PIE1,RCIE ; Разрешаем прерывание по приему символа bsf INTCON,PEIE ; Разрешаем прерывания от периферийных устройств bsf INTCON,GIE ; Разрешаем работу системы прерываний bcf STATUS,RPO ; Возвращаемся в 0-й банк Необходимо сделать несколько замечаний по поводу приведенного фрагмен- та программы. Во-первых, как и большинство микроконтроллеров PIC среднего уровня, PIC16F627/8 имеют четыре банка регистров, выбираемых при помощи В некоторых справочных листках этот бит называется GIEL (младший бит общего разре- шения прерываний), тогда как обычный бит GIE называется GIEH (старший бит общего разре- шения прерываний).
Запись в EEPROM Регистр управления прерываниями GIE МММ PEIE 5 TOIE INTE з RBIE 2 T0IF INTF INT CMIE ------Г” Р|Е1/Э6М_ RCIE P'EVP^S] TXIE PIE1/PIR1[4] TXIF CCP1IE PIE1/PIR1[2] CCP1IF TMR2IE PIE1/PJR1 [1 ] TMR2IF - TMR1IE - PIE1/PIR1 [0] TMR1IF - Компаратор Прием по USART Таймер 2 Дополнительные периферийные устройства Передача по USAH1" Захват/ сравнение / Таймер 1 (R/W0) (R/W0) (R/W0) (R/W0) (R/W 0) RBIF"I INTCON (R/W?) I h’0B’ 6 INTEDG (R/W1) OPTION_REG h’81’ Сигнал пробуждения (если процессор в «спящем» режиме) Сигнал прерывания к ЦПУ Рис. 7.5. Логика системы прерываний микроконтроллеров PIC16F627/8 Глава 7. Обработка прерываний
224 Часть II. Программное обеспечение PIE1 Запись в EEPROM Аналоговый компаратор Прием по USART Передача по USART Синхронный последовательный порт Захват/сравнение 1 Таймер 2 Таймер 1 Рис. 7.6. Регистры системы прерываний микроконтроллеров PIC16F627/8. Формирование запроса прерывания по приему символа портом USART битов RP1 и RP0 регистра STATUS (см. Рис. 5.4 на стр. 121). После сброса микро- контроллера оба бита обнуляются, т.е. используется 0-й банк памяти. Поэтому в приведенном коде мы не стали дополнительно сбрасывать бит RP1. Регистр PIE1 всегда располагается в 1-м банке, так как после конфигурирования дальнейшее его изменение, как правило, не требуется. В то же время из соображений удобства регистр PIR1 размещается в 0-м банке, поскольку он часто опрашивается и изме- няется. Регистр INTCON отображен на все четыре банка. В некоторых процессорах, таких как PIC16F87X, имеется очень много источ- ников прерывания, слишком много даже для описанной схемы. В таких устрой- ствах вводится дополнительная пара регистров PIR2 и PIE2, располагающихся в тех же банках, что и аналогичные регистры первой пары. В этих регистрах нахо- дятся соответственно биты маски и флаги прерываний дополнительных источников. Примеры Пример 7.1 Возьмем конвейерную линию по упаковке консервированного горошка. Од- ним из элементов автоматического упаковщика является фотоэлемент, формирую- щий одиночный короткий импульс при пересечении луча банкой, аналогично схе- ме на Рис. 7.4. После прохода 24 банок на 0-м выводе порта A (RA0) необходимо сформировать импульс длительностью 1 мс переключающий электронику упаковочного механизма. Предположим, что PIC16F84 тактируется от 4-МГц резо- натора.
Глава 7. Обработка прерываний 225 Решение Код программы приведен в Программе 7.2. По адресу вектора сброса (h’000’) расположена команда перехода к основной фоновой программе (MAIN), а по ад- ресу вектора прерывания (h’004) расположена команда перехода к процедуре об- работки прерывания, названной CAN_COUNT. Поскольку после сброса прерывания автоматически запрещаются, различные регистры и порты обычно конфигурируются в самом начале фоновой программы до разрешения прерываний. Это исключает вероятность появления прерываний до завершения инициализационного кода. Инициализация же заключается в сле- дующем: 1. Сброс 0-го бита порта А, что гарантирует наличие НИЗКОГО уровня на выводе RA0 после сброса. 2. Все линии параллельных портов ввода/вывода при сбросе микроконтрол- лера переключаются на вход. Для переключения 0-й линии порта А на вы- ход, необходимо сбросить соответствующий бит регистра TRISA. Посколь- ку этот регистр располагается в 1-м банке, необходимо переключить банки памяти, изменив бит RP1 регистра STATUS (см. стр. 99). Более подробно о работе с портами ввода/вывода можно прочитать в главе 11. 3. Сбрасываются регистры EVENT, в котором подсчитывается количество импульсов от фотодетектора, и BATCH, в который заносится ненулевое значение в обработчике прерывания после прохода 24 банок. 4. Сброс всех битов регистра INTCON сбрасывает все флаги прерываний, ко- торые могли бы установиться с момента сброса. Это очень важно, посколь- ку указанные флаги могут устанавливаться независимо от состояния соот- ветствующих битов маски. Последующая установка бита глобального разрешения прерываний разрешает работу системы прерываний, а уста- новка бита INTE разрешает внешние прерывания с вывода INT. Основной задачей фоновой программы является периодическая проверка значения регистра BATCH. При старте программы он равен нулю, однако обра- ботчик прерывания записывает в него ненулевое значение после прохода группы из 24 банок. При обнаружении ненулевого значения регистр обнуляется, на выхо- де RA0 устанавливается ВЫСОКИЙ уровень и вызывается подпрограмма 1-мс задержки1), названная delay (см. Программу 6.1 на стр. 175). После этого цикл повторяется. Вообще говоря, фоновая программа во встраиваемых системах представляет собой именно такой бесконечный цикл, однако, как правило, в нем выполняется намного больше задач, чем в этом простом примере. Например, можно управлять многоразрядным семисегментным дисплеем аналогично тому, как это показано на Рис. 11.16 (стр. 362), отображая, скажем, общее число банок с момента запуска конвейера. If Разумеется, выполнение подпрограммы также может быть прервано, что случайным образом слегка увеличит формируемую задержку. В случаях, требующих точной выдержки вре- мени, бит GIE необходимо сбрасывать перед вызовом подпрограммы задержки и устанавливать после возврата из нее.
226 Часть II. Программное обеспечение Программа 7.2. Программа автоматического упаковщика include "pl6f84.inc" „work „status EVENT BATCH equ equ equ equ h' 4E1 ; h1 4F' ; h’201 ; h’21’; Для сохранения W при входе в обработчик Для сохранения STATUS при входе в обработчик Счетчик количества банок Флаг прохода 24 банок / org 000 ; Вектор сброса goto MAIN ; Переходим к началу фоновой программы / org 004 ; Вектор прерывания goto CAN_COUNT ; Переходим к началу обработчика прерывания ; Фоновая программа начинается с секции инициализации MAIN bcf PORTA,0 \ : Гарантируем наличие 0 на выводе RA0 bsf STATUS,RPO , ; Переключаемся в 1-й банк bcf TRISA,0 ; Переключаем вывод RA0 на выход ; Примечание. При использовании модели с модулем АЦП, например PIC16F877, ; вывод PortA[0] должен быть сконфигурирован как цифровой вход!!! bcf STATUS,RPO ; Переключаемся обратно в 0-й банк clrf BATCH Обнуляем флаг группы clrf EVENT и счетчик банок clrf INTCON Сбрасываем все флаги прерывания bsf INTCON,GIE ; Разрешаем все прерывания bsf INTCON,INTE; Разрешаем внешнее прерывание ; ПОКА флаг группы равен нулю, ничего не делаем M_LOOP movf BATCH,f ; Проверяем BATCH == 0? btfsc STATUS,Z ; Пропускаем, если нет goto M_LOOP ; В противном случае проверяем снова ; 24 банки прош clrf BATCH ; Обнуляем флаг bsf PORTA,0 ; Выставляем на RA0 ВЫСОКИЙ уровень call DELAY ; Ждем 1 мс bcf PORTA,0 ; Выставляем на RA0 НИЗКИЙ уровень goto M_LOOP ; Возвращаемся к началу ; Это процедура обработки прерывания CAN_COUNT movwf swapf movwf „work ; STATUS,w ; „status ; Сохраняем W в памяти данных ; Считываем текущее состояние STATUS ; и сохраняем его в памяти данных bcf INTCON,INTF ; ; Сбрасываем флаг внешнего прерывания incf EVENT,f ; ; Регистрируем очередное событие movf EVENT,w ; ; Читаем значение счетчика
Глава 7. Обработка прерываний 227 addlw -d’24’ ; Сравниваем c 24 (EVENT - 24) btfss STATUS,C ; ЕСЛИ EVENT больше или равно, TO пропускаем ; (нет заема) goto CAN_EXIT ; ИНАЧЕ выходим clrf EVENT ; Обнуляем счетчик банок и сообщаем incf BATCH,f ; в фоновую программу, что прошло 24 банки CAN_EXIT swapf _status,w ; Восстанавливаем исходное состояние STATUS movwf STATUS ; из памяти данных swapf _work,f ; Теперь восстанавливаем исходное состояние swapf _work,w ; рабочего регистра, не воздействуя на флаги, retfie ; и возвращаемся в фоновую программу При возникновении прерывания (при пересечении банкой луча фотодетекто- ра) управление будет передано в процедуру обработки прерывания, т.е. произой- дет следующая последовательность переходов: прерывание h’004’ -ь CAN_COUNT. Как обычно, эта процедура состоит из трех секций. Сохранение контекста Рабочий регистр и регистр STATUS сохраняются в памяти программ, как это бы- ло описано на стр. 217. Основной код Сбрасывается флаг внешнего прерывания INTF (INTCON[1]), чтобы избежать обработки ложного прерывания при возврате в фоновую программу. При нали- чии нескольких источников прерывания на данном этапе должен был бы произ- водиться опрос различных флагов прерываний (см. стр. 219). Основная задача обработчика прерываний заключается в инкрементировании регистра EVENT. Вычитая число 24 из копии этой переменной и контролируя за- ем (т.е. равенство С единице), программа определяет момент, когда число EVENT становится равно или даже больше 24. При этом инкрементируется переменная BATCH для передачи в фоновую программу информации о том, что прошли оче- редные 24 банки, a EVENT обнуляется. То есть регистр EVENT играет роль счет- чика по модулю 24. Восстановление контекста При возврате из обработчика прерывания восстанавливаются исходные значения регистров W и STATUS, как было описано на стр. 218. Заключительная команда ret fie не изменяет состояние флагов регистра STATUS. Пример 7.2 На фабрике по производству пищевых продуктов банки с тушеной фасолью проходят по конвейеру через туннельную печь, как показано в верхней части Рис. 7.7, где их содержимое стерилизуется. Фотодетекторы используются для подсчета банок, вошедших в печь и вышедших из нее. При пересечении луча на выходе соответствующего фотодетектора устанавливается ВЫСОКИЙ уровень.
228 и Часть II. Программное обеспечение Вам требуется разработать интерфейс данной системы, управляемый преры- ваниями, объединив два сигнала для активизации одного входа INT микроконт- роллера. Звуковой излучатель, подключенный к 0-му выводу порта В, должен по- дать звуковой сигнал, сигнализируя о заторе, если количество банок в печи станет больше четырех. Решение С аппаратной точки зрения в данном примере имеется две задачи. Первая — как определить, какой из детекторов, входной или выходной, сформировал за- прос прерывания. Из Рис. 7.7 видно, что при перекрывании луча оба фотоэле- мента формируют тактовый импульс, подаваемый на тактовый вход соответству- ющего D-триггера. Поскольку вход триггера подтянут к лог. 1, подача тактового импульса вызывает переключение триггера в состояние лог. 1. За счет объедине- ния выходных сигналов обоих триггеров по ИЛИ при перекрытии любого луча на выводе INT формируется нарастающий фронт сигнала. Рис. 7.7. Система контроля печи
Глава 7. Обработка прерываний и 229 Состояние обоих внешних флагов IN и OUT можно считать соответственно с входов RA0 и RA1 порта А, что позволяет различать эти два события (вход банки в печь и ее выход) в обработчике прерывания. Соответствующий флаг затем можно сбросить, подав управляющий сигнал на вход сброса соответствующего триггера. Под эти сигналы задействованы еще две линии порта — RA2 и RA3 (Cancel_IN и Cancel_OUT соответственно). Чтобы проиллюстрировать работу этого узла, предположим, что банка только что пересекла луч выходного детектора, как показано на Рис. 7.7. При этом про- изойдут следующие события: 1. Импульс, сформированный детектором, подается на триггер выходного датчика OUT. 2. Триггер переключается, что, в свою очередь, приводит к появлению ВЫ- СОКОГО уровня на выводе RA1, а также на входе INT/RB0 (через элемент ИЛИ). Последний сигнал является запросом внешнего прерывания. 3. Когда микроконтроллер передает управление на обработчик прерывания, тот проверяет состояние обоих триггеров, считывая биты RA1 и RA2 порта. В данном случае на выводе RA1 будет присутствовать ВЫСОКИЙ уровень, соответственно обработчик выдаст отрицательный импульс на вывод RA3. 4. Этот импульс сбросит триггер выходного датчика (OUT) и таким образом прекратит генерацию запроса прерывания от данного источника. Осталась одна проблема: если событие наступит до того, как программа сбро- сит соответствующий внешний триггер, то это событие будет пропущено, пос- кольку с выхода элемента ИЛИ на вход INT будет подаваться сигнал НИЗКОГО уровня. В данной ситуации последующие фронты не будут сформированы, и сис- тема прерываний надолго окажется заблокированной! Эту ситуацию можно обойти программным способом, опрашивая оба внешних флага перед выходом из обработчика прерывания и выполняя соответствующие действия, если состояние обоих битов порта отлично от нуля. Процедура обработки прерывания для данного случая приведена в Программе 7.3. Контекст программы сохраняется при входе в обработчик и вос- станавливается при выходе из него так, как уже было описано на стр. 217. Программа 7.3. Обработчик прерывания системы контроля печи OVEN movwf swapf movf _work ; STATUS,w ; _status ; ; Сохраняем W в памяти данных ; Считываем текущее состояние STATUS ; и сохраняем его в памяти данных CHECK bcf INTCON,INTF , ; Сбрасываем флаг внешнего прерывания btfsc PORTA,0 ; • Сигнал IN? goto IN ; ; ЕСЛИ не ноль, банка только что вошла в печь btfsc PORTA,1 ; : Сигнал OUT? goto OUT ; : ЕСЛИ не ноль, банка только что вышла из печи ; Точка выхода swapf _status,w ; Восстанавливаем исходное состояние STATUS
230 и Часть II. Программное обеспечение movwf STATUS _work,f _work,w ; из памяти данных ; Теперь восстанавливаем исходное состояние ; рабочего регистра, не воздействуя на флаги, ; и возвращаемся в фоновую программу swapf swapf retfie f ; Основное тело процедуры обработки прерывания IN incf EVENT,f ; Регистрируем вхождение банки в печь bcf PORTA,2 ; Сбрасываем внешний триггер IN, bsf PORTA,2 ; формируя импульс его сброса, goto ALARM ; и проверяем наличие аварийной ситуации OUT decf EVENT,f ; Регистрируем выход банки из печи bcf PORTA,3 ; Сбрасываем внешний триггер OUT, bsf PORTA,3 ; формируя импульс его сброса ALARM movf EVENT,w ; Берем количество банок addlw -5 ; Вычитаем 5 btfss STATUS,C ; ЕСЛИ нет заема, пищим goto BUZ_OFF ; ИНАЧЕ все в порядке, выключаем звук bcf PORTB,7 ; Включаем звуковой излучатель goto BUZ_OFF CHECK ; и снова опрашиваем внешние триггеры bsf PORTB,7 ; Выключаем звук goto CHECK ; и снова опрашиваем внешние триггеры Основная часть кода просто сбрасывает внутренний флаг прерывания INTF и по очереди проверяет состояние внешних триггеров. В зависимости от их состоя- ния выполняется одна из трех секций обработчика: 1. Если на выводе RA0 ВЫСОКИЙ уровень, значит, банка пересекла луч входного детектора. Соответственно к счетчику (регистру EVENT) прибав- ляется единица и триггер входного детектора сбрасывается. Если значение счетчика больше четырех, то путем подачи на выход RB0 НИЗКОГО уров- ня включается звуковой сигнализатор, в противном случае он выключает- ся. Выполняется повторная проверка триггеров. 2. Если на выводе RA1 ВЫСОКИЙ уровень, значит, банка пересекла луч вы- ходного детектора. Соответственно из счетчика EVENT вычитается едини- ца и триггер выходного детектора сбрасывается. Счетчик проверяется на равенство четырем, и звуковой излучатель переключается в соответствую- щее состояние. Выполняется повторная проверка триггеров. 3. Если ни один из триггеров не установлен, выполняется выход из обработчика. Данная последовательность повторяется до завершения секций 1 или 2. Это позволяет гарантировать корректную обработку ситуации, когда оба луча пере- крываются одновременно или в пределах небольшого временного окна. Фоновая программа на листинге не показана. Она аналогична фоновой про- грамме из Программы 7.2, т.е. в ней будут сконфигурированы различные порты, регистр-счетчик событий при старте программы будет сброшен, а прерывания разрешены. Очевидно, что данная фоновая программа будет отвечать за форми-
Глава 7. Обработка прерываний 231 рование сигнала излучателя, а также за выполнение других задач, обработку кото- рых не следует возлагать на обработчик прерывания, чтобы максимально умень- шить размер кода обработчика. В реальной системе фоновая программа могла бы управлять цифровым индикатором, показывающим общее количество банок в печи (четыре — это смешное значение, взятое только для примера). Кроме того, следует реализовать сброс на ненулевое значение после затора, а также предус- мотреть какой-нибудь знак, индицирующий (ошибочно вычисленное) отрица- тельное значение количества банок. Пример 7.3 На стр. 220 мы говорили о часах реального времени системы центрального отопления. Напишите процедуру обработки прерывания, которая при каждом прерывании, генерирующемся с периодом 0.1 с, увеличивала бы на единицу зна- чение времени, хранящееся в четырех регистрах данных. Это значение представ- лено в 24-часовом формате. В каждом байте хранится два BCD-разряда, к приме- ру, BCD-число 40 в регистре MINUTES представляется как Ь’ОЮО 0000’. Этот формат называется упакованным BCD-форматом. Решение При каждом вызове процедуры обработки прерывания необходимо добавлять 1 к четы- рехбайтному числу, хранящемуся в регистрах HOURS:MINUTES:SECONDS:JIFFY. При- чем регистр JIFFY используется как счетчик по модулю 10, SECONDS и MINUTES — по модулю 60, a HOURS — по модулю 24. С учетом этого составим перечень задач: 1. Прибавить 1 к JIFFY. 2. Если JIFFY = 10, то обнулить JIFFY и прибавить единицу к SECONDS. В противном случае перейти к п. 6. 3. Если SECONDS = 60, то обнулить SECONDS и прибавить единицу к MINUTES. В противном случае перейти к п. 6. 4. Если MINUTES = 60, то обнулить MINUTES и прибавить единицу к HOURS. В противном случае перейти к п. 6. 5. Если HOURS = 24, обнулить HOURS. 6. Выйти из обработчика. Код, реализующий описанный алгоритм, приведен в Программе 7.4. Сохра- нение и восстановление контекста реализовано обычным образом. Однако, пос- кольку в обработчике используется регистр FSR, он тоже сохраняется в регистре _f sr и восстанавливается при выходе из обработчика. Программа 7.4. Обработчик прерывания часов реального времени _work equ h'4D' ; Копия W _status equ h'4E' ; Копия STATUS _fsr equ h'4F' ; Копия FSR HOURS equ h'20' ; Часы (2 разряда)
232 Часть II. Программное обеспечение MINUTES equ h’21' Минуты (2 разряда) SECONDS equ h’22’ Секунды (2 разряда) JIFFY equ h’23’ Доли секунды (2 разряда) f X XLA XLA Ji LA Ч^^Ч£У ЧАХ X УХХ’Х ХЧЧ> X X Л Ч» XY Ч- Л RTC movwf _work ; Сохраняем W swapf STATUS,w и регистр STATUS, movwf _status movf FSR,w ; а также регистр FSR movwf _fsr bcf INTCON,INTF ; Сбрасываем флаг внешнего прерывания ; Задача 1 incf JIFFY,f Увеличим Jiffy на единицу movlw d'10' Сравним с десятью subwf JIFFY,W btfss STATUS,Z ЕСЛИ равно, ТО продолжаем goto EXIT ИНАЧЕ выходим из обработчика ; Задача 2 clrf JIFFY ИНАЧЕ обнуляем Jiffy movlw SECONDS Устанавливаем FSR на Seconds movwf FSR call BCD_INC и инкрементируем BCD-число movlw h’60’ Сравниваем с ОНО 0000 (60 BCD) subwf SECONDS,w btfss STATUS,Z ЕСЛИ равно, ТО продолжаем goto EXIT ИНАЧЕ выходим из обработчика ; Задача 3 clrf SECONDS ИНАЧЕ обнуляем Seconds decf FSR,f Устанавливаем FSR на Minutes call BCD_INC и инкрементируем BCD-число movlw '60' Сравниваем с ОНО 0000 (60 BCD) subwf MINUTES,w btfss STATUS,Z ЕСЛИ равно, ТО продолжаем goto EXIT ИНАЧЕ выходим из обработчика ; Задача 4 clrf MINUTES ИНАЧЕ обнуляем Minutes decf FSR,f Устанавливаем FSR на Hours call BCD_INC и инкрементируем BCD-число movlw h'24’ Сравниваем с 0010 0100 (24 BCD) subwf HOURS,w btfsc STATUS,Z ЕСЛИ не равно, ТО продолжаем clrf HOURS ИНАЧЕ обнуляем Hours / ; Задача 5 EXIT movf _fsr,w ; Восстанавливаем FSR
Глава 7. Обработка прерываний 233 movwf FSR swapf _status,w ; Восстанавливаем STATUS movwf STATUS swapf _work,f ; Восстанавливаем W, swapf _work,w ; не воздействуя на флаги, retfie ; и выходим из обработчика Основное тело обработчика прерывания разбито на секции в соответствии с составленным алгоритмом. После каждого инкрементирования из нового значе- ния вычитается константа, равная основанию счета. Если эти значения равны, регистр обнуляется и инкрементируется следующий байт числа. Вместо проверки флага нуля можно было бы контролировать значение флага переноса, проверяя, чтобы полученное значение было равно или больше значения основания, btfss STATUS, С1}. В примере предполагается, что данные хранятся в упакованном BCD-форма- те. То есть число 59 хранится в виде h’0101 1001’ или h’59’. Это означает, что опе- рация инкрементирования должна соответствовать формату BCD. Эту коррек- цию можно осуществить после обычного инкрементирования, проверяя, чтобы младший полубайт не стал больше девяти. В противном случае к числу прибавля- ется шесть. Поскольку число не может принимать значения более 59, нам не нуж- но выполнять аналогичную проверку для старшего полубайта. С алгоритмом пол- ного инкрементирования упакованного BCD-числа можно познакомиться в Примере 4.5 на стр. 111. Поскольку эту операцию надо выполнить 3 раза (для всех байтов, исключая JIFFY, который никогда не становится больше девяти), лучше всего оформить ее в виде подпрограммы. Код такой подпрограммы приведен в Программе 7.5. В данном случае регистр FSR указывает на регистр, который содержит подлежа- щее инкрементированию упакованное BCD-число. Это значение просто инкре- ментируется непосредственно в регистре с использованием косвенной адреса- ции. После этого оно корректируется описанным выше способом. Предполагает- ся, что данные, на которые указывает FSR, уже находятся в BCD-формате, т.е. в подпрограмме отсутствует преобразование натурального двоичного числа в дво- ично-десятичное. Программа 7.5. Подпрограмма инкрементирования упакованного BCD-числа ; * ФУНКЦИЯ : Прибавляет 1 к упакованному BCD-числу (98 макс)* ; * ВХОД ; * ВЫХОД : FSR указывает на регистр с числом : BCD-число инкрементируется; W и STATUS изменяются ************************************************************ BCD_INC incf INDF,f movf INDF,w ; Прибавляем 1 к адресованному байту ; Считываем его Это решение более надежно, чем проверка на равенство, поскольку из-за ошибок в про- грамме в регистры, хранящие время, вполне могло быть записано число, выходящее за коррект- ный диапазон.
234 Часть II. Программное обеспечение addlw 6 ; Прибавляем шесть btfss STATUS,DC ; Проверяем десятичный перенос goto BCD_EXIT ; ЕСЛИ нет, ТО выходим movwf INDF ; ИНАЧЕ возвращаем скорректированное значение BCD_EXIT return Пример 7.4 В торговом автомате монеты разных номиналов проходят через один из шести микропереключателей, подключенных к порту В. При прохождении монеты пе- реключатель замыкается, что вызывает появление на соответствующем выводе сигнала НИЗКОГО уровня, как показано на Рис. 7.8. Напишите процедуру обработки прерывания, которая будет накапливать зна- чение суммы в регистре MONEY. Предполагается, что в фоновой программе ре- гистр INTCON будет сконфигурирован таким образом, чтобы разрешить внеш- нее прерывание с вывода RB0/INT. 4+V RB7 RB6 RB5 RB4 RB3 RB2 RB0/INT Переключатель замкнут — Переключатель разомкнут Рис. 7.8. Монетоприемник торгового автомата Решение Как показано в Программе 7.6, после сохранения контекста и сброса флага INTF производится последовательная проверка всех переключателей. НИЗКО- МУ уровню на каком-либо выводе порта соответствует лог. О в соответствующем бите регистра PORTB. В соответствии с логикой работы механизма монетопри- емника одновременно может быть замкнут только один переключатель, поэтому нет необходимости выходить из процедуры после неудачного поиска.
Глава 7. Обработка прерываний 235 Программа 7.6. Обработчик прерывания монетоприемника торгового автомата VEND movwf swapf movwf _work STATUS,w _status ; Сохраняем W в памяти данных ; Читаем STATUS, не изменяя флагов, ; и сохраняем его в памяти данных CHECK bcf INTCON,INTE , ; Сбрасываем флаг внешнего прерывания movf MONEY,w ; Берем текущее значение MONEY btfss PORTB,7 ; Проверяем $2 addlw d'200' ; ЕСЛИ 0, ТО прибавляем 200 btfss PORTB,6 ; Проверяем $1 addlw d'100' ; ЕСЛИ 0, ТО прибавляем 100 btfss PORTB,5 ; Проверяем 25с addlw d'25' ; ЕСЛИ 0, ТО прибавляем 25 btfss PORTB,4 ; Проверяем 10с addlw d'10' ; ЕСЛИ 0, ТО прибавляем 10 btfss PORTB,3 ; Проверяем 5с addlw 5 ; ЕСЛИ 0, ТО прибавляем 5 btfss PORTB,2 ; Проверяем 1с addlw 1 ; ЕСЛИ 0, ТО прибавляем 1 movwf MONEY ; Сохраняем новую сумму ; Точка выхода swapf _status,w ; Восстанавливаем исходное значение movwf STATUS ; регистра STATUS swapf _work,f ; Восстанавливаем исходное значение W, swapf _work,w ; не воздействуя на флаги, retfie ; и возвращаемся в фоновую программу Вопросы для самопроверки 7.1. Перепишите Программу 7.2 так, чтобы она подсчитывала число банок, рав- ное одному гроссу (144). Это значение следует хранить в упакованном BCD- формате (СОТНИ и ДЕСЯТКИ:ЕДИНИЦЫ), и оно может использоваться фоновой подпрограммой для отображения общего числа банок. 7.2. Какие изменения следует внести в Программу 7.2, чтобы максимальное чис- ло банок в печи могло быть равным 1000? 7.3. Взяв в качестве образца Рис. 7.1, напишите процедуру обработки прерыва- ния, выполняющую следующие операции: • Копирование 16-битного числа в два регистра общего назначения — ТЕМР_Н и TEMP_L. • Вычитание его из предыдущего значения, хранящегося в регистрах LAST_COUNT_H и LAST_COUNT_L, и запись разницы в регистры DIFFERENCE_H и DIFFERNCE_L.
236 Часть II. Программное обеспечение • Замещение предыдущего значения новым. • Запись в РОН с именем NEW ненулевого значения для передачи в фоновую программу информации о том, что доступно новое значение. Фоновая процедура обнулит регистр NEW после обработки данных. 7.4. Скорость вращения вала можно измерить с использованием кодирующего диска, который генерирует импульс при повороте вала на каждые 10°. Этот импульс может использоваться в качестве сигнала внешнего прерывания микроконтроллера. Учитывая, что максимальная скорость вращения состав- ляет 20 000 оборотов в минуту, какое наибольшее время выполнения может иметь процедура обработки прерывания, позволяющее избежать пропуска импульсов? Предполагается, что частота кварцевого резонатора равна 4 МГц. 7.5. Электронная рулетка определяет расстояние путем излучения ультразвуко- вых импульсов и контролируя время прихода отраженного сигнала. Схема такого ультразвукового дальномера приведена на Рис. 7.9 (за его основу взя- та схема с Рис. 7.7). Наибольшее измеряемое расстояние составляет 2.5 м при разрешении 1 см. Скорость звука в воздухе при температуре 20°С равна 344 м/с, т.е. время, за которое сигнал пройдет расстояние 1 см и вернется обратно, равно 58 мкс. Рис. 7.9. Аппаратная часть ультразвукового дальномера
Глава 7. Обработка прерываний 237 Используя в качестве задающего генератор с частотой 17.2 кГц, получим од- но прерывание каждые 58 мкс. Учитывая схему, программа должна выполнять следующие операции: • Фоновая программа 1) Обнулить счетчик JIFFY и флаг NEW 2) Подать импульс на излучатель. 3) Ждать установления ненулевого значения флага NEW 4) Отобразить подсчитанное значение. 5) Перейти кп. 1. • Процедура обработки прерывания 1) При каждом импульсе генератора инкрементировать счетчик JIFFY. 2) При обнаружении сигнала от приемника записать в флаг NEW ненуле- вое значение, извещающее фоновую программу о том, что в перемен- ной JIFFY находится конечное число. 3) Повторять, пока активен хотя бы один сигнал. 4) Выйти из прерывания. Напишите код процедуры обработки прерывания, которая использует ре- гистр NEW для извещения фоновой программы о приходе отраженного сиг- нала. За основу можно взять Программу 7.3. 7.6. Предполагается увеличить диапазон цифрового ультразвукового дальноме- ра до 10 м и разрешение до 1 мм. Какие изменения необходимо внести в ап- паратную и программную части? 7.7. Ультразвуковой дальномер из предыдущего вопроса был собран и протести- рован. Однако обнаружилось, что с течением времени показания медленно изменяются. Сначала грешили на дрейф, но генератор оказался стабиль- ным. Немного подумав, один студент предположил, что скорость звука за- висит от условий окружающей среды. После небольшого исследования он вывел следующую зависимость скорости звука от температуры: 273 где Ио — скорость распространения при 20°С, a Vt — скорость при темпера- туре t. Какое изменение температуры приведет к появлению ошибки в 1 мм при работе дальномера на максимальной дистанции?
ГЛАВА ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА ДЛЯ РАБОТЫ С ЯЗЫКОМ АССЕМБЛЕРА Начиная С главы 3, мы написали уже достаточно программ. Для доходчивости эти программы были написаны таким образом, чтобы их легче было понимать че- ловеку. То есть команды представлялись короткими мнемониками, например return вместо Ь’00000000001000’. Аналогично, регистры имели имена, такие как INTCON, а строки имели метки и комментарии. Однако такое символьное пред- ставление годится только для человека. Микроконтроллер и знать ничего не знает, кроме двоичных кодов (см. стр. 64), составляющих исполнимый код и данные. Воспользовавшись описанием набора команд (см. Приложение Г), можно вручную транслировать программу из подобного удобочитаемого формата в ма- шиночитаемый двоичный код. В принципе для таких устройств, как микроконт- роллеры PIC, имеющих сокращенный набор команд (RISC) и небольшое число режимов адресации, это не так уж и сложно. Однако это довольно долгий и уто- мительный процесс, особенно если программа достаточно большая. Кроме того, при таком подходе велика вероятность возникновения ошибок и усложняется внесение изменений в программу. Знакомые вам компьютеры незаменимы там, где необходимо быстро и акку- ратно выполнять однообразные операции. Очевидно, что задача перевода про- граммы из символьного представления в машинный код полностью подходит под эту категорию. В данной главе мы вкратце рассмотрим различные программные средства, помогающие осуществлять этот процесс трансляции. В следующей главе мы также познакомимся с аналогичными средствами для языка высокого уровня. Прочитав эту главу, вы: • Узнаете, что такое язык ассемблера и как он соотносится с машинным кодом. • Поймете преимущества символьного представления перед машинным кодом. • Разберетесь в назначении ассемблера. • Поймете разницу между абсолютным и перемещаемым кодом. • Поймете назначение компоновщика. • Ознакомитесь с процессом трансляции ассемблерной программы в абсо- лютный машинный код. • Узнаете структуру файла машинных кодов и назначение программы-загруз- чика. • Поймете назначение симулятора.
Глава 8. Инструментальные средства для работы с языком ассемблера 239 • Научитесь использовать интегрированную среду разработки для автомати- зации взаимодействия между различными программными средствами, не- обходимыми для превращения исходного кода в запрограммированный микроконтроллер. Суть процесса преобразования ассемблерной программы показана на Рис. 8.1. Программа была набрана в символьном виде человеком, преобразована компьютером и выдана в машиночитаемом виде. Разумеется, за этой простотой скрываются гораздо более сложные процессы, которые мы разберем довольно подробно, что поможет вам в написании программ' (в степени, достаточной, что- бы вы могли свободно писать программы). incf movf addlw btfsc movwf return COUNT,f ' COUNT,w 6 STATUS,DC COUNT > 00101010100000 00100000100000 11111000000110 01100100000101 00000010100000 00000000001000 Рис. 8.1. Преобразование исходного кода на языке ассемблера в машинный код Вообще говоря, различные трансляторы и утилиты выпускаются и продаются огромным числом компаний, занимающихся разработкой программного обеспе- чения, поэтому конкретные детали и операции в различных коммерческих про- дуктах несколько отличаются. Что же касается конкретно микроконтроллеров PIC, то их производитель, компания Microchip Technology Inc, избрала стратегию бесплатного предоставления программных средств для работы с языком ассемб- лера. Именно это сыграло большую роль в популярности данных микроконтрол- леров. Поэтому коммерческое ПО для работы на таком низком уровне встречает- ся достаточно редко, и, более того, используемый синтаксис обычно совпадает с синтаксисом, принятым в программах самой Microchip. По этой причине в дан- ной главе мы будем рассматривать пакет инструментальных средств Microchip. Использование компьютеров для трансляции кода, представленного в удоб- ном для человека символьном виде (исходный код), в понятный микроконтролле- ру двоичный код (объектный или машинный код) и загрузки его в память устройств началось в конце 1940-х кодов. Помимо всего прочего, применение компьютеров позволило использовать системы счисления высших порядков, например шест- надцатеричную1}. В шестнадцатеричной системе фрагмент кода, представленного на Рис. 8.1, будет выглядеть следующим образом: 0AA0 0820 ЗЕ06 1905 О ОАО 0008 На самом деле в течение нескольких десятилетий была популярна восьмеричная система (по основанию 8).
240 Часть И. Программное обеспечение Шестнадцатеричный загрузчик транслирует этот код в двоичный и поместит его в память по заданному адресу. Данный загрузчик может входить в состав про- граммного обеспечения вашего программатора PIC-EPROM. Написание про- грамм в шестнадцатеричных кодах мало кого прельщает. Несмотря на некоторое уменьшение числа нажатия на клавиши, при таком способе ввода программы очень легко наделать кучу ошибок. Для серьезного занятия программированием требуется, как минимум, сим- вольный транслятор, или ассемблер^. Его применение позволяет программисту использовать для команд и внутренних регистров мнемонические обозначения, а также присваивать имена константам, переменным и адресам. Символьный язык, на котором пишется исходный код, называется языком ассемблера. В отли- чие от языков высокого уровня, таких как Си или Паскаль, язык ассемблера обес- печивает полное (один к одному) соответствие с генерируемым машинным кодом, т.е. из одной строки исходного кода получается одна команда. В качестве примера рассмотрим Программу 8.1, представляющую собой слегка модифицированную версию Программы 6.12 со стр. 199. Эта подпрограмма вычисляет значение квад- ратного корня из 16-битной переменной num, которая размещается в двух байтах памяти данных, и возвращает в рабочем регистре 8-битное целое значение квад- ратного корня. Присвоение имен различным адресам и константам особенно необходимо в случае больших программ, состоящих из нескольких тысяч строк кода. В сово- купности с комментариями это облегчает отладку, разработку и поддержку кода. К примеру, в большинстве наших программ до настоящего момента мы использо- вали следующие строки: STATUS equ 3 ; Регистр STATUS расположен по адресу h'03' С equ 0 ; Флаг переноса - 0-й бит этого регистра Псевдокоманда equ представляет собой образец директивы ассемблера. Ди- ректива не генерирует код как команда, а используется для передачи ассемблеру информации, касающейся его работы. В данном случае написанное означает, что при обнаружении в поле операндов команды имени status оно должно быть за- менено на число 3, а имя С должно быть заменено на число 0. Директива equ лучше всего подходит для указания имен РСН и их битов. Поскольку для каждой модели микроконтроллера эти значения фиксированы и, соответственно, не являются уникальными для какой-либо конкретной програм- мы, компания Microchip предоставляет для каждого устройства файлы с расши- рением .inc. Эти файлы могут быть включены в пользовательскую программу в ка- честве заголовочных файлов* 2). Для примера в Листинге 8.1 приведена начальная часть файла pl6f84a.inc3). Этому названию очень много лет; оно относится к процессу трансляции и сборки (ассем- блирования) различных модулей для формирования программы. 2) Разумеется, никто не мешает вам написать собственный вариант, содержащий дополни- тельную информацию. 3) Микроконтроллер PIC16F84A, выпущенный в 1999 году, представляет собой слегка дора- ботанный вариант стандартного микроконтроллера PIC16F84, выпущенного в 1994 году.
Глава 8. Инструментальные средства для работы с языком ассемблера и 241 Листинг 8.1. Фрагмент файла pl6f84a.inc, предоставляемого компанией Microchip ; This header file defines configurations, registers, and other ; useful bits of information for the PIC16F84 microcontroller. ; These names are taken to match the data sheets as closely as possible. . Register Files INDF EQU H'0000' TMRO EQU H'0001’ PCL EQU H'0002' STATUS EQU H'0003' FSR EQU H'0004' PORTA EQU H'00051 PORTB EQU H'0006' EEDATA EQU H'0008' EEADR EQU H'0009' PCLATH EQU H'000A' INTCON EQU H'000B' OPTION_REG EQU H'0081' TRISA EQU H'0085' TRISB EQU H'00861 EECON1 EQU H'0088 ' EECON2 EQU H'0089' IRP EQU H'0007' RP1 EQU H'0006’ RPO EQU H’0005' NOT_TO EQU H’0004' NOT_PD EQU H'0003 ' Z EQU H'0002' DC EQU H'0001' C EQU H'0000' GIE EQU H'0007 ’ EEIE EQU H'0006' TOIE EQU н'0005' INTE EQU H'0004' RBIE EQU H'0003' TOIF EQU H'0002 ' INTF EQU H'0001' RBIF EQU H'0000' Примечание. Перевод комментариев в заголовке файла: «В этом заголовочном файле определяются биты конфигурации, регистров и другие полезные константы для микроконтроллера PIC16F84. Символические имена выбраны такими, чтобы максимально соответствовать справочному листку на микроконтроллер.» (Примеч. пер.) В Программе 8.1 директива include1* используется для вставки в программу имен регистров специального назначения. Помимо того, что использование этой директивы освобождает программиста от необходимости набирать множество ди- Компания Microchip рекомендует использовать директиву ttinclude.
242 Часть II. Программное обеспечение ректив equ, любой последующий переход на другой процессор, скажем, с PIC16F84A на PIC16F627, можно будет осуществить простой заменой заголовоч- ного файла. Начиная с этого момента, мы будем активно использовать эту воз- можность ассемблера. Хотя мы и применяем директиву include для вставки за- головочного файла, она может использоваться для вставки любого файла подхо- дящего формата, например, содержащего код подпрограммы; в качестве примера обратите внимание на Программу 12.8, приведенную на стр. 401. Программа 8.1. Неперемещаемая программа, использующая нашу подпрограмму вычисления квадратного корня ; Глобальные объявления include "pl6f84a.inc" ; Заголовочный файл cblock h'261 ; Начало блока переменных (с регистра h’26’) NUM:2 ; Старший байт (NUM), младший байт (NUM+1) endc ; Конец блока ; Основной цикл ------------------------------------------------ MAIN call SQR_ROOT ; Фиктивный основной цикл sleep ; Останавливаемся ; * ФУНКЦИЯ : Вычисляет корень квадратный из 16-битного целого * ; * ПРИМЕР : Число = h'FFFF' (65,535), Корень = h'FF' (d'255')* ; * ВХОД : Число в регистрах NUM:NUM+1 * ; * ВЫХОД : Корень в W. Регистры NUM:NUM+1 и * ; * 1:1+1, COUNT изменяются * ************************************************************** ; Локальные объявления ! ; Магическое число и счетчик цикла / cblock 1:2, endc COUNT:1 org h' 200' ; Код размещается в памяти программ начиная с h'200' SQR_ROOT clrf COUNT ; Задача 1: Обнулить счетчик цикла clrf I ; Задача 2: Инициализация магического числа единицей clrf 1 + 1 / incf I + l,f / ; Задача 3: ВЫПОЛНЯТЬ SQR_LOOP movf I + 1,W ; Задача За: Number - I subwf NUM+1,f ; Вычитаем из младшего байта исходного числа movf I ,w ; Берем старший байт магического числа btfss STATUS,C ; ЕСЛИ не было заема (С==1), ТО пропускаем addlw 1 ; Учитываем заем subwf NUM, f ; Вычитаем старшие байты ; Задача 36: ЕСЛИ потеря значимости, ТО выйти
Глава 8. Инструментальные средства для работы с языком ассемблера и 243 btfss goto STATUS,C SQR.END ; ЕСЛИ нет заема (C==l), TO продолжаем ; ИНАЧЕ вычисление завершено incf COUNT,f ; Задача Зв: ИНАЧЕ инкрементируем счетчик цикла movf addlw btfsc incf movwf goto 1 + 1 ,w 2 STATUS,C I,f 1+1 SQR_LOOP ; Задача Зг: Увеличиваем магическое число на 2 ; Если нет переноса, ТО пропускаем ; ИНАЧЕ корректируем старший байт SQR_END movf return end COUNT,w ; Задача 4: Возвращаем счетчик цикла в качестве ; значения корня Директива equ используется также для присваивания имен переменным, хра- нимым в РОН. Так, в Программе 6.12 на стр. 199 имеются следующие строки: NUM_H equ h'261 ; Исходное значение, старший байт NUM_L equ h'27' ; Исходное значение, младший байт Эти имена и адреса должны быть, разумеется, уникальными для данной про- граммы, а не для какого-то конкретного устройства. В Программе 8.1 использует- ся пара директив cblock — endc, выполняющих схожие функции. Эти директи- вы сообщают ассемблеру о том, что переменные должны быть размещены в ука- занных регистрах. В строках, заключенных между данными директивами, перечисляются имена переменных и количество байтов, занимаемых каждой пе- ременной. Возвращаясь к нашему примеру: cblock h'26' ; Начало блока переменных (с регистра h'26') NUM:2 ; Резервируем два байта под NUM endc ; Конец блока где число, записанное через двоеточие после имени переменной, определяет ко- личество байтов, зарезервированных подданную переменную. Отдельные байты, входящие в состав переменной, можно адресовать с использованием арифмети- ческого оператора «+»; например, в 3-байтной переменной SUM: 3 1-й байт обоз- начается как SUM, 2-й байт — как SUM+1 и 3-й байт — как SUM+2. При описании первого блока переменных в Программе 8.1 явно указывается, что он начинается с регистра h’26’. Во всех последующих директивах cblock ука- зание адреса можно опустить, если новые переменные должны располагаться сразу же после уже описанных. Таким образом, переменная 1:2 размещается в регистрах h’27’:h’28’, a COUNT :1 — в регистре h’29’. Такой подход обеспечивает гораздо большую гибкость по сравнению с ручным распределением регистров самим про- граммистом, так как при изменении какого-либо модуля или добавлении новых элементов распределение памяти изменится автоматически. Кроме того, измене- ние начального адреса какого-либо блока, скажем с регистра h’26’ на регистр h’20’, автоматически размещает все переменные программы по новым адресам.
244 Часть II. Программное обеспечение Также для именования (присваивания имен) различных объектов программы можно использовать директиву #def ine. Например, строка вида #define 6,7 BUZZER позволяет нам писать bsf BUZZER вместо bsf 6,7, например для включения звукового излучателя, подключенного к выводу 7 порта В (регистр h’06’). Чтобы проиллюстрировать еще одну возможность ассемблера, подпрограмма из Программы 8.1 размещается, начиная с адреса h’200’ памяти программ. Это осуществляется использованием директивы org (см. также Программу 7.1 на стр. 215). В результате данной операции метке программы SQR_ROOT было при- своено значение h’200’. В последней строке Программы 8.1 размещается директива end. Эта директи- ва указывает ассемблеру игнорировать весь текст, располагающийся ниже ее, т.е. прекратить трансляцию. Разумеется, для работы символьного транслятора требуется больше вычисли- тельных ресурсов, нежели для простого шестнадцатеричного загрузчика, особен- но это касается памяти и устройств резервного хранения. До появления в конце 1970-х персональных компьютеров для выполнения ассемблирования требова- лись мэйнфреймы, мини-компьютеры или же специальные системы разработки микропроцессоров/микроконтроллеров. Естественно, эти решения были весьма дорогостоящими, и по причине невозможности использования указанных вы- числительных средств повсеместно использовалось ручное кодирование. Программы-трансляторы, вообще говоря, выполняют две задачи: 1. Преобразование различных мнемоник команд и меток в их эквивалентные значения в машинном коде. 2. Размещение команд и данных по заданным адресам. Для большинства программ, выполняющихся на микроконтроллерах PIC младшего и среднего уровней, более чем достаточно возможностей абсолютного ассемблера. Чтобы облегчить понимание процесса трансляции, изображенного на Рис. 8.2, мы рассмотрим все этапы преобразования нашей программы, начи- ная с создания файла с исходным кодом и заканчивая итоговым файлом с абсо- лютным машинным кодом. Перемещаемый ассемблер мы затронем чуть позже. Рис. 8.2. Абсолютная трансляция с языка ассемблера
Глава 8. Инструментальные средства для работы с языком ассемблера 245 Редактирование Прежде всего исходный файл необходимо создать. Для этого используется текстовый редактор. Текстовый редактор отличается от текстового процессора тем, что он не вставляет в свой текст никаких управляющих символов, разметки и другой подобной информации. Например, в нем отсутствует перенос строк, по- этому если вы хотите перейти на новую строку, вы должны сами нажать клавишу ввода. В составе большинства операционных систем поставляется простой текс- товый редактор, в частности в Microsoft Windows это программа notepad. Также имеется множество программ сторонних разработчиков, кроме того, большин- ство текстовых процессоров имеют текстовый режим, в котором их можно ис- пользовать как обычный текстовый редактор. Файлы с исходным кодом на языке ассемблера имеют расширение .asm. Типичная строка файла с исходным кодом имеет следующий формат: Метка (необязательно) Операнд-адресат SQREND movf COUNT,W ; Copy into W Мнемоника инструкции Операнд-источник Комментарий (необязательно) За исключением строк, в которых содержится только комментарий, все строки должны содержать инструкцию (либо исполняемую микроконтроллером команду, либо директиву) и соответствующий операнд или операнды. Все метки должны на- чинаться с 1-й позиции строки; если метка отсутствует, то первым символом стро- ки должен быть пробел или символ табуляции, индицирующие этот факт. Метка может состоять из 32 алфавитно-цифровых символов, символов подчеркивания или символов вопроса. При этом первым символом метки обязательно должна быть буква или символ подчеркивания. Обычно метки нечувствительны к регистру символов. Метка строки соответствует адресу памяти программ первой следующей за ней исполняемой команды. Метка должна отделяться от последующей команды или директивы пробелом, двоеточием или даже символом новой строки. Необязательный комментарий обозначается символом точки с запятой, при этом допускается вводить строки, состоящие только из комментария (см. строки 9... 16 Программы 8.1). Ассемблер игнорирует комментарии, т.е. они служат ис- ключительно для документирования текста программы. Комментариев должно быть много, и они должны объяснять, что программа делает, а не просто дублиро- вать команду. Например, строка: movf I,w ; Скопировать I в w является примером напрасной траты времени, тогда как строка movf I,w ; Считать старший байт магического числа гораздо более полезна. Исходный код, не содержащий комментариев или содер- жащий их очень мало, очень часто неработоспособен. Плохо документированную программу трудно отлаживать, а впоследствии изменять или дополнять. Послед- ние действия иногда называются сопровождением программы.
246 Часть II. Программное обеспечение Команда должна отделяться от своих операндов символами пробела или табу- ляции. При наличии в команде двух операндов они отделяются друг от друга за- пятой. В командах, у которых в качестве операнда-адресата может выступать как рабочий регистр, так и регистр данных, в поле операнда-адресата следует писать символы w или f или числа 0 или 1 соответственно. При отсутствии явного указа- ния на операнд-адресат ассемблер по умолчанию задаст регистр данных, но при этом выдаст предупреждение программисту. Ассемблирование Программа ассемблера просматривает файл с исходным кодом, проверяя его на наличие синтаксических ошибок. При отсутствии последних она приступает к трансляции текста программы в абсолютный объектный код, представляющий, по большому счету, обычный машинный код, дополненный информацией, каса- ющейся адресов памяти программ, по которым он должен быть расположен. К синтаксическим ошибкам относится, в частности, ссылка на несуществующую метку или неизвестная команда. В результате работы программы формируется файл сообщений об ошибках, содержащий все подобные «проступки». Если син- таксические ошибки отсутствуют, то генерируется файл листинга и файл с ма- шинным кодом. Возвращаясь к нашему примеру, процесс трансляции запускается вводом строки mpasmwin /aINHX8M /е+ /1+ /с+ /rhex /pl6f84a root.asm где mpaswin.exe — программа ассемблера, a root.asm — наш исходный файл. Флаги задаются в виде /<опция> и могут сопровождаться знаком «+» или «-» соответственно для разрешения или запрещения данной опции. Так, ключ /е+ включает генерацию файла ошибки, /1+ — то же для файла листинга, /с+ делает метки чувствительными к регистру символов, /rhex задает основание счисления по умолчанию (шестнадцатеричное). Флаг /pl6f 84а указывает ассемблеру, что исход- ный код предназначен для модели PIC16F84A. Программа mpaswin может трансли- ровать код для всех микроконтроллеров PIC (с 12-, 14- или 16-битными ядрами). Листинг Файл листинга (см. Листинг 8.2) воспроизводит оригинальный исходный код, добавляя к нему шестнадцатеричное значение адреса каждой команды и ее код. В файл включается также таблица символов, перечисляющая все символы/метки, определенные в программе, например NUM указан как регистр h’26’. Карта ис- пользования памяти показывает использование памяти программ в графическом виде. Любые предупреждающие сообщения (warning) вставляются в файл листин- га в том месте, к которому они относятся. Например, если пропущен признак операнда-адресата (w или f), то ассемблер по умолчанию поставит последний и выведет в этом месте листинга предупреждающее сообщение. Этот файл используется только для документирования и не исполняется про- цессором.
Глава 8. Инструментальные средства для работы с языком ассемблера и 247 Листинг 8.2. Содержимое файла root_abs. 1st MPASM 4.02 Released ROOT_ABS.ASM 9-23-2005 17:15:57 PAGE 1 LOC OBJECT CODE LINE SOURCE TEXT VALUE 00000026 00001 00002 00001 00002 00003 00004 00005 00006 00007 00008 00009 00010 00011 00012 00013 00014 00015 00016 00017 00018 00019 00020 00021 00022 00023 00024 00025 00026 00027 00028 ; Глобальные объявления include "pl6f84a.inc" ; Заголовочный файл LIST ; P16F84A.INC Standard Header File, V2.00 ; Microchip Tech, Inc. cblock h'26' ; Начало блока переменных ; (с регистра h'26') NUM:2 ; Старший байт (NUM), ; младший байт(миМ+1) endc ; Конец блока 0000 0001 2200 0063 MAIN call sleep SQR_ROOT ; Фиктивный основной цикл Останавливаемся 00000028 0200 0200 01AA 0201 01A8 0202 01A9 f t * ; * ФУНКЦИЯ : Вычисляет корень квадратный * * из 16-битного целого * ; * ПРИМЕР : Number = h'FFFF'; (65,535), * * Root = h'FF' (255) * ; * ВХОД : Number в регистре NUM:NUM+1 * ; * ВЫХОД : Корень в W. NUM:NUM+1; * * 1:1+1 и COUNT измен. * . ********************************************* ; Локальные объявления cblock 1:2, COUNT:1 ; Магическое число ; и счетчик цикла endc org h'200' ; Код размещается в памяти ; программ начиная с h'200 SQR_ROOT clrf COUNT ; Задача 1: Обнулить ; счетчик цикла clrf I ; Задача 2: Инициализация ; магического ; числа единицей clrf 1+1 ;
248 Часть II. Программное обеспечение Листинг 8.2 (продолжение) 0203 0АА9 00029 incf I+l,f / 0204 0829 00030 00031 00032 ; Задача SQR_LOOP 3: ВЫПОЛНЯТЬ movf I + l,w ; Задача За: Number - I 0205 02А7 00033 subwf NUM+l,f ; Вычитаем из мл. байта 0206 0828 00034 movf I ,w ; исходного числа ; Берем старший байт 0207 1С03 00035 btfss STATUS,C ; магического числа ; ЕСЛИ не было заема 0208 ЗЕ01 00036 addlw 1 ;(С==1), ТО ; пропускаем ; Учитываем заем 0209 02А6 00037 subwf NUM,f ; Вычитаем старшие байты 020А 1С03 00038 00039 00040 ; Задача 36 : ЕСЛИ btfss потеря значимости, TO выйти STATUS,С ; ЕСЛИ нет заема (С==1), 020В 2А13 00041 goto SQR_END ; ТО продолжаем ; ИНАЧЕ вычисление 020С ОААА 00042 00043 incf COUNT,f ; завершено ; Задача Зв: ИНАЧЕ 020D 0829 00044 00045 movf 1 + 1, w ; инкрементируем ; счетчик цикла ; Задача Зг: Увеличиваем 020Е 020F ЗЕ02 1803 00046 00047 addlw btfsc 2 STATUS,C ; магическое ; число на 2 ; Если нет переноса, ТО 0210 0АА8 00048 incf I,f ; пропускаем ; ИНАЧЕ корректируем 0211 0212 0213 00А9 2А04 082А 00049 00050 00051 00052 SQR_END movwf goto movf 1 + 1 SQR_LOOP COUNT,w ; старший байт ; Задача 4: Возвращаем 0214 SYMBOL LABEL 0008 TABLE 00053 00054 - return end VALUE ; счетчик цикла ; в качестве значения ; корня С 00000000 COUNT 0000002А I 00000028 MAIN 00000000
Глава 8. Инструментальные средства для работы с языком ассемблера 249 Листинг 8.2 (продолжение) NUM SQR_END SQR_LOOP SQR_ROOT STATUS __16F84A 00000026 00000213 00000204 00000200 00000003 00000001 MEMORY USAGE MAP (’X’ = Used, = Unused) 0000 : XX------------------------------------------------------------------------ 0200 : XXXXXXXXXXXXXXXX XXXXX---------------------------- ---------------- All other memory blocks unused. Program Memory Words Used: 23 Program Memory Words Free: 1001 Errors : 0 Warnings 0 reported, 0 suppressed Messages : 0 reported, 0 suppressed Исполняемый код Конечным результатом любого процесса трансляции является объектный файл, иногда называемый также файлом в машинных кодах. После размещения указанного кода в памяти программ микроконтроллера он может запускаться как исполнимая программа. Как видно из Листинга 8.3, этот файл состоит из строк шестнадцатеричных чисел, представляющих двоичный машинный код, каждая из которых начинается с адреса размещения первого байта строки. Этот файл может использоваться про- грамматором для записи кода в ПЗУ программ по корректным адресам. Посколь- ку в файле явно указано местоположение каждого байта, такой тип файлов назы- вается файлом с абсолютным объектным кодом. Часть ПО программатора микро- контроллеров PIC (см. Рис. 17.4 на стр. 616), которая считывает, декодирует и помещает этот код в память программ устройства, иногда называют абсолютным загрузчиком. Листинг 8.3. Содержимое файла с абсолютным машинным кодом root_abs. hex :020000040000FA :040000000022 63 0077 :10040000АА01А801А901А90А2908А7022808031С12 : 10041000013ЕА602031С132ААА0А2908023Е031859 :0A042000A80AA900042A2A0808000F :00000001FF
250 Часть II. Программное обеспечение В мире микроконтроллеров/микропроцессоров используется много разнооб- разных форматов. Хотя большая часть этих стандартов де-факто присущи како- му-либо конкретному производителю, они в большинстве своем могут использо- ваться совместно с любыми марками микроконтроллеров. Формат файла с ма- шинным кодом, используемый нами, известен как «8-bit Intel hex» и был задан с помощью флага /alNHEX8M. Давайте попристальнее взглянем на одну из строк файла root_abs . hex. Признак начала записи с данными ; 10 Адрес (в байтах) первого значения Машинный код f clrf count clrf I clrf 1+1 incf 1+1, f movf 1+1,w submf NUM+1, f movf I,w btfss STATUS, C 0400 00 AAOT А80/ А90Г A90A 2908 A 702 2808 03/C 12 \ Тип записи = код Контрольная сумма Кол-во байтов данных в записи Загрузчик распознает запись по символу двоеточия. Двоеточие сопровождает- ся двухразрядным шестнадцатеричным числом, указывающим количество байтов машинного кода в этой записи; в данном случае оно равно h’10’ = d’16’. Следую- щие четыре шестнадцатеричных разряда являются начальным адресом данных. Поскольку память программ в микроконтроллерах PIC адресуется пословно, ад- рес команды h’200’ транслируется в адрес байта h’400’. Следующее 2-разрядное число является признаком записи: h’00’ — для нормальной записи и h’01 ’ — для записи конца файла (см. последнюю строку в Листинге 8.3). Основным содержимым записи является машинный код, в котором каждая команда записывается двумя 2-разрядными шестнадцатеричными числами в по- рядке младший байт:старший байт. Сначала загрузчик считывает младший байт (например, h’AA’), а затем добавляет к нему старший байт (например, h’01 ’), фор- мируя 12-, 14- или 16-битное слово в зависимости от модели целевого микрокон- троллера. Например, код команды clrf h' 2 А' для 14-битного ядра будет равен h’OlAA’0. Последний байт записи называется контрольной суммой. Контрольная сумма вычисляется как дополнительный код суммы всех предыдущих байтов записи, т.е. отрицательное значение суммы, игнорируя любые переполнения. Для контроля корректности передачи загрузчик складывает все принятые байты записи, вклю- чая байт контрольной суммы. Если при передаче не было ошибок, полученное число должно быть равно нулю. Такой формат размещения многобайтных значений (от младшего байта к старшему), принятый в компании Intel, называется форматом с прямым порядком байтов (little-endian). Существует также формат, при котором байты располагаются в обратном порядке. Соответ- ственно он называется форматом с обратным порядком байтов (big-endian). Этот формат используется преимущественно компанией Motorola.
Глава 8. Инструментальные средства для работы с языком ассемблера 251 Ассемблеры очень привередливы к синтаксису исходного кода. При наличии в исходном коде синтаксических ошибок^ будет сгенерирован файл сообщений об ошибках. Например, если бы при наборе 50-й строки была допущена ошибка: got SQRLOOP то был бы сгенерирован файл сообщений об ошибках, текст которого приведен в Листинге 8.4. Листинг 8.4. Содержимое файла сообщений об ошибках . Warning[207] ROOT_ABS.ASM 50 : Found label after column 1. (got) Error[122] ROOT_ABS.ASM 50 : Illegal opcode (SQRLOOP) Ассемблер не смог распознать слово got как команду или директиву и оши- бочно предположил, что это метка, начинающаяся в 1-й позиции строки. Исходя из этого предположения, он решил, что слово SQRLOOP — это мнемоническое обозначение команды/директивы, и опять же не смог его распознать. Большинство ассемблеров позволяет программисту определять последова- тельность команд процессора в виде макрокоманд. Такие макрокоманды могут в дальнейшем использоваться точно так же, как и обычные команды. Например, в приведенном ниже коде определяется макрокоманда Delay_lms* 2), которая реа- лизует задержку длительностью 1 мс при использовании 4-МГц резонатора. Пос- ледовательность «родных» команд заключена между парой директив macro — endm. Впоследствии эти команды будут подставлены ассемблером в текст про- граммы вместо мнемоники Delay_lms. Заметьте, это просто встраиваемый код, а не вызов подпрограммы. Delay_lms macro local LOOP LOOP movlw d'2501 ; Считаем от 250 addlw -1 ; Декрементируем btfss STATUS,Z ; до нуля goto LOOP ; endm При использовании в теле макрокоманды меток они должны быть объявлены в ней с помощью директивы local. Эта директива применяется для разрешения конфликта совпадения имен меток при многократном использовании макроко- манды в теле программы. Когда ассемблер сообщает о том, что ошибки в программе не найдены, многие склонны думать, что программа будет работать. К сожалению, отсутствие синтаксических ошибок никоим образом не гарантирует, что программа будет делать то, что нужно! 2) Я всегда пишу названия макрокоманд с большой буквы, чтобы отличать их от обычных команд микроконтроллера.
252 Часть И. Программное обеспечение Данный пример достаточно необычен, поскольку созданная нами «команда» не имеет операндов. Как и «родные» команды, макрокоманды могут иметь один и более операндов. Чтобы посмотреть, как это делается, напишем макрокоманду Впе (переход, если не равно нулю)1). Таким образом, команда Bne NEXT приве- дет к передаче управления на указанную метку, если флаг Z равен нулю, в против- ном случае выполнение программы продолжится со следующей команды. Мак- рокоманда Впе определяется следующим образом: Bne macro destination btfss STATUS,Z goto destination endm Обратите внимание, что имя макрокоманды не должно совпадать с именем реальной команды даже из другого семейства микроконтроллеров. Макрокоманды могут быть любой сложности и иметь любое количество опе- рандов, разделяемых запятыми. К примеру, компания Microchip предоставляет большое количество макрокоманд, реализующих различные арифметические операции, такие как умножение 16-битных и 32-битных чисел. Однако интенсив- ное использование макрокоманд может усложнить отладку программы, особенно в тех случаях, когда простая с виду макрокоманда имеет побочные эффекты в ви- де изменения содержимого регистров и состояния флагов. Частым источником ошибок является использование перед макрокомандой команд пропуска с целью обойти ее при некотором событии. Поскольку макрокоманда в реальности состо- ит из нескольких команд, выполнение команды пропуска приведет к переходу внутрь макрокоманды, причем с тяжелыми последствиями. Макроопределения, как приобретенные, так и написанные самостоятельно, можно собрать вместе в один файл и включать в пользовательскую программу ди- рективой include. Так, если ваш файл называется mymacro.mac, то наличие в начале программы строки include "mymacro.mac" позволит программисту использовать все макроопределения, описанные в этом файле. Любой макрос, определенный во включаемом файле, но не использую- щийся в программе, никоим образом не влияет на итоговый машинный код. Описанный выше процесс называется абсолютным ассемблированием. В этом случае исходный код располагается в единственном файле (ну, может быть, еще в нескольких включаемых файлах), и ассемблер помещает итоговый машин- ный код по известным (т.е. абсолютным) адресам памяти программ. Когда же программа состоит из нескольких модулей, очень часто написанных разными людьми или/и полученных из внешних источников и коммерческих библиотек, В старшем семействе PIC18XXXX имеется нормальная команда, выполняющая те же функции, — bnz (см. Табл. 16.1 на стр. 585).
Глава 8. Инструментальные средства для работы с языком ассемблера 253 необходимы средства для взаимного связывания соответствующих модулей с целью получения единственного файла с исполнимым машинным кодом. Напри- мер, вам может потребоваться вызвать модуль, который Фред пишет в данный момент. Вы не можете точно знать, в какой области памяти будет располагаться этот модуль или где будут храниться его переменные вплоть до завершения про- екта. Что же можно сделать в таком случае? Вы должны иметь возможность вызы- вать модуль FRED и ссылаться на его объекты, не зная, по какому адресу они будут размещены. Процесс создания программы, используемый в таких случаях, изображен на Рис. 8.3. Ключевую роль здесь играет программа компоновщика (или, иначе, ре- дактора связей), которая осуществляет поддержку таких перекрестных ссылок между модулями. Перед компоновкой файл с исходным кодом каждого модуля должен быть транслирован в перемещаемый объектный файл. Термин «перемещае- мый» означает, что окончательное местоположение модуля и различные адреса внешних меток еще не определены. Такая трансляция выполняется перемещае- мым ассемблером. В отличие от абсолютного ассемблирования, в данном случае область памяти, в которую будет помещен машинный код, определяется компо- новщиком, а не программистом, хотя абсолютные адреса, скажем, регистров пор- тов ввода/вывода, все равно могут задаваться. Файл листинга перемещаемого кода Д Файл ошибок I ▲ ассемблера Файл листинга абсолютного кода t Файл символов абсолютного кода I А Файл ошибок компоновщика Файл с исходным текстом Г“ Переме- щаемый . ассемблер объектный код Компоновщик Файл с абсолютным машинным кодом (абсолютный объектный код) Файлы с перемещаемым объектным кодом из других модулей и библиотек Загрузчик Двоичные данные/адреса Рис. 8.3. Перемещаемая трансляция с языка ассемблера Если рассматривать эту программу как разновидность компоновщика задач, то ее основными функциями будет следующее: • Объединение кода и данных различных входных модулей. • Присваивание числовых значений символьным меткам, которым не были заданы фиксированные значения самим программистом, с использованием директив equ и аналогичных. • Генерация файла с абсолютным машинным кодом, а также сопутствующих файлов символов, листинга и ошибок этапа компоновки.
254 Часть II. Программное обеспечение Чтобы компоновщик мог выполнить свою работу, он должен иметь представ- ление об архитектуре памяти целевого процессора, т.е. он должен знать, где начи- нается и где заканчивается массив регистров общего назначения, где в памяти программ размещаются вектора, а также по каким адресам может располагаться код программы. Вся эта информация находится в так называемом командном фай- ле компоновщика. Простой пример такого командного файла для модели PIC16F627 приведен в Листинге 8.5. Листинг 8.5. Содержимое командного файла компоновщика rms. Ikr // File: rms.Ikr // Simple linker command file for PIC16F627 Created 23/11/2003 CODEPAGE CODEPAGE NAME=vectors NAME=program START=0x0 START=0x5 END=0x4 END=0x3FF DATABANK NAME=gprs START=0x20 END=0x4F DATABANK NAME=auto START=0x50 END=0x6F SECTION NAME=STARTUP ROM=vectors // Reset and int vectors SECTION NAME=TEXT ROM=program // ROM code space SECTION NAME=BANK0 RAM=gprs // BankO static storage SECTION NAME=TEMP RAM=auto // Temporary auto storage В этом файле использованы три директивы1). codepage Директива codepage используется для описания памяти программ. В данном случае директива используется для задания двух областей памяти — области век- торов сброса и прерывания vectors, расположенной по адресам h’000’...h’004’, а также области program, расположенной в диапазоне адресов h’005’...h’3FF’ и ис- пользуемой для размещения исполнимого кода. Думаю, вы уже догадались, что префикс Ох используется для указания шестнадцатеричных значений. Такая но- тация используется в языке Си. databank Эта директива похожа по своему назначению на директиву codepage, но ис- пользуется для данных, размещаемых в ОЗУ. В данном случае группа регистров с адресами h’20’...h’4F’ названа gprO, а группа регистров с адресами h’50’...h’6F’ — auto. Первая группа регистров используется в качестве области памяти данных общего назначения 0-го банка, а вторая группа определяет область памяти, кото- рую программист может использовать для локальных переменных подпрограмм и которая освобождается после возврата из них. ’) Полный перечень директив компоновщика приведен в фирменном документе MPASM™ Assembler Users Guide with the MPLINK™ Object Linker and MP LIB'™.
Глава 8. Инструментальные средства для работы с языком ассемблера 255 section Эта директива компоновщика определяет две секции кода в памяти программ. Первая из них, названная startup, будет использоваться программистом для раз- мещения двух команд goto, расположенных по адресам имеющихся векторов, тогда как вторая, text, используется для хранения основного кода программы. Директива ассемблера code с соответствующей меткой, помещаемая в файл с ис- ходным кодом, сообщает компоновщику, в каком из двух блоков должен быть раз- мещен следующий за ней код (в качестве примера см. Программу 8.2). Таким об- разом, можно задать сколь угодно много секций кода. Например, все подпрограммы можно разместить в заданной области памяти программ, изменив командный файл компоновщика следующим образом: SECTION NAME=TEXT ROM=program 11 ROM code space SECTION NAME=SUBROUTINES ROM=program // ROM subroutine stream Кроме того, на секции можно разбить области памяти, заданные директивой databank, заменив атрибут RAM директивы CODEPAGE на атрибут ROM. В нашем случае определено две секции. Одна из них, названная BANK0, предназначена для хранения данных, существующих на протяжении всего времени выполнения про- граммы, а другая, названная temp, предназначена для хранения данных, которые можно перезаписывать после завершения подпрограммы. Директива ассемблера udata (Uninitialized DATA — неинициализированные данные) позволяет заре- зервировать пространство для меток в области регистров общего назначения. Ди- ректива udata_ovr (Uninitialized DATA OVeRlay — перегружаемые неинициали- зированные данные) сообщает ассемблеру о том, что данные регистры можно ис- пользовать между вызовами подпрограмм (см. Программу 8.4). Для иллюстрации принципов компоновки напишем программу, реализующую функцию вычисления среднеквадратичного значения д/мим_12+ NUM_22. Предположим, что над этой задачей работает три коллектива программистов^. За- дачи были распределены между ними руководителем проекта (четвертым челове- ком?) следующим образом; 1. Написание основной функции, выполняющей следующие действия: а) Возведение NUM_1 в квадрат. б) Возведение NUM_2 в квадрат. в) Сложение NUM_12 и NUM_22. г) Вычисление квадратного корня суммы (в). 2. Написание подпрограммы возведения в квадрат однобайтного числа, нахо- дящегося в рабочем регистре, которая возвращает двухбайтное значение в двух РОН. 3. Написание подпрограммы, вычисляющей значение корня квадратного из двухбайтного числа и возвращающей результат в W. 1) Разумеется, это слишком простая задача для коллективной разработки. Тем не менее на ее примере можно продемонстрировать основные принципы такого подхода.
256 Часть II. Программное обеспечение В графическом виде процесс разработки, основанный на такой декомпозиции задачи, приведен на Рис. 8.4. Рис. 8.4. Компоновка трех файлов с исходным кодом для реализации программы вычисления квадратного корня Текст основной функции приведен в Программе 8.2. Программа начинается с команды goto, расположенной по адресу вектора сброса и размещенной в сек- ции startup. А, начиная с метки main, код располагается в секции text за счет использования директивы text code. Из map-файла (генерируется компонов- щиком), содержимое которого приведено в Листинге 8.6, видно, что метке MAIN соответствует адрес h’005’. Программа 8.2. Основной перемещаемый исходный файл main. asm include extern "pl6f627.inc" SQR_ROOT, SQR, SQUARE BANKO udata Статические данные NUM_1 res 1 Первое число NUM_2 res 1 Второе число SUM res 2 Два байта суммы RMS res 1 Один байт результата STARTUP code goto MAIN Вектор сброса TEXT code MAIN movf NUM_l,w Берем 1-е число call SQR , ; Возводим его в квадрат movf SQUARE+l,w ; Берем младший байт movwf SUM+1 Он становится младшим байтом суммы movf SQUARE,w Берем старший байт movwf SUM Он становится старшим байтом суммы movf NUM_2,w ; Теперь берем 2-е число call SQR Возводим его в квадрат movf SQUARE+l,w ; Берем младший байт addwf SUM+1,f Прибавляем к младшему байту суммы btfsc STATUS,C Проверяем перенос
Глава 8. Инструментальные средства для работы с языком ассемблера 257 incf SUM,f ; Учитываем перенос movf SQUARE,w ; Берем старший байт addwf SUM,f ; Прибавляем к старшему байту суммы call SQR_ROOT ; Вычисляем корень квадратный movwf RMS ; Получаем среднеквадратичное значение sleep global end SUM ; Прекращаем вычисления В основной процедуре используется четыре переменных, расположенных в секции данных BANK0. Эта секция размещается в инициализированной области ОЗУ с помощью директив udata и res (REServe). Под каждую из входных пере- менных NUM_1 и num_2 зарезервировано по одному регистру. Под переменную SUM, в которой сохраняется значение суммы NUM_12 + NUM_22, зарезервирова- но два байта памяти данных. Поскольку эта переменная является входной для подпрограммы SQR_ROOT, она объявлена в конце файла как глобальная с помо- щью директивы global. Это означает, что ее расположение общеизвестно, так что дополнительные файлы, которые компонуются вместе, могут использовать имя SUM, объявляя его как extern, т.е. внешнее по отношению к файлу. Пере- менные, не объявленные таким образом, «скрыты» от внешнего мира, т.е. явля- ются локальными переменными. Таким образом, директива extern в заголовке Программы 8.2 позволяет основной процедуре вызывать подпрограммы SQR_ROOT и SQR, еще не зная, где они будут расположены. Точно таким же обра- зом переменная SQUARE используется подпрограммой SQR для возврата квадрата байта, переданного ей в регистре W. Место под эту переменную резервируется в области РОН в подпрограмме SQR, и ее точное положение в памяти данных фай- лу main.asm неизвестно, оно будет распределено позже компоновщиком. Из тар- файла, текст которого приведен в Листинге 8.6, видно, что в конечном счете эта переменная была размещена в регистрах h’25’:h’26’ (старший:младший байты). Основная часть кода выполняет перечисленные выше задачи. Значение NUM_12 помещается в регистры SUM:SUM+1, к которым впоследствии будет прибавлено значение NUM_22. Затем результат передается в подпрограмму SQR_ROOT, возвращающую в рабочем регистре значение квадратного корня. В за- ключение это значение сохраняется в регистре RMS, под который был зарезерви- рован один байт в секции данных BANK0. Подпрограмма sqr. asm, текст которой приведен в Программе 8.3, базируется на подпрограмме из Программы 6.7 (стр. 186), выполняющей перемножение двух- байтных значений. В нашем случае при входе в подпрограмму содержимое рабо- чего регистра копируется в регистр с именем X, а в регистрах X_COPY_H:X_COPY_L формируется 16-битная копия этого значения. Используя алгоритм сдвига и сложения, вычисляется значение X х X = X2. Эти три регистра размещаются в секции TEMP с помощью директивы udata_ovr, которая сообща- ет компоновщику о том, что эти регистры могут повторно использоваться другими модулями. Из map-файла можно увидеть, что переменная х была размещена в ре-
258 Часть II. Программное обеспечение гистре h’5O’, как и переменная I, использующаяся в подпрограмме SQRJROOT (см. Программу 8.3). За счет этого достигается гораздо более эффективное использова- ние памяти данных. Переменные, существующие только в пределах той подпро- граммы, в которой они определены, в языке Си называются автоматическими (automatic), поскольку занимаемая ими память автоматически перераспределяет- ся по мере необходимости. Если же память под переменную выделяется фиксиро- ванно, то такая переменная называется статической (static). Глобальные перемен- ные, такие как SQUARE, всегда объявляются как статические. В нашем случае пе- ременная SQUARE создается резервированием двух байтов данных в секции BANK О с использованием директивы udata. Она также публикуется с использованием директивы global, поскольку является именем подпрограммы. Программа 8.3. Перемещаемый исходный файл sqr. asm include ”pl6f627.inc" ; Подпрограмма SQR . ************************************************************ f ; * ФУНКЦИЯ : Возводит в квадрат 1-байтное число и возвращает* ; * 2-байтный результат * ; * ПРИМЕР : X = 10h (16), SQUARE = OlOOh (256) * ; * ВХОД : X в W * ; * ВЫХОД : SQUARE:2 в области неинициализированных данных * ************************************************************ BANK0 SQUARE udata res 2 ; Статические данные ; Старший:младший байты квадрата TEMP udata. _ovr ; Автоматические данные X res 1 ; х X_COPY_L res 1 ; Копия X X_COPY_H res 1 ; Старший байт X TEXT code ; Задача 1: Обнуляем 2-байтное значение квадрата SQR clrf SQUARE clrf SQUARE+1 ; Задача 2: Копируем и расширяем X до 16 битов movwf X ; Сохраняем X в памяти данных movwf X_COPY_L ; Создаем копию X clrf X_COPY_H ; и расширяем до двух байтов ; Задача 3: ВЫПОЛНЯТЬ ; Задача За: Сдвигаем X на один бит вправо SQR_LOOP bcf STATUS,С ; Сбрасываем бит переноса rrf X,f ; Сдвигаем ; Задача 36: ЕСЛИ С == 1, ТО прибавить сдвинутое значение X к квадрату
Глава 8. Инструментальные средства для работы с языком ассемблера и 259 btfss goto STATUS,C SQR_CONT ; ЕСЛИ C -= 1, TO складываем ; ИНАЧЕ пропускаем эту задачу movf X_COPY_L,w ; ВЫПОЛНЯЕМ сложение addwf SQUARE+l,f ; Сначала младшие байты btfsc STATUS,C ; ЕСЛИ нет переноса, ТО переходим к старшим байтам incf SQUARE,f ; ИНАЧЕ учитываем перенос movf X_COPY_H,w ; Теперь старшие байты addwf SQUARE,f ; Задача Зг: Сдвигаем 1б-битную копию X на один бит вправо SQR_CONT bcf STATUS,C ; Сбрасываем бит переноса rlf X_COPY_L,f rlf X_COPY_H,f ; ПОКА X не равен нулю movf X,f ; Проверяем множитель на ноль btfss STATUS,Z goto SQR_LOOP ; ЕСЛИ не ноль, ТО повторяем вычисления FINI return ; ИНАЧЕ выходим global SQUARE, SQR end Код последней подпрограммы приведен в Программе 8.4. Эта программа практически идентична Программе 8.1. Отличие между ними заключается в заме- не директивы org на TEXT code и cblock на TEMP udata_ovr для распреде- ления автоматических локальных переменных. Данные передаются в подпро- грамму посредством 2-байтной глобальной переменной SQR_ROOT, которая объ- явлена как внешняя (место под эту переменную было выделено в файле main.asm). Имя подпрограммы SQR_ROOT опубликовано как глобальное, чтобы ее было видно из файла main.asm. Программа 8.4. Перемещаемый исходный файл root. asm include "pl6f627.inc" extern SUM ; 2-байтное число (старший:младший) TEMP udata. _ovr ; Автоматические переменные I res 2 ; Магическое число (старший:младший) COUNT res 1 ; Счетчик цикла TEXT code SQR_ROOT clrf COUNT ; Задача 1: Обнулить счетчик цикла clrf I ; Задача 2: Записать 1 в магическое число clrf 1 + 1 incf I+l,f
260 Часть И. Программное обеспечение SQR_END SQR_LOOP movf I+l,w ; Задача За: Number - I subwf SUM+l,f , ; Вычитаем мл. байт I из мл. байта Num movf I, w , ; Берем старший байт магического числа btfss STATUS,C ; ; Пропускаем, ЕСЛИ не было заёма addlw 1 ; Корректируем заём subwf SUM,f ; ; Вычитаем старшие байты btfss STATUS,C ; ; ЕСЛИ нет заёма, ТО продолжаем goto SQR.END ; : ИНАЧЕ процесс завершен incf COUNT,f ; : Задача 36: ИНАЧЕ инкрементируем счетчик цикла movf I+l,w ; ; Задача Зв: Увеличиваем магическое число на 2 addlw 2 btf sc STATUS,C ; : ЕСЛИ нет переноса, ТО продолжаем incf i,f ; : ИНАЧЕ прибавляем перенос к старшему байту movwf i+l goto SQR_LOOP movf COUNT,w ; ; Задача 4: Возвращаем счетчик цикла в качестве : корня return global end SQR_ROOT Как и во всех исходных файлах, в файле root.asm используются различные ре- гистры специального назначения, такие как STATUS. Поэтому заголовочный файл pic 16f627.inc включается в каждый из исходных файлов. Поскольку содер- жимое данного файла представляет собой набор директив equ, имена, определяе- мые в этом файле, публикуются как абсолютные и не затрагиваются компонов- щиком. По этой причине в map-файле (Листинг 8.6) эти фиксированные иденти- фикаторы не указываются. Однако они выводятся в файл листинга, генерируемый компоновщиком. Чтобы связать вместе эти три исходных файла, в командной строке при запус- ке компоновщика перечисляются имена входных объектных файлов, имя команд- ного файла компоновщика и имена map-файла и файла с машинным кодом. В на- шем случае эта строка будет следующей: mplink.exe rms.lkr main.о sqr.о root.о /ш rms.map /о rms.hex Понятно, что сгенерированный map-файл будет называться rms .map, а файл с абсолютным машинным кодом — rms . hex. Для документирования проекта компоновщик генерирует составной файл листинга, похожий (но более полный) на файл, текст которого приведен в Листинге 8.2, и опциональный map-файл. Как видно из Листинга 8.6, этот файл состоит из двух списков. В первом из них приводится информация по каждой । секции. Список включает имя секции, тип, начальный адрес, местоположение I
Глава 8. Инструментальные средства для работы с языком ассемблера 261 секции (в памяти программ или памяти данных) и ее размер в байтах. Из таблицы использования памяти программ (Program Memory Usage) видно, что было ис- пользовано 63 ячейки памяти программ, включая два байта вектора сброса ко- манды goto — или примерно 6% от имеющегося объема. Листинг 8.6. Содержимое map-файла rms .тар, генерируемого компоновщиком MPLINK 3.80, Linker Linker Map File - Created Sat Jan 08 23:09:26 2005 Section Info Section Type Address Location Size(Bytes) STARTUP code 0x000000 program 0x000002 .cinit romdata 0x000001 program 0x000004 TEXT code 0x000005 program 0x000078 BANK0 udata 0x000020 data 0x000007 TEMP udata 0x000050 data 0x000003 Program Start Memory Usage End 0x000000 0x000005 63 out of 1024 program addresses 0x000002 0x000040 used, program memory utilization is 6 Symbols - Sorted by Name Name Address Location Storage File FINI 0x00002b program static sqr.asm MAIN 0x000005 program static main.asm SQR 0x000016 program extern sqr.asm SQR_CONT 0x000025 program static sqr.asm SQR_END 0x00003f program static root.asm SQR_LOOP 0x00001b program static sqr.asm SQR_LOOP 0x000030 program static root.asm SQR_ROOT 0x00002c program extern root.asm COUNT 0x000052 data static root.asm I 0x000050 data static root.asm NUM_1 0x000020 data static main.asm NUM_2 0x000021 data static main.asm RMS 0x000024 data static main.asm SQUARE 0x000025 data extern sqr.asm SUM 0x000022 data extern main.asm X 0x000050 data static sqr.asm X_COPY_H 0x000052 data static sqr.asm X_COPY_L 0x000051 data static sqr.asm
262 Часть II. Программное обеспечение Во второй таблице выводится информация об идентификаторах, используе- мых в итоговой программе. Приводится информация о месте расположения каж- дого идентификатора в памяти программ или данных, а также имя исходного файла, в котором он объявлен. Глобальные идентификаторы помечаются словом extern, а идентификаторы локальных переменных помечаются словом static (к ним относятся и автоматические переменные, такие как COUNT и X_COPY_H, которые располагаются в регистре h’52’). Итоговый файл, приведенный в Листинге 8.7, представляет собой обычный исполнимый файл в машинных кодах, который можно загрузить в память про- грамм и запустить обычным образом. Листинг 8.7. Содержимое итогового абсолютного объектного файла rms. hex : 020000000528D1 =040002000034003492 : 0600ОАО0200816202608б4 : 10001000А3002508А200210816202608А30703181С :10002000А20А2508А2072С20А4006300А501А601АЕ :10003000D000D100D2010310D00C031C2528510898 :10004000A6070318A50A5208A5070310D10DD20D63 :10005000D008031D1B280800D201D001D101D10A0C : 100060005108A3025008031C013EA202031C3F28B2 :10007000D20A5108023E0318D00AD1003028520893 =02008000080076 : 00000001FF Разработка, тестирование и отладка программного обеспечения требуют большого числа различных программных средств. С некоторыми из них мы уже познакомились — это редактор, ассемблер и компоновщик. На самом деле су- ществует много других пакетов программ, таких как компиляторы языков высо- кого уровня (см. главу 9), симуляторы и программаторы EEPROM. Все эти паке- ты условно показаны на Рис. 8.5. Настройка данных программных средств и обес- печение взаимодействия между ними в индивидуальном порядке может представлять собой достаточно сложную задачу, особенно при использовании продукции разных производителей. В последнем случае обеспечение совмести- мости между различными форматами промежуточных файлов может превратить- ся в сущий кошмар. Многие компании, занимающиеся разработкой инструментальных средств, предлагают графические среды, делающие процесс разработки программ прос- тым и интуитивно понятным. Что касается микроконтроллеров PIC, то компа- ния Microchip Technology предоставляет интегрированную среду разработки (ИСР) MPLAB®, которая объединяет полностью совместимые средства разра- ботки программ под одной «крышей». Как и все программные продукты компа- нии (за исключением компилятора языка Си), ИСР MPLAB распространяется свободно.
Глава 8. Инструментальные средства для работы с языком ассемблера 263 Рис. 8.5. Инструментальные средства создания и отладки программного обеспечения Среда MPLAB осуществляет интеграцию Microchip-совместимых програм- мных средств с целью создания законченной среды для разработки ПО. В част- ности, в состав MPLAB входят следующие программы: • Менеджер проектов, который группирует заданные файлы, относящиеся к данному проекту; например, файлы с исходным кодом, объектные файлы, файлы симуляции, файлы листингов и hex-файлы. • Редактор для написания исходных файлов и командных файлов компоновщика. • Ассемблер, компоновщик и библиотекарь для трансляции исходного кода и со- здания библиотечных модулей, которые могут использоваться компоновщиком. • Симулятор для моделирования процесса исполнения команд и ввода/выво- да на персональном компьютере (см. Рис. 8.7). • Загрузчик, который используется совместно с программатором, подключаемым к компьютеру через последовательный порт или USB (см. Рис. 17.4 на стр. 616). • Программное обеспечение для эмуляции микроконтроллеров PIC в режиме реального времени на целевой аппаратуре. При этом вместо целевого про- цессора к плате устройства подключается внутрисхемный эмулятор (ICE) или отладчик, управляемый через последовательный порт или USB. В фирменном руководстве пользователя MPLAB IDE User’s Guide содержится учебник и подробная информация по ИСР MPLAB, рассмотрение которой выхо- дит за рамки данной книги. Тем не менее, исключительно для иллюстрации, при- веду два скриншота, полученных во время разработки предыдущего примера, в
264 Часть II. Программное обеспечение котором используются файлы main.asm, sqr.asm и root.asm, как изображено на Рис. 8.6 и Рис. 8.7. На Рис. 8.6 показано окно, отображающее содержимое проекта (файл rms.тер), сформированного после работы начального «мастера». Проект включа- ет три исходных файла, созданных ранее при помощи редактора. Кроме того, в проекте присутствует командный файл компоновщика rms.lkr, который также был создан и сохранен ранее. Итоговый файл с машинным кодом будет называть- ся rms.hex. В rms.mcw В rms.mcp ESource Files ; I-main.asm ; i-root.asm L sqr.asm Header Files г Object Files r Library Files В- Linker Scripts •rms.lkr L Other Files Puc. 8.6. Окно проекта ИСР MPLAB версий 6.x, отображающее имена файлов, используемых для ассемблирования, компоновки и симуляции Программы 8.2 После того как проект создан, можно приступать к выполнению следующих операций: 1. Ассемблирование файла main.asm для получения объектного файла main.o. 2. Ассемблирование файла sqr.asm для получения объектного файла sqr.o. 3. Ассемблирование файла root.asm для получения объектного файла root.о. 4. Компоновка объектных файлов, полученных на этапах 1...3, в соответствии с командным файлом rms.lkr. 5. При отсутствии синтаксических ошибок создание абсолютного исполняе- мого файла, содержимое которого приведено в Листинге 8.7. Для этого необходимо выбрать в меню Project (четвертый пункт слева на Рис. 8.7) команду Make Project. При обнаружении синтаксических ошибок на эк- ране появится окно ошибок со списком. Двойной щелчок на любой ошибке вы- зовет переход к соответствующему окну с исходным кодом и установке курсора на строку, в которой эта ошибка была обнаружена.
Глава 8. Инструментальные средства для работы с языком ассемблера и 265 м MPLAB IDE V6.60 Eile- J-jdit Miew Eroject debugger ftogremrnsr Gwfigure $jndw balp........................ 1 Э a W AM fl i*i «"*»•» e«5«s I i n tv н » fl» 4-1 »C:\yandy\document\Book9\Chapter8''main.asni RRE3jg®>’. 4< •> i*4K_»*uuf> ьцк., >quAr.ir ? Static data *** 1 ; The first number 1 ; Thr secund number 2 ; Two bytes Hl:to for the sum» 1 ; one byte for rhe outcome main ; The Reset vector NUM„l,w ; Get Number 1 SQR ; square it SQUAREtl.w ; cet lower byte SUNtl ; thr lew byte of sum SQUARE,* ; det upper byte SUH ; lx the high byte of sum NUN—I,** ; Now get Number 2 SQR ; square it square*t,w ; dec lower byte siWil.f ; Add to the low byte of r»um 5TATUS«C ; Check if produces carry SUN,f ; Add the carry SQUARt.,w ; set upper byte SUN,f ; Add tn the high hyte nf sum SQR..Rqor ; Work out the square rout rms ; к5 rhe root mean square incf Itl,f SQR.tOOP movf n-l,w ; Task »«): Number ' subwf su»*l,f ; subtract lo byte 1 t&g movf i,w ; Get nigh byte maqtc < btfss STATUS,c ; skip if No Borrow ou v addlw 1 ; Return borrow йй: subwf SUM.f i Subtract high bytes btfss STAiilS.C ; XF No Borrow THIN cos goto sqr_end ; Else the process is incf count,f ; Task SCc^: test inc x res 1 ; rlate for X x cupv i, res 1 ; Holds a copy of x x_COf*Y_H res 1 ; Copy x overflow hi byte i rtxi code ; Task J: Zero doublr-byte square : SQR Clrf SQUARE Clrf SQUARIK1 i ; Task 2c copy and extend X to 16-bjts movwf X ; rut X away into Pate memory movwf x copy .i ; copy of x clrf x„COPY_H ; and extend tn double byte , 1 ; rask Jt DO ; task ЗЛ: shi ft x right once < f Z ~' Puc. 8.7. Снимок экрана при работе ИСР MPLAB версий 6.x во время симуляции проекта, приведенного на Рис. 8.6 После успешного создания программы можно выполнить ее симуляцию. При этом ПК моделирует поведение микроконтроллера PIC, т.е. выполнение его ко- манд и функционирование периферийных модулей. Пользователь может в лю- бой момент сбросить симулируемый микроконтроллер, установить точки оста- нова, выполнять программу в пошаговом или нормальном режиме. Во время си- муляции можно наблюдать за содержимым заданных регистров или даже всей памяти данных. Разумеется, скорость выполнения программы при симуляции будет на несколько порядков ниже, чем при использовании реального микро- контроллера. Симуляцию можно запустить из меню Debugger. Пункты этого меню вынесе- ны на отдельную панель инструментов Debugger (справа вверху на Рис. 8.7). В ре- жиме симуляции оператор может: • Сбросить виртуальный процессор, нажав на кнопку S& . • Запустить о симуляцию на максимальной скорости и приостановить DD | ее. Автоматически выполнять программу ы>| со скоростью несколько шагов в секунду.
266 Часть II. Программное обеспечение • Выполнять программу пошагово в трех различных режимах (по одной стро- ке при каждом щелчке по соответствующей кнопке): — Шаг с заходом — проходит по всей программе, включая подпрограммы. — Шаг без захода — обходятся подпрограммы (они выполняются с максимальной скоростью). - Шаг с выходом {*? — код подпрограммы выполняется за один шаг. На Рис. 8.7 показан конечный результат симуляции нашей программы вычис- ления среднеквадратичного значения. Как и все три окна с исходными файлами, окно просмотра переменных (Watch) было открыто с помощью меню View. В это окно оператор может добавлять любые именованные регистры общего назначе- ния, такие как NUM_1, значение которого может отображаться в двоичном, де- сятичном и шестнадцатеричном виде с различной разрядностью (побитово, один байт, два байта, три байта). Эти значения обновляются после каждого выполне- ния симулятором одиночного или автоматического шага. При работе на макси- мальной скорости обновление окна Watch производится при приостановке симу- ляции или остановке выполнения программы в точке останова. Кроме того, на Рис. 8.7 показано содержимое окна Stop-watch. Из данных это- го окна следует, что для выполнения программы потребовалось 292 машинных цикла при начальных значениях NUM_1 и NUM_2 соответственно 0x05 и 0x08. Поскольку симулировалась работа с кварцевым резонатором частотой 8 МГц, время выполнения программы составило 146 мкс. В процессе симуляции выполняемая в данный момент команда помечается символом Ф в левом поле соответствующего окна с исходным кодом. На Рис. 8.7 этот символ указывает на последнюю команду sleep и нарисован поверх симво- ла точки останова ф|. Точки останова можно устанавливать или сбрасывать, вы- полняя щелчок правой кнопкой мыши на соответствующей команде. При нажа- тии на кнопку * программа будет выполняться с максимальной скоростью до тех пор, пока не встретится следующая точка останова. Симуляция не позволяет выявить все проблемы, особенно те, которые связа- ны со сложным взаимодействием программной части и аппаратных средств. Од- нако более 95% всех проблем вызываются исключительно ошибками при написа- нии программы, и симуляция представляет собой хороший инструмент для тес- тирования и отладки подобного кода. Например, наша программа не будет работать, если результат операции NUM_12 + NUM_22 > 65535, поскольку размер переменной SUM составляет два байта (см. Вопрос для самопроверки 8.5). При отладке всегда необходимо первым делом проверить функционирование программы при максимально и минималь- но возможных значениях переменных. Тем не менее такая проверка никоим об- разом не гарантирует корректную работу программы при всех возможных сочета- ниях значений входных переменных.
Глава 8. Инструментальные средства для работы с языком ассемблера 267 В заключение приведем общую информацию, специфичную для Microchip- совместимых ассемблеров, которая может вам понадобиться при чтении про- грамм из оставшейся части книги: • Представление чисел. — Шестнадцатеричные: начинаются с символа «Ь», после которого следует шестнадцатеричное число, заключенное в кавычки, например Ь’4Г. Также может использоваться завершающий символ «Ь» (например, 41h) или префикс «Ох» (например, 0x41). Как правило, в ассемблере это основание используется по умолчанию, поэтому в некоторых программах указатели шестнадцатеричной системы могут быть опущены. Однако лучше на это не полагаться. — Двоичные: начинаются с символа «Ь», после которого следует двоичное число, заключенное в кавычки, например b’01000001 — Десятичные: начинаются с символа «d», после которого следует десятичное число, заключенное в кавычки, например d’65’. Также могут обозначаться префиксом в виде точки (.65 в нашем случае). - Символы ASCII: заключаются в одинарные кавычки, например ’А’. • Арифметические операции с метками. - Текущее положение в программе: обозначается $, например goto $+2. — Сложение: +, например, goto LOOP+6. — Вычитание:—, например, goto LOOP-8. — Умножение: *, например, subwf LAST*2. — Деление: /, например, subwf last/2. • Директивы. — org: помещает последующий код в память программ, начиная с указанного адреса, например org h*100'. Если директива org не используется, то код размещается, начиная с адреса вектора сброса, т.е. h’000’. Может использоваться только при абсолютном ассемблировании. — code: аналог директивы org для перемещаемого ассемблирования. Реальный адрес секции кода определяется в командном файле компоновщика. В данном файле может быть определено более одной секции кода, в этом случае их имена записываются в поле меток, например SUBROUTINES code. - equ: связывает числовое значение с символьным именем, например PORTB equ Об. Вместо директивы equ можно использовать директиву #de fine (заимствованную из языка Си): #de fine PORTB Об. - cblock...endc: используется при абсолютном ассемблировании для размещения переменных программы в памяти данных, например: cblock h’20’ FRED ; Один байт по адресу h'201 для переменной FRED ЛМ:2 ; Два байта по адресам h’21’ : h’22’ для переменной JIM ARRAY:10 ; Десять байтов по адресам h’23'...h'2C для переменной ARRAY endc После первого использования директивы cblock указывать адрес необязательно.
268 Часть II. Программное обеспечение — udata: аналог cblock для перемещаемого ассемблирования. Начальный адрес блока в памяти данных указывается в командном файле компоновщика. В этом файле может быть определено более одной секции данных, в таком случае их имена записываются в поле меток, например: SCRATCHPAD udata ; Секция неинициализированных данных FRED ; Резервируется один байт для переменной FRED JIM:2 ; Резервируется два байта для переменной ЛМ ARRAY: 10 ; Резервируется десять байтов для переменной ARRAY - udata_ovr: эта директива аналогична udata, за исключением того, что компоновщик пытается повторно использовать регистры, выделенные под объявленные таким образом переменные. - res: используется совместно с директивой udata для резервирования одного или более байтов под переменную в секции данных. — extern: публикует именованные переменные как определенные вне текущего файла. Впоследствии эти переменные связываются компоновщиком. — global: публикует именованные переменные, которые были определены (т.е. под них было зарезервировано место в памяти) в данном файле, и таким образом делает их видимыми для компоновщика. - macro...endm: используется для замены последовательности команд процессора, помещенных между указанными директивами, одной макрокомандой, например макрокоманда: Addf macro N,datum movf datum,w addlw N movwf datum endm прибавляет константу N к заданному регистру datum. Соответственно для прибавления 5 к регистру h’20’ программист может использовать вы- зов Addf 5,h'20'. - include: используется для включения содержимого указанного файла в точке использования директивы, например include "myf ile . asm". Вместо нее можно использовать аналогичную директиву #include. — end: обычно размещается в последней строке исходного ассемблерного файла. Сообщает ассемблеру о том, что содержимое файла, расположенное после нее, следует игнорировать. Примеры Пример 8.1 Следующая последовательность команд позволяет выполнить обмен содер- жимого рабочего регистра и регистра F, не используя при этом никаких дополни- тельных регистров.
Глава 8. Инструментальные средства для работы с языком ассемблера 269 xorwf F,f xorwf F,w xorwf F,f ; [F] <- WAF ; W <- WA(WAF) = 0AF = F ; [F] <- FAWAF = 0AW = W Символ «Л» означает операцию Исключающее ИЛИ. Создайте из этой последовательности макрокоманду Exgwf F, в которой F является заданным регистром, например Exgwf h' 2 0 '. Решение Обрамив код соответствующими директивами, получим макрокоманду Exgwf macro FILE xorwf FILE,f xorwf FILE,w xorwf FILE,f endm Обратите внимание, что эта макрокоманда не влияет на состояние флага С, а флаг Z устанавливается в соответствии с содержимым рабочего регистра, которое было в нем при вызове макрокоманды. Пример 8.2 Линейка микроконтроллеров PIC18XXXX имеет команду Ьпс (перейти, если не было переноса), которая выполняет переход по указанному адресу при нуле- вом значении флага переноса С. Напишите макрокоманду, выполняющую те же действия, для микроконтроллеров с 12- и 14-битным ядром. Решение Для написания этого кода мы возьмем макрокоманду Ьпе, текст которой при- веден на стр. 225, и заменим флаг Z флагом С. Назовем полученную макрокоман- ду Вес (Branch if Carry Clear), поскольку имя Ben в ассемблерах версий 3+ явля- ется зарезервированным словом, т.е. мнемоническим обозначением команды микроконтроллеров PIC18XXXX. Зсс macro destination btfss STATUS,С goto destination endm Пример 8.3 Напишите макрокоманду, формирующую задержку длительностью п машин- ных циклов, где п является целым числом не более 1024. Так, например, написав в программе строку Delay_cycles d1 400 ', мы должны будем получить задерж- ку длительностью 400 машинных циклов.
270 Часть II. Программное обеспечение Решение В макрокоманде, приведенной ниже, один проход цикла выполняется за 4 ма- шинных цикла, поэтому операнд макроса делится на четыре для получения на- чального значения счетчика цикла. В указанном примере операнд 400 загружает- ся в рабочий регистр как число d’ 100’. Delay_cycles macro cycles local LOOP movlw cycles/4 ; Один проход - 4 маш. цикла LOOP addlw -1 ; Декрементируем btfss STATUS,Z ; Ноль? goto LOOP endm Метка, используемая в макрокоманде, объявлена при помощи директивы local, чтобы гарантировать, что при каждом использовании макрокоманды имя LOOP не будет добавляться в таблицу идентификаторов транслятора. Если бы мы не сделали этого, то при повторном использовании макрокоманды возникла бы ошибка «Address label duplicated» (дублирование метки). Пример 8.4 Макрокоманды могут быть вложенными, т.е. при написании одной макроко- манды можно использовать другие. В качестве примера напишем макрокоманду в которой РОН инициализируется заданным значением, а затем декрементирует- ся до нуля. Предполагая, что макрос Movlf уже определен: Movlf macro literal,destination movlw literal ; Загружаем константу в W movwf destination ; и пересылаем ее в заданный регистр endm напишите код требуемой макрокоманды. Решение Возможное решение выглядит следующим образом: Countdown macro literal/destination local C_LOOP ; Метка макрокоманды Movlf literal,counter ; Инициализируем счетчик CLOOP decfsz counter,f ; Декрементируем goto C_LOOP ; Повторяем, пока не равно нулю endm
Глава 8. Инструментальные средства для работы с языком ассемблера 271 Заданный регистр, обозначенный именем counter, сначала инициализиру- ется константой с помощью макрокоманды Movlf. Операция обратного счета ре- ализована с помощью команды decfsz, которая как декрементирует содержимое регистра, так и осуществляет выход из цикла при достижении нуля. Таким обра- зом, вставка строки Countdown d'100'^'40' проинициализирует регистр h’40’ десятичным числом 100 и декрементирует его до нуля. Этот процесс займет (3 х count) + 1 циклов задержки, т.е. 301 цикл в нашем случае. Заметьте, что при выполнении этой макрокоманды помимо заданного РОН изменяется также содержимое рабочего регистра и регистра STATUS. Такие по- бочные эффекты очень опасны при использовании макрокоманд, особенно если такая макрокоманда была написана кем-то другим и ее код скрыт от нас во вклю- чаемом файле. На всякий случай всегда считайте, что регистры W и STATUS из- менились, пока не доказано обратное. Смена банков памяти внутри макроопре- делений также несет в себе потенциальную опасность. Пример 8.5 Модель PIC16F84 имеет одну особенность, а именно: в ней все РОН отобра- жены на оба банка памяти (см. Рис. 4.7 на стр. 97). Обычно в каждом банке памя- ти располагаются уникальные РОН. Например, модели PIC16F627/8 имеют 80 уникальных РОН в 0-м банке, 80 уникальных РОН в 1-м банке, 48 уникальных РОН во 2-м банке, а также 16 общих РОН, отображенных на все четыре банка па- мяти (см. карту памяти, приведенную на Рис. 5.4, стр. 121). Чтобы выбрать регистр в 1-м банке, необходимо соответствующим образом изменить биты RP0:RPl регистра STATUS. Так, для копирования содержимого рабочего регистра в регистр h’EO’ требуется следующее: bsf STATUS,RP0 ; Переключаемся на 1-й банк bcf STATUS,RP1 movwf h'EO1 ; Копируем W в регистр h'EO' bcf STATUS,RPO ; Переключаемся обратно на 0-й банк При использовании перемещаемого ассемблера программист не всегда знает, в какой из банков компоновщик поместил переменную. Более того, при измене- нии набора исходных файлов и их содержимого номер этого банка может произ- вольно изменяться на различных этапах проекта! Чтобы обойти эту проблему, в ассемблере имеется директива выбора банка banksei. Эта директива автоматически отслеживает местоположение именован- ной переменой и вставляет в программу соответствующий код, учитывающий из- менения. Покажите, как следует использовать эту директиву при сохранении де- сятичных констант 1,10,100 в трех РОН, названных var_0, var_l и var_2 соот- ветственно.
272 Часть II. Программное обеспечение Решение Возможная последовательность команд приведена ниже. Директива вставляет в код перед выполнением следующей команды соответствующую комбинацию команд bs f STATUS,RPx И be f STATUS,RPx. movlw banksei movwf 1 var_0 var_0 ; Первая константа ; Переключаемся на соответствующий банк ; Сохраняем movlw d' 10' ; Вторая константа banksei var_l ; Переключаемся на соответствующий банк movwf var_l ; Сохраняем movlw d'100' ; Третья константа banksei var_2 ; Переключаемся на соответствующий банк movwf var_2 ; Сохраняем При использовании косвенной адресации в моделях с 4 банками памяти, не- обходимо соответствующим образом изменять бит IRP регистра STATUS (см. стр. 127). Для этого предназначена директива bankisel, использующаяся анало- гично директиве banksei. Вопросы для самопроверки 8.1. Напишите макрокоманды, аналогичные командам условного перехода be (переход при переносе) и bz (переход при нуле), имеющимся в моделях PIC18XXXX. 8.2. Напишите макрокоманду, которая реализует функцию PRODUCT: 2 = VARI х VAR2 (формат вызова макрокоманды — Mui xplier, xcand, PRODUCT). Подсказка: обратите внимание на Программу 6.7, приведенную на стр. 186. Как вы думаете, какие есть пре- имущества и недостатки использования макрокоманд вместо подпрограмм при большом объеме составляющего их кода, как в данном случае? 8.3. В кодах команд goto и call используется 11-битный адрес, позволяющий выполнять переход в пределах 2 Кбайт памяти программ (см. Рис. 5.17 на стр. 153). Как видно из этого рисунка, содержимое счетчика команд замеща- ется 11-битным адресом, содержащимся в коде команды совместно с битами 4:3 регистра PCLATH (h’OA’) для формирования полного 13-битного адреса. Некоторые микроконтроллеры среднего уровня имеют память программ объемом 4 или 8 Кбайт (скажем, PIC16F74 и PIC16F876 соответственно). Для формирования 13-битного значения счетчика команд при использова- нии команд goto и call в этих моделях тоже используются биты PCLATH[4:3] (см. стр. 117), разбивая память программ по сути дела на две или четыре страницы. Программист должен самостоятельно устанавливать эти биты для выбора страницы перед вызовом команд goto или call. На-
Глава 8. Инструментальные средства для работы с языком ассемблера 273 пример, в модели PIC16F876 для вызова подпрограммы FRED, начинающей- ся с адреса h’OBOO’ (т.е. на 1-й странице), мы имеем bcf PCLATH,3 ; Переходим на 1-ю страницу памяти программ bsf PCLATH,4 call FRED ; Вызываем подпрограмму В перемещаемой программе адреса меток, таких как FRED, не определены, и в моделях с несколькими страницами памяти программ эти метки могут быть помещены компоновщиком на любую-страницу. Чтобы ассемблер мог изменить биты РС1АТН[4:3] соответствующим образом, в нем предусмот- рена директива pagesel, которая должна использоваться перед любой ко- мандой goto или call аналогично директиве banksei, использованной нами в Примере 8.5. Покажите, как можно использовать данную директиву для поддержки последовательности вызовов подпрограмм, названных SUB-0, SUB-1, SUB-2. 8.4. Недостаток использования директивы banksei для выбора банка памяти заключается в том, что дополнительные команды вставляются в код даже в том случае, если микроконтроллер уже работает с требуемым банком. Поду- майте над тем, как можно избежать этого при написании подпрограмм, кри- тичных к размеру кода или ко времени выполнения. 8.5. Определите максимальное значение переменных num_1 и num_2 из нашей программы вычисления среднеквадратичного значения двух переменных, при котором программа будет работать корректно. 8.6. Перепишите код основной процедуры main. asm из Программы 8.2 и под- программы root .asm из Программы 8.4 таким образом, чтобы программа могла работать с любыми значениями переменных num_1 и NUM_2. Для это- го потребуется использовать подпрограммы сложения и вычисления квад- ратного корня, оперирующие 3-байтными значениями. 8.7. В следующем фрагменте используется макрокоманда Movl f из Примера 8.4. Эти строки не работают так, как требуется. По всей видимости, переменная COUNT меняется произвольным образом, причем никак не связанным с тре- буемой константой 32. Почему? movf COUNT,f btfsc STATUS,Z Movlf d'32',COUNT ; Проверяем COUNT на ноль ; ЕСЛИ не ноль, ТО пропускаем ; ИНАЧЕ реинициализируем 8.8. Программист, имеющий опыт работы с микроконтроллером 68НС05 компа- нии Motorola, перешел к микроконтроллерам семейства PIC и собирается на- писать макросы, симулирующие, помимо всего прочего, приведенные ниже команды 68НС05. Заметьте, что регистр аккумулятора в семействе 68НС05 эк- вивалентен по назначению рабочему регистру в Р1С-микроконтроллерах. Ida memory Загрузить в аккумулятор (LoaD Accumulator) байт из памяти данных.
274 Часть IL Программное обеспечение Ida #data Загрузить в аккумулятор (LoaD Accumulator) константу. sta memory Сохранить содержимое аккумулятора (STore Accumulator) в памяти данных. tst memory Проверить (TeST) байт памяти данных на нулевое значение. tsta Проверить аккумулятор (TeST Accumulator) на нулевое значение. Напишите соответствующие макроопределения. Как вы думаете, почему та- кой подход является не слишком хорошей идеей?
ГЛАВА ЯЗЫК ВЫСОКОГО УРОВНЯ Для написания всех программ в последних шести главах мы с вами использо- вали язык ассемблера. Хотя ассемблерные программы достаточно сильно отлича- ются от чистого машинного кода (см. стр. 239), тем не менее между любой ма- шинной инструкцией и соответствующей командой ассемблера сохраняется од- нозначное соотношение. То есть программист вынужден мыслить объектами внутренней структуры микроконтроллера — регистров и памяти, а не объектами реализуемого алгоритма. Несмотря на то что большинство ассемблеров поддер- живают макрорасширения, благодаря которым несколько машинных команд мо- гут быть сгруппированы в виде псевдокоманды высокого уровня, это не более чем попытка обойти недостаток машинно-ориентированного языка. В чем же выра- жается этот недостаток? А в том, что для улучшения эффективности, качества и увеличения степени повторного использования программ кодирующий язык дол- жен быть максимально независим от архитектуры процессора и должен иметь синтаксис, более ориентированный на решение задач. Разумеется, не стоит и пытаться выучить какой-либо язык высокого уровня в одной короткой главе. Тем не менее, прочитав эту главу, вы: • Поймете необходимость использования языка высокого уровня. • Оцените преимущества, предоставляемые языком высокого уровня. • Поймете, какие проблемы связаны с использованием языка высокого уров- ня для встраиваемых приложений на базе микроконтроллеров. • Научитесь писать коротенькие программы на Си. Уже в первые годы после появления коммерческих программных систем люди осознали, что писать большие программы на родном для компьютера языке очень сложно. Дело в том, что помимо всего прочего компьютеры начали периодически устаревать, и для каждой новой модели программы приходилось переписывать. Большие же программы даже в то время состояли из многих тысяч строк кода. Программисты встречались так же редко, как зубы у курицы, и ценились на вес золота. Поэтому, чтобы компьютеры были коммерчески выгодными, следовало найти средство, позволяющее сохранить инвестиции в дефицитное время про- граммистов. При разработке универсального языка, независимого от аппаратной платформы, основное внимание было уделено тому, чтобы программист мог за-
276 Часть II. Программное обеспечение писывать код более естественным образом, в терминах, соответствующих решае- мой задаче, а не на уровне памяти, регистров и флагов. Разумеется, существует множество различных классов задач, требующих про- граммирования, поэтому с тех пор было разработано большое количество языков программирования1). Одними из первых языков были Fortran (FORmula TRANslator) и COBOL (Common Business Oriented Language) в начале 50-х. Пер- вый из указанных языков имел синтаксис, ориентированный на решение науч- ных и инженерных задач, а второй — на решение бизнес-приложений. Несмотря на более чем 40-летний возраст этих языков, многие приложения до сих пор пи- шутся на них — сказывается инерция многих миллионов строк кода. Другими по- пулярными языками были Algol (ALGOrithmic Language), BASIC, Pascal, Modula, Ada, C, C++ и Java — последние три языка относятся к одному семейству. Хотя, с точки зрения программиста, написание программ на языке высокого уровня может быть легче и продуктивнее, процесс трансляции с языка высокого уровня в конечный машинный код представляет собой гораздо более сложную за- дачу по сравнению с процессом ассемблирования, описанным в главе 8. Пакет предназначенных для этого программ называется компилятором, а процесс — со- ответственно компиляцией. Сложность компиляторов и их стоимость были приемлемыми при разработке программ для относительно мощных и чрезвычайно дорогих универсальных ЭВМ (мэйнфреймов) того времени. Однако в области микропроцессорных уст- ройств языки высокого уровня практически не использовались вплоть до середи- ны 80-х годов, т.е. до появления достаточно мощных и сравнительно недорогих персональных компьютеров и рабочих станций, на которых могли запускаться компиляторы. Повсеместное распространение таких компьютеров в сочетании с постоянно увеличивающейся вычислительной мощностью микроконтроллерных и микропроцессорных устройств, а также экономической значимостью этого сек- тора рынка привело к тому, что большинство программ для данных устройств то- же стали писаться на языке высокого уровня. Если вы собираетесь описать задачу на языке высокого уровня для встраивае- мой микроконтроллерной системы, например контроллера стиральной машины, то этот процесс условно можно разбить на следующие этапы: 1. Уточнение постановки задачи и разбиение ее на совокупность модулей, каждый из которых выполняет четко определенные операции с известным набором входных и выходных данных. 2. Продумывание реализации каждого модуля. 3. Создание в редакторе исходного файла в соответствии с синтаксисом ис- пользуемого языка высокого уровня. 4. Компиляция исходного файла в его эквивалент на языке ассемблера. 5. Ассемблирование и компоновка промежуточного файла для получения файла в машинных кодах. ’) Как в шутку говорилось, настоящим специалистом по вычислительной технике является тот, кто вместо решения поставленной задачи разрабатывает новый язык программирования.
Глава 9. Язык высокого уровня 277 6. Загрузка итогового машинного кода в память программ конечного устройства. 7. Запуск программы, ее тестирование и отладка. Этот процесс практически идентичен процессу, изображенному на Рис. 8.3 (стр. 253), просто появился дополнительный пункт — компиляция. Некоторые компиляторы сразу формируют из исходного файла машинный код. Однако при наличии фазы ассемблирования достигается большая гибкость (Рис. 9.1), что особенно важно при разработке программ для встраиваемых устройств на базе микроконтроллеров и микропроцессоров. L2 8 а) Сначала компилируем в ассемблерный код movf btfsc goto movf addwf btfsc incf decf goto _n, f STATUS,Z L41 _n,f _sum,f STATUS,C _sum+l,f _n, f L28 L28 movf btfsc goto movf addwf btfsc incf decf goto L41 STATUS,Z L41 _n, f _sum,f STATUS,C _sum+l,f _n, f L2 8 0000100010010011 0001100100000011 0010100000001111 0000100000010011 0000100000010011 0000011110010100 0001100000000011 0000101010010101 0111100000000111 б) Затем ассемблируем и компонуем в машинный код Рис. 9.1. Преобразование исходного кода, написанного на языке высокого уровня, в машинный код При работе со встраиваемыми системами очень важно правильно выбрать язык высокого уровня. Причем основным критерием в данном случае будет объ- ем машинного кода, генерируемого компилятором с языка высокого уровня, по сравнению с эквивалентным кодом, написанным на ассемблере. Ведь большин- ство встраиваемых микроконтроллерных устройств являются малогабаритными, имеют невысокую вычислительную мощность, а также ограниченные ресурсы памяти и малую стоимость — взять, к примеру, контроллер пульта дистанционно- го управления телевизора. В большинстве недорогих микроконтроллеров исполь- зуется процессор с невысокой производительностью, имеющий, в лучшем случае, несколько сот байт ОЗУ и несколько килобайт ПЗУ. Так что язык высокого уров- ня должен быть таким, чтобы его компилятор мог генерировать код, который ес- ли и не будет таким же эффективным, каким он мог бы быть при использовании ассемблера, то, по крайней мере, сравнимым с ним1). 11 По личному опыту автора код, генерируемый компилятором, обычно в 1.25...2.5 раза больше.
278 Часть II. Программное обеспечение Наиболее часто для написания программ встраиваемых микропроцессорных и микроконтроллерных систем (Рис. 9.2) используется язык Си. Изначально язык Си был разработан как язык для написания операционных систем. На простей- шем уровне операционная система (ОС) представляет собой программу, которая делает низкоуровневую работу компьютерных периферийных устройств, таких как клавиатура и дисковые накопители, незаметной для оператора. А раз так, то разработчик ОС должен иметь возможность обращаться к различным регистрам и участкам памяти периферийных устройств и легко интегрироваться с драйверами, пишущимися, как правило, на ассемблере. Поскольку обычные языки высокого уровня и их компиляторы были очень требовательны к вычислительным ресур- сам, вплоть до начала 70-х годов невозможно было обойтись без ассемблера, кото- рый обеспечивал очень тесное взаимодействие с аппаратурой и позволял генери- ровать компактный быстрый код. Однако довольно большой конечный размер та- ких проектов наводит на мысль, что они скорее всего были результатом коллективной разработки со всеми вытекающими отсюда проблемами объедине- ния кода, написанного разными людьми. От участников таких проектов требова- лась огромная самодисциплина, а также огромные усилия, затрачиваемые на до- кументирование своей работы. Даже при соблюдении всех этих условий конечный результат нельзя было с легкостью перевести на систему с другим процессором — для этого требовалась практически полная переработка программы. В начале 70-х один из сотрудников компании Bell Laboratories Кен Томпсон (Ken Thompson) разработал первую версию операционной системы UNIX. Она была написана на языке ассемблера для мини-компьютера DEC PDP-7. В попыт- ке внедрения данной ОС во всей компании была проведена работа по ее перепи- сыванию на языке высокого уровня. К тому времени уже существовал язык CPL (Combined Programming Language — комбинированный язык программирова- ния), разработанный в середине 60-х Лондонским и Кембриджским университе- тами, который имел некоторые особенности, делавшие его полезным для использования в данной области. Язык BCPL (Basic CPL— базовый CPL) был бо- лее простым и в то же время более эффективным языком, разработанным в конце 60-х годов как инструмент для написания компиляторов. Язык В (по первой бук- ве аббревиатуры BCPL) был разработан специально для переноса ОС UNIX на машину DEC PDP-11 и представлял собой, по существу, язык BCPL с изменен- ным синтаксисом. И BCPL, и В оперировали объектами только одного типа — машинным сло- вом (16 бит для PDP-11). Отсутствие типизации в этих языках вызывало затрудне- ния при работе с отдельными байтами и при реализации вычислений с плаваю- щей точкой. Для решения этих проблем в 1972 году был разработан язык Си (вто- рая буква аббревиатуры BCPL), который поддерживал различные объекты, как целочисленные, так и с плавающей запятой. Это значительно увеличило его пе- реносимость и гибкость. Весной 1973 года операционная система UNIX была полностью переписана на Си. Объем исходного кода составил около 10 000 строк на языке Си и 1000 строк на языке ассемблера, а итоговый размер получившейся программы увеличился на 30% по сравнению с оригинальной версией.
Глава 9. Язык высокого уровня 279 Рис. 9.2. Этапы создания исполнимой программы в виде пирамиды Хотя в момент своего появления язык Си был тесно связан с UNIX, уже через несколько лет появились компиляторы с этого языка, работающие практически под всеми известными ОС. Более того, изначально являясь языком системного программирования, сейчас он используется для написания самых различных прикладных программ, начиная с пакетов автоматизированного проектирования и заканчивая программным обеспечением интеллектуальных яйцеварок! Через десять лет появилось официальное описание языка (первая редакция), выпущенное создателями языка Брайаном Керниганом (Brian W. Kemighan) и Де- нисом Ритчи (Dennis К. Ritchi) в виде книги «Язык программирования Си». О мощи и простоте языка свидетельствует тот факт, что за много лет он практически не изменился, избежав разделения на диалекты и новые версии. В 1983 году Нацио- нальный Институт Стандартизации США (American National Standards Institute — ANSI), признав возросшее влияние языка Си, основал комитет X3J11 для разра- ботки современного и всестороннего определения этого языка. Итоговый доку- мент, известный как ANSI С, был окончательно утвержден в 1990 году междуна- родной организацией по стандартизации (International Organization for Standardization — ISO). Язык Си (а также его объектно-ориентированные потомки Си++ и Java) не только используется при разработке программного обеспечения для встраивае- мых микроконтроллерных и микропроцессорных систем, но также, без сомне- ния, является наиболее популярным языком программирования общего приме- нения. Завистники даже прозвали его «высокоуровневый ассемблер». Однако именно эта близость языка к ассемблеру вместе с возможностью использования в
280 Часть IL Программное обеспечение одной программе ассемблерного и высокоуровневого кода и является, в частнос- ти, преимуществом для встраиваемых систем. Основными преимуществами использования языка высокого уровня для на- писания программного обеспечения встраиваемых устройств являются: • Большая продуктивность, в том смысле, что в среднем для написания, про- верки и отладки одной строки кода требуется одно и то же время независи- мо от языка. По определению, одна строка на языке высокого уровня экви- валентна нескольким строкам ассемблерного текста. • Синтаксис, более ориентированный на решение задач. За счет этого увели- чивается производительность труда программиста и точность решения пос- тавленных задач. Кроме того, код становится легче документировать, отла- живать, поддерживать и адаптировать к изменяющимся условиям. • Лучшая переносимость программ на другие аппаратные платформы, хотя переносимость на все 100% обеспечивается очень редко. За счет этого уве- личивается время жизни программ, которые к тому же становятся относи- тельно независимыми от аппаратной части. • Более широкий круг пользователей, обусловленный более или менее аппа- ратной независимостью языка. В результате появляется экономический стимул создания многочисленных библиотек стандартных функций (мате- матические библиотеки, библиотеки поддержки коммуникационных моду- лей и др.), которые можно повторно использовать во многих проектах. Разумеется, использование языка высокого уровня не лишено и недостатков, особенно ярко проявляющихся при написании кода, который должен выпол- няться в системе на базе микроконтроллера или микропроцессора с ограничен- ными ресурсами: • Полученный код имеет больший объем и часто выполняется медленнее ана- логичной программы, написанной на ассемблере. • Компилятор стоит намного дороже ассемблера. Стоимость профессиональ- ных пакетов может достигать нескольких тысяч фунтов/долларов. • Могут возникнуть затруднения при отладке, поскольку на целевом процес- соре выполняется сгенерированный ассемблерный код, а не исходный код, написанный на языке высокого уровня. Средства, облегчающие отладку на высоком уровне, могут быть очень дорогими. В качестве примера посмотрим на Программу 9.1. Программа 9.1. Простая функция на Си 1: unsigned long summation(unsigned int n) 2: { 3: unsigned long sum - 0; 4: while(n > 0) 5: { 6: sum = sum + n; 7: --n; 8: } 9: return sum; 10: }
Глава 9. Язык высокого уровня 281 В Программе 9.1 приведен код Си-функции (функции в Си — аналог подпро- грамм), вычисляющей следующее соотношение: п sum= ^k- k=l Например, если п = 5, то мы получим sum = 5 + 4 + 3 + 2+ 1. В нашей реализации п — целое число, передаваемое в функцию, которая вы- числяет и возвращает целое значение sum. Поставленная задача реализуется цик- лическим прибавлением п к предварительно обнуленному значению sum, с одно- временным декрементированием п до нуля. Давайте разберем эту функцию по строкам. Каждая строка помечена номе- ром. Эти номера вставлены исключительно для удобства и не являются частью кода программы. Строка 1: В этой строке объявляется имя функции (подпрограммы) summation и указывается, что она возвращает целое число типа unsigned long (в компиляторе, используемом нами в данной главе, этому типу соот- ветствует 16-битное целое число без знака), а в качестве параметра п ожидает передачи целого числа типа uns igned int (8-битное целое число без знака). Строка 2: Открывающая фигурная скобка означает начало блока. Как можно догадаться, у каждого начала должен быть свой конец, который в дан- ном случае обозначается закрывающей фигурной скобкой. Хорошим тоном считается располагать тело блока с некоторым отступом (один символ табуляции) относительно фигурных скобок. Такое формати- рование облегчает поиск парных скобок, т.е. начала и конца блока, однако компилятору нет никакого дела до того, какой стиль исполь- зует программист. В нашем случае соответствующая закрывающая скобка находится в строке 10. Между строками 2 и 10 заключено тело функции summation (). Строка 3: В нашей функции используется только одна локальная переменная. В этой строке определяется ее имя (sum) и тип (unsigned long). В языке Си все объекты должны быть определены перед их использо- ванием. Таким образом, компилятору передается информация о свойствах именованной переменной. В данном случае мы сообщаем компилятору о том, что под эту переменную необходимо выделить 16 бит и что она используется для хранения беззнаковых чисел. В этом же объявлении задается начальное значение переменной sum. Все выражение завершается символом точки с запятой, как и любой оператор. Строка 4: При вычислении sum нам необходимо выполнять одну и ту же опера- цию до тех пор, пока п не станет равно нулю. В этой строке находится
282 Часть II. Программное обеспечение начальная часть оператора цикла while. В общем виде этот цикл вы- глядит следующим образом: while(ИСТИНА) { делаем это; делаем то; делаем что-нибудь еще; } Тело цикла, т.е. совокупность операторов, расположенных между фи- гурными скобками (строки 5 и 8), выполняется до тех пор, пока ре- зультат выражения в круглых скобках будет не равен нулю (в языке Си любое значение, не равное нулю, считается истинным). Эта про- верка осуществляется перед каждым проходом цикла. В нашем случае вычисляется выражение п > 0. Если это соотношение истинно, то число п прибавляется к sum. После этого п декрементируется, и цикл повторяется. В какой-то момент выражение п > 0 становится лож- ным, и управление передается на оператор, расположенный после за- крывающей фигурной скобки (строка 9). Строка 5: Открывающая фигурная скобка обозначает начало тела цикла while. В соответствии с принятым стилем операторы, составляющие тело цикла, записываются с отступом. Строка 6: Вычисляется выражение в правой части оператора присваивания «=» (sum + п), и полученное значение заносится в переменную, распо- ложенную слева от оператора присваивания, т.е. в sum. При прибав- лении 8-битной переменной к 16-битной компилятор автоматически расширяет первую до 16 бит (см. Листинг 9.1, команды с адресами h’000E’...h’00H’). Строка 7: Значение п декрементируется в результате выполнения оператора де- кремента --------------!). Записанное выражение эквивалентно выражению n = п - 1. Замечу, что большинство Си-программистов вставили бы эту операцию непосредственно в заголовок цикла: whi 1 е (- - п > 0). Строка 8: Закрывающая скобка тела цикла while. Обратите внимание, что и открывающая (строка 5), и закрывающая скобки имеют одинаковый отступ от начала строки. Компилятор не обращает внимания на все эти изыски, это сделано исключительно для удобочитаемости про- граммы и уменьшения вероятности возникновения ошибок. Строка 9: Оператор return возвращает одну переменную обратно в вызываю- щую процедуру. В нашем случае такой переменной является значение sum. Компилятор проверяет, чтобы тип этой переменной соответ- ствовал типу, указанному при объявлении функции, т.е. unsigned long. Возвращаемый параметр является результатом функции, т.е. Аналогичный оператор инкремента ++ был использован в названии языка Си++, симво- лизируя следующий уровень развития языка Си.
Глава 9. Язык высокого уровня и 283 функция может использоваться в качестве переменной в других вы- ражениях наравне с обычными переменными. Так, если у нас есть функция sqr_root (), возвращающая значение квадратного корня из переданного в нее целого числа (см. Программу 9.2), то в результа- те выполнения выражения х = sqr_root(y); значение, возвращенное функцией sqr_root (у), будет присвоено переменной х. Строка 10: Закрывающая фигурная скобка тела функции summat ion (). Из Рис. 9.1 видно, что на выходе компилятора получается ассемблерный код, который впоследствии может быть ассемблирован и скомпонован с другими мо- дулями^ обычным образом. Чтобы проиллюстрировать это, в Листинге 9.1а при- веден ассемблерный код, получившийся в результате компиляции Программы 9.1 кросс-компилятором компании Custom Computer Services (CCS)* 2). Это недорогой Си-компилятор (~ 125 долл.), который может быть интегрирован в ИСР MPLAB (см. Рис. 9.3). В файл листинга каждая строка исходного кода на Си выводится как комментарий вместе с соответствующим ей ассемблерным кодом. Для гене- рации этого демонстрационного листинга в исходный код программы было вне- сено два незначительных изменения: • Имя функции было изменено на main (), поскольку любая программа на Си должна, по меньшей мере, содержать хотя бы функцию main (). Эта функция похожа на любую другую Си-функцию, но при ее компиляции компилятор генерирует различные команды инициализации программной среды (см. далее). • Была добавлена директива # include для включения заголовочного файла, содержащего информацию, касающуюся конкретной модели микроконт- роллера PIC16F627. Листинг 9.1. Результат работы компилятора CCS а) Ассемблерный листинг, сгенерированный компилятором CCS CCS PCM С Compiler, Version 3.227, 6513 27-Oct-05 15:04 Filename: SUM.LST ROM used: 25 words (2%) Largest free fragment is 999 RAM used: 8 (5%) at main() level 8 (5%) worst case Stack: 0 locations 0000: MOVLW 00 Одни из которых могут быть написаны самостоятельно на ассемблере (для наибольшей эффективности), а другие — взяты из библиотек, входящих в поставку компилятора или приоб- ретенных отдельно. 2) См. сайт http://www.ccsinfo.com/picc.shtm.
284 Часть II. Программное обеспечение Листинг 9.1 (продолжение) 0001: MOVWF 0А 0002: GOTO 004 0003: NOP ................... ttinclude <16f627.h> .................... 11111111 Standard Header file for the PIC16F627 device .................... ttdevice.PIC16F627 ttlist unsigned long main(unsigned int n) .............................. { 0004: CLRF 04 0005: MOVLW IF 0006: ANDWF 03,F 0007: MOVLW 07 0008: MOVWF IF ........................ unsigned long sum = 0; 0009: CLRF 22 000A: CLRF 23 .................................. while(n>0) .................................. { 000B: MOVF 21,F 000C: BTFSC 03.2 GOOD: GOTO 014 .................................. sum = sum + n; 000E: MOVF 21,W 000F: ADDWF 22,F 0010: BTFSC 03.0 0011: INCF 23,F .................................. —n; 0012: DECF 21,F .................................. } 0013: GOTO 00B .................................. return sum; 0014: MOVF 22,W 0015: MOVWF 78 0016: MOVF 23,W 0017: MOVWF 79 .................................. } 0018: SLEEP б) Исполняемый файл в формате Intel HEX :1000000000308A000428000084011F308305073077 :100010009F00A201A301A108031914282108A20727 :100020000318A30AA1030B282208F8002308F900EB : 0200300063006В :00000001FF ;PIC16F627
Глава 9. Язык высокого уровня 285 Давайте посмотрим, как компилятор транслировал нашу программу. unsigned long main(unsigned int n) Точка входа в функцию main () всегда располагается по адресу вектора сброса h’000’. Сначала обнуляется регистр PCLATH (h’OA’), поскольку все последующие команды размещаются в младших адресах памяти программ. Далее управление пе- редается по адресу вектора прерывания h’004’. Поскольку в данном случае прерыва- ния не используются, компилятор разместил по этому адресу код функции main (). Функция main () начинается с очистки регистра FSR (h’004’). Затем сбрасыва- ются биты IRP, RP1 и RP0 регистра STATUS, обеспечивая работу с 0-м банком. Наконец, специально для модели PIC16F627 путем установки трех младших би- тов регистра управления компаратором CMCON (h’lF’) выключается модуль аналогового компаратора (см. Рис. 14.6 на стр. 497). Наличие этой фазы инициализации является отличительной особенностью функции main (). Благодаря ей выполнение «полезного» кода после сброса будет начинаться с определенного состояния микроконтроллера. Обычно программа на языке Си состоит из множества функций, но только в функции main () произ- водится настройка окружения программы. I unsigned long sum = 0; i Компилятор CCS резервирует два байта под объект типа long. В данном случае [ младший и старший байты переменной main. sum были размещены в регистрах i h’22’ и h’23’ соответственно. Для обнуления этих двух РОН компилятор сгенери- i ровал две команды clrf: clrf 11'22’ ; Обнуляем младший байт суммы clrf h’23' ; Обнуляем старший байт суммы | while(п > 0){ : Компилятор выделил регистр h’21’ под однобайтный объект main.n. По-хоро- шему его значение должно задаваться вызывающей функцией. Оператор while реализуется проверкой main.n на ноль и переходом к оператору возврата return в случае, если это условие истинно. movf h'211,f ; Проверяем на ноль btfsc STATUS,Z ; ЕСЛИ не ноль, ТО пропускаем команду goto h'014' ; ИНАЧЕ переходим к адресу h'014' (return) sum = sum + n; Это выражение реализовано в виде операции прибавления однобайтного числа к двухбайтному следующим образом: movf h'21',w ; Считываем main.n addwf h’22’,f ; Складываем с младшим байтом суммы btfsc STATUS,С ; Пропускаем команду, ЕСЛИ нет переноса incf h'23',f ; ИНАЧЕ инкрементируем старший байт суммы
286 Часть И. Программное обеспечение Большинство программистов на Си в этом случае воспользовались бы альтер- нативным оператором sum +=п; результатом которого является переменная sum, увеличенная на п. —п; Теперь декрементируем однобайтное число в регистре h’21 ’: decf h'21',f ; Декрементируем main.n В более сложных выражениях результат может зависеть от того, где располага- ется оператор декремента---(и аналогичный ему оператор инкремента ++) — перед объектом или после него. Когда оператор записывается перед объектом: number = --п + 4; то значение п декрементируется перед прибавлением к нему числа 4. В другом случае: number - п-- + 4; операция декрементирования выполняется после сложения. В нашем примере положение оператора декремента не влияет на логику рабо- ты программы. Однако в последнем случае компилятор добавит дополнительную команду для перегрузки main. п в рабочий регистр перед его декрементировани- ем, чтобы обеспечить возможность выполнения вычислений с использованием исходного значения main. п, которые могут иметь место. } Возврат к началу цикла while осуществляется переходом к командам проверки условия, которые размещаются, начиная с адреса h’OOB’. goto h'OOB’ return sum; В конце функции, возвращающей объект типа unsigned long, компилятор CCS заносит двухбайтное значение в регистры с фиксированными адресами h’78’:h’79’ (младший и старший байты). В нашем случае в эти регистры просто копируется содержимое регистров h’22’:h’23’, т.е. значение main. sum. movf h'22',w ; Копируем младший байт суммы movwf h'78' ; в младший байт возвращаемого значения movf h'23',w ; Копируем старший байт суммы movwf h'79' ; в старший байт возвращаемого значения Обычно функции завершаются командой возврата, однако функция main () завершается командой sleep (см. стр. 308). Итоговый файл в машинных кодах приведен в Листинге 9.16. Этот файл состоит всего из 24 команд, включая однократно выполняемые команды настройки окружения. Программы на языке Си можно компилировать и симулировать непосред- ственно в ИСР MPLAB (см. стр. 264). На скриншоте, показанном на Рис. 9.3,
Глава 9. Язык высокого уровня 287 Рис. 9.3. Симуляция нашего примера в ИСР MPLAB версии 6.x видны окна с исходным текстом на языке Си и сгенерированным ассемблерным кодом. Несмотря на то что симуляция осуществляется на уровне ассемблера, в окне с кодом на языке Си всегда выделяется строка, соответствующая симулируе- мой (и выделенной) в данный момент команде ассемблера1). В окне Witch выво- дится состояние двух объектов программы — unsigned int п (соответствует ассемблерному идентификатору main.n из списка идентификаторов) и unsigned long sum (main. sum). Идентификатор _RETURN_ генерируется са- мим компилятором для именования двух РОН с адресами h’78’:h’79’. Окно Witch можно использовать, как обычно, для контроля состояния объектов программы на Си. На Рис. 9.3 обе указанные переменные выводятся как в шестнадцатерич- ной, так и в десятичной системе. Как правило, последняя лучше подходит для отображения значений высокоуровневых объектов. Можно выбрать любое осно- вание системы счисления — достаточно щелкнуть правой кнопкой мыши на зна- чении переменной и выбрать пункт Properties контекстного меню. Также значе- ние объекта можно изменить, сделав двойной щелчок на имени переменной (в нашем примере мы задали значение п, равное 100). Снимок экрана был сделан при достижении переменной п значения 71 в процессе декрементирования. Пос- ле завершения симуляции п становится равным нулю, a sum — десятичному 5050. ° Точнее говоря, уровень симуляции зависит от того, какое из окон активно в настоящий момент. Если активным является окно с исходным кодом, то и симуляция осуществляется на уровне исходного кода. — Примеч. пер.
288 Часть II. Программное обеспечение Использование языка Си позволяет программисту работать со структурами, операторами и библиотечными функциями, свойственными современному язы- ку высокого уровня. И все же при работе с микроконтроллерами программисту необходимо предоставить возможность легкого доступа к заданным ячейкам па- мяти данных и к отдельным их битам. Это позволит ему отслеживать состояние, а также изменять содержимое различных регистров специального назначения, та- ких как параллельные порты ввода/вывода. Благодаря этому процессор сможет взаимодействовать со своими встроенными периферийными устройствами и ок- ружающей средой. Разумеется, эти операции можно выполнить и с помощью стандартных операторов языка Си. Однако во многих компиляторах, предназна- ченных для микроконтроллеров и микропроцессоров, реализованы нестандарт- ные расширения языка, упрощающие такое «жонглирование» битами. Ну, а пос- кольку мы решили использовать компилятор CCS, то будем рассматривать имен- но его расширения. В качестве примера рассмотрим подпрограмму, которая генерирует импульсы на 0-м выводе порта А (т.е. на выводе RA0) до тех пор, пока на выводе 7 порта В присутствует ВЫСОКИЙ уровень (см. стр. 152). Вот как это можно записать на стандартном языке Си (префикс Ох используется в языке Си для обозначения шестнадцатеричной системы) ttdefine PORTA *(unsigned int *)0x05 ttdefine PORTB *(unsigned int *)0x06 while(PORTB & 0x80) /* Выделяем 7-й бит, проверяем, не равен ли он нулю */ PORTA = PORTA I 0x01; /* ИЛИ с 00000001; RAO -> ВЫСОКИЙ уровень */ PORTA = PORTA & 0xF7; /* И с 11111110; RAO -> НИЗКИЙ уровень */ Обратите внимание на использование парных символов /*...*/ для выделе- ния комментариев. Особое внимание необходимо уделить использованию указателей для имено- вания абсолютных адресов в памяти данных, причем это касается даже опытных программистов на Си. Например, строка: Содержимое указателя на байт данных, / находящегося в ячейке 0x06 ttdefine PORTB *(unsigned int *)0x06 определяет имя PORTB в качестве синонима содержимого регистра h’06’. В ком- пиляторе CCS тип unsigned int занимает один байт, однако в других компиля- торах для хранения 8-битных данных используется либо unsigned short int, По умолчанию в языке Си используется десятичная система, но будьте внимательны, пос- кольку числа, начинающиеся с нуля, интерпретируются как числа в восьмеричной системе. То есть константа 026 является восьмеричным числом 26 (которое равно десятичному 2 х 8 + 6 = 22).
Глава 9. Язык высокого уровня 289 либо unsigned char. В дальнейшем именованный объект может использовать- ся как обычная глобальная переменная типа int. В процедуре, приведенной выше, осуществляется логическое умножение (&) содержимого PORTB и константы Ь’10000000’, чтобы определить, установлен 7-й бит регистра или нет; если это так, результат выражения будет отличным от нуля (см. стр. 143). При этом будет выполнен очередной проход цикла while. В теле цикла для установки бита регистра PORTA используется операция ИЛИ «|» (см. стр. 144), а для сброса бита — операция И. Как можно увидеть из приведенного ниже ассемблерного кода, сгенерированного компилятором CSS версии 3, эти выражения были совершенно верно интерпретированы как операции установки и сброса единственного бита. В результате были корректно использованы команды btfss, bcf и bsf. Если же в программе осуществляется сброс или установка нескольких битов, то используются соответствующие команды ior и and1). btfss goto 6,7 NEXT ; Проверяем 7-й бит регистра PORTB ; ЕСЛИ 0, ТО выходим из цикла bsf 5,0 ; Выставляем на RA0 ВЫСОКИЙ уровень bcf 5,0 ; Выставляем на RA0 НИЗКИЙ уровень NEXT ..... Этот исполнимый код в точности соответствует тому, который мы написали бы при программировании на ассемблере. В конкретном случае компилятора CCS для именования содержимого ячейки памяти данных можно было бы использовать нестандартную директиву #byte. Например, строка # byte INTCON = 0x0В присваивает регистру с адресом h’06’ имя INTCON. Аналогичным образом в ком- пиляторе CCS можно именовать отдельные биты, используя директиву #bit. Так, строка # bit INTF = ОхОВ.1 присваивает имя 1-му биту регистра h’OB’. Причем если имя INTCON было уже определено, как показано выше, то эту же строку можно было бы записать как # bit INTF = INTCON.1 Определенные таким образом объекты могут принимать значения только 0 и 1* 2). Таким образом, оператор INTF = 0; сбросит 1-й бит регистра INTCON. Многие компиляторы не могут распознать операции изменения отдельных битов и используют для их реализации менее эффективные команды логических операций. Это, в част- ности, относится ко 2-й версии рассматриваемого компилятора. 2) Объекты такого рода, принимающие только два значения, иногда называются булевыми. В отличие от большинства других компиляторов в компиляторе CCS для булевых значений используется тип unsigned int.
290 и Часть II. Программное обеспечение Используя эти директивы компилятора, перепишем наш тестовый фрагмент: #byte PORTA =5 #byte PORTB =6 #bit RAO = PORTA.0 #bit RB7 = PORTB.7 while(RB7) { RAO = 1; RAO = 0; /* Порт A - регистр h'051 */ /* Порт В - регистр h’06’ */ /* 0-й бит регистра h'05' - RAO */ /* 7-й бит регистра h'06’ - RB7 */ /* На выводе RA0 - ВЫСОКИЙ уровень */ /* На выводе RA1 - НИЗКИЙ уровень */ При компиляции данного фрагмента будет сгенерирован точно такой же испол- нимый код, как и раньше. Наличие в компиляторе подобных специальных средств гарантирует, что при их использовании будут сгенерированы эффектив- ные команды манипуляций с битами. Однако все это достигается в ущерб перено- симости программы. Начиная с этого момента, мы будем использовать указан- ную нотацию. Для удобства все стандартные операторы языка Си приведены в Приложении В. Примеры Пример 9.1 Напишите на базе алгоритма, показанного на Рис. 6.11 (стр. 198), функцию, возвращающую квадратный корень из положительного 16-битного целого числа. Решение Приведя алгоритм из Примера 6.5 к структуре цикла while, получим следующее: 1. Обнулить счетчик цикла. 2. Присвоить 1 переменной i (магическое число). 3. Пока i меньше или равно заданному числу: а) Вычесть i из числа. б) Добавить 2 к i. в) Инкрементировать счетчик цикла. 4. Вернуть счетчик цикла в качестве значения квадратного корня из числа. В заголовке функции указывается ее имя (sqr_root) и задаются параметры, передаваемые в функцию, а также возвращаемое ею значение. Строка unsigned int sqr_root(unsigned long number) означает, что функция возвращает значение типа unsigned int и ожидает пере- дачи одного объекта типа unsigned long, который внутри функции будет из- вестен под именем number. Собственно код функции приведен в Программе 9.2. Поскольку квадратный корень из 16-битного числа поместится в одном байте,
Глава 9. Язык высокого уровня 291 счетчик цикла был объявлен как переменная типа unsigned int. А магическое число i будет в 2 раза больше числа count, поэтому оно объявлено как unsigned long. Одновременно с объявлением локальных переменных можно также задавать их начальные значения. Программа 9.2. Функция вычисления квадратного корня unsigned int sqr_root(unsigned long number) { unsigned int count = 0; unsigned long i = 1; while(number >- i) { number - number - i; i = i + 2; count++; } return count; } Цикл while выполняется до тех пор, пока значение числа number не станет меньше i; начиная с этого момента, любая последующая операция вычитания при- ведет к получению отрицательного результата. Количество проходов цикла является искомым значением квадратного корня и возвращается в вызывающую программу. При использовании компилятора CS версии 3.18 размер полученной функ- ции составил 29 команд, тогда как исходная реализация этой функции на языке ассемблера (Программа 6.12 на стр. 199) имеет 21 команду. Таким образом, эф- фективность компилятора составляет 72%. Пример 9.2 Термопара К-типа в диапазоне температур 0,„ 1300°С характеризуется соотно- шением t = 7.550162+ 0.0738326x0 + 2.8121386 х 10"7о2, где t — температура спая в градусах Цельсия, а и — генерируемая ЭДС, находя- щаяся в диапазоне 0...52.398 мкВ, представленная 14-битным беззнаковым дво- ичным числом. Напишите функцию, которая будет принимать в качестве входно- го параметра 14-битное выходное значение аналого-цифрового преобразователя и возвращать измеренное термопарой целочисленное значение температуры в градусах Цельсия. Решение Текст нашей функции, названной thermocouple (), приведен в Программе 9.3. Эта функция имеет один параметр emf типа unsigned long (16 бит) и возвращает также 16-битное значение. Локальная переменная
292 Часть II. Программное обеспечение temperature определена в 3-й строке как число с плавающей точкой1*. Это не- обходимо для поддержки сложных математических вычислений с дробными чис- лами, выполняющихся в 6-й строке. Поскольку мы договорились, что значение имеют только 14 младших битов параметра emf, в 5-й строке выполняется логи- ческое умножение 16-битной переменной и константы h’3FFF’ (0x3FFF) для сброса двух старших битов. И наконец, в 8-й строке переменная temperature типа float приводится к типу unsigned long и возвращается в вызывающую программу. Программа 9.3. Линеаризация характеристики термопары К-типа unsigned long thermocouple(unsigned long emf) { float temperature; unsigned long outcome; emf = emf & 0x3FFF; /* Сбрасываем два старших бита */ temperature = 7.550162 + 0.073832605*(unsigned long)emf + + 2.8121386e-7*emf*emf; outcome = (unsigned long)temperature; return outcome; } Итоговый код, скомпилированный для микроконтроллера PIC семейства среднего уровня, занимает 653 слова памяти программ — это около 2/3 всего объ- ема памяти программ модели PIC16F627! По этой причине во встраиваемых мик- роконтроллерах везде, где только возможно, используется арифметика с фикси- рованной точкой. Пример 9.3 На стр. 255 была приведена программа вычисления среднеквадратичного зна- чения -^NUM_12+ NUM_22. Напишите функцию на языке Си, вычисляющую это выражение и возвращающую 8-битное значение. В функцию должны переда- ваться две 8-битные переменные — num_l и num_2. Решение В Программе 9.4 для хранения суммы квадратов двух 8-битных переменных используется локальная переменная sum типа unsigned long. Операция возве- дения в квадрат реализована с помощью оператора умножения «♦» вместо ис- пользования функции возведения в квадрат, как это было сделано в Программе 8.3 (стр. 258). Однако, чтобы результат арифметических операций со- ответствовал 16-битной переменной sum, программист должен дать понять ком- пилятору, что необходимо использовать 16-битную арифметику. Для этого каж- Выражаемое с помощью мантиссы и экспоненты в виде т х 10е.
Глава 9. Язык высокого уровня 293 дый из операндов явно приводится к типу unsigned long с помощью конструк- ции (unsigned long). Функция, текст которой приведен в Программе 9.2, используется для вычисления квадратного корня из 16-битного целого sum и вы- зывается в 6-й строке функции variance (). Значение, возвращаемое функцией, присваивается локальной переменной rms. При использовании компилятора CCS для реализации этой задачи требуется 94 машинных команды. Ассемблер- ный вариант этой функции состоит из 62 команд, соответственно эффективность составляет 66%. Программа 9.4. Вычисление среднеквадратичного значения двух переменных unsigned int variance(unsigned int num_l, unsigned int num_2) { unsigned long sum; unsigned int rms; sum = (unsigned long)num_l*num_l + (unsigned long)num_2*num_2; rms = sqr(sum); return rms; Пример 9.4 Напишите функцию, выполняющую сдвиг содержимого регистра h’20’ справа налево и выставляющую выдвигаемый бит на вывод RA0. При выдаче очередного бита на выход RA0 на выводе RA1 должен формироваться импульс _ЛЛ_, ин- формирующий внешние устройства о готовности нового бита. Решение В Программе 9.5 для восьмикратного сдвига содержимого регистра h’20’ (на- званного datum) вправо используется оператор цикла for (). Сам сдвиг реализу- ется с помощью оператора Си«»»(сдвиг вправо). Перед очередным сдвигом вы- вод RA0 (названный SER_OUT) устанавливается или сбрасывается в зависимости от значения 0-го бита (LSB) переменной DATUM с использованием условного опе- ратора if-else. В любом случае на выводе RA1 (названном CLOCK) формируется одиночный импульс. Кстати, написанная нами функция реализует простейший последовательный канал синхронной передачи данных (см. главу 12). Программа 9.5. Простейшая функция передачи по последовательному каналу #byte DATUM = 0x20 #bit LSB = DATUM.0 #byte PORTA = 5 #bit SER_OUT = PORTA.0 #bit CLOCK = PORTA.1 /* Регистр h'20' /* 0-й бит регистра h'20' /* Порт А - регистр h'05' /* 0-й бит порта /* 1-й бит порта void put_char(void) */ */ */ */ */ /* Параметры и возвращаемое значение отсутствуют (void) */
294 Часть II. Программное обеспечение { int i; /* Счетчик цикла */ for(i=0; i<8; i++) /* ВЫПОЛНЯЕМ восемь раз */ { if(LSB) /* ЕСЛИ 0-й бит равен 1, SER_OUT = 1; /* выставляем на RA0 ВЫСОКИЙ уровень */ else SER_OUT =0; /* ИНАЧЕ выставляем на RA0 НИЗКИЙ уровень */ CLOCK =1; /* Выдаем на RA1 ВЫСОКИЙ уровень, */ CLOCK =0; /* а затем НИЗКИЙ */ DATUM = DATUM » 1; /* Сдвигаем байт данных на один разряд вправо */ } } Пример 9.5 Напишите Си-программу для упаковщика консервных банок из Примера 7.1 (стр. 224), рассчитанную на компилятор CCS. В программе должны использо- ваться прерывания. Решение Как и в ассемблерном варианте, в Программе 9.6 имеется две функции. В ос- новной функции main () сначала используется встроенная функция компилято- ра set_tris_a () для переключения 0-й линии порта А в режим выхода. Затем с помощью другой встроенной функции enable_interrupts () устанавливают- ся биты маски прерываний INTE и GIE (см. Рис. 7.3 на стр. 213). После этого сбрасывается 0-й бит порта А, гарантируя наличие НИЗКОГО уровня на выводе RA0 при старте программы. Программа 9.6. Программа автоматического упаковщика банок #include <16f84.h> #use delay (clock=8000000) /* Сообщаем компилятору о тактовой частоте (8 МГц) */ #bit RA0 =5.0 /* 0-й бит порта А */ /* Объявляем функцию can_count(), которая не имеет параметров и не возвращает значения */ void can_count(void); int EVENT, BATCH; /* Две глобальные переменные */ void main(void) { set_tris_a(OxFE) ; /* Конфигурируем RAO как выход */ enable_interrupts(INT_EXT); /* Устанавливаем бит INTE регистра INTCON */ enable_interrupts(GLOBAL); /* Устанавливаем бит GIE регистра STATUS */ RAO = 0; /* Выставляем на RA0 НИЗКИЙ уровень */ while(1) /* Бесконечный цикл */ { if(BATCH) /* Если переменная BATCH не равна нулю, */ {
Глава 9. Язык высокого уровня 295 BATCH =0; /* ТО обнуляем ее */ RA0 =1; /* и формируем на выводе RA0 */ delay_ms(1); /* импульс длительностью 1 мс */ RA0 = 0; } } } /* Процедура обработки прерывания */ #int_ext /* Обработчик внешнего прерывания */ void can_count(void) { if(++EVENT == 24) /* Инкрементируем счетчик, и ЕСЛИ он равен 24, */ { EVENT=0; /* ТО обнуляем его */ ВАТСН++; /* и заносим в переменную BATCH ненулевое значение */ } } В теле бесконечного цикла непрерывно проверяется значение переменной BATCH. При ненулевом значении (ИСТИНА) переменная сбрасывается и на вы- воде RA0 формируется положительный импульс длительностью 1 мс. Использо- вание встроенной функции компилятора delay_ms () является самым простым способом генерации точных задержек длительностью до 65 535 мс в этой реализа- ции языка. Чтобы воспользоваться указанной возможностью, программист дол- жен сообщить компилятору значение тактовой частоты микроконтроллера. Для формирования более коротких задержек можно использовать функции delay_us () и delay_cycles (). При работе с компиляторами, не имеющими подобных нестандартных функций, можно использовать собственные подпро- граммы задержки, написанные на ассемблере. Функция can_count () объявлена как процедура обработки внешнего преры- вания с помощью директивы #int_ext (). Аналогичные директивы предусмот- рены для всех источников прерываний. Компилятор самостоятельно генерирует код для поддержки прерываний от нескольких источников, а также для сохране- ния и восстановления контекста. Поскольку функция can_count () является обработчиком прерывания, в нее нельзя обычным образом передать параметры, о чем сигнализирует ключевое слово void. Вместо этого все контролируемые и изменяемые переменные долж- ны быть объявлены глобальными. В нашей программе обе переменные BATCH и EVENT объявлены вне функции и, таким образом, видны всем функциям, как об- работчику прерывания, так и фоновой. В функции can_count () сначала инкрементируется переменная EVENT — оператор ++ записан перед переменной. Если получившееся значение равно 24, то счетчик обнуляется, а переменная BATCH инкрементируется. Таким образом, фоновая программа извещается о том, что упаковка из 24 банок уже заполнена. По сравнению с 40 командами программы, написанной на ассемблере, при использовании языка высокого уровня размер программы получается равным 94
296 Часть II. Программное обеспечение командам. Однако в последнем случае поддержка прерываний была бы более гиб- кой при необходимости обработки запросов от нескольких источников прерыва- ний, а функция задержки позволяла бы формировать более длинные интервалы, которые вполне могут возникнуть в реальной жизни. Пример 9.6 Массив однотипных объектов определяется в Си с помощью конструкции fred[n], где fred — имя массива (в действительности этому идентификатору соответствует адрес первого элемента массива), ап — количество элементов мас- сива. Так, обнаружив в тексте программы строку unsigned int fred[16] компилятор зарезервирует в памяти данных 16 регистров, расположенных подряд. При объявлении массива можно задать для каждого элемента начальное зна- чение. Например, запись unsigned int svn_seg[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; определяет массив из десяти байтов, содержащих коды управления 7-сегментным индикатором, приведенные на Рис. 6.8 (стр. 183). Эти десять значений от svn_seg [ 0 ] до svn_seg [ 9 ] будут размещены в де- сяти последовательно расположенных регистрах. Большинство микроконтролле- ров PIC имеют достаточно ограниченный размер памяти данных, и в таком слу- чае, когда значения не изменяются в дальнейшем, имеет смысл разместить эти десять констант в ПЗУ программ в виде набора команд retlw <константа>, подобно тому, как это было показано в Программе 6.6 на стр. 184. Для этого доста- точно к объявлению массива добавить ключевое слово const: unsigned int const svn_seg[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; Используя описанные способы, напишите программу, реализующую элект- ронный аналог игральной кости с семью светодиодами, подключенными к стар- шим семи выводам порта В, как показано на Рис. 9.4, а. Основная программа бу- дет просто инкрементировать глобальную переменную целого типа с максималь- но возможной скоростью. При нажатии на кнопку, подключенную к выводу INT/RB0, программа будет переходить к обработке прерывания (см. Пример 9.5). Процедура обработки прерывания отобразит одну из шести комбинаций светоди- одов, а после 10 с выключит светодиоды, чтобы сэкономить энергию батареи. Ис- пользование часового кварца частотой 32 768 Гц также уменьшит потребление схемы, как можно увидеть на Рис. 10.3 (стр. 306). Решение Коды управления СИД задаются в Программе 9.7 в виде глобального массива из шести констант в соответствии с Рис. 9.4, б. Значения этих кодов сдвинуты на один бит влево по сравнению со значениями, приведенными в таблице истиннос- ти, — для их вывода через старшие семь линий порта В.
Глава 9. Язык высокого уровня 297 PIC RBO/INT RB1 RB2 RB3 RB4 RB5 RB6 RB7 Очки П Код [п] gfedcba О 0111111 1 1110110 2 0110110 3- 1100100 4 0100100 5 1000000 а) Принципиальная схема а Ь f 9с е д] h'3F’ h'76’ h’36' h’64’ h'24' h’40’ б) Таблица кодов Рис. 9.4. Коды управления светодиодами электронной игральной кости Основная программа просто инкрементирует однобайтную переменную throw и сбрасывает ее в ноль, когда она становится больше пяти. Таким образом, реализуется программный счетчик по модулю 6, т.е. 0, 1, 2, 3, 4, 5, 0,... Компилятор CCS версии 3.18 генерирует функцию main(), состоящую из восьми команд, включая несколько команд перехода и пропуска. В результате при указанной тактовой частоте инкрементирование переменной происходит около 1000 раз в секунду. Такая частота обеспечивает практически случайный выбор одного из шести значений по нажатию кнопки, подключенной к выводу INT. Программа 9.7. Электронная игральная кость #include <16f84.h> #use delay (clock=32768) #byte PORTB = 6 void die(void); unsigned int const array[6] = {0x7e, Oxec, 0x6c, 0xc8, 0x48, 0x80}; unsigned int throw; void main(void) { set_tris_b(0x01); enable_interrupts(INT_EXT); enable_interrupts(GLOBAL); /* Устанавливаем INTE в 1 /* Устанавливаем GIE в 1 */ */ while(1) { PORTB = 0; if(++throw > 5) throw=0; /* Бесконечный цикл /* Выключаем светодиоды */ /* Инкрементируем (по модулю 6) */
298 Часть II. Программное обеспечение #int_ext void die(void) { PORTB - array[throw]; delay_ms(10000); } /* Обработчик внешнего прерывания */ /* Отображаем n-ю комбинацию точек */ /* в течение 10000 мс */ Функция обработчика прерывания die () копирует n-й элемент нашего мас- сива констант в регистр PORTB и, прежде чем вернуться в основную программу, приостанавливает выполнение программы на 10 с. Поскольку в функции main () регистр PORTB постоянно сбрасывается, после возврата из обработчика индика- тор будет очищен. Вопросы для самопроверки 9.1. Для управления светодиодами игральной кости из Примера 9.6 требуется семь линий параллельного порта, а для некоторой электронной игры требуется две такие кости. Посмотрите внимательно на Рис. 9.4 — можно ли уменьшить ко- личество линий, используемых для управления одной костью, до четырех? 9.2. В рамках реализации некоторой электронной игры необходимо написать функцию, возвращающую следующее псевдослучайное число из 127 чисел, сгенерированных генератором, показанным на Рис. 6.12 (стр. 206). В функ- цию передается текущее, а возвращается следующее число последовательнос- ти. Предполагается, что передаваемое в функцию значение отлично от нуля. Как можно доработать функцию, чтобы она выдавала через порт В всю после- довательность случайных чисел, начиная с заданного числа? 9.3. Цифровой термометр на базе микроконтроллера PIC показывает температу- ру от 0 до 100°С. Чтобы это устройство можно было продавать в США, в тер- мометре следует предусмотреть возможность отображения значения темпе- ратуры в градусах по шкале Фаренгейта. Напишите для этого термометра функцию, выполняющую перевод целого значения из шкалы Цельсия в шкалу Фаренгейта. Соответственно, передаваемое и возвращаемое значе- ния должны иметь тип unsigned int. Зависимость между шкалами выра- жается следующим образом: F = (Cx9)/5 + 32. Во избежание ошибок переполнения следует использовать 16-битную арифметику. 9.4. Индикатор холодной погоды на приборной панели автомобиля представля- ет собой три светодиода, подключенных к трем младшим линиям порта А. Ко 2-й линии подключен красный СИД, который включается, если темпе- ратура снаружи ниже 34°Е К 1-й линии подключен желтый СИД (Темпера- тура ниже 40°F), а к 0-й линии подключен зеленый СИД. Предположим, что соответствующие линии порта уже сконфигурированы как выходы и что СИД включается при подаче на вывод НИЗКОГО уровня. Напишите функ- цию для управления этими светодиодами, в которую передается значение температуры Е
ЧАСТЬ III ОКРУЖАЮЩИЙ МИР Глава 10. Реальное окружение Глава 11. Ничего, кроме байтов Глава 12. Ох уж эти биты! Глава 13. Главное — время Глава 14. Этот безумный аналоговый мир Глава 15. Хранить вечно! Глава 16. Дальнейшее развитие Глава 17. Учебный пример
300 Часть III. Окружающий мир Не считая краткого обсуждения гарвардской архитектуры, имевшего место в 3-й главе книги, мы до сих пор ограничивались рассмотрением внутренней струк- туры микроконтроллеров PIC среднего уровня и их программного обеспечения. Только вскользь упоминались параллельные порты ввода/вывода. Последняя же часть книги посвящена взаимодействию ядра микроконтроллера со средой, физи- чески находящейся за пределами корпуса микроконтроллера. То есть в данной части мы изучим вопросы использования портов ввода/вывода и встроенных пе- риферийных устройств микроконтроллера. В самом конце мы разберем учебный пример создания законченного автономного встраиваемого контроллера. На Рис. 4.1, приведенном на стр. 89, изображена внутренняя структура и цо- колевка микроконтроллера PIC16F84. У этой 18-выводной модели имеется два параллельных порта ввода/вывода, причем 4-й вывод порта А совмещен со счет- ным входом 8-битного таймера, а 0-й вывод порта В — с входом внешнего преры- вания. Помимо этого, в составе микроконтроллера имеется EEPROM-память данных размером 64 байта и сторожевой таймер. Микроконтроллер PIC16F84 был одним из первых представителей семейства среднего уровня, в которых наряду с параллельными портами и таймерами, унас- ледованными от более старого базового семейства, имелась поддержка прерыва- ний и модуль EEPROM-памяти. По мере появления новых моделей расширялся и набор периферийных устройств. В этой части книги на примере 8-выводных моделей PIC12F629/75, 18-выводных моделей PIC16F627/28/48 и 28/40-вывод- ных моделей PIC16F873/74/76/77 мы рассмотрим наиболее часто используемые периферийные устройства. Вообще говоря, функционирование любого модуля практически не зависит от модели устройства, в которой он реализован, однако модули в более новых моделях могут иметь расширенные функциональные воз- можности. По мере прочтения, вы: • Изучите сопутствующие вопросы, такие как выбор источника питания, вы- бор источника тактового сигнала, управление энергопотреблением микро- контроллера и конфигурирование устройства. • Познакомитесь с параллельным и последовательным вводом/выводом циф- ровых данных. • Разберетесь с подсистемами счетного и сторожевого таймеров. • Узнаете, каким образом микроконтроллер обрабатывает аналоговые сигналы. • Самостоятельно разработаете встраиваемый таймер со звуковой индика- цией. • Узнаете, каким образом можно протестировать и отладить созданную систему.
Часть III. Окружающий мир 301 • PlC18Uf 452-1/Р . 0.3411U8 : ЧХ Microchip Microchip PI.C18LF452 ’ ' -1 Л1 031528К •” PIC16F628A -е/р ф 03331BS , * L 4В0$С 4 шоюл к >-S£W ГГГТ .IJULJL 12F675 ' UP0H0 ф0340' Микроконтроллеры PIC в различных корпусах
ГЛАВА РЕАЛЬНОЕ ОКРУЖЕНИЕ До этого момента мы главным образом изучали взаимодействие программы с внутренними регистрами процессора и памятью данных. Но прежде чем перейти к изучению встроенных периферийных устройств микроконтроллера, посред- ством которых он может отслеживать состояние и управлять окружающей его средой, т.е. реальным миром вне его выводов, нам необходимо рассмотреть требо- вания, предъявляемые к источнику питания, а также вопросы тактирования и сброса микроконтроллеров PIC. Прочитав эту главу, вы: • Ознакомитесь с рекомендуемыми величинами напряжения питания, а так- же допустимыми уровнями входных и выходных напряжений. • Поймете, в чем разница между статической и динамической рассеиваемой мощностью, и узнаете, что величина последней прямо пропорциональна тактовой частоте микроконтроллера и квадрату напряжения питания. • Научитесь переключать микроконтроллер в режим пониженного энерго- потребления и выводить его из этого режима, а также узнаете, как этот ре- жим влияет на процессор. • Познакомитесь со встроенным тактовым генератором. • Узнаете, как можно во время программирования микроконтроллера зада- вать его конфигурацию. • Разберетесь в нюансах различных вариантов сброса. В качестве своеобразной прелюдии к нашему разговору предлагаю взглянуть на Рис. 10.1, где показана структура микроконтроллеров PIC16F874 и PIC16F877, на примере которых мы в основном и будем далее изучать микроконтроллеры PIC. Эти модели полностью идентичны, за исключением большего объема памя- ти программ, данных и EEPROM в последней. Поэтому основное внимание мы уделим микроконтроллеру PIC16F877. Микроконтроллеры PIC16F873/6 являют- ся 28-выводными вариантами тех же микроконтроллеров и соответственно име- ют урезанный набор периферии. В дальнейшем для ссылки на эти четыре модели мы будем использовать обозначение PIC16F87X. За исключением объемов различных областей памяти, ядра этих процессоров очень похожи на ядра остальных моделей среднего уровня, поскольку поддержи- вают набор из 33 команд, описанный в главе 5. Если сравнить структуру микро-
Глава 10. Реальное окружение 303 Vdd Vss Шина данных* памяти программ программ Конвейер Регистр команд i Регистр команд 2 КОП 8 MCLR OSC1 OSC2 PIC16F874/77A ___________П'О2’ Счетчик команд |^i Память данных EEPROM Шина данных памяти данных 8 h'lOF'h'IOD' EEADRH I EtAbh R - Стёк - - (8 уровней)Z озу Регистры общего назначения 192/368x8 Память данных 7 Непосредственный адрес 8 Значение константы /Мультиплексору адреса \ Шина данных памяти данных Косвенный адрес ___И04- FSR И Память данных EEPROM 128/256x8 ^И’ЮЕ’^'ЮС’ EEDATH EEDATA Таймер О Дешифратор команд и схема управления QI Q2 Q3 Q4 1—£ h'03‘ 1RP1 RPO IRP ZOCC Регистр STATU Порт ввода/вывода А |Рабочий регистр! Генератор RA1/AN1 RAO/ANO Сторожевой таймер Е 3 Таймер запуска генератора Таймер включения питания Сброс по включению питания Сброс по снижению напряжения питания Шдоск1/сюит SLEEP !/AN3/Vref+ RA2/AN2/Vref-/CVREF S. S Порт ввода/вывода В Порт ввода/вывода С RA5/SS7AN4/C2OUT Рис. 10.1. Архитектура микроконтроллеров PIC16F874/77A
304 Часть III. Окружающий мир контроллера PIC16F84, приведенную на Рис. 4.1 (стр. 89), со структурой микро- контроллеров, изображенной на Рис. 10.1, то можно заметить, что у последних имеется больше периферийных модулей. Разумеется, даже при наличии 40 выво- дов невозможно предоставить каждому периферийному устройству отдельные линии ввода/вывода для общения с внешним миром. Поэтому большинство вы- водов является разделяемым ресурсом. Например, вывод RA3 является 3-м битом порта А, но также может использоваться в качестве 3-го аналогового входа AN3 или даже как вход для подключения внешнего источника опорного напряжения Fref+ для модуля аналого-цифрового преобразователя. В более миниатюрных мик- роконтроллерах, таких как 18-выводной PIC16F6271) и 8-выводной PIC12F675, цоколевка которых приведена на Рис. 10.2, тоже имеются различные периферий- ные модули. Только в этом случае один и тот же вывод может использоваться не- сколькими модулями, что накладывает более серьезные ограничения на одновре- менное применение различных модулей в конкретном приложении. В моделях с малым числом выводов разработчик обычно может использовать внутренний тактовый генератор, а также исключить вход внешнего сброса для экономии дра- гоценных ресурсов (см. Табл. 10.2). GP5/T1CKI/0SC1/CLKIN Е GP4/T1G/OSC2/CKOIIT Q gp3/mclr/vpp □; 3 GPO/ANO/CIN+ 3] GP1/AN1/CIN-/Vref JO GP2/AN2/TOCKI/INT/COUT RA2/AN2/Vref [T RA3/AN3/CMP1 E RA4/T0CKI/CMP2 E RA5/MCLR/THV E VSS E RBO/INT E RB1/RX/DT E RB2/TX/CK E RB3/CCP1 E TJJ RA1/AN1 Л1 RAO/ANO 33 RA7/0SC1/CLKIN ' JO RA6/OSC2/CLKOUT Л1 RB7/T10SI 33 RB6/T10S0/T1CK1 Ш RB5 a) PIC12F675 6) PIC16F627/28/48 (16F62X) Puc. 10.2. Цоколевка некоторых моделей микроконтроллеров PIC Все микроконтроллеры PIC обычно имеют номинальное напряжение пита- ния KDD = 5 В. Типичный представитель семейства PIC16F87X может работать на частотах до 20 МГц при напряжении питания 5 ±0.5 В. Если тактовая частота не превышает 16 МГц, то напряжение может быть снижено до 4 В. Многие предста- вители семейства также имеют низковольтные исполнения. Например, микро- контроллер PIC16LF87X может работать на частотах до 10 МГц при напряжении 3...5.5 В, а при снижении частоты до 4 МГц напряжение питания может быть Микроконтроллер PIC16F628 полностью идентичен указанному, но имеет в 2 раза больше памяти программ (2 Кслова). А в микроконтроллере PIC16F648 объем памяти программ еще в 2 раза больше, т.е. 4 Кслова. Везде, где это возможно, при ссылке на эти три микроконт- роллера мы будем использовать обозначение PIC16F62X.
Глава 10. Реальное окружение 305 уменьшено до 2 В. А вот модели PIC12F629/6751) даже в обычном исполнении могут работать при напряжении 2...5 В. С точки зрения элементов схемы микроконтроллеры PIC являются обычны- ми цифровыми микросхемами. Напряжение НИЗКОГО уровня на выводе, скон- фигурированном как выход, не превышает значения K0L = 0.6 В при втекающем токе до 8.5 мА в диапазоне температур —4О...+85°С. Вывод, установленный мик- роконтроллером в состояние ВЫСОКОГО уровня, может отдавать ток до 3 мА, при этом напряжение на нем будет не менее KDD — 0.7 В, т.е. при KDD = 5 В напря- жение ВЫСОКОГО уровня Ион = 4.3 В. Для вывода, сконфигурированного как вход, напряжение величиной, состав- ляющей менее 15% (для входов с триггером Шмитта — менее 20%) от напряжения питания, будет восприниматься как напряжение НИЗКОГО уровня. Таким обра- зом, при KDD = 5 В входное напряжение НИЗКОГО уровня KIL составит 0.75 В. За небольшим исключением2), все выводы, функционирующие как входы, воспри- нимают напряжение величиной более 25% (для входов с триггером Шмитта — бо- лее 80%) от напряжения питания плюс 0.8 В как напряжение ВЫСОКОГО уров- ня, т.е. Ин = 2 В при KDD = 5 В. В Табл. 10.1 приведены значения тока потребления рассматриваемых моделей микроконтроллеров при различных режимах работы. Таблица 10.1. Ток потребления Генератор PIC16F87XA-I PIC16F62XA-I PIC12F629/675-I 20 МГц Режим HS 7...15 мА при 5.5 В 3...3.3 мА при 5 В 2.4...3 мА при 5 В 4 МГц Режим XT 1.6...4 мА при 5.5 В 0.6...2мАприЗ В* 670...780 мкА при 5 В 240...300 мкА при 2 В* 0.6... 1.4 мА при 5 В 70...110 мкА при 2 В 32 кГц Режим LP 20...35 мкА при 3 В* 38...48 мкА при 5 В 12...15 мкА при 2 В* 35...54мкАпри 5 В 9.16 мкА при 2 В Спящий режим 1.5...1.6 мкА при 4 В 0.9...5 мкА при 3 В* 200...900 нА при 5 В 100...800 нА при 2 В* 2.9 мкА...995 нА при 5 В 0.99 мкА...700 нА при 2 В * Низковольтное исполнение (PIC16LF). Многие микроконтроллерные устройства питаются от батарей, и в этих случа- ях величина тока потребления микроконтроллера является одним из его важней- ших параметров. Для облегчения разработки таких устройств компания Microchip выпускает семейство nanoWatt, к которому, в частности, относятся модели PIC12F629/675. Представители этого семейства имеют очень маленькое потреб- ление и способны работать в широком диапазоне питающих напряжений. Из до- кументации на микроконтроллеры можно увидеть, что максимальный и мини- Микроконтроллер PIC12F629 идентичен PIC12F675, но не имеет модуля аналого-цифро- вого преобразователя. ______ 2) Основными исключениями являются вход внешнего сброса MCLR и вход внешнего так- тового сигнала OSC1. Для первого напряжение ВЫСОКОГО уровня, при котором микроконт- роллер выходит из состояния сброса, составляет 0.85 FDd. А для вывода OSC1 при подключении к нему внешнего генератора, сигнал которого предполагается использовать в качестве такто- вого, напряжение Ин составляет 0.7 l^DD.
306 Часть III. Окружающий мир мальный токи потребления могут отличаться в десятки миллионов раз. Поэтому крайне необходимо четко понимать, какие факторы влияют на суммарное пот- ребление микроконтроллера. Типичная зависимость тока потребления микроконтроллеров PIC от их тактовой частоты приведена на Рис. 10.3. Ясно видно, что рассеиваемая мощность rDD х /DD прямо пропорциональна рабочей частоте. Так, при тактовой частоте 10 МГц ток пот- ребления микроконтроллера в 100 раз больше, нежели при частоте 100 кГц. Тактовая частота [МГц] Рис. 10.3. Типичная зависимость тока потребления от тактовой частоты Чтобы понять, почему так происходит, представьте себе ключ, заряжающий и разряжающий емкостную нагрузку С, как показано на Рис. 10.4. В качестве такого ключа выступает транзистор, а нагрузкой является паразитная емкость связи со следующим полевым транзистором и его затвором. представляет собой сопро- тивление открытого транзистора. При разомкнутом ключе (транзистор закрыт) емкость заряжается по экспо- ненциальному закону до уровня V вольт с постоянной времени т = CRL- В устой- чивом состоянии в конденсаторе хранится энергия, равная l/2 CV2 Дж. Кроме то-
Глава 10. Реальное окружение 307 Рис. 10.4. Эквивалентная схема выходного каскада (конденсатор С представляет собой как внутреннюю емкость, так и емкость нагрузки) го, при протекании этого тока заряда конденсатора через сопротивление нагруз- ки в последнем рассеивается энергия, которая вычисляется следующим образом: Начальный ток заряда (Vc = 0): Мгновенное значение тока: Мгновенное значение мощности, рассеиваемой в RL: Суммарная энергия, рассеиваемая в T?L: /0=r/T?L ic = ic2RL=i02RLe~2^ = (r2/7?L)e~2f E=V2/Rl \™e~2'dt = = JT2/RL-Le-2^ = = V2/Rl(^) = \CV2 Итак, 7г СИ2 Дж рассеивается на сопротивлении нагрузки (независимо от ве- личины этого сопротивления AJ) и еще столько же накапливается в ЭЛСКТРИЧСС- КОМ поле конденсатора. При разряде конденсатора эта накопленная энергия рас- сеивается на сопротивлении, образованном параллельным соединением резисто- ров 7?s и (и опять же значение рассеиваемой энергии не зависит от значений этих сопротивлений). Таким образом, при каждом переключении транзистора рассеивается энергия, равная СИ2 Дж. Суммарная рассеиваемая мощность равна произведению этого значения на число циклов переключения в секунду (CV2f) плюс потери в статическом режиме из-за токов утечки. Из предыдущего соотношения (CV2f) видно, что рассеиваемая мощность пря- мо пропорциональна частоте при любом заданном напряжении питания. Более того, она пропорциональна квадрату напряжения питания, так что, снизив на- пряжение питания в 2 раза (скажем, с 5 до 2.5 В), мы уменьшим рассеиваемую мощность (HDD х /DD) в 4 разаЧ '* Именно по этой причине напряжение питания большинства современных микропроцес- соров, использующихся в качестве ЦПУ персональных компьютеров (например, Pentium 4), не превышает 3 В, в отличие от более старых устройств, работающих от 5 В.
308 Часть III. Окружающий мир Значение динамической рассеиваемой мощности, вычисленной выше, следу- ет сложить с мощностью, рассеиваемой в статическом режиме (при тактовой час- тоте устройства, равной нулю). Из нижней строки Табл. 10.1 видно, что величина этого базового тока (или тока в режиме Power Down), который в документации обозначается как /PD, обычно не превышает 1 мкА. При этом подразумевается, что периферийные модули, имеющие собственные тактовые генераторы (такие, как сторожевой таймер и схема сброса по снижению питания), отключены. Разумеется, оставить процессор без тактового сигнала будет слишком непро- дуктивно, поскольку в этом случае он ничего не сможет делать! Однако во многих встраиваемых системах необходимость в вычислениях возникает спорадически, поэтому было бы полезно иметь возможность перевода процессора в режим ожи- дания в те периоды, когда от него не требуется никаких действий. Возьмем, к примеру, микроконтроллерный датчик системы радиотелеметрии, находящийся на дне озера. Он может проводить измерение температуры всего один раз в час, при этом время его работы от одной батареи должно составлять не менее года. Для поддержки ситуаций, подобных описанной, все микроконтроллеры PIC имеют «спящий» режим, в котором внутренний тактовый генератор отключается. Переход в этот режим осуществляется по команде sleep. При нахождении в «спящем» режиме содержимое памяти данных не изменяется при условии, что напряжение питания больше 1.5 В (параметр KDR в документации). Микроконт- роллеры PIC могут выходить из спящего режима при сбросе устройства (см. стр. 316), при появлении запроса разрешенного прерывания или при переполне- нии сторожевого таймера. _____ Когда процессор исполняет команду sleep, он сбрасывает бит PD (Power Down) в регистре STATUS (см. Рис. 4.6 на стр. 95) и выключает внутренний так- товый генератор. Если сторожевой таймер (см. стр. 453) включен, то при выпол- нении этой команды он вместе со своим предделителем сбрасывается, но продол- жает работать, поскольку имеет собственный внутренний генератор. При этом устанавливается флаг ТО (Time Out), индицирующий отсутствие тайм-аута. Со- держимое всех регистров, включая различные установки портов, остается неиз- менным. Для пробуждения микроконтроллера по прерыванию флаг этого прерывания должен быть сброшен, а соответствующий бит маски прерывания — установлен, чтобы разрешить запрос прерывания от этого источника. Если флаг общего раз- решения прерываний GIE (см. Рис. 7.3 на стр. 213) установлен, то после выполне- ния команды, следующей за командой sleep, процессор перейдет к процедуре обработки прерывания, каки при обычном отклике на запрос прерывания. Одна- ко если бит GIE сброшен, запрещая реакцию на прерывания, то процессор не бу- дет передавать управление в обработчик прерывания, а просто выполнит коман- ду, следующую за sleep, и продолжит работу в обычном режиме. Обратите вни- мание, что в этом случае программист должен будет после выхода из спящего режима сбросить соответствующий флаг прерывания. Если при сброшенном бите GIE разрешенное прерывание произойдет до вы- полнения команды sleep (на что указывает установленный флаг прерывания), то команда sleep будет выполнена как команда пор (нет операции). При этом
Глава 10. Реальное окружение 309 бит PD сброшен не будет, так что программа сможет, при необходимости, опреде- лить, действительно ли микроконтроллер находился в «спящем» режиме после выполнения команды sleep. Программа также может определить ситуацию про- буждения по тайм-ауту сторожевого таймера, контролируя состояние бита ТО ре- гистра STATUS (в этом случае он должен быть сброшен). При использовании в программе сторожевого таймера хорошим тоном считается помещение перед ко- мандой sleep команды сброса сторожевого таймера clrwdt. Проверяя состоя- ние флага соответствующего прерывания в регистре INTCON, можно будет опре- делить, вышел ли микроконтроллер в результате прерывания из спящего режима или нет. Независимо от причины, вызвавшей выход микроконтроллера из «спящего» режима, перед выполнением команды, следующей за sleep, формируется за- держка длительностью 1024 такта/osc- Эта задержка необходима для запуска так- тового генератора и установления его сигнала. Данная задержка, показанная на Рис. 10.10, не формируется, если микроконтроллер использует тактовый генера- тор на АС-цепочке (см. Рис. 10.5, б и Ткбл. 10.3). При выключенном сторожевом таймере ток ждущего режима /PD намного меньше. К примеру, в модели PIC16F87XA-I типичные значения тока /PD равны 1.5 мкА (максимальное — 16 мкА) и 10.5 мкА (максимальное — 42 мкА) при вы- ключенном и включенном сторожевом таймере соответственно. Указанные зна- чения справедливы для KDD = 4 В, при этом все линии ввода/вывода микроконт- роллера сконфигурированы как входы и подтянуты либо к Kss (как правило), ли- бо к KDD. Во всех микроконтроллерах PIC имеется встроенный тактовый генератор, который в совокупности с узлами схемы синхронизации формирует внутренние синхросигналы, показанные на Рис. 4.4 (стр. 92). Все модели могут работать в од- ном из четырех стандартных режимов, перечисленных в Табл. 10.2. В трех из этих режимов в качестве времязадающего элемента используется кварцевый или кера- мический резонатор, подключаемый между выводами OSC1 и OSC2 микроконт- роллера. Для бюджетных приложений, не предъявляющих повышенных требова- ний к стабильности частоты тактового сигнала, можно использовать режим, в ко- тором частота тактового сигнала задается АС-цепочкой. Таблица 10.2. Режимы работы тактового генератора Режимы, стандартные для всех моделей LP XT HS RC/EXTRC Экономичный режим для резонаторов с частотой до 200 кГц Обычный режим для резонаторов с частотами от 200 кГц до 4 МГц Высокопроизводительный режим для резонаторов с частотами от 4 до 20 МГц Генератор с внешней RC-цепочкой. На вывод CLKOUT выдается тактовый сигнал Дополнительные режимы для PIC12F629/675 INTOSC1 Внутренний RC-генератор 4 МГц. Вывод CLKOUT используется в качестве линии ввода/вывода INTOSC2 Внутренний RC-генератор 4 МГц. На вывод CLKOUT выдается тактовый сигнал ЕС Внешний генератор. Вывод CLKOUT используется в качестве линии ввода/вывода RC2 Генератор с внешней RC-цепочкой. Вывод CLKOUT используется в качестве линии ввода/вывода
310 Часть III. Окружающий мир Из Рис. 10.5, а видно, что в режимах с резонатором для формирования сигна- ла используется инвертирующий усилитель, который отключается вместе с под- ключенными к нему узлами по команде sleep. Единственное различие между режимами заключается в коэффициенте усиления этого инвертирующего усили- теля. В режиме LP коэффициент усиления имеет минимальное значение, за счет чего уменьшается потребление генератора. Режим HS используется при высоких частотах резонатора, при этом потребление генератора максимально. Вообще го- воря, следует выбирать режим с минимально возможным коэффициентом усиле- ния. В документации на конкретное устройство подробно указаны диапазоны ра- бочих частот и номиналы внешних компонентов. Максимальная тактовая частота моделей среднего уровня составляет 20 МГц1), однако некоторые модели имеют пониженное быстродействие — их тактовая частота, как правило, не может пре- вышать 4 МГц. а) Кварцевый/керамический резонатор sleep Внутренний тактовый fosc/4 б) Внешняя RC-цепочка Рис. 10.5. Типовые конфигурации тактового генератора В типичном устройстве с тактовой частотой 10 МГц используется 10-МГц кристалл с АТ-срезом в режиме HS, при этом С1 = 22 пФ и С2 = 33 пФ. В режиме LP используется 32-кГц резонатор с С\ = 68 пФ и С2 = 100 пФ. Несмотря на то что оба конденсатора могут иметь одинаковые номиналы, большее значение С2 улуч- шает пусковые характеристики после сброса и выхода из спящего режима. Неко- торые резонаторы в режиме HS могут потребовать подключения к выводу OSC2 последовательного резистора. Более подробно это расписано в документе AN588 «Р1С16/17 Oscillator Design». Керамические резонаторы менее дороги, чем кварце- вые, однако они имеют худшую точность установки частоты (порядка 0.5%) и меньшую температурную стабильность. Некоторые керамические резонаторы могут уже иметь встроенные конденсаторы, что позволяет уменьшить количество компонентов схемы. В документе AN588 приводится сравнение керамических и кварцевых резонаторов, используемых в таких приложениях. Модели расширенного семейства PIC18XXXX могут работать на частотах до 40 МГц при использовании 10-МГц резонатора за счет наличия режима ФАПЧ, в котором осуществляется умножение частоты.
Глава 10. Реальное окружение 311 Четвертый из стандартных режимов позволяет использовать в качестве время- задающего элемента внешнюю ЛС-цепочку. В этом режиме, как показано на Рис. 10.5, б, вывод OSC2 играет роль буферизированного выхода тактового сигна- ла и может использоваться для синхронизации внешних цифровых схем, в том числе и других микроконтроллеров PIC. Такой режим в основном применяется в бюджетных приложениях, которые не предъявляют жестких требований к точ- ности установки тактовой частоты и ее стабильности. Частота зависит от сопро- тивления Rlt емкости конденсатора и напряжения питания KDD. Как правило, в документации на конкретную модель приводятся таблицы и графики, показы- вающие типичные зависимости частоты от этих параметров. Так, в микроконт- роллере PIC16F87X усредненное значение частоты будет равно 1.7 МГц ±10% при KDD = 5 В, 7?! = 3.3 кОм и Q = 100 пФ (при температуре +25°С). Разумеется, необ- ходимо принимать во внимание точность параметров используемых компонен- тов, а также их температурные характеристики. Кроме того, микроконтроллеры PIC можно тактировать от внешнего генера- тора. Эта возможность может быть полезна при работе нескольких устройств от одного тактового сигнала. В этом случае внешний генератор подключается к вы- воду OSC1 микроконтроллера, а вывод OSC2 оставляют неподключенным или заземляют через резистор, чтобы уменьшить уровень помех. Сигнал генератора должен удовлетворять следующим требованиям: напряжение НИЗКОГО уровня KIL < 0.3 KDD, а напряжение ВЫСОКОГО уровня И1Н > 0.7 При использова- нии внешнего генератора микроконтроллеры PIC должны работать в соответ- ствующем по частоте режиме, рассчитанном на подключение резонатора. Использование целых двух выводов для подключения времязадающих эле- ментов’слишком расточительно в случае 8-выводных устройств, особенно если учесть, что два вывода в любом случае используются для подачи питания! По этой причине в моделях с малым числом выводов обычно имеются дополнительные режимы схемы тактирования, предназначенные для высвобождения одного или даже обоих выводов под нужды ввода/вывода. В Табл. 10.2 для примера приведе- ны режимы работы микроконтроллеров PIC 12F629/675. В этих режимах исполь- зуется генератор с внутренней ЛС-цепочкой, формирующий сигнал частотой 4 МГц. Как и в случае внешней ЛС-цепочки, реальное значение частоты можно определить только с известной долей приближения, однако для каждого конкрет- ного экземпляра микроконтроллера программист может подстроить частоту, из- меняя значение четырех младших битов регистра OSCAL (Oscillator CALibrate — калибровка генератора). За счет этого можно получить 16 слегка отличных друг от друга значений. Наилучшее из этих значений заносится при изготовлении мик- роконтроллера в последнюю ячейку памяти программ в виде команды retlw п. Таким образом, программист может в блоке инициализации программы выпол- нить эту команду с последующим копированием значения, возвращенного в W, в регистр OSCAL. ° При использовании генератора с ТТЛ-совместимым выходом для достижения требуе- мого значения И1Н может потребоваться подтягивающий резистор.
312 Часть III. Окружающий мир В режиме INT0SC2 на вывод OSC2 выдается тактовый сигнал для управления внешними узлами, как показано на Рис. 10.5, б. Остальные три режима высво- бождают вывод OSC2 под нужды ввода/вывода. В частности, режим RC2 иденти- чен стандартному режиму RC (иногда называемому EXTRC), но не формирует выходного сигнала. Режим External Clock позволяет использовать внешний гене- ратор, но, в отличие от обычного режима кварцевого генератора, также не пре- пятствует использованию вывода OSC2 в качестве линии порта ввода/вывода. Режим работы тактового генератора является всего лишь одной из опций, ко- торые можно задавать в момент записи программы в память программ микрокон- троллера. В принципе если вы не собираетесь разрабатывать собственный про- грамматор, то вам будет совершенно неважно, каким образом осуществляется собственно процесс программирования. Но скорее всего вы будете использовать коммерческий программатор, например PICSTART®, выпускаемый компанией Microchip. Окно ИСР MPLAB при работе совместно с этим программатором по- казано на Рис. 17.4 (стр. 616). Тем не менее для полноты изложения на Рис. 10.6, а приведена схема подклю- чения микроконтроллера при так называемом высоковольтном программировании (High-Voltage Programming — HVP). Это специальное состояние микроконтролле- ра, используемое для его программирования и верификации, инициируется пода- чей на вход сброса MCLR напряжения +13 В при НИЗКОМ уровне на выводах RB6 и RB7. После этого требуемые данные можно будет пересылать в микроконт- роллер через вывод RB6 синхронно с внешним тактовым сигналом, подаваемым на вывод RB7. Такими данными могут быть команды режима программирования или же машинный код. Аналогично, можно будет считать содержимое незащи- щенных областей памяти программ и сравнить с оригинальным кодом. Большинство новых микроконтроллеров PIC поддерживают также режим низковольтного программирования (Low-Voltage Programming — LVP), не требую- щий использования источника питания высокого напряжения. Этот режим осо- Данные Тактовый сигнал h’2007’ 13___________________4 3 2 1 0 I СР |EWe| WOTE|FdSCt|FPSc| 00 = LP 01 = XT 10 = HS 11 = RC а) Программирование при высоком напряжении б) Слово конфигурации микроконтроллера PIC16F84 13 12 11 | CP1 | cpo |debug| h’2007’ 10 9 8 7 6 5 4 3 2 1 0 | wrt ] lvp |boden| cpi | cpo |РЖ№| wdte|fqsci|fosco| i' ----------" -------------- Защита содержимого EEPROM-памяти данных 00 - Защищена вся память программ 00 = LP 01 = Защищена первая половина памяти программ 01 = XT 10 = Защищены первые 256 ячеек памяти программ 10 = HS 11 = Защита памяти программ отсутствует 11 = RC в) Слово конфигурации микроконтроллеров PIC16F87X Рис. 10.6. Конфигурирование некоторых моделей микроконтроллеров PIC
Глава 10. Реальное окружение 313 бенно полезен для осуществления внутрисхемного последовательного програм- мирования (ICSP™), при котором перепрограммирование микроконтроллера осуществляется непосредственно на печатной плате устройства. Для входа в дан- ное состояние необходимо во время сброса удерживать на выводе RB3 НИЗКИЙ уровень. Затем, после подачи на него напряжения ВЫСОКОГО уровня, можно выполнять программирование через выводы RB7 и RB6. К сожалению, после раз- решения этого режима программирования вывод RB3 нельзя будет использовать в качестве вывода порта. Пока микроконтроллер находится в режиме программирования, программа- тор имеет доступ к памяти программ и может загружать в нее код программы. Кроме того, программатор может обращаться к некоторым «секретным» участкам памяти программ (скрытая область памяти), которые не видны прикладной про- грамме при работе микроконтроллера в нормальном режиме. В микроконтролле- рах среднего уровня таким участком является ячейка памяти программ с адресом h’2007’, зарезервированная под слово конфигурации^. Различные биты этого слова (биты конфигурации) используются для задания конфигурации тактового генератора и других узлов микроконтроллера при его работе в нормальном режиме. В качестве примера на Рис. 10.6, б показан формат конфигурационного слова модели PIC16F84, посредством которого можно зада- вать значения четырех конфигурируемых параметров. Эти параметры являются базовыми, т.е. имеются во всех микроконтроллерах: • Биты FOSC1:0 определяют режим работы тактового генератора (Тзбл. 10.2). • Бит WDTE используется для включения и выключения сторожевого тайме- ра, показанного на Рис. 13.1 (стр. 451). • Бит PWRTE используется для управления таймером включения питания, который рассматривается на стр. 318. • Биты СР предназначены для защиты памяти программ от считывания. Как уже говорилось, в режиме программирования содержимое памяти про- грамм доступно для считывания. Это сделано для того, чтобы программатор мог осуществлять верификацию кода, загруженного в память программ (см. Рис. 17.4 на стр. 616). Если сбросить все биты СР, то данная возможность будет заблокирована. Это позволяет предотвратить копирование прошивки микроконтроллера. После программирования биты СР нельзя стереть даже в исполнениях с кварцевым окошком или памятью программ EEPROM-ти- па. По этой причине Microchip не рекомендует использовать эту опцию во время разработки и отладки устройств. Более новые модели семейства имеют дополнительные опции. Например, в микроконтроллере PIC16F87X, формат слова конфигурации которого показан на Рис. 10.6, в, можно защищать всю память программ, первую половину или вооб- ще только 256 первых ячеек. Также в этих моделях имеются следующие опции: ° За пользовательской областью памяти программ по адресам h’2000’...h’3FFF’ располо- жена специальная область тестирования и конфигурирования, доступная только в режиме про- граммирования/верификации микроконтроллера.
314 Часть III. Окружающий мир • Бит BODEN, установленный в 1, разрешает работу схемы сброса по сниже- нию напряжения питания (Brown-Out Reset — BOR). Эта схема позволяет перезапускать микроконтроллер при провалах напряжения питания ниже заданного уровня, как показано на Рис. 10.11. • Установка бита LVP в 1 позволяет использовать режим низковольтного про- граммирования. • Бит CPD позволяет защитить содержимое EEPROM-памяти данных (см. главу 15). • В этих моделях имеется возможность изменения содержимого незащищен- ных областей памяти программ непосредственно из самой программы, как показано на Рис. 15.4 (стр. 551). Сброс бита WRT запрещает это. • В данных моделях имеется специальный режим отладки, который отключа- ется при установке бита DEBUG в 1. Ы Configuration Bits Address [ value [ category............ , [setting, 2007 3F71 Oscillator XT Watchdog Timer Off Power Up Timer On Brown Out Detect On Lo» Voltage Frosjram QSBSIH] Flash Program Write Enabled Data EE Read Protect Off Code Protect Off Puc. 10.7. Задание конфигурации микроконтроллера в среде MPLAB версии 7.x при использовании программатора PICSTART Программное обеспечение большинства программаторов позволяет пользо- вателю «вручную» задать значение требуемых битов слова конфигурации (как, скажем, на Рис. 10.7) перед собственно записью программы в память микроконт- роллера. Однако наилучшим решением будет указание требуемого значения сло- ва конфигурации непосредственно в программе для автоматического задания конфигурации микроконтроллера при его программировании. В качестве приме- ра возьмем микроконтроллер PIC16F87X, который должен иметь следующую конфигурацию: Режим генератора — XT Биты 1:0 = 01 Сторожевой таймер — выключен Бит 2 = 0
Глава 10. Реальное окружение 315 Таймер включения питания — включен Бит 3 = 0 Защита памяти программ — отключена Биты 5:4 и 13:12= 11 Схема сброса по снижению питания — включена Бит 6 = 1 Низковольтное программирование — запрещено Бит 7 = 0 Тогда наличие в исходном ассемблерном файле директивы __config b'11111101110001’; или h'3F71' приведет к формированию следующего машинного кода: : 02 400Е 00 713F 00 (формат этой строки1* был описан на стр. 250). При программировании микро- контроллера требуемое значение будет записано в ячейку с адресом h’2007. По умолчанию все биты слова конфигурации установлены в 1, поэтому без указания этой директивы микроконтроллер PIC16F87X будет иметь следующую конфигу- рацию: • Используется тактовый генератор с внешней ЛС-цепочкой. • Защита кода отсутствует. • Работа таймера включения питания, сторожевого таймера, а также схемы сброса по напряжению питания разрешена. • Низковольтное программирование и запись в память программ разрешены. Во включаемых файлах, предоставляемых компанией Microchip для всех моде- лей микроконтроллеров (см. Листинг 8.1 на стр. 241), определены константы для всех опций, доступных в конкретном устройстве. Для формирования значения слова конфигурации необходимо объединить эти константы по И. Например, строка: _____config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BODEN_ON & _LVP_OFF формирует тот же самый машинный код, но более понятна программисту, что уменьшает вероятность возникновения ошибок. Кроме того, такой код имеет большую переносимость, поскольку при замене процессора на другой (который может иметь совершенно иное расположение битов в слове конфигурации2)) до- статочно будет поменять только название включаемого файла. Обращаю ваше внимание на то, что при включении некорректного заголовочного файла могут быть запрограммированы неправильные биты. ° Напоминаю, что адрес байта h’400E’ равен удвоенному значению адреса соответствую- щего слова h’2007’. Эти слова хранятся, начиная с младшего байта. 2 ) Даже у таких близких «родственников», как PIC16F87X и PIC16F87XA, биты в слове кон- фигурации располагаются по-разному (см. Рис. 10.6 и Рис. 15.6), поэтому использование кор- ректного включаемого файла очень важно.
316 Часть III. Окружающий мир В компиляторах Си тоже имеется похожий механизм для задания значений би- тов слова конфигурации. Так, в компиляторе CCS для этого предусмотрена дирек- тива #fuses, размещаемая в начале файла. В нашем случае строка с данной ди- рективой будет выглядеть следующим образом: #fuses XT, NOWDT, PUT, NOPROTECT, BROWNOUT, NOLVP Все последовательностные цифровые системы должны выходить из выклю- ченного или какого-либо неисправного состояния строго определенным обра- зом. Сброс микроконтроллеров PIC может быть осуществлен несколькими спосо- бами: • Вручную, с помощью кнопки, подключенной к выводу MCLR, как показа- но на Рис. 10.8, а. • При подаче питания на микроконтроллер, как показано на Рис. 10.10. • При снижении напряжения питания ниже определенного значения во вре- мя работы микроконтроллера (см. Рис. 10.11). • При наступлении тайм-аута сторожевого таймера (см. Рис. 13.1 на стр. 451), который может возникнуть из-за ошибки в программе или даже из-за вы- броса напряжения питания. а) Параметры источника питания соответствуют спецификации б) Большое время нарастания напряжения питания Рис. 10.8. Внешний сброс микроконтроллеров PIC Мы рассмотрим все четыре механизма, а по традиции начнем с внешнего сбро- са. У всех микроконтроллеров PIC имеется, хотя бы как опция, вывод внешнего сброса MCLR. К этому выводу можно подключить кнопку или другую схему, как это показано на Рис. 10.8, а, посредством которых можно будет осуществлять пе- резапуск устройства, в результате чего программа снова начнет выполняться с ко-
Глава 10. Реальное окружение 317 манды, расположенной по адресу вектора сброса. До тех пор пока напряжение на выводе MCLR не превышает 0.2 KDD, устройство остается в состоянии сброса (фаза Q1 внутреннего цикла, см. Рис. 4.4 на стр. 92). Для предотвращения ложных срабатываний схемы сброса НИЗКИЙ уровень на выводе MCLR должен удержи- ваться в течение не менее 2 мкс (см. Пример 10.2). Максимальное рекомендуемое сопротивление подтягивающего резистора составляет 33 кОм. Такое значение выбрано для того, чтобы ток утечки вывода при разомкнутой кнопке не вызвал появления на выводе напряжения ниже 0.8 KDD. Максимальное значение тока утечки /jL вывода MCLR составляет ±5 мкА -при напряжении на входе Kss < MCLR < KDD. Резистор сопротивлением 100 Ом предназначен для ограниче- ния тока при появлении на выводе отрицательного выброса напряжения, в ре- зультате которого один из защитных диодов откроется. Когда на выводе MCLR появляется лог. 1 (т.е. напряжение, больше или равное 0-8 KDD), процессор начинает работать в нормальном режиме, при этом счетчик команд и регистр PCLATH обнуляются, указывая таким образом на команду с ад- ресом h’000’ (вектор сброса). Биты выбора банка ОЗУ IRP, RP0 и RP1 регистра STATUS также обнуляются, так что при запуске процессор работает с 0-м банком. Если вывод MCLR использовался для «пробуждения» микроконтроллера, то бит ТО будет установлен в 1 (не было тайм-аута сторожевого таймера), а бит PD будет сброшен в 0 (процессор находился в «спящем» режиме), в противном случае со- стояние этих битов не изменяется. В любом случае состояние флагов арифмети- ческих операций регистра STATUS не изменится. Состояния всех РСН после сброса приведены в Приложении Б. Помимо сброса, инициируемого подачей сигнала на вход MCLR, во всех мик- роконтроллерах PIC предусмотрен сброс по включению питания (Power-On Reset — POR). Эта внутренняя схема автоматически определяет готовность процессора к работе после подачи питания на микроконтроллер. При сбросе по включению питания сбрасывается бит POR регистра РСОКР, формат которого приведен на Рис. 10.9. Благодаря этому программа может опре- делить наступление данного события (см. Табл. 10.4). При чтении бита POR он не устанавливается автоматически в 1, это необходимо делать самостоятельно. Рис. 10.9. Формат регистра PCON микроконтроллеров PIC16XXXX 1} В самых первых представителях семейства, например PIC16F84, схема сброса по сниже- нию питания отсутствовала, поэтому наличия регистра PCON не требовалось.
318 Часть III. Окружающий мир Чтобы проиллюстрировать работу схемы сброса по включению питания, рас- смотрим некоторую идеализированную ситуацию (см. Рис. 10.10). В момент вре- мени /о был включен источник питания, напряжение которого (KDD) стало воз- растать по экспоненциальному закону до номинального значения +5 В. Если скорость нарастания этого напряжения превышает 0.05 В/мс, то при достижении уровня 1.5...2.1 В будет сформирован внутренний сигнал сброса. По этому сигна- лу происходит следующее: 1. С помощью 10-битного счетчика, тактируемого встроенным генератором, формируется задержка Tpwrt фиксированной длительностью 72 мс. Фор- мирование этой задержки может быть отключено установкой бита PWRTE слова конфигурации в 1 (см. Рис. 10.6). Рис. 10.10. Процесс запуска 5-В устройства при подаче напряжения питания
Глава 10. Реальное окружение 319 2. Если используется один из режимов с резонатором, то после задержки Tpwrt формируется еще одна задержка 7qST длительностью 1024 такта сис- темного тактового сигнала. Эта задержка формируется с помощью 10-бит- ного счетчика, тактируемого системным генератором. Таким образом, га- рантируется, что к моменту старта программы основной генератор уже запущен и работает в нормальном режиме. Понятно, что длительность за- держки T0ST зависит от частоты резонатора. Например, при частоте резона- тора 32 кГц длительность задержки будет не менее 32 мс, тогда как при ре- зонаторе частотой 10 МГц длительность задержки будет равна 102 мкс. Если по окончании этой задержки генератор все еще не запустится1*, то бу- дет сформирована дополнительная задержка неопределенной длительнос- ти. При использовании ЯС-генератора эта задержка не формируется. Также задержка Tost формируется при выходе микроконтроллера из «спящего» режима и опять с той же целью — чтобы обеспечить выход тактового гене- ратора на нормальный режим к моменту запуска программы. 3. Как и в случае внешнего сброса, исполнение кода начинается с адреса век- тора сброса (h’000’). Однако в отличие от внешнего сброса, который не влияет на биты ТО и PD, при сбросе по включению питания оба бита уста- навливаются в неактивное состояние. Все задержки, формируемые в том или ином случае, указаны в Табл. 10.3. Таблица 10.3. Задержки, формируемые при сбросе по питанию и выходе из «спящего» режима Режим работы генератора Включение питания Выход из «спящего» режима PWRT включен PWRT выключен LP, XT, HS 72 мс + 10247oSc 1024TOSc 1024TOSc RC 72 мс — — Если в конечном устройстве ручной сброс не требуется, вывод MCLR можно подключить непосредственно к линии VDD через токоограничивающий резистор. В моделях с небольшим числом выводов, таких как 8-выводной PIC12F629/675, этот вывод можно использовать в качестве линии порта ввода/вывода, если сбро- сить бит MCLRE слова конфигурации в 0. Возможна такая ситуация, когда напряжение на выходе источника питания микроконтроллера возрастает настолько медленно, что внутренний импульс сброса по питанию не генерируется, или же напряжение не достигает требуемого значения даже по истечении задержек TPWRT и Г05т. Как правило, это значение со- ставляет 4.5 В для 5-В моделей с генератором, работающим в режиме HS, и 4 В — в других режимах. В этом случае микроконтроллер может начать выполнять про- грамму некорректно или вообще не запуститься. Когда надежность встроенной схемы сброса по включению питания вызывает сомнение, можно использовать ° Типичное время запуска генератора с 32-кГц кварцевым резонатором составляет 1...2 с. При частоте кварцевого резонатора более 100 кГц время запуска генератора не превышает Ю...20мс, а при использовании керамического резонатора не превышает, как правило, 1 мс. Эти значения зависят от напряжения питания микроконтроллера.
320 Часть III. Окружающий мир дополнительные узлы, предназначенные для удерживания НИЗКОГО уровня на выводе MCLR в течение достаточно длительного времени, необходимого для до- стижения напряжением KDD своего рабочего значения. Указанные функции вы- полняет схема, показанная на Рис. 10.8, б. Емкость конденсатора следует выби- рать таким образом, чтобы постоянная времени RC в несколько раз превышала время нарастания напряжения питания до рабочего значения. При заданном зна- чении сопротивления и емкости конденсатора 2.2 мкФ постоянная времени будет равна примерно 100 мс. Более подробно об этом можно прочитать в документах AN522 «Power-ир Consideration» и AN607 «Power-ир Trouble Shooting». Нормально работающий микроконтроллер может начать работать неправиль- но, если напряжение питания упадет ниже рабочего значения. Это может про- изойти из-за кратковременной просадки напряжения на линии VDD при пере- ключении мощной нагрузки или из-за разряда батареи. В обоих случаях микро- контроллер PIC может начать работать неправильно из-за этого снижения напряжения (brownout)1}. Указанное явление может привести к серьезным пос- ледствиям; например, нагревательный элемент посудомоечной машины может включиться при отсутствии воды в резервуаре! Из Рис. 10.11 видно, что при включенном таймере BOR (т.е. при установлен- ном бите BODEN в слове конфигурации) внутренний сигнал сброса будет сфор- мирован при снижении напряжения питания EDD ниже порогового значения *Kdd* 2). В устройствах, работающих при номинальном напряжении питания 5 В, этот порог составляет 4 ±0.3 В. Для устройств, способных работать в более широ- ком диапазоне питающих напряжений, таких как PIC 12F629/675, это пороговое значение может быть снижено вплоть до 2 В. Этот термин появился из-за аналогичного явления в питающей сети, которое вызывало снижение яркости осветительных ламп, в результате чего окружающие предметы приобретали коричневатый (brownish) оттенок. 2) Если напряжение питания снизится еще больше, а затем восстановится, то произойдет сброс по включению питания.
Глава 10. Реальное окружение и 321 Из рисунка видно, что напряжение питания через некоторое время превыси- ло пороговое значение. Если время, в течение которого напряжение находилось ниже порогового значения, составило более 100 мкс, то, прежде чем процессор выйдет из состояния сброса, таймер включения питания (если его работа разре- шена) сформирует задержку длительностью 72 мс, как показано на Рис. 10.10. По окончании этой задержки процессор приступит к исполнению команды, распо- ложенной по адресу вектора сброса h’000’. В одних моделях, таких как PIC16F627, состояние бита PWRTE изменяется автоматически при включении схемы BOD, чтобы гарантировать наличие задержки при снижении (провале) на- пряжения питания, тогда как в других, например PIC12F675, это остается на со- вести программиста, так что читайте документацию внимательно! Работающий таймер включения питания уменьшает вероятность многократного формирова- ния сигнала сброса из-за помех на шине питания при медленном нарастании на- пряжения питания KDD. После сброса по снижению напряжения питания в РСН заносятся те же зна- чения, что и после сброса по включению питания, за исключением того, что сбрасывается флаг BOR в регистре PCON. Благодаря этому программа может оп- ределить причину, по которой произошел сброс. При включении микроконтрол- лера состояние флага BOR не определено, поэтому он должен быть установлен в самом начале программы. Одной из проблем, возникающих при включении схемы сброса по снижению напряжения питания, является повышение тока потребления микроконтроллера. К примеру, для моделей линейки PIC16F87X это приращение A/B0R составляет обычно 85 мкА (максимум до 200 мкА). Указанное значение следует прибавить к базовому значению тока, приведенному в Табл. 10.1. Более того, ток, потребляе- мый этой схемой, сравним с током потребления микроконтроллера в «спящем» режиме. С другой стороны, в моделях PIC12F629/675 семейства nanoWatt этот ток составляет всего 0.3/1.5 мкА (typ/max) при KDD = 2 В. Помимо того, микроконтроллеры PIC могут сбрасываться по тайм-ауту сто- рожевого таймера. При этом процессор немедленно начинает исполнение про- граммы с адреса вектора сброса, а также сбрасывает флаг ТО и устанавливает флаг PD регистра STATUS. Если тайм-аут сторожевого таймера наступит во время на- хождения процессора в «спящем» режиме, то это приведет к продолжению вы- полнения программы, начиная с команды, расположенной после команды sleep (при работе от кварцевого генератора — после задержки Tost)- При этом оба фла- га ТО и PD регистра STATUS будут сброшены. Различные варианты сброса микроконтроллера сведены в Табл. 10.4. В этой же таблице указана реакция на выход из «спящего» режима по прерыванию. При сбросе по включению питания устанавливаются оба флага — ТО и PD, тогда как при внешнем сбросе состояние этих битов не изменяется. Флаг ТО сбрасывается по тайм-ауту сторожевого таймера и устанавливается после выполнения команд clrwdt или sleep. Команда clrwdt также устанавливает флаг PD, который сбрасывается при выполнении команды sleep. Оба этих флага доступны только для чтения, т.е. их нельзя явно изменить, скажем, командой bsf.
322 Часть III. Окружающий мир Таблица 10.4. Действия при сбросе Причина сброса «Спящий» режим Исполнение начинается с адреса TO PD Регистр STATUS POR BOR Внешний Нет h’000’ и u ooou uuuu и и Внешний Да h’000’ 1 0 0001 ouuu и и POR — h’000’ 1 1 0001 1??? 0 ? BOR — h’000’ 1 1 0001 1000 1 0 Сторожевой таймер Нет h’000’ 0 1 0000 1UUU 1 1 Сторожевой таймер Да PC+1 0 0 uuuoouuu 1 1 Прерывание Да PC+1 1 0 UUU1 ouuu 1 1 Условные обозначения: ? Не определено U Не изменяется При сбросе микроконтроллера обнуляется счетчик команд и различные биты, отвечающие за переключение банков памяти данных, такие как RP0. Состояние битов статуса арифметических операций Z, С и DC после сброса по включению питания не определено, а при других видах сброса оно остается неизменным. Примеры Пример 10.1 Схема, приведенная на Рис. 10.12, похожа на схему с Рис. 10.5, б, но содержит два последовательно соединенных резистора. По задумке разработчика с по- мощью такой схемы программа сможет самостоятельно изменять скорость своего выполнения. Докажите его правоту, учитывая, что при заданной емкости конден- сатора тактовая частота прямо пропорциональна сопротивлению, подключенно- му к выводу OSC1. Можете ли вы придумать применение для такой возможности? Рис. 10.12. Тактовый генератор с изменяемой частотой
Глава 10. Реальное окружение и 323 Решение Точка соединения резисторов подключена к выводу порта микроконтроллера. Любой вывод микроконтроллера может быть сконфигурирован как вход или как выход (это будет описано в следующей главе). При сбросе любого вида все выво- ды портов конфигурируются как входы. В данном случае, если не учитывать ми- зерный ток утечки, вывод RA0 не будет оказывать влияние на подключенные к нему резисторы. Таким образом, общее сопротивление цепочки будет немного больше 100 кОм. При емкости конденсатора 100 пФ и напряжении питания KDD = 5 В тактовая частота микроконтроллера будет равна примерно 100 кГц. В результате частота выполнения команд составит около 25 000 команд/с (на са- мом деле немного меньше, поскольку команды, вызывающие сброс конвейера, выполняются за два цикла). Если программа переключит вывод RA0 на выход, установив при этом бит 0 регистра PORTA, то точка соединения резисторов окажется подключенной к ли- нии VDD. При этом к выводу OSC1 реально окажется подключенным только один резистор сопротивлением 3.3 кОм. При таком значении сопротивления RC-це- почки частота генератора будет составлять 1.7 МГц, что в 17 раз увеличит частоту исполнения команд (до 450 000 команд/с). Таким образом, программа сможет при необходимости самостоятельно увеличивать свою скорость выполнения и за- медляться в остальных случаях для экономии энергии источника питания. Пример 10.2 В документации на микроконтроллер PIC16F84 сказано, что минимальная длительность сигнала НИЗКОГО уровня на выводе MCLR, распознаваемого как сигнал внешнего сброса, составляет 100 нс. Можете ли вы предположить, какие проблемы могут возникнуть в связи с наличием такого условия? Решение В зашумленной среде микроконтроллер может работать неустойчиво из-за кратковременных импульсов, случайным образом сбрасывающих устройство. В таких случаях сигнал, подаваемый на вывод MCLR, следует пропускать через фильтр нижних частот (ФНЧ). Обычно хватает высокочастотного конденсатора емкостью 1 нФ, подключенного к выводу MCLR вместе с подтягивающим резис- тором сопротивлением 10 кОм. Кроме того, необходимо предусмотреть развязку по питанию, элементы которой должны располагаться в непосредственной бли- зости от выводов питания микроконтроллера. Из-за проблем такого рода в более современных моделях, даже в PIC16F84A, минимальная длительность импульса сброса увеличена до 2 мкс. Тем не менее все указанные методики справедливы и для них, особенно при работе устройства в среде с высоким уровнем помех.
324 Часть III. Окружающий мир Вопросы для самопроверки 10.1. Что произойдет, если из-за программной ошибки на выходе RA0 микрокон- троллера, включенного в соответствии с Рис. 10.12, будет присутствовать лог. 0? Такая ситуация может возникнуть, если вывод будет сконфигуриро- ван как выход до установки 0-го бита регистра PORTA в 1. 10.2. Каким образом схема, приведенная на Рис. 10.13, будет влиять на частоту тактового генератора? <ZZ 0SC1 Рис. 10.13. Альтернативная схема управления частотой тактового генератора 10.3. Пытаясь уменьшить потребление схемы при нахождении микроконтролле- ра в состоянии сброса, один студент поставил в цепь ручного сброса (Рис. 10.8) резистор сопротивлением 10 МОм. Почему микроконтроллер пе- рестал выходить из состояния сброса? 10.4. Потребление микроконтроллера PIC, работающего на частоте 4 МГц и при напряжении 5 В, составило 550 мкА при отсутствии нагрузки на портах вво- да/вывода. Каким будет потребляемый ток, если устройство будет работать на частоте 100 кГц и при напряжении питания 4 В?
ГЛАВА НИЧЕГО, КРОМЕ БАЙТОВ Способность программы изменять или отслеживать состояние выводов, под- ключенных к внешним цепям, является наиболее важной среди разнообразных возможностей по приему и передаче данных, присущих микропроцессору или микроконтроллеру. Эти выводы обычно объединяются в группы, при этом число выводов в группе может достигать числа разрядов внутренней шины данных. В микроконтроллерах PIC такие параллельные порты дают возможность ядру про- цессора считывать или передавать вовне до восьми битов данных побайтно. Сум- марное количество таких линий ввода/вывода, имеющихся в каждой конкретной модели семейства, зависит от типа корпуса и от того, сколько имеется используе- мых разделяемых ресурсов. Количество этих линий ввода/вывода может варьиро- ваться от четырех в 6/8-выводных PIC10FXXX до 52 в 64-выводном PIC16C924. Прочитав эту главу, вы: • Разберетесь в назначении параллельных портов ввода/вывода. • Научитесь конфигурировать линии портов ввода/вывода. • Познакомитесь со схемотехникой портов ввода/вывода и поймете различие между активной и пассивной подтяжкой. • Узнаете, каким образом с параллельными портами ввода/вывода взаимо- действуют команды типа «чтение/модификация/запись». • Познакомитесь с электрическими и нагрузочными характеристиками пор- тов ввода/вывода. • Узнаете, как включать встроенные подтягивающие резисторы на линиях портов. • Поймете, как работает функция генерации прерывания по изменению уровня сигналов на выводах порта В. • Узнаете, как с помощью дополнительных микросхем можно увеличить ко- личество линий ввода/вывода. Вообще говоря, параллельный порт ввода/вывода может рассматриваться как обычный регистр, содержимое которого доступно остальным элементам схемы. Именно такое несколько упрощенное представление показано на Рис. 11.1. На рисунке изображена небольшая область памяти данных микроконтроллера PIC16F84, структура которой полностью была показана на Рис. 4.7 (стр. 97). В микроконтроллерах PIC16XXXX среднего уровня в обязательном порядке име-
326 Часть Ill. Окружающий мир ется, как минимум, 13 линий ввода/вывода. В микроконтроллерах группы PIC16F87X есть дополнительный вывод RA5 (портА), в то время как в микрокон- троллерах группы PIC16F62X имеется уже три дополнительных линии, показан- ные на Рис. 11.1 пунктиром (если пожертвовать выводами OSC1, OSC2 и MCLR1*). В «миниатюрных» микроконтроллерах PIC10FXXX/12XXX присутству- ет только один параллельный порт ввода/вывода общего назначения (General- Purpose parallel I/O — GPIO), в котором сочетаются характеристики портов А и В прочих микроконтроллеров и который имеет не более 6 линий ввода/вывода. <.RA7 <RA6 <RA5 БанкО Банк 1 FSR h’04’ FSR h’84’ PORTA PORTB h’05’ h’06’ Не реализовано h’07’ Порты ввода/ вывода TRISA TRISB h’85’ h’86’ Не реализовано h’87’ Теневой регистр направления передачи данных Рис. 11.1. Упрощенное представление параллельных портов А и В микроконтроллеров линейки PIC16XXXX В моделях среднего уровня, имеющих 28 выводов и более, реализованы до- полнительные порты ввода/вывода, как указано в Табл. 11.1. В то же время эти модели имеют более богатый набор встроенных периферийных устройств, ис- пользующих линии ввода/вывода, так что увеличение емкости параллельных портов ввода/вывода может оказаться не более чем иллюзией. К примеру, в моде- ли PIC16F87X пять линий порта A (RA5, RA[3:0]) и 3-битный порт Е используют- ся в качестве аналоговых входов 8-канального АЦП. И все же, несмотря на возможность такого упрощенного представления (как на Рис. 11.1), поведение портов ввода/вывода несколько отличается от поведения остальных регистров микроконтроллера. Так, необходимо иметь возможность конфигурирования портов либо на считывание сигналов с соответствующих вы- водов микроконтроллера (вход), либо на выдачу сигналов на эти выводы (выход). Помимо этого, нам нужно определить, каким образом та или иная конфигурация порта будет влиять на результат операций изменения или чтения состояния порта. Вывод порта, разделенный с выводом MCLR, может работать только как вход.
Глава 11. Ничего, кроме байтов 327 Из Рис. 11.1 видно, что каждому регистру параллельного порта в 0-м банке со- ответствует регистр TRIS в 1-м банке. В Приложении Б можно увидеть, что это справедливо для любого порта. С каждым битом п параллельного порта связан бит п соответствующего регистра TRIS, который предназначен для задания кон- фигурации вывода: вход (TRIS[«] = 1) или выход (TRIS[«] = 0)1}. В большинстве микроконтроллеров других производителей такие регистры называются регист- рами направления передачи данных (Data Direction Register — DDR), однако компания Microchip использует аббревиатуру TRIS, образованную от словосоче- тания TRI-State (тристабильный). Причину, по которой регистры называются именно так, вы узнаете чуть позже в данной главе. Таблица 11.1. Параллельные порты ввода/вывода, встречающиеся в микроконтроллерах PIC среднего уровня Порт Количество линий Краткое описание А 3...8 I/O RA4 — выход с открытым стоком, объединенный с тактовым входом Таймера 0. Используется аналоговыми модулями GP 3...6 I/O 6/8-выводной порт ввода/вывода общего назначения в моделях PIC10FXXX/12XXXX В 8I/O RB0 также используется как вход внешнего прерывания. «Слабая» подтяжка. RB7:4 могут генерировать прерывание по изменению состояния вывода С 8I/O Только в моделях с 28 и более выводами. Используется модулями последовательного ввода/вывода D 8 I/O Только в моделях с 40 и более выводами. Используется также как ведомый параллельный порт или для управления ЖКИ Е ' 3...8 I/O В моделях с 40 и более выводами — используется совместно с модулем АЦП. В моделях PIC16C92X с 64 и более выводами — 8-битный вход, используемый также для управления ЖКИ F 81 В моделях PIC16C92X с 68 и более выводами — используется совместно с драйвером ЖКИ G 81 В моделях PIC16C92X с 68 и более выводами — используется совместно с драйвером ЖКИ Условные обозначения: I — вход, О — выход, I/O — вход/выход. В качестве примера рассмотрим ситуацию, при которой вывод RA0 и выводы RB[7:0] являются выходами, а остальные выводы порта А — входами. Следующий фрагмент кода, как правило, размещается в самом начале основной процедуры (см. Программу 11.1, а): bsf STATUS,RPO ; Переключаемся на 1-й банк movlw b11111110’ ; Вывод RAO - выход movwf TRISA ; Остальные выводы - входы clrf TRISB ; Все выводы порта В - выходы bcf STATUS,RPO ; Возвращаемся в 0-й банк ]) Для знакомых с английским языком можно предложить простое мнемотехническое пра- вило: 0 — Output (выход), 1 — Input (вход).
328 Часть III. Окружающий мир Разумеется, на языке Си тоже можно написать код, выполняющий аналогич- ные действия. К примеру, в используемом нами компиляторе CCS этот код будет выглядеть следующим образом: #bit BANK_SWITCH =3.5 #byte TRISA = 0x85 #byte TRISB = 0x86 main () { BANK_SWITCH = 1; TRISA = OxFE; TRISB = 0; BANK_SWITCH =0; /* Бит RPO регистра STATUS */ /* Регистр направления передачи данных TRISA */ /* Регистр направления передачи данных TRISB */ /* Переключаемся на 1-й банк */ /* Вывод RA0 - выход, остальные выводы - входы */ /* Все выводы порта В - выходы */ /* Возвращаемся в 0-й банк */ Однако в отдельных компиляторах могут иметься встроенные функции для поддержки операций инициализации и обращения к портам. Так, в компиляторе CCS для каждого порта X имеется своя функция set_tris_x () для установки соответствующего регистра TRISX: main() { /* В начале идут описания разных переменных */ set_tris_a(OxFE); /* Вывод RA0 - выход, остальные выводы - входы */ set_tris_b(0); /* Все выводы порта В - выходы */ При любом сбросе все биты регистров TRIS устанавливаются в 1, т.е. после сброса все выводы микроконтроллера работают как входы. Такой выбор не случа- ен, так как если бы вывод переключался на выход до задания ему программой на- чального значения, то напряжение, появляющееся на этом выводе после выхода из состояния сброса, было бы непредсказуемым. А это, в свою очередь, может привести к нежелательной активизации управляемых цепей. Например, при уп- равлении нагревательным элементом стиральной машины нагреватель мог бы включиться до заполнения емкости водой. Если существует вероятность возник- новения подобной ситуации, то начальное состояние соответствующих битов порта необходимо задавать до конфигурирования регистра TRIS. После того как задано направление передачи данных через выводы порта, программа может считывать данные из порта или записывать их в него, как в обычный регистр, и таким образом взаимодействовать с окружающим миром. А именно: • Для отслеживания состояния любого вывода, сконфигурированного как вход, можно использовать команды btfsc и btfss. Так, команда btfss PORTA, 1 пропустит следующую команду, если на выводе RA1 при- сутствует ВЫСОКИЙ уровень (т.е. если 1-й бит регистра PORTA установлен в 1). Можно одновременно считать состояние нескольких битов, копируя содержимое всего регистра порта в рабочий регистр, например командой movf PORTA, w. При необходимости это значение можно будет затем пере- писать в какой-нибудь РОН для дальнейшей обработки.
Глава 11. Ничего, кроме байтов и 329 • Для изменения состояния любого вывода, сконфигурированного как выход, можно использовать команды be f или bsf. Так, команда be f PORTA, 0 ус- тановит на выводе RA0 НИЗКИЙ уровень (т.е. 0-й бит регистра PORTA сбросится в 0). Можно одновременно изменять несколько битов, копируя содержимое рабочего регистра в регистр данных порта. К примеру, если все выводы порта В являются выходами, то для выдачи на выводы RB[7:6] ВЫ- СОКОГО уровня, а на выводы RB[5:0] — НИЗКОГО, можно воспользовать- ся следующими командами: movlw Ь'11000000' movwf PORTB Наверняка у вас возник вопрос: а что произойдет, если будет считано состоя- ние вывода, сконфигурированного как выход? Ответ на этот и некоторые другие вопросы вы узнаете чуть позже, когда мы приступим к изучению электрических характеристик портов ввода/вывода. А пока давайте рассмотрим использование портов на реальном примере. Представим ситуацию, изображенную на Рис. 11.2, в которой внешнее пери- ферийное устройство (скажем, принтер) собирается через порт В считывать по запросу содержимое регистра h’20’, подавая на вывод RA1 микроконтроллера на- пряжение НИЗКОГО уровня. Обозначим этот сигнал от периферийного устрой- ства как RFD (Ready For Data — готовность к приему данных). При обнаружении этого запроса микроконтроллер копирует требуемый байт данных в порт В, а за- тем формирует отрицательный импульс на выводе RA0, информируя периферий- ное устройство о готовности запрошенных данных. Назовем этот сигнал DAV (Data Available — готовность данных). При сбросе по питанию на всех выводах порта В должен устанавливаться НИЗКИЙ уровень, а на выводе RA0 — ВЫСО- КИЙ. Рис. 11.2. Передача данных через порт В с использованием квитирования
330 Часть III. Окружающий мир Такой принцип обмена с использованием семафоров называется обменом с квитированием (handshaking). Квитирование позволяет осуществлять обмен меж- ду несинхронизированными устройствами без потери данных. В Программе 11.1, а приведен пример реализации обмена с квитированием на языке ассемблера. Обратите внимание, что начальное состояние портов в про- грамме задается перед их конфигурированием. Символические имена PORTA и PORTB определены во включаемом файле (для регистров h’05’ и h’06’ соответ- ственно). После обратного переключения в 0-й банк памяти выводы портов, сконфигурированных как выходы, устанавливаются в начальное состояние: вы- вод RA0 — ВЫСОКИЙ уровень (бит 0 регистра PORTA = 1), а выводы RB[7:0] — НИЗКИЙ уровень (соответствующие биты регистра PORTB = 0). Программа 11.1. Реализация параллельного обмена с квитированием а) Ассемблер (17 команд) include "pl6f627a.inc" __config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF DATUM equ h'20’ ; Инициализируем порты и задаем начальные состояния выводов MAIN clrf PORTB ; Начальное состояние порта В - 0 bsf PORTA,0 ; Начальное состояние сигнала DAV - 1 bsf STATUS,RP0 ; Сначала переключаемся на 1-й банк movlw b'llllllO' ; Вывод RA0 - выход movwf TRISA ; Остальные выводы - входы clrf TRISB ; Все выводы порта В - выходы bcf STATUS,RP0 ; Возвращаемся в 0-й банк ; Ожидаем появления НИЗКОГО уровня на выводе RA1 RFD_YES btfsc PORTA,1 ; 1-й бит порта А равен 0? goto RFD_YES ; ЕСЛИ нет, ТО считываем снова ; Копируем запрошенные данные в порт В movf DATUM,w ; Копируем в W movwf PORTB ; и вовне ; Теперь формируем отрицательный импульс DAV на выводе RA0 bcf PORTA,0 ; DAV (вывод RAO) - НИЗКИЙ уровень пор ; на короткое время, bsf PORTA,0 ; а затем ВЫСОКИЙ уровень ; Теперь ждем i появления ВЫСОКОГО уровня на линии RFD RFD_NO btfss PORTA,1 Пропускаем, если на RA1 ВЫСОКИЙ уровень goto RFD_NO ; ЕСЛИ нет, ТО считываем снова goto RFD_YES ; И так до бесконечности end б) Язык Си (26 команд) Hnclude <16f627a.h> #byte PORTB = 6 #byte DATUM = 0x20 /* Порт В - регистр 0x06 /* В регистре 0x20 хранится байт данных */ */
Глава 11. Ничего, кроме байтов 331 #bit DAV = 5.0 #bit RFD =5.1 void main(void) { DAV = 1; PORTB = 0; set_tris_a(OxFE); set_tris_b(0); while(TRUE) { while(RFD) {;} /* Вывод RAO - линия DAV /* Вывод RAI - линия RFD /* Неактивный уровень на линии DAV - 1 /* Начальное состояние порта В - 0 /* Вывод RAO (DAV) - выход /* Все выводы порта В - выходы /* БЕСКОНЕЧНЫЙ ЦИКЛ */ /* Ждем, пока результат чтения RFD не станет FALSE*/ /* (НИЗКИЙ уровень) */ PORTB = DATUM; DAV = 0; delay_cycles(1) ; DAV = 1; /* Копируем байт данных в порт В /* Выставляем на DAV (вывод RA0) НИЗКИЙ уровень */ /* Немного подождем, /* а затем снова выставляем ВЫСОКИЙ уровень */ while(IRFD) {;} /* Ждем, пока результат чтения RFD не станет TRUE */ /* (ВЫСОКИЙ уровень) */ } } После инициализации портов микроконтроллер ожидает появления НИЗКО- ГО уровня на выводе RA1 — этому состоянию соответствует 0 в 1-м бите регистра PORTA. Когда это происходит, содержимое регистра h’20’ копируется через рабо- чий регистр в регистр PORTB и на вывод RA0 выставляется НИЗКИЙ уровень. Перед повторным переводом RA0 в состояние ВЫСОКОГО уровня вставляется одна команда пор, формирующая задержку длительностью в один машинный цикл. Здесь мы не оговаривали длительность импульса DAV, но в реальном уст- ройстве команда пор будет заменена вызовом подпрограммы формирования за- держки. В конце процедуры микроконтроллер снова проверяет состояние вывода RA1 (отслеживая значение 1-го бита регистра PORTA), ожидая появления на линии RFD сигнала ВЫСОКОГО уровня, свидетельствующего о завершении транзак- ции (этот сигнал выставляется периферийным устройством). Разумеется, случа- ется и так, что периферийное устройство не сможет ответить, в результате чего микроконтроллер просто зависнет. Поэтому для надежности в таких случаях сле- дует предусматривать некоторый тайм-аут — скажем, переходить к процедуре об- работки ошибок, если после 65 536 обращений к порту не было обнаружено тре- буемого отклика. В Программе 11.1, б приведена эквивалентная реализация такого обмена на языке Си. Эта программа имеет похожую структуру; обратите только внимание на то, как осуществляется проверка состояния входа. Для этого используется конст-
332 Часть III. Окружающий мир рукция вида while (RFD) {;}, которая ничего не делает ({;} является пустым оператором) до тех пор, пока при считывании вывода, названного RFD, не воз- вращается ИСТИНА, т.е. ненулевое значение. Когда RFD становится равным О, т.е. на выводе RA1 появляется НИЗКИЙ уровень, цикл завершается и управление передается на следующий оператор программы. В конце программы имеется ана- логичная конструкция while(’RFD) {;}, в которой используется оператор языка Си «!» (NOT). В данном случае выход из цикла произойдет при установке RFD в 1 (операция !RFD вернет нулевое значение). Встроенная функция delay_cycles () формирует дополнительную задерж- ку заданной длительности (в машинных циклах) и при необходимости может быть заменена любой подходящей функцией задержки. Может показаться, что эти программы бесполезны, поскольку содержимое регистра h’20’ нигде не задается и не изменяется. Однако в реальной жизни дан- ное значение могло бы изменяться в каком-либо прерывании, скажем, по сигна- лу от внешнего или внутреннего таймера. Также в этот регистр по прерыванию от модуля АЦП мог бы заноситься результат преобразования. Все эти модули мы бу- дем рассматривать в последующих главах книги. Наша программа рассчитана на микроконтроллер PIC16F84A. Разумеется, она может выполняться и на других моделях семейства, следует только быть бо- лее внимательными при использовании моделей, имеющих аналоговые модули. В этих моделях выводы портов, используемые также и аналоговыми модулями, после сброса работают как аналоговые входы. Так сделано потому, что аналого- вое напряжение, формируемое внешней схемой, может повредить входные тран- зисторы, рассчитанные на работу с логическими сигналами. Поэтому если про- граммист собирается использовать указанные выводы для ввода/вывода цифро- вых сигналов, то ему необходимо задать такую конфигурацию аналогового модуля, при которой требуемые выводы подключены не к модулю, а к цифрово- му порту. Обычно эта операция выполняется во время процедуры инициализа- ции вместе с установкой значений регистров TRIS. К примеру, в моделях PIC16F87X входы модуля АЦП выведены на линии порта А. Чтобы выводы RA[3:0] и RA5 работали как цифровые, необходимо загрузить число Ь’ОООООИО’ в 1-й регистр управления АЦП ADCON1 (см. Рис. 14.12 на стр. 512). Поскольку этот регистр находится в 1-м банке, то к командам movlw b'00000110' ; Все выводы - цифровые movwf ADCON1 следует добавить команды, осуществляющие переключение банков памяти. В компиля- торе CCS для этих целей предусмотрена специальная функция setup_adc_ports (), вызов которой с параметром setup_adc_ports (NO_ANLOGS) выполняет те же самые операции. Чтобы хорошо понимать характеристики портов ввода/вывода, необходимо знать, как они устроены. Несколько упрощенная схема одного канала порта вво- да/вывода показана на Рис. 11.3. Основными элементами этой схемы являются D-триггер данных и тристабильный (с тремя состояниями) буфер данных.
Глава 11. Ничего, кроме байтов 333 Шина данных памяти данных Буфер TRIS Запись в PORTx ID >С1 Контакт ввода/вывода Запись в TRISx Чтение из TRISx Триггер данных Защита от перенапря- - жения 1D >С1 Входной буфер с триггером Шмитта Триггер TRIS Защелка Буфер данных синхронизатора Чтение из PORTx Рис. 11.3. Упрощенная схема одной линии порта ввода/вывода • Запись в порт вызывает переключение D-триггера данных, в результате чего в него будет записано значение, находящееся на линии шины данных. Эти данные будут храниться до тех пор, пока на микроконтроллер подается на- пряжение питания (см. Рис. 2.16, в и г на стр. 46). К примеру, в результате выполнения команд movlw b'11111111' movwf h'06' ; Все биты рабочего регистра установлены в 1 ; Копируем его в порт В (регистр h'06') во все восемь D-триггеров, составляющих регистр порта В (PORTB), будет записана лог. 1. Установка битов порта будет происходить независимо от того, как сконфи- гурированы его выводы (на вход или на выход). Однако для выдачи состоя- ния триггера на вывод микроконтроллера должен быть включен буфер TRIS (тристабильный). В этом случае, как показано на Рис. 11.4, б, триггер дан- ных оказывается напрямую подключенным к остальным узлам схемы. • При чтении этого регистра данных порта включается буфер данных, в результате чего состояние защелки1* выдается на линию шины данных * * В микроконтроллерах младшего 12-битного семейства эти защелки отсутствуют.
334 Часть III. Окружающий мир микроконтроллера. Если чтения порта не происходит, то эта D-защелка прозрачна и состояние ее выхода соответствует состоянию вывода микро- контроллера (см. Рис. 2.16, а и б на стр. 46). При чтении порта на входе раз- решения D-защелки устанавливается ВЫСОКИЙ уровень, в результате че- го значение на выходе тристабильного буфера данных «замораживается», оставаясь неизменным в процессе операции чтения. Для увеличения поме- хоустойчивости вход защелки данных отделен от вывода микроконтроллера буфером с гистерезисом (триггером Шмитта). К примеру, чтобы считать со- стояние порта В, достаточно выполнить следующую команду: movf h'06',w ; Считываем состояние всех восьми линий порта В в W Эта операция чтения, показанная на Рис. 11.4, а, будет выполнена незави- симо от того, являются ли соответствующие линии ввода/вывода входами или выходами. а) Чтение из порта, сконфигурированного как вход б) Запись в порт, сконфигурированный как выход в) Чтение из порта, сконфигурированного как выход г) Запись в порт, сконфигурированный как вход Рис. 11.4. Различные варианты чтения/записи одного бита порта при различных конфигурациях соответствующего вывода
Глава 11. Ничего, кроме байтов 335 Микроконтроллеры младшего уровня первого поколения PIC12C5XXX с 12-битным ядром не имеют отдельных регистров TRIS, т.е. триггеры TRIS не отображены на адресное пространство памяти данных. Вместо этого в указан- ных микроконтроллерах используется команда tris, копирующая содержи- мое рабочего регистра во внутренний управляющий регистр, не отображен- ный на память данных. Так, в нашем случае: movlw b'00001111' ; Старшие 4 линии - выходы, остальные - входы tris h'06' ; Конфигурируем порт В Даже когда появились первые представители 14-битных микроконтроллеров среднего уровня с регистрами TRIS, команда tris была сохранена в системе ко- манд. Причем компания Microchip не гарантирует, что эта команда будет реали- зована в будущих устройствах. Тем не менее, эту команду до сих пор используют многие программисты, а также некоторые компиляторы языка Си, такие как CCS. Из Рис. 11.3 видно, что бит TRIS доступен не только для записи, но и для чте- ния. На первый взгляд эта возможность может показаться бесполезной. Однако предположим, что программист хочет переключить вывод RB7 в режим выхода (см. Пример 11.4): bcf h'86',7 ; Сбросить бит 7 регистра TRISB Команда be f относится к командам типа «чтение — модификация — запись» (см. стр. 138), т.е. содержимое регистра TRISB считывается процессором, модифици- руется и записывается обратно в регистр. Для выполнения указанной операции про- цессор должен иметь возможность как читать из регистра, так и записывать в него. Поскольку параллельный порт не только может быть сконфигурирован как входной или выходной, но также может содержать линии различных направле- ний, необходимо знать ограничения, имеющие место при чтении или изменении состояния таких не совсем обычных регистров. Например, что произойдет, если программа прочитает бит порта, который был сконфигурирован как выход? Всего возможно четыре ситуации, которые представлены на Рис. 114, а) Чтение вывода, сконфигурированного как вход (TRIS = 1) В данном случае буфер TRIS отключен, и состояние триггера данных остается не- изменным. Например, команда movf h'06',w считает состояние выводов порта В в рабочий регистр. б) Запись в вывод порта, сконфигурированного как выход (TRIS = 0) В данном случае буфер TRIS включен, а состояние триггера данных изменяется процессором, выполняющим запись в порт. Значение, сохраненное в этом триг- гере, появляется на выводе микроконтроллера. К примеру, если все выводы порта В сконфигурированы как выходы, то в результате выполнения команд movlw b'10101010' movwf h'06' выводы порта В будут установлены в состояние HLHLHLHL (Н — ВЫСОКИЙ уровень, L — НИЗКИЙ уровень).
336 Часть III. Окружающий мир в) Чтение вывода, сконфигурированного как выход (TRIS = 0) В данном случае буфер TRIS включен, поэтому вывод микроконтроллера будет подключен к выходу соответствующего триггера данных. В большинстве случаев в результате чтения вывода порта, сконфигурированного как выход, будет счита- но состояние триггера данных и соответствующего вывода. Однако так происхо- дит не всегда. Если ток, отбираемый подключенным к выводу устройством, до- статочно велик, то напряжение на выводе может довольно сильно отличаться от значений, соответствующих нормальным логическим уровням. Так, подключе- ние биполярного транзистора непосредственно к выводу порта (как на Рис. 11.5, а) приведет к потреблению значительного тока от буфера TRIS, что вы- зовет снижение напряжения на выводе до уровня 0.7 В (падение напряжения на переходе база-эмиттер типового транзистора) а) Вытекающий ток б) Втекающий ток Рис. 11.5. Втекающий и вытекающий ток Ситуация, изображенная на Рис. 11.5,6, аналогична предыдущей, только в этом случае ток втекает в порт микроконтроллера* 2* через светодиод (СИД), в ре- зультате чего напряжение на входе буфера TRIS возрастет до 3 В (учитывая, что падение напряжения на открытом СИД составляет около 2 В). В таких ситуациях результат чтения состояния вывода порта, являющегося выходом, очень часто не соответствует состоянию триггера данных из-за некорректных значений напря- жения на выводе. Например, при исполнении команды btfsc PORTB, 7 может быть ошибочно пропущена следующая за ней команда, если с вывода RB7 отби- рается или в него втекает слишком большой ток. г) Запись в вывод порта, сконфигурированного как вход (TRIS = 1) В этом случае будет изменено соответствующим образом состояние триггера дан- ных. Однако, поскольку буфер TRIS отключен, это изменение никоим образом не отразится на состоянии соответствующего вывода микроконтроллера до тех пор, пока он не будет переведен в режим выхода. Эту возможность установки со- стояния портов «незаметно» для внешних цепей мы использовали в Программе 11.2 при инициализации параллельных портов после сброса. Напоми- наю вам, что после сброса все порты работают как входы, другими словами, во всех регистрах TRIS находится значение b’l 1111 111’. Обычно находится в пределах от 25 до 35 мА (см. Пример 11.1). 2) Обычно около 60 мА (см. Рис. 11.17).
Глава 11. Ничего, кроме байтов и 337 Большинство выводов, сконфигурированных как входы, имеют буферы с триггером Шмитта. Особенностью этих триггеров является то, что при измене- нии уровня входного сигнала с НИЗКОГО на ВЫСОКИЙ они переключаются при напряжении на входе, составляющем более 80% от напряжения питания, а при изменении сигнала в обратном направлении — менее 20% от напряжения питания. Такой гистерезис значительно увеличивает надежность при считыва- нии логических состояний в средах с повышенным уровнем помех. Порт GPIO в 8-битных устройствах (исключая линию GP3), порт В, а в некоторых старых мо- делях, таких как PIC16F84, еще и порт А (кроме-RA4) имеют обычные буферы без гистерезиса, с порогами переключения KIL < 0.5 В и И1Н > 2 В. Любой вывод, сконфигурированный как выход, должен быть способен давать ток, требуемый для управления нагрузкой. В большинстве случаев выходы мик- роконтроллера нагружаются током величиной всего несколько миллиампер. И тем не менее очень важно иметь представление об ограничениях нагрузочной способности выходов портов. В документации на устройства обычно указываются два параметра: 1. Ток, потребляемый выводом (/0L) при наличии на выходе напряжения НИЗКОГО уровня, не должен превышать 8.5 мА, если напряжение НИЗ- КОГО уровня K0L не превышает 0.6 В. 2. Ток, отдаваемый выводом (/он) при наличии на выходе напряжения ВЫ- СОКОГО уровня, не должен превышать —3 мА, если напряжение ВЫСО- КОГО уровня падает не более чем на 0.7 В ниже KDD. Отрицательное значе- ние тока соответствует источнику тока, т.е. ток вытекает из устройства. Если допускается изменение логических уровней относительно их номиналь- ных значений, то устройство может потреблять или отдавать большие токи (как показано на Рис. 11.5). При этом следует учитывать, что имеется еще одно огра- ничение — ток через любой вывод порта не должен превышать ±25 мА во избежа- ние повреждения микроконтроллера. Если для управления используется более одного вывода, то необходимо учитывать ограничения, накладываемые на сум- марный ток порта. В 8-выводных устройствах суммарный ток их единственного порта ввода/вывода (который является комбинацией портов А и В более старших собратьев) должен находиться в пределах ±125 мА. В моделях с большим числом выводов суммарный ток портов А, В и, при его наличии, С ограничивается на уровне ±200 мА. Аналогично, суммарный ток портов D и Е тоже должен нахо- диться в пределах ±200 мА. Каждый вывод, отдающий или потребляющий ток, будет рассеивать мощ- ность, что проявляется в виде нагрева корпуса. Из упрощенной модели, изобра- женной на Рис. 11.6, можно заметить, что рассеивание мощности происходит на трех компонентах (на рисунке они изображены в виде резисторов): 1. С линии питания VDD мы потребляем ток /DD. Однако ток через сопротив- ление 7?!, представляющее собой сопротивление всего микроконтроллера в целом, будет меньше на величину токов, вытекающих через выводы пор- тов. Таким образом, рассеиваемая мощность (определяемая, как известно, выражением Их /) равна KDDх (/DD - £/он).
338 Часть III. Окружающий мир Рис. 11.6. Модель для расчета рассеиваемой мощности 2. Падение напряжения на эквивалентном сопротивлении А2 между выход- ными контактами и выводом питания составляет ДИ = KDD-Ион . Соот- ветственно рассеиваемая мощность равна А Их /он. 3. Ток, протекающий от выходов к общему проводу (через вывод Vss), рассеи- вает на резисторе 7?3 мощность V0L х /0L . Сложив эти компоненты, получим выражение, которое приводится в доку- ментации: = ^DD Х (^DD “ 2 Л)Н ) + S((^DD“ ^ОН ) Х 4)Н ) + S(^OL Х Л)ь) • В данном выражении учитывается тот факт, что выходные напряжения на каждом из выводов будут отличаться, поскольку через эти выводы протекают токи разной величины. Для моделей в маленьких корпусах PDjS составляет 800 мВт, а для мо- делей в 40-выводных корпусах — 1 Вт. В любом случае максимальный ток, отби- раемый выводом VDD, не должен превышать 250 мА, а через вывод Vss не должен вытекать ток более 300 мА. В действительности эквивалентное сопротивление Т?2 имеет нелинейный характер и изменяется достаточно сложным образом (см. Рис. 11.13). То есть изменение напряжения Ион не прямо пропорционально току. В документации приводятся графики этой зависимости напряжения от тока (см., например, Рис. 11.13 и Рис. 11.17). Однако в наихудшем варианте при больших значениях токов можно считать, что напряжение Ион падает до нуля, a K0L воз- растает до напряжения питания KDD. В этом случае разница между током /dd и суммарным током ^/он, потребляемым ЦПУ и другими периферийными моду- лями, будет минимальной и ей можно будет пренебречь. Тогда суммарная рассеи- ваемая мощность будет определяться выражением 2^Уон-
Глава 11. Ничего, кроме байтов 339 Структурная схема, приведенная на Рис. 11.3, представляет собой типовую схему одной линии параллельного порта ввода/вывода. Отдельные порты (в частности, порты А и Е) могут иметь другую структуру, что кардинальным образом влияет на их электрические характеристики. В большинстве портов бу- феры TRIS построены по схеме, приведенной на Рис. 11.7, а, т.е. имеют двухтакт- ный выход на двух последовательно соединенных полевых транзисторах с кана- лами п- и p-типа. Этот буфер работает следующим образом: • Когда в триггере TRIS записана 1, на выходе нижнего элемента И присут- ствует лог. О, а на выходе верхнего элемента ИЛИ — лог. 1. В этом случае оба транзистора закрыты, и выход триггера отключен от вывода микроконтрол- лера. При этом вывод порта работает как вход. • Когда в триггере TRIS записан 0, инвертированное значение с выхода триг- гера данных подается на затворы обоих транзисторов. Если на выходе триг- гера данных присутствует НИЗКИЙ уровень, то «-канальный транзистор открывается, а р-канальный закрывается, в результате чего на выводе мик- роконтроллера появляется НИЗКИЙ уровень. Если же на выходе триггера данных присутствует ВЫСОКИЙ уровень, то р-канальный транзистор от- крывается, а «-канальный закрывается. В результате на выводе микроконт- роллера появляется ВЫСОКИЙ уровень. В данном случае напряжение на выводе микроконтроллера соответствует состоянию триггера данных, при этом ток вытекает или втекает через относительно малое сопротивление со- ответствующего открытого транзистора. Для примера представим, что нам необходимо управлять электромагнитным реле, для включения которого требуется ток 200 мА и напряжение 12 В. При рабо- те с такими большими напряжениями и токами мы должны использовать внеш- ние буферы. На Рис. 11.7, в в качестве такого внешнего ключа используется бипо- лярный транзистор. Если минимальный коэффициент усиления транзистора ра- вен 100, то при сопротивлении резистора 1.8 кОм базовый ток будет равен 2 мА (считаем, что на открытом переходе база-эмиттер падает 0.7 В, а напряжение ВЫ- СОКОГО уровня на выходе микроконтроллера составляет не менее 4.3 В). Выходной каскад линии RA4/GP3, схема которого приведена на Рис. 11.7, б, отличается тем, что в нем присутствует только транзистор нижнего плеча. В отли- чие от схемы с тремя состояниями (Рис. 11.7, а) данная схема имеет только два состояния — лог. 0 и разомкнутая цепь. Выходы такого типа называются выходами с открытым стоком или с открытым коллектором (см. Рис. 2.3 на стр. 33). Этот выход работает следкющим образом: • Когда в триггере TRIS записана 1 (состояние по умолчанию), то на выходе элемента И присутствует НИЗКИЙ уровень, транзистор закрыт, а выход микроконтроллера находится в состоянии с высоким входным сопротивле- нием. При этом вывод RA4/GP3 работает как вход. • Когда в триггере TRIS записан 0, то, при наличии лог. 0 в триггере данных, выходной транзистор находится в открытом состоянии, формируя на выходе микроконтроллера НИЗКИЙ уровень. Когда же в триггере данных находит- ся лог. 1, транзистор закрыт и выход микроконтроллера «висит» в воздухе.
340 Часть III. Окружающий мир К защелке синхронизатора \/Может Y иметь вход _____________| с триггером Шмитта Триггер данных Триггер TRIS - К защелке синхронизатора К Таймеру 0 (вход внешнего тактового сигнала) а) Буфер TRIS с двухтактным выходом б) Буфер TRIS с выходом с открытым стоком +12 В г) Управление электромагнитным реле Рис. 11.7. Структурные схемы выходных каскадов Выход с открытым стоком не может быть источником тока — необходимо либо подключить саму нагрузку между выходом и шиной питания, либо использовать в качестве нагрузки внешний подтягивающий резистор. В частности, второй вариант изображен на Рис. 11.7, г — при выключенном выходе RA4/GP3 ток базы внешнего транзистора формируется подтягивающим резистором сопротивлением 1.8 кОм. Если вывод RA4 используется в качестве входа Таймера 0, то он обычно кон- фигурируется как вход. Если он будет сконфигурирован как выход, то в соответ- ствующий бит порта необходимо записать лог. 1, которая будет удерживать вы- ходной транзистор в закрытом состоянии и предотвратит его воздействие на вход внешнего тактового сигнала таймера. Во многих приложениях приходится считывать состояние групп переключа- телей (кнопок). Вместо того чтобы использовать для формирования двух логичес- ких уровней однополюсные переключатели на два направления (Single-Pole Double-Throw — SPDT), такие как приведены на Рис. 11.8, а, в большинстве слу- чаев (см., к примеру, Рис. 11.10) используют более дешевые однополюсные (Single-Pole Single-Throw — SPST). В этом случае для формирования напряжения ВЫСОКОГО уровня при разомкнутых контактах переключателя требуется внеш- ний подтягивающий резистор, как показано на Рис. 11.8, б. Аналогичная ситуа-
Глава 11. Ничего, кроме байтов 341 ция возникает при считывании состояния устройства, имеющего выход с откры- тым стоком/коллектором, например фототранзистора. Сопротивление подтяги- вающего резистора не должно быть слишком маленьким, так как в этом случае через закрытый ключ будет течь большой ток. Вместе с тем сопротивление не должно быть и слишком большим, иначе устройство станет чувствительным к электромагнитным помехам, наводимым от внешних источников. Хорошим ком- промиссом будет сопротивление из диапазона 10... 100 кОм. ЗЗк а) Однополюсный переключатель б) Однополюсный переключатель на два направления на одно направление Рис. 11.8. Подключение переключателей к выводу порта Чтобы упростить подключение таких устройств, входы порта В уже имеют внутренние подтягивающие резисторы. Эти резисторы называются слабой под- тяжкой (weak pull-up), поскольку их эквивалентное сопротивление (около 20 кОм) достаточно велико, чтобы они не оказывали влияния на операции чтения устройств, имеющих «нормальные» логические выходы. Из Рис. 11.9 видно, что внутренние подтягивающие резисторы (являющиеся в действительности полевым транзистором с p-каналом) включаются только в том случае, если бит RBPU регистра OPTION_REG сброшен в 0. Несмотря на то что этот бит управляет всеми восемью подтягивающими резисторами, на тех линиях, которые сконфигурированы как выходы (TRIS[л] = 0), этот резистор будет от- ключен. После сброса бит RBPU устанавливается в 1, так что по умолчанию внут- ренние подтягивающие резисторы отключены. Порт ввода/вывода устройств в 8-выводных корпусах (он в них один-един- ственный) имеет похожую схему. При этом в моделях PIC16F629/75 внутренние подтягивающие резисторы (вывод GP3 не имеет такового) можно включать или отключать в индивидуальном порядке с помощью регистра специального назна- чения WPU (Weak Pull-Up), который, в свою очередь, управляется 7-м битом ре- гистра OPTION_REG, названным GPPU. В качестве типичного примера использования внутренней подтяжки рассмот- рим задачу определения состояния клавиатуры, например, такой как показана на Рис. 11.10, а. В данном случае клавиатура состоит из 12 кнопок. В принципе ничто не мешает нам использовать столько же линий ввода/вывода. Однако более эффек- тивным решением будет организация этих кнопок в виде матрицы 4x3, как показа- но на Рис. 11.10, б. При таком подключении количество требуемых выводов умень- шается до 7. В случае клавиатур большего размера эта экономия будет еще больше. Так, для клавиатуры на 64 кнопки (8 х 8) потребуется всего 16 линий ввода/вывода. Хотя организация матриц может быть различной, на рисунке представлена наиболее типичная. Сигналы трех столбцов считываются с выводов RB[7:5] с включенными внутренними подтягивающими резисторами. Поочередный выбор
342 Часть Ш. Окружающий мир Рис. 11.9. «Слабая» подтяжка линий порта В управляется битом RBPU регистра OPTION_REG каждой из строк (сканирование матрицы), подключенных к выводам RB[3:0], осуществляется выдачей на соответствующий вывод НИЗКОГО уровня, как по- казано на Рис. 11.10, в. Кнопки имеют нормально-разомкнутые контакты, поэто- му если кнопка не нажата, то из-за подключенных подтягивающих резисторов считывается лог. 1. Однако стоит замкнуть кнопку, подключенную к строке, на которую подан НИЗКИЙ уровень, как на соответствующей линии столбца тоже появится НИЗКИЙ уровень. Таким образом, нажатую кнопку можно определить по пересечению строки и столбца. Резисторы сопротивлением 330 Ом ограничи- вают ток через контакты кнопок, если из-за ошибки в программе на каком-либо выводе RB[7:5] случайно появится ВЫСОКИЙ уровень. Возьмем на вооружение оба принципа и напишем подпрограмму опроса кла- виатуры, которая будет возвращать либо номер нажатой кнопки (первой из нажа- тых, если одновременно нажали несколько кнопок), либо если ни одна из кнопок не нажата, то —1 (т.е. h’FF’). Прежде чем перейти к собственно программирова- нию, условимся, что порт В уже соответствующим образом сконфигурирован, а бит RBPU регистра OPTION_REG сброшен. Скажем, так: include "pl6f627.inc" MAIN bsf STATUS,RPO movlw b111110000' movwf TRISB be f OPTION_REG,NOT_RBPU bcf STATUS,RPO ; Переключаемся на 1-й банк памяти, ; в котором расположены ; регистры TRISB и OPTION_REG ; RB[7:4] - входы, RB[3:0] - выходы ; Включаем внутреннюю подтяжку ; Возвращаемся в 0-й банк
Глава 11. Ничего, кроме байтов 343 б) Обнаружено нажатие кнопки «5» в) Сканирование матрицы кнопок Рис. 11.10. Подключение клавиатуры Код, приведенный в Программе 11.2, соответствует следующему алгоритму: 1. Установить key_COUNT = 1. 2. Для i = 0...3 • Выбрать строку У. • Дляу = 0...2: — Проверить столбец./. — Если ноль, то перейти к шагу 4. - Иначе инкрементировать key_count. 3. Установить KEY_COUNT равным -1 (нажатых клавиш не обнаружено). 4. Вернуть KEY_COUNT.
344 Часть III. Окружающий мир Программа 11.2. Сканирование клавиатуры ; * ФУНКЦИЯ : Сканирует клавиатуру 4 х 3 и возвращает номер клавиши ; * ВХОД : Нет ; * ВЫХОД : Номер клавиши в W ([МЕМ]=10, [0]=11, [SET]=12) ; * ВЫХОД : Возвращает -1 (h'FF') если не нажато ни одной клавиши ; * ОКРУЖЕНИЕ : Переменные KEY, PATTERN *************************************************************************** cblock ; Две глобальные переменные KEY_COUNT:1, PATTERN:1 endc SCANJET clrf incf movlw movwf KEY_COUNT KEY_C0UNT,f b'lllllllO1 PATTERN ; Первая клавиша - "1" ; Начальное значение шаблона SLOOP movf PATTERN,w movwf PORTB ; Теперь проверяем каждый столбец btfss PORTB,5 goto GOT_IT incf KEY_COUNT,f btfss PORTB,6 goto GOT_IT incf KEY_COUNT,f btfss PORTB,7 goto GOT_IT incf KEY_COUNT,f ; Считываем шаблон из памяти ; Выдаем на строку НИЗКИЙ уровень ; Проверяем 1-й столбец ; ЕСЛИ ноль, ТО клавиша обнаружена! ; ИНАЧЕ инкрементируем счетчик ; Проверяем 2-й столбец ; ЕСЛИ ноль, ТО клавиша обнаружена! ; ИНАЧЕ инкрементируем счетчик ; Проверяем 3-й столбец ; ЕСЛИ ноль, ТО клавиша обнаружена! ; ИНАЧЕ инкрементируем счетчик ; Сюда попадаем, если нет нажатых rlf PATTERN,f btfsc PATTERN,4 goto SLOOP ; Сдвигаем шаблон ; Появился ли 0 в 4-м бите? ; ЕСЛИ нет, ТО переходим к следующей строке ; ИНАЧЕ на клавиатуре нет нажатых клавиш -------------------------- movlw -1 ; Возвращаем -1 goto S_EXIT GOT_IT movf KEY_COUNT,w ; Копируем значение счетчика в W S_EXIT return ; и выходим Отсчет начинается с кнопки №1, при этом на 0-ю строку выставляется напря- жение НИЗКОГО уровня. По мере проверки каждого столбца на ноль содержи- мое рабочего регистра, выполняющего роль счетчика, инкрементируется. При от- сутствии замыкания (нулевого значения) осуществляется переход к следующей строке, для чего значение шаблона PATTERN сдвигается на одну позицию влево.
Глава 11. Ничего, кроме байтов 345 При этом справа вдвигается бит переноса, но поскольку предыдущие строки мы уже проверили, то его значение безразлично. Выход из цикла сканирования происходит в двух случаях: • Если в процессе сканирования будет обнаружено нулевое значение. В пере- менной key_COUNT при этом будет находиться номер нажатой клавиши, ко- торый перегружается в рабочий регистр перед возвратом из подпрограммы. • Если в результате сдвига активный бит (0) шаблона окажется в 4-м бите ре- гистра. В этом случае в рабочий регистр заносится число h’FF’, означаю- щее, что нажатых клавиш не обнаружено. На практике подобные подпрограммы часто возвращают всякую ерунду из-за дребезга контактов, а также различных наводок в проводах, соединяющих клави- атуру и электронные узлы. Один из возможных вариантов решения этой пробле- мы приведен в Программе 11.3. В этой программе для опроса клавиатуры исполь- зуется подпрограмма SCAN_IT, код которой был приведен в Программе 11.2. Со- храняя в памяти значение, полученное при предыдущем опросе, можно отслеживать любые изменения состояния клавиатуры. Подпрограмма GET_IT возвращает код кнопки только в том случае, если состояние клавиатуры не меня- лось на протяжении 256 последовательных опросов. В зависимости от качества клавиатуры, уровня помех и частоты процессора надежность считывания можно увеличить (при этом возрастет время отклика), добавив в тело цикла короткую за- держку или увеличив размер счетчика до двух байтов. Программа 11.3. Сканирование клавиатуры с защитой от дребезга ; * ФУНКЦИЯ : Сканирует клавиатуру 4 х 3 и возвращает номер клавиши ; * ФУНКЦИЯ : (имеется защита от дребезга) ; * ВХОД : Нет ; * ВЫХОД : Номер клавиши в W ([МЕМ]=10, [0]=11, [SET]=12) ; * ВЫХОД : Возвращает -1 (h’FF'), если не нажато ни одной клавиши ; * ОКРУЖЕНИЕ : Переменные COUNT, NEW_KEY, OLD_KEY ОКРУЖЕНИЕ : Подпрограмма SCAN_IT cblock ; Три глобальные переменные COUNT:1, NEW_KEY:1, OLD_KEY:1 endc GET_IT clrf COUNT ; Обнуляем счетчик GLOOP call SCAN_IT ; «Сырое» значение находится в W movwf NEW_KEY ; Сохраняем новое значение subwf OLD_KEY,w ; Отличается от предыдущего? btfsc STATUS,Z goto EQUAL ; ЕСЛИ одинаковы, ТО переходим к EQUAL ; Результат отличается от предыдущего, поэтому: ---------------------------- movf NEW_KEY,w ; Переписываем предыдущее значение новым movwf OLD_KEY
346 Часть III. Окружающий мир goto GET_IT ; и начинаем цикл опроса сначала ; ЕСЛИ значения одинаковы, ТО ---------------------------------------------- EQUAL incfsz COUNT,f ; Инкрементируем счетчик. ЕСЛИ нет goto GLOOP ; переполнения, считываем новое значение movf OLD_KEY,w ; ИНАЧЕ возвращаем требуемое значение] return Программа 11.4. Подпрограмма сканирования клавиатуры на Си unsigned int scan_it(void) { unsigned int key, pattern; key=l; pattern = OxFE; while(key<13) { PORT_B = pattern; if (1 COLD {break;} key++; if(!COL2} {break;} key++; if(!COL3) {break;} key++; pattern - pattern « 1; } if(key==13) {key = OxFF;} клавиши */ return key; /* Начальное значение маски b'11111110' */ У нас 12 клавиш /* Выбираем строку */ /* Считываем состояние каждого столбца, */ /* выходя из цикла при нулевом значении */ /* ИНАЧЕ инкрементируем счетчик цикла */ /* Сдвигаем маску на один бит влево */ /* Если в счетчике число 13, нажатые /* отсутствуют */ В Программе 11.4 приведен текст Си-программы для компилятора CCS, кото- рая выполняет те же действия, что и код в Программе 11.2. При этом предполага- ется, что порт В уже сконфигурирован следующим образом: #include <16f627.h> #use fast_io(b) #byte PORT_B - 6 #bit COL1 = PORT_B.5 #bit COL2 = PORT_B.6 #bit COL3 = PORT_B.7 /* Столбец 1 - RB5 */ /* Столбец 2 - RB6 */ /* Столбец 3 - RB7 */ unsigned int scan_it(void) ; int main() { set_tris_b(0xF0) ; port_b_pullups(TRUE); В компиляторе CCS предусмотрены различные средства поддержки парал- лельного ввода/вывода. Так, выражение #use f ast_io (b), использованное на-
Глава 11. Ничего, кроме байтов 347 ми в предыдущем фрагменте кода, предоставляет программисту возможность явно задавать конфигурацию регистров TRIS. Альтернативная директива #use standard_io (b) позволяет программисту не обращать внимание на ус- тановки этих регистров, но тогда компилятор будет конфигурировать порт при каждом обращении к нему, даже если его конфигурация и не изменялась с момен- та последнего использования. Ну, а функция рогt_b_pullaps (true) предна- значена для установки бита RBPU регистра OPTION_REG. Логика программы практически не отличается от логики ассемблерной про- граммы, написанной нами ранее. Единственное отличие заключается в том, что количество проходов цикла задается заранее, а не определяется моментом сброса 4-го бита маски. Это делает процесс вычислений более прозрачным, хотя и менее эффективным. Реализация интерфейса с клавиатурой является настолько частой задачей, что в большинстве микроконтроллеров PIC имеется возможность определять изме- нения состояний входов порта В. Логика работы этой схемы показана на Рис. 11.11. Старшие четыре линии порта имеют вторую D-защелку, подключен- ную параллельно основной, но работающую с ней в противофазе. При чтении порта В состояние входа, как обычно, запоминается в защелке Capture. Однако в то же время защелка Change становится прозрачной. По завершении операции чтения защелка Change фиксируется, в результате чего в ней сохраняется состоя- ние вывода, которое было в момент считывания. Выходы обеих защелок объеди- нены посредством элемента Исключающее ИЛИ (XOR). Как вы уже знаете (см. стр. 28), логический элемент XOR фиксирует различие между двумя входами. Рис. 11.11. Логика работы схемы, формирующей прерывание по изменению состояния порта В
348 Часть III. Окружающий мир Поскольку защелка Capture в это время прозрачна, любое последующее измене- ние сигнала на входе приведет к появлению лог. 1 на выходе соответствующего элемента XOR. По такой схеме построены линии RB[7:4] порта В. Выходы всех четырех элементов XOR объединены с помощью 4-входового элемента ИЛИ, сиг- нал с выхода которого используется для установки флага прерывания RBIF регис- тра INTCON (см. Рис. 7.3 на стр. 213) в 1. Если бит RBIE (разрешение прерыва- ния от порта В) также установлен в 1, то это событие позволяет выводить микро- контроллер PIC из «спящего» режима. А если установлен бит глобального разрешения прерываний GIE, то изменение состояния четырех старших линий порта В приведет еще и к генерации прерывания. Обратите внимание, что сигнал от каждого из элементов XOR проходит через двухвходовый элемент И, второй вход которого подключен к соответствующему биту регистра TRIS. Благодаря этому в формировании итогового сигнала участвуют только те линии, которые сконфигурированы как входы. В нашем конкретном случае (см. клавиатуру на Рис. 11.10) если на линиях всех строк выставить НИЗКИЙ уровень, то при нажатии любой клавиши изме- нится состояние линии столбца. Если бит RBIE будет при этом установлен, то од- новременно с установкой флага RBIF будет сгенерировано прерывание. После этого в обработчике прерывания можно будет выполнить сканирование клавиа- туры для определения нажатой клавиши. Порт ввода/вывода GPIO моделей сред- него уровня, выпускающихся в 8-выводных корпусах, имеет схожую функцио- нальность^. Причем для большей гибкости реакция на изменение состояния мо- жет быть разрешена или запрещена индивидуально для каждого из выводов порта. При использовании этой возможности необходимо быть очень аккуратным. Например, при изменении младших битов порта В (скажем, командой be f PORTB, 0) во все защелки будут записаны новые значения с выводов микро- контроллера, что может повлиять на работу этой функции. В более старых уст- ройствах также существует вероятность пропустить изменение состояния вывода, если оно произойдет в момент чтения порта. Однако ни один из этих недостатков не является сколько-нибудь существенным, если нажатие на клавиатуру исполь- зуется для «пробуждения» процессора. После отклика микроконтроллера на данное прерывание необходимо повтор- ным чтением порта В снять внутренний сигнал, вызвавший установку бита RBIF. При этом в обоих D-защелках окажется одинаковое значение. И только затем можно будет сбросить флаг RBIF. Если этого не сделать, то сразу же после сброса флаг прерывания снова установится. Приведем пример использования клавиатуры для вывода микроконтроллера из «спящего» режима (предполагается, что бит GIE сброшен, т.е. прерывания в программе не используются): movf PORTB,w ; Считываем порт В, чтобы сбросить защелки bcf INTCON,RBIF ; Сбрасываем флаг прерывания по изменению порта !) Большинство моделей линейки PIC12CXXX, таких как PIC12C508/9, на самом деле имеют 12-битное ядро.
Глава 11. Ничего, кроме байтов 349 bsf INTCON,RBIE sleep ; ссс-п-и-и-и-и-м.. . call DELAY movwf PORTB,w bcf INTCON,RBIF bcf INTCON,RBIE ; Разрешаем генерацию прерывания ; Переходим в «спящий» режим ; После «пробуждения» ждем некоторое время ; перед сбросом защелок ; Сбрасываем флаг прерывания ; Запрещаем генерацию прерывания В большинстве микроконтроллеров PIC имеется относительно мало линий портов ввода/вывода (см. Табл. 11.1). Даже в развитых моделях (например, PIC16F877), имеющих в общей сложности 33 линии ввода/вывода, этих ресурсов может в ряде случаев оказаться недостаточно, особенно если часть выводов будет задействована встроенными периферийными устройствами. В качестве примера рассмотрим многофункциональную систему охранной сиг- нализации, которая может контролировать до восьми зон — скажем, этажей мно- гоэтажного здания. В каждой зоне может быть расположено до восьми датчиков движения. Дисплей в виде восьми лампочек, расположенный на посту охраны, ис- пользуется для индикации зоны, в которой было обнаружено проникновение. Исходя из условий задачи, нам потребуется 72 линии ввода/вывода (64 входа и 8 выходов). Вместо того чтобы устанавливать в каждой зоне по микроконтролле- ру, каждый из которых отсылал бы информацию центральному контроллеру^, было принято решение расширить возможности ввода/вывода одного-единствен- ного микроконтроллера PIC16F627. Один из возможных вариантов решения этой задачи изображен на Рис. 11.12. В данном случае порт В используется для реализации внешней шины данных, подключенной к восьми микросхемам параллельных регистров (буферов) с тремя состояниями (по одной для каждой зоны) и одному регистру индикации. Датчики каждой из зон подключаются к локальной шине через эти 8-битные буферы с тре- мя состояниями. Разрешение одного из восьми буферов осуществляется с ис- пользованием дешифратора 3 на 8, подключенного к порту А. К примеру, если RA[2:0] = b’l 1Г и RA3 = 0, то будет разрешен буферный регистр 7-й зоны, в ре- зультате чего с выводов порта В можно будет считать состояния восьми датчиков этой зоны. Для управления лампочками бит RA3 необходимо установить в 1, а порт В сконфигурировать как выход. После этого содержимое порта можно будет занес- ти в регистр, выставив на вывод RA0 напряжение НИЗКОГО, а затем ВЫСОКО- ГО уровня, формируя тем самым нарастающий фронт. Количество выходных портов в этой системе можно увеличить до восьми, до- бавив в схему второй декодер 3 на 8, посредством которого будет осуществляться выбор требуемого порта при RA3 = 1. Правда, один или два дополнительных вы- ходных порта можно добавить еще проще, подключив тактовые входы новых ре- гистров к линиям RA1 и RA2. Один из этих портов, к примеру, может использо- Такое решение является более правильным с точки зрения надежности и эффективности (см. Вопрос для самопроверки 11.1).
350 Часть HI. Окружающий мир Индикаторы зон Рис. 11.12. Многозонная система охранной сигнализации ваться для индикации активного датчика раздела, а для управления звуковым из лучателем, включающимся при обнаружении вторжения в любую из зон, можм использовать вывод RA4. Чтобы продемонстрировать управление такой схемой, рассмотрим подпро грамму, код которой приведен в Программе 11.5. Эта подпрограмма считывает со стояние TV-й зоны и, если возвращаемое значение отлично от нуля, включает jV-i лампу (N — число от 0 до 7, загружаемое перед вызовом подпрограммы в регист ZONE). Предполагается, что на выходе сработавшего датчика формируется лог. 1 а индикаторная лампа загорается при лог. 0.
Глава 11. Ничего, кроме байтов 351 Программа 11.5. Управление системой охранной сигнализации ; * ФУНКЦИЯ : Считывает состояние N-й зоны и включает N-ю лампу * ; * ВХОД : N передается в регистре ZONE в виде b'OOOOOnnn' * ; * ВЫХОД : Включается N-я лампа, если состояние N-й зоны отлично от О* ; * ВЫХОД : Регистр ZONE обнуляется, регистр TEMP не изменяется * ZONE_N bsf STATUS,RPO movlw h’FF' movwf TRISB clrf TRISA bcf STATUS,RPO ; Переключаемся в 1-й банк ; Конфигурируем порт В как вход ; Конфигурируем порт А как выход ; Возвращаемся в 0-й банк movf ZONE,w ; Считываем N, используемое ; для выбора буферов N-й зоны ; Формируем задержку для установления сигнала ; в случае длинных соединительных линий ; Теперь считываем данные с порта В ; ЕСЛИ не 0, ТО проникновение! ; ИНАЧЕ выключаем все лампы movwf PORTA nop nop movf PORTB,w btfsc STATUS,Z goto LAMP_OFF ; Обнаружено проникновение, bsf STATUS,RPO clrf TRISB bcf STATUS,RPO включаем сигнальную лампу ; Переключаемся в 1-й банк ; Теперь конфигурируем порт В как выход ; Возвращаемся в 0-й банк — ; Преобразуем двоичное число movlw h'FF' movwf TEMP bcf STATUS,C в унарный эквивалент для включения соотв. ; Все биты переменной TEMP ; установлены в 1 ; Обнуляем бит переноса лампы incf ZONE,f Z_LOOP rlf TEMP,f bsf STATUS,C decfsz ZONE,f goto Z_LOOP ; Транслируем номер зоны в диапазон 1... ; Сдвигаем маску влево ; Устанавливаем бит переноса ; Декрементируем номер зоны ; и повторяем N раз .8 ; В TEMP теперь находится маска для включения требуемой лампы movf TEMP,w ; Сохраняем ее в рабочем регистре LAMP_OUT bsf PORTA,3 ; Разрешаем выходной порт movwf PORTB ; Выставляем маску bsf PORTA,0 ; Формируем тактовый импульс bcf PORTA,0 return ; Все сделано ; Сюда переходим при отсутствии проникновения (нужно выключить все лампы) -- LAMP_OFF bsf STATUS,RPO ; Переключаемся в 1-й банк clrf TRISB ; Теперь конфигурируем порт В как выход bcf STATUS,RPO ; Возвращаемся в 0-й банк movlw h'FF' ; Выключаем все лампы goto LAMP_OUT
352 Часть Ш, Окружающий мир Для проверки состояния датчиков N-й зоны номер зоны из переменной ZONE копируется в порт А, все линии которого работают как выходы. При НИЗКОМ уровне на RA3 дешифратор разрешает буфер соответствующей зоны. После ко- роткой задержки, введенной для установления сигналов на выходе буфера, состо- яние датчиков выбранной зоны считывается с порта В. В реальных системах (при больших расстояниях между буферными регистрами зон) для обеспечения надеж- ного считывания данных могут потребоваться задержки порядка нескольких сот миллисекунд, а также операции цифровой фильтрации, подобные реализованной в Программе 11.3. Все это связано с тем, что буферные регистры зон могут распо- лагаться далеко друг от друга. Управление восемью лампами более мудреное. Для этой операции порт В не- обходимо сконфигурировать как выход. Включение требуемых ламп осуществля- ется копированием соответствующей маски в порт В, установкой RA3 в состоя- ние ВЫСОКОГО уровня для отключения дешифратора буферов зон и последую- щего формирования импульса на линии RA0. В Программе 11.5 эти операции выполняет подпрограмма lamp_OUT. При отсутствии проникновения, т.е. когда выходы всех датчиков зоны сброшены в 0, байт маски, управляющий свечением ламп, равен h’FF’ (все лампы выключены). При обнаружении проникновения необходимо включить TV-ю лампу. Для это- го двоичный код зоны, хранящийся в переменной ZONE, необходимо преобразо- вать в соответствующий унарный (один из п) код. Так, число Ь’00000010’ (2-я зона) преобразуется в число Ь’11111011’, число Ь’ООООООН’ (3-я зона) преобразуется в число Ь’11110111’ и т.д. В программе унарный код формируется в переменной temp, которой первона- чально присваивается значение ЬТ1111111 ’. Сбрасывая флаг переноса перед вхо- дом в цикл z_LOOP, но устанавливая его в теле цикла, можно выполнить сдвиг сброшенного бита влево с помощью команды rlf temp, f. В результате содер- жимое регистра будет изменяться следующим образом: Ь’11111111’*-Ь’11111110’ <— b’ 11111101’ b’01111111 В процессе сдвига переменная ZONE (приведен- ная к диапазону 1...8, так что выполняется, по крайней мере, один сдвиг) декре- ментируется, а выход из цикла производится, когда она становится равной нулю. Таким образом, позиция единственного нулевого бита (начальное значение С = 0) соответствует исходному номеру зоны. Этот унарный код затем выдается в порт в секции LAMP_OUT. Примеры Пример 11.1 Для управления обмоткой возбуждения небольшого шагового двигателя используется биполярный п-р-п транзистор 2N3055. Принимая во внимание минимальный коэффициент усиления этого транзистора в диапазоне температур —4О...+85°С, было принято решение, что ток базы должен быть не менее 10 мА. Транзистор подключен к линии ввода/вывода микроконтроллера, причем полага-
Глава 11. Ничего, кроме байтов 353 ется, что падение напряжения на переходе база-эмиттер не превышает 0.7 В при KDD = 5 В. Какое максимальное сопротивление может иметь базовый резистор /?в и чему в самом худшем случае будет равен максимальный ток базы при таком ре- зисторе? Решение Для таких величин токов можно предположить, что напряжение на выводе будет меньше 5 В. В документации приводится минимальная величина выходно- го напряжения при /он = —3 мА, которая равна 4.'3 В (на 0.7 В ниже напряжения питания), но для больших значений токов нам придется воспользоваться графи- ками. На Рис. 11.13 приведены графики зависимости выходного тока /Он от напря- жения ВЫСОКОГО уровня Ион при граничных значениях температуры (—40°С и +80°С). Напряжение Ион зависит от сопротивления базового резистора в соответствии с уравнением Кон = 0.7 + /он х 7?в- Прямая линия, выражающая это соотношение (называемая нагрузочной линией), проведена на рисунке через точку (0,0.7) и точку, Рис. 11.13. Зависимость выходного напряжения ВЫСОКОГО уровня оттока
354 Часть III. Окружающий мир соответствующую минимальному напряжению при токе —10 мА. Эта точка явля- ется единственной, удовлетворяющей обоим соотношениям ток-напряжение. ЛИ Крутизна нагрузочной линии — представляет собой сопротивление в кОм (так Л/ как ток выражается в мА) и получается равной 280 Ом. Обратите внимание, что напряжение ВЫСОКОГО уровня при таком токе снижается до 4 В (—10,4.0). Продолжив линию, мы можем определить максимальный ток как координату X точки пересечения линии с верхней кривой. Этот ток равен примерно 11.5 мА, что не слишком отличается от предыдущего значения. Если бы нам требовалось получить больший ток, то вы бы увидели, что его величина очень сильно зависит от температуры. Например, чтобы получить минимальный базовый ток, равный 20 мА, нам потребуется резистор сопротивлением около 120 Ом (учитывая, что напряжение базы равно 0.8 В). Максимальный базовый ток в этом случае будет равен уже 28 мА. Пример 11.2 Микроконтроллер PIC среднего уровня используется в качестве цифрового компаратора, который сравнивает 8-битное значение Р, считываемое с выводов порта, с байтом, хранящимся в регистре TRIP. Компаратор формирует три сигна- ла — «меньше, чем», «равно» и «больше, чем» и должен иметь гистерезис ±1 бит. То есть если в результате сравнения будет получено Р < TRIP, то уровень пере- ключения для сигнала «равно» увеличится до значения TRIP + 1. Аналогично, при обратном соотношении уровень переключения для сигнала «равно» стано- вится равным TRIP — 1. Значение Р считывается из порта В, все линии которого сконфигурированы как входы, а для вывода результатов сравнения используются три младших выво- да порта А — RA2 («меньше»), RA1 («равно») и RA0 («больше») с ВЫСОКИМ ак- тивным уровнем. Решение Напишем алгоритм, удовлетворяющий заданию: 1. Вычесть Р из LEVEL. 2. ЕСЛИ Р= LEVEL (Z = 1), то активизировать выход «Равно». 3. ИНАЧЕ, ЕСЛИ Р > LEVEL (С - 0), то активизировать выход «Больше» и присвоить LEVEL = TRIP — 1. 4. ИНАЧЕ, ЕСЛИ Р < LEVEL (С = 1), то активизировать выход «Меньше» и присвоить LEVEL — TRIP + 1. Подпрограмма, текст которой приведен в Программе 11.6, а, написана с рас- четом на то, что порты уже настроены соответствующим образом, а в переменную trip уже занесено фиксированное значение. Сначала значение регистра LEVEL принимается равным trip, но впоследствии оно изменяется в пределах ±1 по указанному выше алгоритму, формируя гистерезис.
Глава 11. Ничего, кроме байтов 355 Программа 11.6. Цифровой компаратор с гистерезисом а) Подпрограмма на ассемблере СОМР movf subwf btfss goto PORTB,w ; LEVEL,w ; STATUS,Z ; CONTINUE ; : Берем входное значение P ; LEVEL - P ; Пропускаем, если равно : ИНАЧЕ проверяем остальные варианты ; Сюда попадаем при равенстве movlw b'lllllOlO’ ; : Выставляем на'вывод «==» лог. 1 movwf PORTA ; ; На остальных выходах - лог. 0 goto COMP_END ; : и выходим CONTINUE btfsc STATUS,C ; ; Пропускаем, если заем (Р > LEVEL) goto LO ; ; ИНАЧЕ Р < LEVEL ; Сюда попадаем при P > LEVEL HI movlw b'lllllOOl’ ; Выставляем на вывод «>» лог. 1 movwf PORTA ; На остальных выходах - лог. 0 decf TRIP,w ; Копируем TRIP-1 в W movwf LEVEL ; Новое значение порога goto COMP_END ; и выходим ; Сюда попадаем при P < LEVEL LO movlw Ь'ШШОО’ ; Выставляем на вывод «<» лог. 1 movwf PORTA ; На остальных выходах - лог. 0 incf TRIP,W ; Копируем TRIP+1 в W movwf LEVEL ; Новое значение порога COMP_END return б) Функция на Си (компилятор CCS) void compare(unsigned int trip) { EQ = HI = LO = 0; if(PORTB == LEVEL) {EQ =1;} else if(PORTB > LEVEL) {HI = 1; LEVEL = trip - 1;} else {LO = 1; LEVEL = trip +1;} Преимущество программной реализации функций, традиционно реализуе- мых аппаратно (таких как функция сравнения), заключается в большей гибкости, хотя и за счет снижения пропускной способности. Использование недорогих «вычислителей», таких как микроконтроллеры PIC, означает, что относительно простые функции, традиционно реализуемые специальными микросхемами, мо- гут выполняться с помощью встраиваемых процессоров.
356 и Часть IIL Окружающий мир В данном случае гибкость состоит в том, что вместо фиксированного уровня можно легко начать использовать произвольное значение, считываемое, скажем, с порта С (см. Вопрос для самопроверки 11.5). В Примере 12.1 на стр. 435 показа- но, как можно считывать внешние данные последовательно. Также один или оба уровня могут быть сформированы из аналоговых сигналов с использованием встроенного модуля АЦП (см. главу 14). Во всех этих случаях гистерезис может задаваться в виде доли от порогового значения, например ±7з2> а не как фикси- рованное значение ± 1 бит. В Си-варианте подпрограммы, код которой приведен в Программе 11.6, б, ис- пользуются символические имена — EQ, YI и L0, которые определены в основ- ной программе как соответствующие биты порта А. В данном случае пороговое значение trip передается в подпрограмму в качестве переменной. Сама функция только сравнивает значения и управляет соответствующими выводами. При не- обходимости также изменяется глобальная переменная level для изменения уровня переключения компаратора. Если значение trip фиксированно, то его не требуется передавать в функцию, и оно может задаваться константой. Пример 11.3 На Рис. 11.14 изображен принцип работы шагового двигателя. В двигателе имеется четыре обмотки, обозначенные буквами А, В, С и D, которые могут воз- буждаться по одиночке или попарно для формирования магнитного поля в одном из восьми направлений с шагом 45о1). Так, обмотка А формирует поле в направле- Рис. 11.14. Принцип работы шагового двигателя В настоящем шаговом двигателе имеется несколько таких групп обмоток, располагаю- щихся по периметру статора с некоторым смещением друг относительно друга. За счет этого уменьшается дискретность установки вала двигателя. Так, при наличии 4 групп статорных обмоток дискретность установки вала составит 11.25°.
Глава 11. Ничего, кроме байтов 357 нии север, А + В — в направлении северо-восток, В — восток и т.д. Соответ- ственно ротор вращается вслед за изменением направления магнитного поля, при условии, что конструкция двигателя обеспечивает стабилизацию положения ротора при разгоне и торможении. Напишите подпрограмму, размещаемую по адресу h’050’ памяти программ, которая будет управлять перемещением ротора. В подпрограмму будет переда- ваться количество шагов от 1 до 256. Предполагается, что выводы порта A RA[3:0] подключены к обмоткам А, В, С, D соответственно. Скорость вращения должна быть равна 100 шагам в секунду, что обеспечивается 10-мс задержкой. Подпро- грамма формирования этой задержки должна быть написана таким образом, что- бы в минимальной степени зависеть от тактовой частоты микроконтроллера. Последняя указывается программистом в виде константы freq, являющейся множителем 100 кГц, т.е. для 4-МГц резонатора freq = d’40’. Решение Прежде всего, нам потребуется составить таблицу, содержащую коды управле- ния обмотками шагового двигателя для всех восьми возможных направлений магнитного поля (см. Табл. 11.2). Таблица 11.2. Коды управления обмотками шагового двигателя для восьми направлений магнитного поля Позиция А в с D Направление 0 1 0 0 0 т 1 1 1 0 0 2 0 1 0 0 -> 3 0 1 1 0 4 0 0 1 0 4 5 0 0 1 1 6 0 0 0 1 7 1 0 0 1 Код, приведенный в Программе 11.7, состоит из трех подпрограмм. MOTOR Это основная подпрограмма, которая просто инкрементирует по модулю 8 пере- менную, хранящую номер вектора направления магнитного поля. Чтобы после числа 7 счет снова начинался с 0, результат обычного инкрементирования логи- чески умножается (AND) на константу Ь’ОООООШ’. Затем номер вектора преоб- разуется в соответствующий код, который после 10-мс задержки выдается на вы- воды управления двигателем. Процесс повторяется до тех пор, пока декрементируемый регистр STEP не станет равным 0; если он изначально был ну- левым, то будет сделано 256 шагов.
358 Часть III. Окружающий мир PATTERN Эта подпрограмма возвращает один из восьми кодов в соответствии с Табл. 11.2. Принцип реализации подобных таблиц был описан в Программе 6.6 (стр. 184). Поскольку подпрограммы располагаются в памяти, начиная с адреса h’050’, опе- рация 8-битного сложения номера шаблона со счетчиком команд не вызовет пе- рехода через границу страницы памяти программ. Программа 11,7. Управление шаговым двигателем ttdefine FREQ d'40' ; Задается программистом как множитель 100 кГц org h.150’ ; Код начинается с адреса h'50' *************************************************************** ; * ФУНКЦИЯ : Поворот ротора на заданный угол (1...256 шагов) * ; * ВХОД : Число шагов в STEP * ; * ВХОД : Номер текущего вектора магнитного поля в POSITION * ; * ВЫХОД : POSITION обновляется, STEP = -1, W изменяется * ; * РЕСУРСЫ : Подпрограммы PATTERN, DELAY_10MS * MOTOR incf POSITION,w ; Берем следующий вектор andlw movwf b'OOOOOlll' POSITION ; Делим по модулю 8 ; Корректируем call PATTERN ; Получаем управляющий код movwf call PORTA DELAY_10MS ; Выдаем на шаговый двигатель ; Ждем 10 мс decfsz STEP,f ; Декрементируем число шагов, goto return MOTOR ; пока не станет равно 0 . ************************************************************* / ; * ФУНКЦИЯ : Преобразует целое число 0... 7 в управляющий код * ; * ВХОД : Целое число от 0 до 7 в W * ; * ВЫХОД : Код для управления обмотками ШД в W * PATTERN addwf PCL,f ; Изменяем счетчик программ retlw b'1000' ; Север retlw b'1100’ ; Северо-восток retlw b'0100' ; Восток retlw b’0110' ; Юго-восток retlw b'0010' ; Юг retlw b’0011' ; Юго-запад retlw b'0001' ; Запад retlw b’1001• ; Северо-запад ; * ФУНКЦИЯ : Формирует 10-мс задержку, не зависящую * ; * от тактовой частоты * ; * ВХОД : Значение тактовой частоты, деленное на 100 кГц, * ; * в TEMP * ; * ВЫХОД : 10-мс задержка; DELAY обнуляется, W изменяется *
Глава 11. Ничего, кроме байтов 359 DELAY_10MS movlw FREQ ; Тактовая частота указывается movwf TEMP ; программистом ; Цикл 10-мс задержки при тактовой частоте 100 кГц (1 цикл = 40 мкс) DLOOP1 movlw d' 62 1 ; Счетчик цикла movwf DELAY DLOOP2 decf DELAY,f ; 62 * 40 мкс btfss STATUS,Z ; 62 * 40 мкс goto DLOOP2 ; 62 * 80 мкс decfsz TEMP,f ; Декрементируем параметр и повторяем, goto return DLOOP1 ; пока он не станет равным нулю DELAY.IOMS Эта подпрограмма формирует задержку длительностью 10 мс, независимую от частоты резонатора. Значение частоты задается программистом константой freq посредством директивы #define. Данная константа представляет собой множитель, равный значению тактовой частоты, деленной на 100 кГц. К примеру, для 8-МГц резонатора константа FREQ будет равна 80. В основе подпрограммы лежит цикл, формирующий задержку длительностью 10 мс при тактовой частоте 100 кГц, т.е. при длительности машинного цикла, рав- ной 40 мкс. Этот цикл повторяется FREQ раз. Так, в нашем примере с 8-МГц резо- натором длительность базового цикла будет равна — мс, однако этот цикл будет повторён 80 раз, что и даст нам искомые 10 мс. 80 Пример 11.4 Доработайте функцию дешифратора клавиатуры из Программы 11.4, добавив к нему процедуру подавления дребезга подобно тому, как это было сделано в Программе 11.3. Решение Функция get_.it (), текст которой приведен в Программе 11.8, накапливает в переменной count число вызовов функции scan_it (), каждый раз сравнивая возвращаемое значение, которое присваивается переменной new_key, с преды- дущим значением, хранящимся в переменной old_key. Если эти значения не совпадают, то счетчик обнуляется. Выход из цикла происходит только после 254 идентичных считываний, в результате чего в вызывающую программу возвраща- ется стабильное значение. Программа 11.8. Подавление дребезга для драйвера клавиатуры unsigned int get_it(void) { unsigned int count, old_key, new_key; count = 0;
360 и Часть III. Окружающий мир while(count<255) { new_key = scan_it() ; if(new_key -= old_key) { count++;} else { old_key - new_key; count = 0; } } return (old_key); Пример 11.5 Несмотря на все более широкое распространение жидкокристаллических ал- фавитно-цифровых матричных дисплеев, для отображения многоразрядных чи- сел до сих пор очень часто используются дискретные 7-сегментные светодиодные индикаторы. Применение таких индикаторов особенно эффективно при низкой освещенности и при необходимости использования больших дисплеев. Если учесть, что для управления каждым индикатором требуется восемь ли- ний (семь сегментов плюс десятичная точка), то получается, что для управления «-разрядным дисплеем нам потребуется 8 х п параллельных линий. Типичное ре- шение этой проблемы представлено на Рис. 11.15. В этой схеме 3-разрядный дис- плей управляется тремя параллельными регистрами, подключенными к локаль- ной шине, наподобие той, что была использована в схеме на Рис. 11.12. Этот же принцип можно использовать и с дисплеями большей разрядности, взяв соот- ветствующее число регистров. Индикаторы, показанные на схеме, выполнены по схеме с общим катодом, поэтому каждый сегмент включается тогда, когда на соответствующем выходе ре- гистра присутствует ВЫСОКИЙ уровень. Резисторы, включенные последова- тельно с сегментами, служат для ограничения тока. На практике некоторые логи- ческие схемы могут отдавать больший выходной ток в состоянии НИЗКОГО уровня, поэтому чаще используются индикаторы с общим анодом, в которых включение сегментов осуществляется подачей НИЗКОГО уровня. В крупногаба- ритных дисплеях, например высотой 5 см (2 дюйма), каждый сегмент может со- стоять из нескольких СИД, включенных последовательно и/или параллельно. В этом случае для управления индикатором может потребоваться большее напря- жение и/или ток, для обеспечения которых необходимо подключить к выходам регистра подходящие драйверы. Альтернативное решение, показанное на Рис. 11.16, часто применяется для дисплеев на базе светодиодных индикаторов. Вместо использования отдельного регистра для каждого разряда, все индикаторы подключены параллельно к одному из портов микроконтроллера. Каждый индикатор поочередно включается на ко- роткий промежуток времени, отображая соответствующие данные с выходного
Глава И. Ничего, кроме байтов 361 Рис. 11.15. Расширение порта для управления 7-сегментными индикаторами порта. Если обновлять изображение чаще чем 50 раз в секунду (а еще лучше — ча- ще 100 раз в секунду), то из-за инерционности системы зрения будет казаться, что индикаторы не мерцают1*. Разумеется, ток, протекающий через каждый сегмент, следует увеличить, чтобы скомпенсировать снижение яркости, вызванное им- пульсным режимом работы. А поскольку в таком режиме СИД работают более 11 Именно из-за этой особенности человеческого зрения мозг воспринимает последова- тельность неподвижных кадров, сменяющихся 24 раза в секунду, как движущееся изображение. За счет использования двухлопастного обтюратора каждый кадр отображается дважды, таким образом, смена кадров происходит 48 раз в секунду.
362 Часть III. Окружающий мир Рис. 11.16. Динамическое управление тремя 7-сегментными индикаторами эффективно, зависимость между сопротивлением последовательных резисторов и коэффициентом заполнения управляющего сигнала будет не прямо пропорци- ональной. Прикиньте все плюсы и минусы обоих решений, учитывая как аппаратные за- траты, так и затраты на программирование. Для иллюстрации своего ответа напи- шите программу, отображающую число, хранящееся в регистре h’20’. Например, если в этом регистре (назовем его BINARY) находится число h’FF’, то на дисплее должно отображаться BBS-
Глава 11. Ничего, кроме байтов 363 Решение Что касается программного обеспечения, то тут можно выделить две основ- ные функции. Сначала код, находящийся в регистре BINARY, необходимо преоб- разовать в три BCD-разряда (HUNDREDS, TENS и UNITS). После этого значе- ние каждого из разрядов (0...9) следует преобразовать в 7-сегментный код, чтобы включить соответствующие сегменты индикаторов, отображая требуемую цифру. У нас уже есть подпрограммы для реализации 1-го (см. Программу 6.11 на стр. 196) и последнего (см. Программу 6.6 на стр. 1'84) этапов. С учетом этих про- грамм, можно составить алгоритм управления схемой, приведенной на Рис. 11.15: 1. Преобразовать двоичное однобайтное число в BCD-число. 2. ВЫПОЛНЯТЬ: а) Скопировать содержимое HUNDREDS в W и преобразовать его в 7-сег- ментный код. б) Вывести полученный код в порт В. в) Сформировать импульс на выходе RA2. 3. ВЫПОЛНЯТЬ: а) Скопировать содержимое TENS в W и преобразовать его в 7-сегментный код. б) Вывести полученный код в порт В. в) Сформировать импульс на выходе RA1. 4. ВЫПОЛНЯТЬ: а) Скопировать содержимое UNITS в W и преобразовать его в 7-сегмент- ный код. б) Вывести полученный код в порт В. в) Сформировать импульс на выходе RAO. Код, реализующий этот алгоритм, приведен в Программе 11.9. Программа 11.9. Отображение трехразрядного десятичного числа (статическая индикация) ; Задача 1 --------------------------------------------- DISPLAY movf BINARY,w call BIN_2_BCD ; Задача 2 movf HUNDREDS,w call SVN_SEG movwf PORTB bsf PORTA,2 bcf PORTA,2 ; Задача 3 movf TENS,w call SVN_SEG movwf PORTB ; Берем двоичное значение ; Преобразуем его в три BCD-разряда ; Берем число сотен ; Преобразуем в 7-сегментный код ; Высылаем в порт В ; Заносим в регистр ; Берем число десятков ; Преобразуем в 7-сегментный код ; Высылаем в порт В
364 Часть III. Окружающий мир bsf PORTA,1 ; Заносим в регистр bcf PORTA,! ; Задача 4 -------------------------------------------------- movf UNITS,w ; Берем число единиц call SVN_SEG ; Преобразуем в 7-сегментный код movwf PORTB ; Высылаем в порт В bsf PORTA,0 ; Заносим в регистр bcf PORTA,О Управление схемой, показанной на Рис. 11.16, несколько сложнее, поскольку в ней отсутствуют регистры, хранящие данные! Поэтому данные необходимо не- прерывно выдавать друг за другом одновременно с включением соответ- ствующего индикатора. Если мы собираемся обновлять изображение 100 раз в се- кунду, то перед переходом к следующему знакоместу эти данные должны удержи- ваться в течение 10 мс. Таким образом, мы получаем новый алгоритм: 1. Преобразовать двоичное однобайтное число в BCD-формат. 2. ВЫПОЛНЯТЬ бесконечно: а) • Скопировать содержимое HUNDREDS в W и преобразовать его в 7-сегментный код. • Выдать полученный код в порт В. • Выставить на RA2 НИЗКИЙ уровень . • Подождать 10 мс. • Выставить на RA2 ВЫСОКИЙ уровень _Г~. б) • Скопировать содержимое TENS в W и преобразовать его в 7-сегментный код. • Выдать полученный код в порт В. • Выставить на RA1 НИЗКИЙ уровень . • Подождать 10 мс. • Выставить на RA1 ВЫСОКИЙ уровень _/~. в) • Скопировать содержимое UNITS в W и преобразовать его в 7-сегментный код. • Выдать полученный код в порт В. • Выставить на RA0 НИЗКИЙ уровень . • Подождать 10 мс. • Выставить на RA0 ВЫСОКИЙ уровень _/~. В коде, приведенном в Программе 11.10, используется подпрограмма форми- рования 10-мс задержки, которую мы использовали в Программе 11.7 для зада- ния скорости сканирования. За исключением длительности импульса разреше- ния, основная часть программы идентична предыдущей. Однако чтобы цифры на дисплее светились постоянно, код программы должен выполняться непрерывно. В этом и заключается компромисс между затратами на аппаратную и программ-
Глава 11. Ничего, кроме байтов 365 ную части. Действительно, как уже было показано, все ресурсы микроконтролле- ра PIC уйдут на обслуживание индикатора! На самом деле ситуацию может спас- ти прерывание микроконтроллера с периодом Юме, что позволит избежать ис- пользования подпрограмм формирования задержки. В листинге на стр. 475 показано, как это можно реализовать. Разумеется, в этом случае таймер нельзя будет использовать для других задач. Также можно воспользоваться внешним ге- нератором с частотой 100 Гц, однако при этом схема не будет столь эффективной с аппаратной точки зрения. При длительности свечения одного знакоместа, рав- ной 10 мс, можно без использования дополнительных интерфейсных схем обслу- живать до десяти разрядов и все равно изображение будет обновляться чаще 100 раз в секунду. Программа 11.10. Отображение трехразрядного десятичного числа (динамическая ицдикация) ; Задача 1 — DISPLAY movf call BINARY,w BIN_2_BCD ; Берем двоичное значение ; Преобразуем его в 3 BCD-разряда LOOP movf HUNDREDS,w ; Берем число сотен call SVN_SEG ; Преобразуем в 7-сегментный код movwf PORTB ; Высылаем в порт В bcf PORTA,2 ; Включаем индикатор сотен call DELAY_10MS ; на 10 мс bsf PORTA,2 ; и выключаем его ; Задача 2, 6 — movf TENS,w ; Берем число десятков call SVN_SEG ; Преобразуем в 7-сегментный код movwf PORTB ; Высылаем в порт В bcf PORTA,1 ; Включаем индикатор десятков call DELAY_10MS ; на 10 мс bsf PORTA,1 ; и выключаем его movf UNITS,w ; Берем число единиц call SVN_SEG ; Преобразуем в 7-сегментный код movwf PORTB ; Высылаем в порт В bcf PORTA,0 ; Включаем индикатор единиц call DELAY_10MS ; на 10 мс bsf PORTA,0 ; и выключаем его goto LOOP ; Образуем бесконечный цикл Другим моментом, который следует учитывать при использовании динами- ческой индикации, являются электромагнитные помехи, вызываемые периоди- ческими импульсами относительно большого тока. При наличии в устройстве аналоговых цепей эти помехи могут представлять серьезную проблему, которая в какой-то степени может быть решена за счет хорошей развязки источника пита- ния.
366 Часть III. Окружающий мир Вопросы для самопроверки 11.1. Одним из недостатков схемы охранной сигнализации, приведенной на Рис. 11.12, является необходимость использования многожильного кабеля для соединения зон (8 линий плюс по одной на зону). В качестве альтерна- тивы можно было бы заменить тристабильный буфер каждой зоны микро- контроллером PIC. При этом связь базового микроконтроллера с микро- контроллерами зон осуществлялась бы по 4-проводной общей шине. Одну из линий шины можно было бы использовать для передачи сигнала квити- рования, извещающего базовый контроллер об обнаружении проникнове- ния в зоне, номер которой присутствует на остальных трех линиях шины. Покажите, как можно было бы сконфигурировать микроконтроллер PIC16F84 для использования в качестве локального контроллера зоны, об- ращая особое внимание на то, что линия квитирования должна совместно использоваться контроллерами всех зон. Можно ли уменьшить число линий до трех? Как можно добавить в схему локальные дисплеи, отображающие сработавший датчик? 11.2. К порту С микроконтроллера PIC, работающего на частоте 20 МГц, подклю- чена группа СИД. При этом каждый вывод порта подключен к линии пита- ния через резистор сопротивлением 1 кОм и к общему проводу через кон- денсатор емкостью 300 пФ. Все светодиоды выключены, и программист пытается включить 7-й и 0-й светодиоды следующим образом: bcf PORTC,7 ; Включить 7-й СИД bcf PORTC,0 ; Включить 0-й СИД Однако в действительности включается только 0-й СИД. Почему так проис- ходит? 11.3. Выводы RC[l:0] должны быть сконфигурированы как выходы, на которых после сброса по питанию присутствует лог. 0. Приведенный ниже фрагмент предполагалось использовать для сброса обоих триггеров перед переключе- нием линий порта на выход. При проверке оказалось, что результат для RC0 обратен ожидаемому. Почему так происходит, и можете ли вы исправить код, чтобы он выполнялся правильно? bcf PORTC,0 ; Сбрасываем триггер бита 0 (см. Рис. 11.3, г) bcf PORTC,1 ; Сбрасываем триггер бита 1 bcf STATUS,RP0 ; Переключаемся на 1-й банк movlw b'11111100' ‘ Делаем RC [ 1: 0] выходами movwf TRISC bcf STATUS,RPO ; Переключаемся обратно в 0-й банк 11.4. В системе необходимо управлять восемью СИД и считывать состояние вось- ми кнопок с нормально разомкнутыми контактами. В принципе для обеих целей можно использовать один порт В, который в первом случае конфигу- рируется как выход, а во втором — как вход. Можете ли вы нарисовать соот- ветствующую схему?
Глава 11. Ничего, кроме байтов 367 11.5. Доработайте цифровой компаратор из Примера 11.2 так, чтобы он сравни- вал два однобайтных числа, поступающих извне в 28-выводной микроконт- роллер PIC, причем число Р подается на порт В, а число Q — на порт С. 11.6. В беспроводной системе сбора данных с низким потреблением перевод микроконтроллера в «спящий» режим не влияет на потребление радиопере- датчика. Было предложено использовать для питания передатчика один из выводов порта. Таким образом, можно будет включать и выключать вспомо- гательные узлы по мере необходимости. Подумайте над этим. 11.7. Зависимость выходного напряжения лог. О 7QL от тока /Ol Для двух крайних значений коммерческого температурного диапазона показана на Рис. 11.17. Используя графический способ, определите максимальное значение сопро- тивления резистора, включенного последовательно с СИД, которое обеспе- чит ток в цепи не менее 20 мА во всем диапазоне температур. Чему будет ра- вен ток при —40°С? Предполагается, что падение напряжения на светодиоде равно 2 В. Рис. 11.17. Зависимость выходного напряжения низкого уровня от тока
ГЛАВА 12 ОХ УЖ ЭТИ БИТЫ! Параллельная передача данных может осуществляться с высокой скоростью и требует минимальных программных затрат для реализации. Однако имеется мно- жество приложений, в которых параллельная передача данных неприменима либо из-за удорожания аппаратной части (см., например, Рис. 11.12 на стр. 350), либо, что встречается гораздо чаще, по причине значительного удаления узлов друг от друга. В последнем случае организация множества коммуникационных каналов вместе с соответствующим интерфейсным оборудованием невозможна в прин- ципе или же требует неоправданных затрат. В таких случаях на помощь может прийти последовательная передача данных, при которой данные пересылаются побитно (по одному биту за раз) и объединяются в приемном устройстве в исход- ные байты. Здесь можно провести сравнение с параллельным портом персональ- ного компьютера, обычно используемого для подключения локальных перифе- рийных устройств (например, принтера), и последовательным или USB-портом, которые часто используются совместно с модемом для выхода через телефонную линию в сеть Интернет. В данной главе мы познакомимся с различными средствами, использующи- мися для последовательной передачи данных (в основе всех этих средств лежат специализированные сдвиговые регистры), а также с серийно выпускаемыми микросхемами, поддерживающими стандартные протоколы обмена. После про- чтения этой главы вы: • Осознаете необходимость в последовательной передаче данных. • Сможете разрабатывать последовательные порты и сопутствующее про- граммное обеспечение для обмена со стандартными параллельными пери- ферийными устройствами. • Научитесь работать с последовательными периферийными устройствами, поддерживающими протоколы SPI™ и 12С. • Поймете необходимость асинхронной последовательной передачи данных и сможете писать программные драйверы, поддерживающие этот протокол. • Научитесь использовать интегрированный модуль универсального синх- ронно-асинхронного приемопередатчика (USART) в асинхронном режиме. • Поймете причины, по которым возникает необходимость буферизации в устройствах, передающих данные на большие расстояния.
Глава 12. Ох уж эти биты! 369 В качестве примера последовательной передачи данных рассмотрим смарт- карту, которая наверняка имеется в вашем кошельке1J. В каждую такую карту встроен микроконтроллер, обычно 8-битный, который, собственно, и делает кар- ту «умной» (smart). Себестоимость изготовления этих карт не должна превышать 1 долл., причем большая часть этих денег уходит на коррозионно-стойкие кон- такты с золотым покрытием, через которые на микроконтроллер подается пита- ние и тактовый сигнал, когда карта устанавливается в считывающее устройство. Чтобы снизить требования к точности изготовления механических узлов считы- вателя и, соответственно, увеличить его надежность, количество контактов необ- ходимо свести к минимуму, а их размеры, наоборот, должны быть максимально возможными. Стандартная цоколевка такого микроконтроллера показана на Рис. 12.1. Как видно из рисунка, два контакта используются для подачи напряжения питания, еще два контакта предназначены для подачи тактового сигнала и сигнала сброса, и всего лишь один контакт используется для побитовой передачи данных в обоих направлениях. Несмотря на то что такой обмен происходит достаточно медленно, на фоне скорости системы «человек — машина» он незаметен. Кроме того, связь между считывателем/банкоматом и центральным компьютером, который может быть расположен на расстоянии нескольких тысяч миль/километров, обычно осуществляется по одной телефонной линии. Рис. 12.1. Смарт-карта Вернемся к схеме параллельного интерфейса с 3-разрядным 7-сегментным дисплеем, приведенной на Рис. 11.15 (стр. 361), в которой используются оба па- раллельных порта А и В. Хотя это вполне рабочая схема, в ней задействована большая часть выводов 18-выводных микроконтроллеров. Поскольку в данном 11 А скорее всего в вашем мобильном телефоне — это всем известная SIM-карта. — Примеч. пер.
370 Часть III. Окружающий мир случае скорость не очень критична, можно использовать более медленный способ передачи данных. Взгляните на схему с использованием последовательного интерфейса, приве- денную на Рис. 12.2. В этой схеме используются только два вывода порта. Один, называемый SDO (Serial Data Out — выход последовательных данных), использу- ется для побитовой передачи данных. Второй, названный SCK (Serial ClocK — последовательный тактовый сигнал), используется для одновременного тактиро- вания трех сдвиговых регистров, осуществляя, таким образом, побитовый сдвиг данных вправо аналогично тому, как это было изображено на Рис. 3.8 (стр. 78). Рис. 12.2. Последовательный интерфейс с 3-разрядным 7-сегментным дисплеем Каждый индикатор дисплея подключен к своему 8-битному сдвиговому ре- гистру^ 74НСТ164 (см. Рис. 2.22 на стр. 51). Эта микросхема имеет тактовый вход С1 (активный фронт — нарастающий) и два входа данных, объединенных по И. Один из этих входов может использоваться для стробирования второго, но в на- шем примере они объединены, образуя один вход данных. Также имеется вход сброса с активным НИЗКИМ уровнем для очистки содержимого регистра (в на- шей схеме на него подается ВЫСОКИЙ уровень). При необходимости для управ- ления этим входом можно задействовать еще один вывод микроконтроллера. Чтобы сменить изображение на дисплее, необходимо задвинуть в указанную цепочку регистров 24 бита данных. Чтобы разобраться, каким образом это можно сделать, обратимся снова к процедуре управления 7-сегментным индикатором из Программы 11.9 (стр. 363), в которой осуществляется преобразование двоичного числа в набор BCD-разрядов, хранящихся в регистрах HUNDREDS, TENS и 1) В этом регистре имеются отводы от всех триггеров данных, поэтому такие регистры называ- ются регистрами с последовательным входом и параллельным выходом (Serial-In Parallel-Out — SIPO).
Глава 12. Ох уж эти биты! 371 UNITS. Эти значения преобразовывались в 7-сегментный код, который затем выставлялся на 8-битную шину. Чтобы преобразовать этот процесс к последовательному виду, нам потребует- ся написать подпрограмму, которая будет по очереди выдавать все биты регистра, скажем DATA_OUT, на вывод RAO/SDO, начиная с самого левого (старшего) би- та. Одновременно на выводе RA1/SCK будут формироваться тактовые импульсы для загрузки этих битов в регистры. Алгоритм работы данной подпрограммы бу- дет следующим: 1. Выставить на SCK НИЗКИЙ уровень. 2. COUNT = 8. 3. ПОКА COUNT > О, ВЫПОЛНЯТЬ: а) Копировать старший бит DATA_OUT в SDO. б) Сдвинуть DATA_OUT на один бит влево. в) Сформировать импульс \_ на SCK. г) Декрементировать COUNT. В Программе 12.1 содержится две подпрограммы. Первая из них, названная DISPLAY, очень похожа на ту, что была написана нами в Программе 11.9, пос- кольку точно так же вызывает подпрограмму BIN_2_BCD и копирует значения кодов 7-сегментного индикатора в интерфейсный регистр. В данном случае сна- чала загружается значение единиц (поскольку этот байт в конце концов будет за- двинут в самый последний регистр цепочки), затем десятков и, наконец, сотен. Программа 12.1. Отображение трехразрадного десятичного числа с использованием последовательной передачи данных include "pl6f627а.inc" SDO SCK equ 0 equ 1 DISPLAY bcf PORTA,SCK ; Инициализируем линию SCK movf BINARY,w ; Берем двоичное значение call BIN_2_BCD ; Преобразуем его в три BCD разряда movf UNITS,w ; Берем число единиц call SVN_SEG ; Преобразуем в 7-сегментный код movwf DATA_OUT ; Копируем в регистр последовательной передачи call SPI_WRITE ; Выдвигаем его movf TENS,w ; Берем число десятков call SVN_SEG ; Преобразуем в 7-сегментный код movwf DATA-OUT ; Копируем в регистр последовательной передачи call SPI_WRITE ; Выдвигаем его movf HUNDREDS,w ; Берем число сотен call SVN_SEG ; Преобразуем в 7-сегментный код movwf DATA-OUT ; Копируем в регистр последовательной передачи call SPI_WRITE ; Выдвигаем его return
372 Часть III. Окружающиймир • •k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k'k t ; * ФУНКЦИЯ : Побитно передает байт данных, начиная со * ; * старшего бита * ; * ВХОД : Байт данных в DATA_OUT * ; * ВЫХОД : DATA_OUT обнуляется * • ****************************************************** / ; Задача 1 SPI_WRITE bcf PORTA,SCK ; В режиме ожидания на линии SCK - НИЗКИЙ : ; Задача 2 movlw 8 ; Инициализируем счетчик цикла movwf COUNT ; Задача 3, a : и 3, 6 LOOP bcf PORTA,SDO ; Выставляем на линию данных 0 btfsc DATA_OUT,7 ; Пропускаем, ЕСЛИ старший бит = 0 bsf PORTA,SDO ; ИНАЧЕ выставляем на линию данных 1 rlf DATA_OUT,f ; Сдвигаем байт данных на один бит влево ; Задача 3, в bsf PORTA,SCK ; Формируем тактовый импульс bcf PORTA,SCK ; Задача 3, г decfsz COUNT,f ; Декрементируем счетчик goto LOOP ; и повторяем, пока он не станет равным 0 return Собственно последовательная передача данных осуществляется подпрограм- мой SPI_WRITE, работающей по приведенному выше алгоритму. В подпрограм- ме проверяется 7-й бит содержимого регистра DATA_OUT и в соответствии с его значением на вывод RA0 выставляется ВЫСОКИЙ или НИЗКИЙ уровень. Затем на выводе RA1 формируется положительный импульс __Г~\_ для загрузки оче- редного бита в цепочку сдвиговых регистров, после чего байт данных сдвигается влево. Этот процесс повторяется 8 раз. На все это требуется не более 87 машин- ных циклов (конкретная цифра слегка зависит от значения байта данных). Таким образом, на полное обновление изображения 3-разрядного дисплея уйдет около 120 мкс при частоте процессора 8 МГц (не учитывая время, затраченное на преоб- разование данных). В Программе 12.2 приведена одна из возможных реализаций данной подпро- граммы на языке Си. Функция. spi_write () 8 раз выдает 7-й бит переданного ей байта данных на вывод SDO и сдвигает значение этого байта влево. Предпола- гается, что оба вывода последовательного интерфейса SPI уже определены как соответствующие линии порта ввода/вывода микроконтроллера. Программа 12.2. Реализация подпрограммы SPI_WRITE на Си void spi_write(int datum) { int k;
Глава 12. Ох уж эти биты! 373 for(к=0;к<8;к++) { if((datum & 0x80)) {SDO - 1;} /* Проверяем 7-й бит и ЕСЛИ TRUE, выставляем 1 */ else {SDO - 0;} /* ИНАЧЕ выставляем 0 */ SCK =1; /* Загружаем бит в цепочку регистров */ SCK = 0; datum = datum << 1; /* Сдвигаем байт данных влево и повторяем 8 раз */ } При работе с длинной цепочкой сдвиговых регистров скорость загрузки дан- ных можно немного увеличить, если выделить каждому регистру собственные ли- нии данных, при этом тактовые входы всех регистров будут подключены к выводу SCK. Также можно подключить входы данных всех регистров к одной-единствен- ной линии данных и управлять регистрами с помощью отдельных сигналов разре- шения. Последний способ использован в схеме, показанной на Рис. 12.7. У нашей схемы, использующей сдвиговые регистры, есть один недостаток: значения, появляющиеся на выходах регистров во время их загрузки (в нашем случае в течение 23 тактовых импульсов), некорректны. Разумеется, в данном конкретном случае из-за инерционности зрения мы просто не заметим эти крат- ковременные изменения в свечении индикаторов. Однако иногда такое поведе- ние схемы оказывается неприемлемым, поэтому к выходам сдвиговых регистров необходимо подключить буферы на D-триггерах или защелках. Загрузка содер- жимого регистров в эти буферы будет осуществляться после завершения переда- чи данных, в результате чего изображение на дисплее будет меняться только еди- ножды. Чтобы не использовать отдельные буферные регистры, в большинстве уст- ройств, рассчитанных на последовательную передачу данных, имеется встроен- ный регистр с параллельным входом/выходом. В качестве примера можно при- вести микросхему 74НСТ595, изображенную на Рис. 12.3. Эта микросхема пред- ставляет собой стробируемый сдвиговый регистр, на выходе которого имеется встроенный 8-битный PIPO-регистр. По нарастающему фронту сигнала на выводе RCK (Register ClocK) содержимое сдвигового регистра выставляется на параллельные выходы. В микросхеме также выведен выход последнего триггера сдвигового регистра, что позволяет объединять эти регистры в цепочку любой длины. В этом случае на все выводы RCK может подаваться один и тот же строби- рующий импульс для одновременного обновления выходов всех микросхем. Примером ситуации, когда пульсации данных могут быть нежелательными, является преобразование цифровых данных в аналоговый сигнал. В схеме на Рис. 12.3 преобразование осуществляется с помощью микросхемы ЦАП DAC0800 фирмы National Semiconductor. Выходное налоговое напряжение является линей- ной функцией от 8-битного цифрового входа и изменяется от —9.96 В для входно- го значения Ь’00000000’ до +9.96 В для значения Ь’ШППГ (см. Рис. 14.17 на стр. 527).
374 Часть III. Окружающий мир 4.7к RA2 RA1 RAO а) Интерфейс SCI с микросхемой 74НСТ595 б) Обозначение по ANSI/IEC Рис. 12.3. Последовательный интерфейс с микросхемой ЦАП, реализованный посредством сдвигового регистра 74НСТ595 Благодаря наличию регистра 74НСТ595 состояние входа ЦАП не меняется до тех пор, пока не будет загружен новый байт и микроконтроллер не сформирует на выводе RCK положительный импульс. Таким образом, обеспечивается отсутствие шумов в выходном аналоговом сигнале. Аналогичным образом можно реализовать прием данных в последовательном режиме, используя сдвиговые регистры с параллельным входом и последователь- ным выходом (PISO-регистры). Схема, приведенная на Рис. 12.4, представляет собой последовательный вариант системы охранной сигнализации с Рис. 11.12 (стр. 350), использующий только три линии для подключения всех восьми групп датчиков. Это гораздо меньше 16 линий, задействованных нами в исходной схеме. Каждая группа датчиков подключена к 8-битному сдвиговому PISO-регистру 74НСТ165. При этом последовательный выход каждого регистра подключен к последовательному входу следующего регистра. После загрузки данных в регистр их можно будет побитно передать на вывод RA1 порта A (SDI). В данном конк- ретном случае многозонной системы охранной сигнализации после каждого восьмого сдвига полученный байт можно проверять на ненулевое значение и осу- ществлять соответствующие действия. Для управления отображением активной зоны в схеме на Рис. 12.4 тоже ис- пользуется один выход микроконтроллера. Поскольку и входной (SDI), и выход-
PIC Рис. 12.4. Вариант многозонной системы охранной сигнализации с последовательным интерфейсом Глава 12. Ох уж эти биты! 375
376 Часть III. Окружающий мир ной (SDO) каналы используют один и тот же тактовый сигнал SCK, то одновре- менно с приемом данных будет осуществляться и их передача. И наоборот, пере- дача данных через выходной порт приведет к побитному приему данных из регистров зон. В данном случае это не страшно, поскольку микросекундные из- менения в свечении ламп совершенно незаметны, и после загрузки в регистр ин- дикаторов требуемого значения все нормализуется. Когда же такое взаимное вли- яние операций приема и передачи нежелательно, то либо во время считывания данных с вывода SDI на выводе SDO должны всегда присутствовать требуемые данные, либо необходимо использовать стробируемый регистр, например 74НСТ595, для одновременного вывода всех битов данных. В качестве альтерна- тивного решения можно также задействовать отдельные линии тактовых сиг- налов. Подпрограмма для работы с последовательным интерфейсом SPI_READ яв- ляется точной противоположностью подпрограммы SPI_WRITE, код которой был приведен в Программе 12.1, и реализует следующий алгоритм: 1. Выставить на SCK НИЗКИЙ уровень. 2. COUNT = 8. 3. ПОКА COUNT > О, ВЫПОЛНЯТЬ: а) Сформировать на линии SCK импульс _J \_. б) Сдвинуть содержимое регистра DATA_IN на один бит влево. в) Скопировать значение с вывода SDI в старший бит регистра DATA_IN. г) Декрементировать COUNT. Этот алгоритм похож на приведенный ранее (см. стр. 371), за исключением того, что регистр DATA_IN сдвигается влево, а состояние вывода SDI становится значением 0-го бита. После восьмого цикла «такт — сдвиг — проверка» содержи- мое регистра DATA_IN представляет собой байт данных, считанный с последова- тельного порта. При этом первый принятый бит окажется старшим битом итого- вого значения. Подпрограмма SPI_READ, текст которой приведен в Программе 12.3, похожа на подпрограмму вывода SPI_write из Программы 12.1. И их действительно можно объединить для того, чтобы одновременно передавать и принимать дан- ные. Такой тип обмена называется полнодуплексным (или просто дуплексным) в от- личие от полудуплексного, при котором передача информации в каждый момент времени осуществляется только в одном направлении. Последовательный обмен, при котором поток данных может передаваться только в одном фиксированном направлении, называется симплексным. Программа 12.3. Подпрограмма приема байта по последовательному каналу • •k-k-k-k-k-k-k'k'k-k'k'k'k'k-k'k-k'k-k-k-k-k-k'k'k-k-k'k-k'k'ic'ic'ic'k'k'ic'ic'k'ic'ic'ic'ic'ic'k'ic'k'iclc'ic'k'ic'ic'ic'k'k ; * ФУНКЦИЯ : Побитно принимает байт данных, начиная со * ; * старшего бита * ; * ВХОД : Нет * ; * ВЫХОД : Принятый байт в DATA_IN; COUNT =0 *
Глава 12. Ох уж эти биты! 377 ; Задача 1: Выставляем на SCK НИЗКИЙ уровень SPI_READ bcf PORTA,SCK ; В режиме ожидания на линии SCK - НИЗКИЙ уровень ; Задача 2: COUNT=8 movlw 8 ; Инициализируем счетчик цикла movwf COUNT ; Задача 3: ПОКА COUNT>0, ВЫПОЛНЯТЬ: ; Задача 3, а: Формируем импульс SCK SER_IN_LOOP bsf PORTA,SCK bcf PORTA,SCK ; Задача 3, б: Сдвигаем байт данных влево bcf STATUS,С ; Обнуляем флаг переноса rlf DATA_IN,f ; Сдвигаем байт влево ; Задача 3, в: ЕСЛИ SDI = 1, ТО устанвливаем 0-й бит (самый правый) btfsc PORTA,SDI ; Пропускаем, ЕСЛИ SDI == 0 bsf DATA_IN,0 ; ИНАЧЕ заносим в 0-й бит 1 ; Задача 3, г: Декрементируем COUNT и повторяем задачу 3, пока COUNT > 0 decfsz COUNT,f ; Декрементируем счетчик goto SER_IN_LOOP ; и повторяем, пока он не станет равным 0 return Си-вариант подпрограммы, приведенный в Программе 12.4, использует тот же алгоритм, что и его ассемблерный предшественник. Обратите внимание, как для установки 0-го бита переменной data_IN используется Си-оператор ИЛИ (|) с константой b’0000001 ’. Аналогично, операция И с константой b’ll 111110’ сбра- сывает 0-й бит. В компиляторе CCS имеются специальные нестандартные функ- ции bset (DATA_IN, 0) и bclr (DATA_IN, 0), которые можно использовать для установки или сброса любого бита переменной и которые при необходимости из- менения единственного бита часто более эффективны, чем использование логи- ческих операторов. Программа 12.4. Реализация подпрограммы SPI_READ на Си unsigned int spi_read() { int k; for(k=0;k<8;k++) /* Повторяем 8 раз */ { SCK =1; /* Формируем тактовый импульс, по которому */ SCK =0; /* ведомый передает бит на SDI */ DATA_IN - DATA_IN << 1; /* Сдвигаем на один бит влево */ if(SDI) {DATA_IN = DATA_IN | 0x01;} /* Сбрасываем бит, если SDI =0 */ else {DATA_IN - DATA_IN & OxFE;} /* ИНАЧЕ устанавливаем его */
378 Часть III. Окружающий мир } return DATA_IN /* Возвращаем принятый байт */ } Последовательный протокол, как две капли воды похожий на только что рас- смотренный нами, известен как последовательный периферийный интерфейс (SPI™)1). Существует еще один похожий последовательный протокол — Microwire, но он несколько отличается от рассмотренного2^ Интерфейс SPI имеется в большинстве микроконтроллеров, и он в достаточ- ной степени стандартизован, чтобы производители могли выпускать широкий ас- сортимент микросхем, предназначенных для непосредственного (без использова- ния дополнительных сдвиговых регистров) подключения к этой шине. Возьмем в качестве примера микросхему сдвоенного цифро-аналогового пре- образователя (ЦАП) МАХ549А, показанную на Рис. 12.5, которая работает при напряжении питания от +2.5 до +5.5 В. Типичный ток потребления в рабочем ре- жиме составляет около 150 мкА/канал при напряжении питания 5 В. Кроме того, один или оба модуля ЦАП можно отключить для уменьшения тока потребления до уровня менее 1 мкА. Максимальная частота шины SPI составляет 12.5 МГц. И все эти возможности заключены в крошечном 8-выводном корпусе — сравните с 20-выводной микросхемой МАХ506, изображенной на Рис. 14.16 (стр. 526), ко- торая рассчитана на подключение к параллельному порту микроконтроллера. Из упрощенной функциональной схемы МАХ549А, приведенной на Рис. 12.5, видно, что в микросхеме имеется встроенный 16-битный сдвиговый регистр, так- товый вход которого соединен с выводом SCLK микросхемы, а вход данных — с выводом DIN. Поэтому данные в этот регистр могут загружаться в соответствии с обычным протоколом SPI. Дополнительные восемь разрядов регистра использу- ются для хранения четырех управляющих битов, выполняющих следующие функции: АО Разрешает работу входного PIPO-регистра канала А, тактирование которого осу- ществляется по нарастающему фронту сигнала на выводе СЕ. А1 Разрешает работу входного PIPO-регистра канала В, тактирование которого осу- ществляется по нарастающему фронту сигнала на выводе СЕ. С1 Управляет работой обоих регистров ЦАП; при установленном бите содержимое этих регистров одновременно обновляется по нарастающему фронту _j— сигна- ла на выводе СЕ. *) SPI™ — торговая марка, принадлежащая фирме Motorola. 2) Microwire™ — торговая марка, принадлежащая National Semiconductor Corporation.
Глава 12. Ох уж эти биты! 379 Рис. 12.5. Микросхема сдвоенного ЦАП с интерфейсом SPI МАХ549А (Maxim)
380 Часть III. Окружающий мир С2 Установка этого бита переводит ЦАП, определяемый битами АО и/или А1, в «спя- щий режим». При этом источник опорного напряжения Kref отключается от ре- зистивной цепи выбранного ЦАП (см. Рис. 14.14 на стр. 516), в результате чего ток потребления ЦАП уменьшается до значения менее 1 мкА (содержимое внут- ренних регистров ЦАП остается неизменным). Между каждым из модулей ЦАП и сдвиговым регистром имеется 2-уровне- вый регистровый конвейер. На первом уровне расположены регистры INREGx, разрешение которых осуществляется установкой бита АО (канал А) или А1 (канал В) в 1. При установленном бите данные, находящиеся в 1-м байте сдвиго- вого регистра, можно загрузить в регистр, подав на вывод СЕ отрицательный им- пульс. Однако это значение не появится на параллельном входе ЦАП до тех пор, пока тактовый импульс не будет подан на регистры второго уровня — DACREGx. Разрешение этих регистров осуществляется установкой бита С1 в 1, а тактирова- ние — подачей отрицательного импульса на вывод СЕ микросхемы. То есть мы можем загрузить один байт, скажем, в ЦАП А, а другой — в ЦАП В. Содержимое же обоих регистров DACREGx обновляется одновременно, что приводит к одно- временному изменению сигналов на выходах VoutA и V0UtB (см. Программу 12.5). Это можно сделать даже в то время, когда МАХ549А находится в режиме пони- женного потребления, поскольку при переходе в данный режим содержимое ре- гистров не изменяется. Из всего сказанного становится понятно, что каждая транзакция между микроконтроллером и микросхемой ЦАП состоит из пересыл- ки двух 8-битных значений | Control | Data |, сопровождающихся подачей нарас- тающего фронта _J на вывод СЕ. Для примера, перешлем содержимое регистра h’20’ в ЦАП А, а содержимое регистра h’21’ — в ЦАП В. Затем перегрузим переданные значения в регистры ЦАП, в результате чего на выводах VoutA и VoutB одновременно появятся напряже- ния, эквивалентные содержимому регистров h’20’ и h’21’ соответственно. В общей сложности для выполнения указанной задачи нам придется пере- слать четыре байта: 1. Управляющий байт 1: Ь’ХХХООХОГ Рабочий режим, обновить канал А, не формировать аналоговый сигнал. 2. Байт данных 1: Содержимое регистра h’20’. 3. Подаем импульс —\_/~ на вывод СЕ. 4. Управляющий байт 2: Ь’ХХХО 1X10’ Рабочий режим, обновить канал В, формировать аналоговый сигнал на обоих каналах. 5. Байт данных 2: Содержимое регистра h’21 ’. 6. Подаем импульс —\_/~ на вывод СЕ. Код, осуществляющий указанные операции, приведен в Программе 12.5. Для передачи каждого из четырех байтов используется подпрограмма SPI_write,
Глава 12. Ох уж эти биты! 381 после передачи каждой пары байтов | Control | Data | формируется импульс на выводе СЕ. В управляющем байте второй пары бит С1 устанавливается в 1, в результате чего одновременно с загрузкой входного регистра канала В оба байта данных перегружаются в регистры ЦАП. Программа 12.5. Работа с двухканальным ЦАП МАХ549 СЕ equ 2 . ******************************************** *«* ****************** / ; * ФУНКЦИЯ : Загружает новые данные в каналы А и В ЦАП МАХ549А * ; * и осуществляет одновременное обновление выходов * ; * РЕСУРСЫ : Подпрограмма SPI_WRITE * ; * ВХОД : Значение канала А - регистр h’20', канала В - h'21'* ; * ВЫХОД : Изменяется состояние обоих аналоговых выходов * . **************************************************************** I МАХ549А movlw b'00000001' ; 1-й управляющий байт movwf DATA_0UT ; Помещаем в требуемый регистр call SPI_WRITE ; и пересылаем в МАХ549А movf CHANNEL_A,W ; Берем значение канала А movwf DATA_0UT ; Помещаем в требуемый регистр call SPI_WRITE ; и пересылаем в МАХ549А bsf PORTA,CE ; Формируем импульс на СЕ bcf PORTA,CE movlw b'OOOOlOlO' ; 2-й управляющий байт movwf DATA_0UT ; Помещаем в требуемый регистр call SPI_WRITE ; и пересылаем в МАХ549А movf CHANNEL_B,w ; Берем значение канала В movwf DATA_0UT ; Помещаем в требуемый регистр call SPI_WRITE ; и пересылаем в МАХ549А bsf PORTA,CE ; Формируем импульс на СЕ bcf PORTA,CE return Если мы снимем осциллограммы с трех выводов микросхемы МАХ549А, то увидим сигналы, похожие на те, что изображены на Рис. 12.6 (на рисунке показа- на передача 1-й пары байтов | Control | Data |)- Во время передачи на выводе СЕ удерживается НИЗКИЙ уровень, а данные побитно загружаются во внутренний сдвиговый регистр. После 2-го байта, т.е. после 16-го тактового импульса, подача на вывод СЕ напряжения ВЫСОКОГО уровня активирует регистры ЦАП, задан- ные в управляющем байте. Из Рис. 12.6 видно, что изменение состояния линии DIN, управляемой выво- дом SDO микроконтроллера, происходит перед формированием активного на- растающего фронта на выводе SCK. Очевидно, что состояние должно измениться за определенное время до появления фронта и удерживаться в течение короткого интервала времени после него. В документации на МАХ549А сказано, что мини- мальное время установки rDS составляет 30 нс, а время удержания rDH — 10 нс. На-
382 Часть III. Окружающий мир __ Инструкция выполнена.-. (выв. 3) |С <в^к5> ГПППППППЕ! jmirnriRTlRl сыв.4) х,х,х ,С2 ,C1 ,х ,A11 Управляющий байт №1 Байт 1 = (регистр h’20’) Рис. 12.6. Передача данных в МАХ549А по шине SPI ша схема будет удовлетворять этим требованиям в любом случае, поскольку даже при тактовой частоте 20 МГц длительность машинного цикла микроконтроллера PIC будет равна 200 нс. Благодаря наличию у микросхем входа СЕ к линиям SCK/SDO можно под- ключить несколько ЦАП — последовательные данные будут заноситься только в ту микросхему, на выводе СЕ которой будет присутствовать НИЗКИЙ уровень. На Рис. 12.7 изображены две микросхемы МАХ549А, подключенные к одной ши- не SPI и формирующие 4 аналоговых выхода. А, подключив к выводам RA[3:2] дешифратор 2-4, мы сможем управлять четырьмя МАХ549А, используя для этого всего четыре вывода порта. Рис. 12.7. Подключение нескольких МАХ549А к одной шине SPI Большинство микроконтроллеров среднего уровня и все микроконтроллеры старшего уровня имеют в своем составе модуль синхронного последовательного пор- та (SSP), который реализует, помимо всего прочего, протокол SPI. В зависимос- ти от функциональной насыщенности конкретной модели микроконтроллера су- ществует три очень похожих исполнения этого модуля. Первое из этих исполне- ний называется базовым SSP (BSSP), из которого позже «вырос» обычный SSP. В самых последних моделях появился модуль MSSP (ведущий синхронный последо- вательный порт). В этом модуле вводится несколько дополнительных опций фор- мирования тактового сигнала SPI, однако гораздо большее значение имеет тот
Глава 12. Ох уж эти биты! 383 факт, что данный модуль может использоваться в качестве ведущего шины 12С (отсюда и название). Несколько упрощенная структурная схема модуля MSSP, сконфигурирован- ного для работы по протоколу SPI, приведена на Рис. 12.8. Основным узлом мо- дуля MSSP является регистр специального назначения SSPBUF, расположенный по адресу ЬТЗ’. Байт данных, загруженный в этот РСН, автоматически перегру- жается в сдвиговый регистр модуля SSPSR и побитно выдается на вывод RC5/SDO микроконтроллера. Одновременно с этим восемь битов данных считы- ваются с вывода RC4/SDI. После завершения указанных операций принятый байт автоматически пересылается в регистр SSPBUF, откуда его можно считать. Это индицируется установкой флага BF (Buffer Full) в регистре состояния SSPSTAT, формат которого приведен на Рис. 12.9. После чтения регистра SSPBUF флаг BF автоматически сбрасывается. Рис. 12.8. Модуль MSSP, сконфгурированный для работы по протоколу SPI. Выводы модуля задействуют линии параллельного порта С В отличие от параллельных портов ввода/вывода, конфигурирование и конт- роль состояния интерфейсных модулей микроконтроллеров, как правило, осу- ществляются с помощью ряда соответствующих регистров управления и состоя- ния. Помимо этого используются биты масок и флагов прерываний, расположен- ные либо в регистре INTCON, либо в одном или двух регистрах разрешения прерываний от периферийных устройств и регистрах флагов прерываний от пе- риферийных устройств, аналогичных показанным на Рис. 7.6 (стр. 224). Конфи- гурирование регистров управления, состояния и прерываний периферийных уст- ройств обычно выполняется в той же части программы, в которой производится конфигурирование параллельных портов ввода/вывода. Так как интерфейсные
384 Часть III. Окружающий мир модули в обязательном порядке задействуют выводы параллельных портов, то эти выводы часто необходимо конфигурировать даже в том случае, если порты вво- да/вывода в программе не используются. Конфигурация линий ввода/вывода пе- реопределяется автоматически при включении периферийного модуля или же должна явно задаваться самим программистом. К сожалению, ответ на этот воп- рос далеко не всегда очевиден, поэтому для получения точной информации необ- ходимо обращаться к документации на конкретный микроконтроллер. Но вернемся к нашему модулю MSSP. На Рис. 12.9 показаны установки ре- гистров SSPCON и SSPSTAT, используемые при работе в режиме SPL Ну, а для подключения к внешним устройствам в этом режиме в общей сложности задейс- твуется четыре вывода. RC5/SDO Бит TRISC[5] должен быть сброшен для переключения этого вывода в режим вы- хода. RC4/SDI Этот бит конфигурируется модулем MSSP как вход независимо от состояния со- ответствующего бита TRISC[4]. RC3/SCK При работе в качестве ведущего бит TRSC[3] должен быть сброшен, поскольку на этом выводе формируется тактовый сигнал. И, наоборот, при работе в режиме ве- домого 6htTRISC[3] должен быть установлен, чтобы принимать тактовый сигнал от ведущего. RA5/SS В режиме ведомого Ь’0100’ этот вывод должен быть сконфигурирован как вход (TRISA[5] = 1), чтобы другой ведущий мог выбрать данное устройство. При любом сбросе оба регистра SSPCON и SSPSTAT очищаются, а внутрен- ний счетчик битов обнуляется. При этом модуль отключен, и если программист собирается использовать MSSP, то он должен задать соответствующие значения различных управляющих битов1). SSPEN Установка бита SSPCON[5] в 1 разрешает работу модуля последовательного синх- ронного порта. Если модуль выключен, то соответствующие выводы могут ис- пользоваться в качестве линий-обычных параллельных портов ввода/вывода. SSPM[3:0] Четыре бита выбора режима работы модуля, расположенные в SSPCON[3:0], ис- пользуются для выбора протокола обмена, а также различных опций работы веду- щего/ведомого. На Рис. 12.9 указано шесть комбинаций этих битов, относящихся к протоколу SPI. Некоторые из управляющих битов из-за недостатка места располагаются в регистре STATUS!
7 6 SMP (R/WO) CKE (R/WO) (RO) SSPSTAT 4 (RO) 3 (RO) (RO) Выбор фронта тактового сигнала 0: Данные передаются по переходу тактового сигнала из пассивного состояния в активное 1: Данные передаются по переходу тактового сигнала из активного состояния в пассивное h’94’ 0 BF (RO) Регистр состояния модуля MSSP Буфер чтения полон Фаза выборки бита (режим ведущего) 0: Данные с вывода SDI считываются в конце периода; или режим ведомого 1: Данные с вывода SDI считываются в середине периода SSPCON h’14' R — чтение W — запись () — состояние после сброса 7 WOOL (R/WO) 6 5 4 3 SSPOV SSPEN CKP SSPM3 (R/WO) (R/WO) (R/WO) (R/WO) 2 1 0 SSPM2 SSPM1 SSPMO (R/WO) (R/WO) (R/WO) Регистр управления модуля MSSP Режим SSP (SSPM [3:0]) ( 0000: Режим ведущего, тактовый сигнал — Fj,sc/4 0001: Режим ведущего, тактовый сигнал — /bsc/16 , 0010: Режим ведущего, тактовый сигнал — fisc/64 0011: Режим ведущего, тактовый сигнал —1/2 частоты Таймера 2 0100: Режим ведомого, вывод SS подключен к MSSP* \ 0101: Режим ведомого, вывод SS не подключен к MSSP** * Бит СКЕ должен быть сброшен в этом режиме * * Бит SMP должен быть сброшен в этом режиме ; ; выбор полярности тактового сигнала ; : 0: Пассивный уровень — НИЗКИЙ • • 1: Пассивный уровень — ВЫСОКИЙ • - — - Включение модуля MSSP - Переполнение приемника SSP (режим ведомого) Обнаружен конфл икт записи (была предпринята попытка записи Глава 12. Ох уж эти биты! в SSPBUF во время передачи предыдущего байта) Рис. 12.9. Регистры управления (SSPCON) и состояния (SSPSTAT) модуля MSSP при его работе в режиме SPI
386 Часть III. Окружающий мир Режимы ведущего (а их четыре) отличаются друг от друга только значением частоты тактового сигнала и ее источниками. В трех из этих режимов тактовый сигнал формируется из тактового сигнала микроконтроллера. Например, при ис- пользовании резонатора с частотой 20 МГц частота сигналов на выводе SCK мо- жет быть равна 5 МГц, 1.25 МГц и 312.5 кГц (период 200 нс, 800 нс и 3.2 мкс соот- ветственно). В последнем же из режимов частота тактового сигнала шины SPI равна половине частоты сигнала, формируемого при переполнении Таймера 2 (см. Рис. 13.8 на стр. 474). Этот режим используется, когда требуется очень низкая скорость передачи данных. При работе модуля в качестве ведомого тактовый сигнал поступает на вывод SCK извне от внешнего ведущего устройства. Кроме того, ведущий может управ- лять выводом SS для выбора одного из нескольких ведомых (см. Рис. 12.12). SSPOV При работе модуля в режиме ведомого единица в этом бите указывает на то, что новый байт был принят раньше, чем считан предыдущий, т.е. то, что произошла потеря одного или нескольких байтов. Этот бит не сбрасывается автоматически при чтении регистра SSPBUF и должен быть сброшен самостоятельно, из про- граммы. В режиме ведущего данный бит не используется. WCOL Если программа попытается записать данные в регистр SSPBUF до завершения передачи предыдущего байта, то операция записи будет прервана, а бит конфлик- та записи WCOL установится в 1. В случае обнаружения такой ситуации необхо- димо сбросить этот бит программно и повторить операцию записи. СКР, СКЕ, SMP Эти три бита предназначены для задания моментов формирования фронтов так- тового сигнала, по которым будет осуществляться считывание битов с линии дан- ных и выставление их на линию. Чтобы проиллюстрировать использование указанных битов, рассмотрим си- туацию, когда модуль MSSP работает в режиме ведущего. Как ведущий, модуль полностью управляет тактовым сигналом на линии SCK, который используется для тактирования сдвигового регистра как передатчика, так и приемника. Для ве- домого-приемника активный фронт тактового сигнала должен формироваться в тот момент, когда значение бита данных, выставленного ведущим на вывод SDO, станет стабильным. А сдвиговый регистр ведомого-передатчика должен тактиро- ваться таким образом, чтобы выставляемые им значения битов данных были ста- бильны в то время, когда ведущий считывает их с вывода SDI. Такая схема изоб- ражена на Рис. 12.12, где ведущее устройство на базе микроконтроллера PIC мо- жет взаимодействовать с одним из двух ведомых устройств, каждое из которых может одновременно принимать и передавать данные. Этими ведомыми устрой- ствами могут быть другие микроконтроллеры PIC (как показано на рисунке) или любые SPI-совместимые устройства. Процесс передачи каждого байта состоит из восьми фаз, как показано на Рис. 12.11. В любом случае очередной бит данных Dn выставляется на вывод SDO
Глава 12. Ох уж эти биты! 387 вскоре (как правило, не позже чем через 50 нс) после начала соответствующей фазы тактового сигнала (см. верхнюю часть рисунка). Удаленный ведомый-при- емник должен «защелкнуть» это значение в середине фазы. Подобным образом удаленный ведомый-передатчик должен своевременно выдавать на вывод SDI ве- дущего очередной бит своих данных dn, чтобы ведущий мог его считать. На Рис. 12.10 изображены две часто встречающиеся ситуации. Диаграммы сигнала SCK в верхней части рисунка соответствуют случаям, когда передатчики и приемники используют различные активные фронты тактового сигнала. Пос- кольку тактирование передатчика осуществляется в начале каждой фазы, его дан- ные необходимо считывать в середине фазы, для чего бит SMP должен быть уста- новлен в 1. SDI SDI 1:0 SCK 1:1 SCK SDI SDI Рис. 12.10. Прием и передача данных удаленными ведомыми устройствами Ведомый SDO SCK SDI SDO SCK SDI SDO SCK SDO SCK
388 Часть III. Окружающий мир СКЕ:СКР = О:О Когда тактирование удаленного передатчика осуществляется по нарастаю- щему фронту _Г~ сигнала SCK, ведущий должен считывать его данные с вывода SDI в середине фазы. Эти данные должны присутствовать на выводе как минимум за 100 нс до наступления указанного момента и удерживаться в течение, как минимум, 100 нс после. Удаленный ведомый-приемник считы- вает переданные ему данные с вывода SDO по спадающему фронту сигнала SCK также в середине фазы. В спецификации протокола SPI такой ре- жим работы называется режимом 0,1 (или просто режим 1). СКЕ:СКР = 0:1 Режим 1,1 (режим 3) похож на предыдущий, за исключением того, что такти- рование передатчика осуществляется по спадающему фронту , а прием- ника — по нарастающему __Г~. Диаграммы в нижней части Рис. 12.10 соответствуют ситуации, когда так- тирование сдвиговых регистров удаленных передатчиков и приемников осу- ществляется по одному и тому же активному фронту. Поскольку передатчик выдает данные в середине фазы, то данные от него, присутствующие на выво- де SDI, должны считываться в конце фазы, для чего бит SMP должен быть сброшен в 0. СКЕ:СКР = 1:0 Режим 0,0 (режим 0) используется тогда, когда тактирование ведомых-пере- датчиков и ведомых-приемников производится одновременно в середине фазы по нарастающему фронту _Г~ • К этому моменту значение бита дан- ных от ведущего Dn станет стабильным, что позволит ведомому устройству считать его. Соответственно, данные от ведомого устройства должны быть готовы для считывания к концу фазы. СКЕ:СКР = 1:1 В режиме 1,0 (режим 2) тактирование ведомых-передатчиков и ведомых- приемников осуществляется в середине фазы по спадающему фронту тактового сигнала. Когда модуль MSSP работает в качестве ведомого, тактовый сигнал на него поступает от внешнего устройства. Как и прежде, любые данные, предварительно загруженные в регистр SSPBUF, будут в начале каждой фазы тактового сигнала выставляться на вывод SDO. При этом биты СКЕ и СКР все равно необходимо устанавливать в зависимости от того, по какому фронту тактового сигнала уда- ленный передатчик выставляет свои данные и какой фронт для удаленного при- емника является активным. Установки указанных битов зависят также от того, когда ведущий выставляет первый бит своих данных Dn — до первого тактового импульса или после. В любом случае модуль MSSP, сконфигурированный как ве- домый, должен считывать значения этих битов со своего вывода SDI в конце каж- дой фазы, т.е. бит SMP должен быть сброшен в 0. Чтобы посмотреть подробные
Глава 12. Ох уж эти биты! 389 временные диаграммы, советую вам обратиться к фирменной документации на используемый микроконтроллер. Когда микроконтроллер PIC работает в качестве ведомого SPI-устройства, то для его выбора удаленным ведущим используется вывод выбора ведомого SS. При появлении на выводе SS напряжения ВЫСОКОГО уровня, даже в середине тран- закции, внутренний счетчик битов сбрасывается в 0. Кроме того, вывод SDO пе- реключается в режим выхода с открытым стоком, чтобы дать возможность друго- му устройству захватить линию. BF, SSPIF После считывания микроконтроллером полного фрейма из восьми битов и пере- сылки его в буферный регистр SSPBUF бит BF устанавливается в 1, извещая о приеме нового байта. При этом также устанавливается флаг прерывания SSPIF в регистре PIR1 (см. Рис. 7.6 на стр. 224), и, если установлен соответствующий бит маски прерывания SSPIE в регистре PIE1, генерируется прерывание. Если модуль MSSP работает в качестве ведомого, а микроконтроллер находится в «спящем» режиме, то данное прерывание можно использовать для «пробуждения» микро- контроллера. Это возможно благодаря тому, что тактовые импульсы на выводе SCK формируются внешним ведущим устройством, и поэтому микроконтролле- ру не обязательно находиться в активном режиме, т.е. системный генератор мо- жет быть выключен. При считывании нового байта из регистра SSPBUF бит BF автоматически сбрасывается. Если же новое значение своевременно не считать, то принятый байт будет потерян и, сигнализируя об этом, будет установлен флаг SSPOV. Флаг прерывания SSPIF необходимо сбрасывать самостоятельно в процедуре обработ- ки прерывания. Вооружившись информацией, приведенной на Рис. 12.8 и Рис. 12.9, мы те- перь легко можем перечислить операции, которые необходимо выполнить для осуществления передачи байта и/или приема нового байта: 1. Сконфигурировать модуль SSP; • Сделать SCK, RC5/SDO выходами, a RC4/SDI и, при необходимости, RA5/SS — входами. • Выбрать режим работы модуля (ведущий/ведомый) и источник тактового сигнала. • Задать активные фронты с помощью битов СКР, СКЕ и SMP. • Разрешить работу модуля SSP установкой бита SSPEN. 2. Загрузить байт в регистр SSPBUF для инициирования передачи. 3. Если бит WCOL = 1, то сбросить его и перейти к п. 2. 4. Ждать установки бита ВЕ 5. Скопировать полученный байт данных из SSPBUF, при этом бит BF будет сброшен автоматически. Для иллюстрации описанного процесса рассмотрим подпрограмму SPI_IN_OUT, которая объединяет в себе функции SPI_READ и SPI_WRITE, т.е. передает содержимое регистра DATA_OUT и возвращает полученный байт в ре-
390 Часть III. Окружающий мир гистре DATA_IN. Предполагается, что сдвиговые регистры удаленного устрой- ства «защелкиваются» по нарастающему фронту, т.е. используется режим SPI 0,0. Реализация этой подпрограммы зависит от установок модуля MSSP, заданных во время инициализации основной программы. В следующем фрагменте кода мы переводим модуль MSSP в режим ведущего и задаем тактовую частоту шины SPI равной/озс/4: include "pl6f877.inc MAIN bsf STATUS,RPO movlw b111010111' movwf TRISC movwf b'11000000' movwf SSPSTAT ; Переключаемся в 1-й банк ; RC5/SDO и RC3/SCK - выходы ; RC4/SDI - вход ; Устанавливаем биты SMP и СКЕ bcf STATUS/RPO movlw b'001000001 movwf SSPCON ; Возвращаемся в 0-й банк ; Включаем SSP, пассивный уровень - НИЗКИЙ ; Режим ведущего SPI, Fosc/4 Код, приведенный в Программе 12.6, в точности соответствует вышеприве- денному списку операций. Байт данных, который необходимо передать, копиру- ется из указанного регистра в регистр SSPBUF, после чего проверяется бит состо- яния WCOL, чтобы удостовериться, что новое значение действительно было за- гружено в буфер. Если в этот момент осуществлялась передача предыдущего байта, то новый байт не будет загружен в регистр SSPBUF, а бит WCOL будет уста- новлен в 1. Если обращение к модулю SSP осуществляется только из указанной подпрограммы, то возникновение такой ситуации маловероятно, и в большин- стве случаев эта проверка может быть исключена. Тем не менее наличие такой проверки увеличивает надежность системы. Программа 12.6. Использование модуля SSP для приема и передачи данных по шине SPI • ************************************************************ / ; * ФУНКЦИЯ : Передает и одновременно принимает один байт * ; * ФУНКЦИЯ : данных по шине SPI с использованием модуля SSP * ; * ВХОД : Передаваемый байт - в DATA_OUT * ; * ВЫХОД : Принятый байт - в DATA_IN * . ************************************************************ / SPI_IN_OUT movf DATA_OUT,w ; Берем байт для передачи movwf SSPBUF Загружаем его в SSPBUF SSP_IN_OUT_ .LOOP btfss SSPCON,WCOL ; Он загрузился? goto SPI_IN_OUT_CONT ; ЕСЛИ да, ТО продолжим bcf SSPCON,WCOL ; ИНАЧЕ сбросим WCOL и goto SSP_IN_OUT_LOOP ; попытаемся снова SPI_IN_OUT_ _CONT bsf STATUS,RPO ; Переключаемся в 1-й банк btfss SSPSTAT,BF ; Проверяем состояние буфера goto SPI_IN_OUT_CONT ; ЕСЛИ не полон, проверяем снова bcf STATUS,RPO ; Возвращаемся в 0-й банк
Глава 12. Ох уж эти биты! 391 movf SSPBUF,w movwf DATA_IN return ; ИНАЧЕ считываем принятый байт ; и помещаем его в требуемый РОН После загрузки передаваемого байта в буфер сразу же начинается процесс пе- редачи, изображенный на Рис. 12.11. По окончании передачи устанавливается флаг BF, и принятый байт может быть скопирован из регистра SSPBUF в требуе- мый РОН. Флаг BF при этом сбросится автоматически. Запись в SSPBUF Сдвиг Сдвиг Сдвиг Сдвиг Сдвиг Сдвиг Сдвиг Сдвиг SMP= 1 СКЕ= 1 СКР = 0 Рис. 12.11. Временные диаграммы, соответствующие работе модуля SSP в режиме ведущего SPI Помимо небольшого уменьшения размера кода, преимуществом использова- ния, аппаратного модуля является увеличение скорости передачи. Одна транзак- ция приема/передачи состоит из восьми тактов SCK, которые в нашем случае становятся равными восьми машинным циклам. При/Osc = 20 МГц частота сиг- нала SCK равна 5 МГц (т.е. скорость передачи составляет 5 миллионов битов в се- кунду; обычно это записывается как 5 Мбит/с). Таким образом, для передачи од- ного бита требуется всего 1.6 мкс. На Рис. 12.11 показаны временные диаграммы работы модуля в режиме SPI, который используется в нашей подпрограмме. Поскольку мы сбросили бит СКР и установили бит СКЕ, то в режиме ожидания на линии SCK будет присутство- вать НИЗКИЙ уровень. Сразу же после загрузки байта в регистр SSPBUF на вы- вод SDO выдается старший бит передаваемого байта. Это значение будет занесе- но в сдвиговый регистр удаленного приемника по нарастающему фронту тактово- го сигнала в середине фазы. Поскольку удаленный приемник также тактируется в середине фазы, у него имеется достаточно времени для выдачи очередного бита данных на вход SDI мик- роконтроллера. Этот бит считывается микроконтроллером в конце каждой фазы. Одним из применений последовательной передачи данных является объеди- нение нескольких устройств в одну многопроцессорную систему. Например, каж- дый сустав манипулятора робота может управляться своим микроконтроллером,
392 Часть III. Окружающий мир обменивающимся данными с основным процессором. Простая многоабонентская система из одного ведущего и двух ведомых процессоров показана на Рис. 12.12. В этой схеме ведущий PIC-микроконтроллер управляет выводами SCK обоих ведомых, определяя, таким образом, периодичность и скорость обмена данными по сети. Оба ведомых устройства работают в режиме Ь’0100’, в котором разрешена работа входов SS. Таким образом, если ведущий собирается прочитать данные из ведомого №2, то он подает на вход SS последнего НИЗКИЙ уровень и считывает восемь битов из регистров SSPBUF/SSPSR 2-го ведомого в свои регистры SSPBUF/SSPSR. Одновременно с этим ведомый принимает любые данные, пере- даваемые ведущим. Рис. 12.12. Многоабонентская сеть на базе шины SPI Си-процедуры для работы с шиной SPI могут быть написаны либо по анало- гии с ассемблерными процедурами (выполняя установку/чтение соответствую- щих регистров специального назначения), либо с использованием специальных встроенных функций компилятора. Основными функциями компилятора CCS, управляющими модулем SSP в режиме SPI, являются: setup_spi(spi_masterIspi_h_to_lIspi_clk_div_4); Функция setup_spi с указанными параметрами переводит модуль SSP в режим ве- дущего SPI с активным нарастающим фронтом сигнала и частотой шины SPI, равной % частоты основного тактового генератора. Эти константы, как и многие другие, скажем spi_slave, spi_sample_at_end и spi_xmit_l_to_h, определены в стандартных заголовочных файлах, таких как 16f877a.h. Данная функция также кон- фигурирует соответствующие выводы портов А и С. spi_write(value); Эта функция используется для передачи байта по шине SPI. Возврат из функции осуществляется после установки флага BF.
Глава 12. Ох уж эти биты! 393 spi_read(); Эта функция практически идентична функции spi_write (), за исключением того, что она возвращает значение байта, принятого модулем SSP. Если в данную функцию будет передано значение, то оно будет передано по шине. spi_data_is_in(); Эта функция возвращает ненулевое значение, если по шине SPI были получены данные, т.е. если флаг BF установлен. Чтобы проиллюстрировать использование указанных функций, напишем процедуру для взаимодействия с микросхемой МАХ549А (см. Программу 12.5). Прежде всего, нам необходимо сконфигурировать модуль SSP. Это можно сделать следующим образом: #include <16f877a.h> #bit СЕ - 5.2 /* 2-й бит порта А - сигнал СЕ для МАХ549А */ void МАХ549А(unsigned int channel_A, unsigned int channel_B); void main(void) { set_tris_a(OxFB); /* CE - RA2 - выход */ setup_adc(NO_ANALOGS); /* Все входы портов А и E - цифровые */ setup_spi(spi_masterIspi_l_to_h|spi_clk_div_4); В приведенном выше фрагменте предполагается, что вывод СЕ микросхемы МАХ549А подключен к выводу RA2 порта А, как показано на Рис. 12.7. В подпрограмме (см. Программу 12.7) четыре раза вызывается функция spi_write (), причем после передачи каждой пары значений | Control | Data | на вывод СЕ микросхемы подается положительный импульс. Программа 12.7. Управление ЦАП МАХ549А на Си void МАХ549А(unsigned int channel_A, unsigned int channel_B) { 5pi_write(0x01); /* Передаем 1-й управляющий байт */ spi_write(channel_A); /* Передаем байт данных */ СЕ-0; /* Формируем импульс */ СЕ-1; spi_write(ОхОА); /* Передаем 2-й управляющий байт */ spi_write(channel_B); /* Передаем байт данных */ СЕ=0; /* Формируем импульс */ СЕ-1; } Несмотря на то что протокол SPI достаточно быстрый, для его реализации требуется как минимум три линии плюс по одной линии для выбора каждого ве- домого устройства. Даже если не принимать во внимание ценовой фактор, добав- ление в законченную схему нового устройства потребует модификации аппарат- ной части изделия. Однако, увеличив степень «интеллектуальности» ведомых уст- ройств, мы сможем использовать один-единственный последовательный поток для передачи управляющей информации, адреса и данных. Именно эта концеп-
394 Часть III. Окружающий мир ция легла в основу протокола I2C™ (Inter-Integrated Circuit — протокол межсо- единения интегральных схем), разработанного компанией Philips/Signetics Corporation1 2) в начале 1980-х годов. В интерфейсе 12С количество линий связи уменьшено до двух за счет использования двунаправленной передачи данных (см. Рис. 12.13). SCL Это линия тактового сигнала, используемая для синхронизации передачи данных и выполняющая те же функции, что и линия SCK в протоколе SPI. Однако линия SCL является двунаправленной — это позволяет различным ведущим устрой- ствам захватывать управление ею в разные моменты времени. Йзначально в спецификации 12С максимальная скорость передачи была огра- ничена на уровне 100 Кбит/с (частота сигнала SCL — 100 кГц), однако в 1993 году спецификация была дополнена высокоскоростным режимом Fast, обеспечиваю- щим скорость передачи до 400 Кбит/с, который в настоящее время стал стандар- том де-факто. В 1998 году был введен режим High-Speed, обеспечивающий ско- рость передачи до 3.4 Мбит/с. SDA Это линия данных, позволяющая передавать данные в обоих направлениях — как от ведущего к ведомому (ведущий-передатчик), так и от ведомого к ведущему (ве- дущий-приемник). Более того, наличие двунаправленной линии позволяет при- емнику отсылать свое состояние передатчику после передачи каждого байта. Восемь битов данных Рис. 12.13. Передача данных по шине 12С Протокол 12С достаточно сложен, и полную его спецификацию можно найти на сайте компании Philips Corporation2). Прежде чем перейти к изучению основ этого протокола, рассмотрим подробнее работу линий SDA и SCL. В отсутствие передаваемых данных на обеих линиях присутствует ВЫСОКИЙ уровень (состо- яние ожидания). Устройство, собирающееся захватить управление шиной, нахо- 1) j2q™ _ торговая марка компании Philips Corporation. 2) www.semiconductors.com/acrobat/literature/9398/5934001 l.pdf
Глава 12. Ох уж эти биты! 395 дящейся в состоянии ожидания, должно выставить на вывод SDA НИЗКИЙ уро- вень. Такое состояние шины называется состоянием СТАРТ. Чтобы любое уст- ройство, желающее стать ведущим, могло выставить на эту линию НИЗКИЙ уровень, выводы SDA всех остальных устройств, подключенных к шине, должны быть «отключены» от линии, а сама линия шины должна быть подтянута внеш- ним резистором до напряжения ВЫСОКОГО уровня (см. Рис. 12.14, а). Для этого выходы SDA (а также SCL) всех 12С-совместимых устройств выполняются по схе- ме с открытым коллектором или открытым стоком (см. Рис. 2.2, б на стр. 32). Это означает, что любое устройство, подключенное К шине, может перевести ее ли- нию в состояние НИЗКОГО уровня, выставив на свой выход лог. 0. Помимо формирования состояния СТАРТ, ведущий должен генерировать тактовый сигнал, а также передавать значение адреса другим устройствам на ши- не для установления соединения с одним или более ведущими устройствами. Один из битов в байте адреса используется для того, чтобы сообщить ведомому, какая посылка ожидается далее: от ведущего к ведомому (ведущий-передатчик) или от ведомого к ведущему (ведущий-приемник). Каждый пакет, передаваемый по шине, состоит из девяти битов. Восемь из них представляют собой байт данных, синхронизируемый тактовым сигналом. Состояние линии SDA должно изменяться только при НИЗКОМ уровне на линии SCL. Данные считываются приемником по следующему спадающему фронту сиг- нала SCL. Эти байты могут являться адресом или управляющей информацией, посылаемой ведущим, или же данными, передаваемыми как ведущим, так и ведо- мым. В протоколе 12С предусмотрен механизм квитирования (см. Рис. 11.2 на стр. 329). Во время девятого тактового импульса передающее устройство высво- бождает линию SDA, и принимающее устройство подтверждает (acknowledges) данные, посланные передатчиком. Если данные были успешно приняты, то ли- ния SDA удерживается приемником в состоянии НИЗКОГО уровня — это состо- яние называется АСК (подтверждение) (см. Рис. 12.15). Если же при приеме воз- никли некие проблемы или приемник не хочет больше принимать данные, то он оставляет на линии ВЫСОКИЙ уровень — такое состояние называется NACK (нет подтверждения). В последнем случае передатчик, как правило, пытается повторить передачу еще несколько раз, прежде чем принять решение о заверше- нии обмена. В качестве менее радикального способа ведомое устройство может удерживать линию тактового сигнала в состоянии НИЗКОГО уровня. Растягивание тактового сигнала (clock stretching) полезно в тех случаях, когда ведомое устройство не может обрабатывать поступающие данные с требуемой скоростью. После высвобождения ведомым линии SCL ведущий продолжит формирование тактовых импульсов. В любом случае только ведущий может прекратить обмен, выставляя на ли- нии SDA ВЫСОКИЙ уровень при ВЫСОКОМ уровне на линии SCL; такое со- стояние называется состоянием СТОП. При необходимости ведущий может на- чать обмен с другим ведомым, повторно сформировав на шине состояние СТАРТ. Кроме того, ведущий может формировать состояние СТАРТ, не генерируя перед этим состояние СТОП; в таком случае это состояние называется повторный СТАРТ или просто ПОВСТАРТ. К примеру, ведущий собирается передать (веду-
396 Часть 1И. Окружающий мир а) Подключение 12С-совместимых устройств к шине На выходе — НИЗКИЙ уровень Выход подтянут к ВЫСОКОМУ уровню б) Эмулирование в PIC выхода с открытым коллектором Рис. 12.14. Совместное использование линий SCL и SDA несколькими устройствами щий-передатчик) внутренний адрес ячейки в 12С-совместимую микросхему па- мяти (см. Пример 12.3), а затем прочитать (ведущий-приемник) данные из этой ячейки. Для выполнения этой операции потребуется сменить направление пере- дачи данных, что осуществляется повторным формированием состояния СТАРТ и посылкой нового адресного пакета с требуемым значением бита направления (см. Рис. 12.27). Различие между использованием состояния ПОВСТАРТ и СТОП заключается в том, что последнее сигнализирует другим устройствам на шине, что ведущий освободил шину и что другое устройство может стать ведущим. При программной реализации интерфейса 12С в микроконтроллерах PIC воз- никает проблема, заключающаяся в том, что выходы портов не являются выхода- ми с открытым стоком, т.е. состояние лог. 1 не формируется разомкнутой цепью, как того требует спецификация (см. Рис. 12.14, а). Однако состояние с высоким входным сопротивлением можно эмулировать путем переключения линии порта с выхода на вход. Так, если мы хотим использовать для управления линией SCL вывод RA2, то для формирования на этой линии отрицательного импульса мы должны сделать так: bcf PORTA,2 ; При конфигурировании RA2 = О bsf STATUS,RPO ; Переключаемся в 1-й банк
Глава 12. Ох уж эти биты! 397 bcf TRISA,2 nop bsf TRISA,2 bcf STATUS,RPO ; На выходе RA2 - лог. О ; Короткая задержка ; Переводим RA2 в третье состояние, делая его входом ; Возвращаемся в 0-й банк При этом ВЫСОКИЙ уровень на линии формируется совместным влиянием внешнего подтягивающего резистора и высокого входного сопротивления, как показано на Рис. 12.14, б (справа). Собственно процесс обмена между ведущим и ведомым устройствами состоит из передачи различных пакетов (один пакет — 8 бит данных плюс бит квитирова- ния), передаваемых между состояниями СТАРТ и СТОП. Содержимое этих паке- тов может слегка изменяться в зависимости от требований ведомого устройства, однако последовательность передачи пакетов всегда остается неизменной (см. Рис. 12.15): сначала передается адрес ведомого, затем — управляющий байт или команда, а потом передается один или несколько байтов данных. Рис. 12.15. Порядок передачи пакетов по шине 12С В соответствии со спецификацией 12С каждое ведомое устройство должно иметь уникальный адрес. Этот адрес выдается1} производителю 12С-совместимых микросхем и заносится в них на этапе изготовления. Чтобы на одной шине мож- но было использовать несколько однотипных микросхем, большинство 12С-сов- местимых устройств позволяют разработчику задавать до 4 битов адреса самосто- ятельно, обычно путем подачи на выводы адреса соответствующих логических уровней. После обнаружения состояния СТАРТ все ведомые устройства на шине проверяют первые семь битов на совпадение со своим адресом. Если совпадения не обнаружено, то до появления нового состояния СТАРТ это устройство игно- рирует все изменения на шине. Восьмой бит пакета адреса (R/W) определяет на- правление передачи данных. Этот бит равен 0, если ведущее устройство намере- вается передавать данные, т.е. писать (Write) в ведомое устройство, и 1 — если ве- дущий собирается считывать (Read) данные из ведомого устройства. Не все 7-битные адреса являются допустимыми. В частности, все адреса фор- мата Ь’ООООХХХ’ или b’ 1111ХХХ’ зарезервированы для специальных применений; таким образом, в распоряжении пользователей остается 224 возможных адреса. Комитетом поддержки шины 12С.
398 Часть III. Окружающий мир Например, адрес Ь’ООООООО’ обозначает широковещательный пакет, посылаемый всем ведомым на шине, а не какому-то конкретному устройству. Одновременно с введением режима Fast в протоколе 12С появилась возможность использования 10-битных адресов. На использование расширенной адресации указывает переда- ча зарезервированного адреса Ь’ШЮХХХ’, три младших бита которого будут объединены с содержимым последующего 7-битного пакета адреса. Байт, передаваемый вслед за байтом (или байтами) адреса, обычно интерпре- тируется адресованным ведомым как команда или управляющий/байт, посред- ством которого передается конфигурационная информация. Например, для Не- совместимых микросхем памяти может потребоваться передача внутреннего ад- реса, начиная с которого будут заноситься данные (см. Пример 12.3). Все после- дующие байты обычно являются простыми данными или же данными, перемежа- ющимися управляющими байтами. Для иллюстрации рассмотрим микросхему ЦАП МАХ518 компании Maxim, блок-схема которой приведена на Рис. 12.16. Эта микросхема аналогична SPI- совместимой МАХ459, имеет двухуровневый регистровый конвейер, два канала и режим пониженного потребления. Микросхема МАХ518 имеет 7-битный адрес вида 01011AD1AD0, где значения битов AD1 и ADO определяются логическими уровнями на соответствующих вы- водах микросхемы. Если предположить, что оба вывода подключены к общему проводу, то для адресации микросхемы ведущий должен будет послать следую- щий пакет: | Q1Q11100|0|• Бит R/W равен 0, поскольку эта микросхема может только принимать данные. Байт команды имеет формат ООО RST PD XX АО и содержит три управляющих бита. АО Этот бит используется для разрешения входного PIPO-регистра 0-го (АО = 0) и 1-го (АО = 1) каналов. PD Когда этот бит равен 1, оба канала АЦП переключаются в режим пониженного потребления, при этом суммарный ток потребления микросхемы не превышает 4 мкА. Содержимое внутренних регистров остается неизменным, кроме того, в этом состоянии можно загружать данные и обновлять содержимое регистров. Из- менение режима работы происходит только после формирования ведущим состо- яния СТОП, т.е. учитывается только последнее переданное значение бита. RST Если этот бит равен 1, то все внутренние регистры сбрасываются независимо от значения последующего байта данных. После формирования на шине состояния СТОП напряжение на выходах обоих каналов становится равным нулю. В любом случае изменение сигнала на аналоговом выходе в соответствии со значением управляющего байта и байта данных происходит только после форми- рования на шине состояния СТОП. Если с момента формирования последнего
Глава 12. Ох уж эти биты! 399 Рис. 12.16. Микросхема сдвоенного ЦАП с интерфейсом I2C МАХ518 (Maxim) состояния СТОП было послано несколько пар байтов, то на состояние устройства окажут влияние только последние значения. Для работы с ЦАП МАХ518 нам потребуется написать подпрограмму, форми- рующую состояния СТАРТ, СТОП и передающую по шине байты данных. Для на- писания подобного драйвера устройства необходимо более внимательно рассмот- реть временные соотношения между тактовым сигналом и сигналом данных, на которые в большинстве своем накладываются более жесткие ограничения, чем в случае интерфейса SPI. Микросхема МАХ518 и большинство современных 12С-совместимых уст- ройств поддерживают режим Fast, поэтому диаграммы, приведенные на Рис. 12.17, соответствуют тактовой частоте для этого режима (400 кГц). В част- ности, для корректного формирования состояния СТАРТ требуется, чтобы ВЫ- СОКИЙ уровень на линии SCL удерживался в течение не менее 0.6 мкс (Zhd;sta) после появления активного фронта ~\_ на линии SDA. Аналогично, для коррек-
400 Часть III. Окружающий мир тного формирования состояния СТОП требуется, чтобы ВЫСОКИЙ уровень на линии SCL был выставлен не позже чем за 0.6 мкс (/su;sto) Д° появления активно- го фронта _/~ на линии SDA. А время нахождения шины в незанятом состоянии (/buf) между формированием состояния СТОП и последующего состояния СТАРТ должно быть не менее 1.3 мкс. Указанные значения позволяют всем ведо- мым устройствам однозначно обнаруживать эти специальные состояния шины. Рис. 12.17. Минимальные значения временных параметров для режима Fast Во время передачи байта данных тактовый сигнал должен удовлетворять сле- дующим условиям: длительность интервала НИЗКОГО уровня (/low) — не менее 1.3 мкс, длительность интервала ВЫСОКОГО уровня (/high) — не менее 0.6 мкс и, следовательно, полный период сигнала — не менее 2.5 мкс, что соответствует частоте 400 кГц. Изменение состояния линии данных может происходить только при НИЗКОМ уровне на линии тактового сигнала, причем все изменения долж- ны быть прекращены не позже чем за 100 нс (/su;dat) Д° появления нарастающего фронта тактового сигнала. На Рис. 12.17 не указаны максимальные значения времени нарастания и спа- да, которые не должны превышать 300 нс при наибольшей емкости шины, рав- ной 400 пФ. Чтобы удовлетворить этому требованию, при такой емкости шины сопротивление подтягивающих резисторов, изображенных на Рис. 12.14, не должно превышать 1.8 кОм. При небольшой длине шины и малом количестве подключенных к ней устройств сопротивление подтягивающих резисторов мож- но увеличить в десятки раз для уменьшения потребления. Если мы воспользуемся микроконтроллером PIC, работающим на частоте бо- лее 3.2 МГц (время исполнения команды менее 1.25 мкс), то для формирования временных интервалов, удовлетворяющих спецификации 12С, может потребовать- ся вставка коротких задержек между отдельными операциями. Например, если мы используем резонатор с частотой 20 МГц, то при выполнении следующих строк: bcf TRISA,SCL ; Выставить на линию НИЗКИЙ уровень, записав в порт 0 bsf TRISA,SCL ; Сформировать на линии ВЫСОКИЙ уровень, переключив вывод на вход
Глава 12. Ох уж эти биты! 401 длительности интервалов ВЫСОКОГО и НИЗКОГО уровней тактового сигнала будут составлять всего 0.2 мкс. Обычно короткие задержки формируются коман- дами пор, каждая из которых выполняется за один машинный цикл (/bsc/4)- Со- ответственно, для формирования тактового сигнала частотой 400 кГц при 20-М Гц резонаторе можно написать: bcf PORTA,SCL ; Выставляем НИЗКИЙ уровень пор ; 0.2 мкс пор ; 0.4 мкс пор ; 0.6 мкс пор ; 0.8 мкс пор ; 1.0 мкс пор ; 1.2 мкс bsf PORTA,SCL ; Выставляем ВЫСОКИЙ уровень пор ; 1.6 мкс пор ; 1.8 мкс пор ; 2.0 мкс пор ; 2.2 мкс пор ; 2.4 мкс пор ; 2.6 мкс Разумеется, меньшее значение тактовой частоты потребует меньше операций пор. Вместо того чтобы корректировать наши подпрограммы в соответствии с ис- пользуемым в каждом конкретном случае резонатором, мы воспользуемся макро- командой Delay_600, код которой приведен в Программе 12.8. Эта макроко- манда вставляет в программу столько операций пор, сколько требуется для фор- мирования задержки длительностью 600 нс (0.6 мкс), в зависимости от значения константы xtal, заданной программистом. Например, чтобы запустить Программу 12.9 на микроконтроллере с 12-МГц резонатором, необходимо просто заменить строку #define XTAL 20 на #define XTAL 12 и перекомпилировать программу. Программа 12.8. Макрокоманда формирования короткой задержки, независимой от частоты резонатора Delay_600 macro ; Формирует задержку длительностью 0.6 мкс if (XTAL <= 6) nop ; Одна команда пор, если частота резонатора < б МГц endif if ((XTAL > 6) && (XTAL < = 13)) пор ; Две команды пор, если частота резонатора пор ; от 6 до 13 МГц endif if (XTAL > 13)
402 Часть III. Окружающий мир пор ; Три команды пор, если частота резонатора пор ; выше 13 МГц пор endif endm В Программе 12.8 используются ассемблерные директивы условной компиля- ции if — endif. Директива if похожа на условный оператор языка Си (см. стр. 293) тем, что вставляет в программу все команды, расположенные между ней и последующей директивой endif, если аргумент директивы if имеет значение ИСТИНА. Например, выражение if ((XTAL>6&& (XTAL<=13)) означает, что если значение константы больше 6 и меньше или равно 13, то в программу будет вставлено две команды пор. При частоте 13 МГц время их выполнения будет рав- но примерно 600 нс. На практике различные команды, управляющие состоянием линий шины и выполняющие вспомогательные задачи, будут вносить дополни- тельные задержки, поэтому если необходимо достичь максимальной скорости пе- редачи, то длительности задержек придется подбирать более точно. Используя макрокоманду из Программы 12.8 и учитывая приведенный ниже инициализационный код (в котором из соображений удобства обращение к ре- гистру направления порта А осуществляется с использованием косвенной адреса- ции): include "pl6f877a.inc" #define XTAL 20 SCL equ 0 SDA equ 1 MAIN movlw h’ 85 ’ ; Инициализируем регистр FSR, movwf FSR ; чтобы он указывал на TRISA (регистр h'85') bcf PORTA,SCL ; Сбрасываем биты порта в 0, чтобы впоследствии bcf PORTA,SDA ; можно было выставлять на линии НИЗКИЙ уровень bsf INDF,0 ; Формируем на линии тактового сигнала (TRISA[0]) bsf INDF,1 ; и линии данных (TRISA[1]) ВЫСОКИЙ уровень мы можем написать три подпрограммы для работы с 12С-совместимой микросхе- мой МАХ518 (предполагается, что под линии SCL и SDA задействованы 0-й и 1-й выводы порта А микроконтроллера PIC16F84, работающего на частоте 20 МГц). Код этих подпрограмм приведен в Программе 12.9. START Эта подпрограмма сначала высвобождает линии SCL и SDA, на которых в резуль- тате формируется ВЫСОКИЙ уровень на время не менее 1.3 мкс (1‘buf)- Затем пу- тем выдачи на вывод SDA НИЗКОГО уровня на шине формируется состояние СТАРТ, после чего формируется задержка длительностью 0.6 мкс для выдержива- ния интервала ?hd;Sta (см. Рис. 12.17). После возврата из подпрограммы на обеих линиях будет присутствовать НИЗКИЙ уровень.
Глава 12. Ох уж эти биты! 403 STOP Для формирования состояния СТОП на обе линии сначала выставляется НИЗ- КИЙ уровень (в принципе шина и так должна находиться в таком состоянии пос- ле передачи сброшенного бита квитирования). Затем высвобождается линия SCL, в результате чего на ней появляется ВЫСОКИЙ уровень. А после задержки длительностью 0.6 мкс (/su;Sto) высвобождается линия SDA, формируя тем самым состояние СТОП. После возврата из подпрограммы на обеих линиях шины будет присутствовать ВЫСОКИЙ уровень, т.е. шина будет находиться в состоянии ожидания, готовая к формированию следующего состояния СТАРТ. I2C-OUT Эта подпрограмма передает по шине восемь битов содержимого регистра DATA_OUT, начиная со старшего бита, и контролирует подтверждение приема ведомым. Первая операция реализуется путем многократного сдвига содержимого ре- гистра с проверкой флага переноса, значение которого выдается на линию SDA. После выдачи каждого бита данных на линии SCL формируется тактовый им- пульс, параметры которого ZLow и /high удовлетворяют значениям, указанным на Рис. 12.17. Программа 12.9. Подпрограммы низкого уровня для управления шиной 12С ; * ФУНКЦИЯ : Формирует на шине состояние СТАРТ * ; * ВХОД : FSR указывает на регистр TRIS порта, * ; * подключенного к шине I2C * ; * ВЫХОД : Формируется состояние СТАРТ, SCL и SDA - НИЗКИЙ уровень * START bsf INDF,SDA bsf INDF,SCL Delay_600 Delay_600 bcf INDF,SDA Delay_600 bcf INDF,SCL return ; Гарантируем, что перед состоянием СТАРТ ; линии тактового сигнала и данных находились ; в режиме ожидания (ВЫСОКИЙ уровень) ; в течение не менее 1.3 мкс ; Формируем спадающий фронт на линии данных ; Ждем, чтобы ведомый мог его обнаружить ; Выходим, при этом на SCL - НИЗКИЙ уровень ; * ФУНКЦИЯ : Формирует на шине состояние СТОП * ; * ВХОД : FSR указывает на регистр TRIS порта, * ; * подключенного к шине I2C * ; * ВЫХОД : Формируется состояние СТОП, SCL и SDA - ВЫСОКИЙ уровень * ********************************************************************* STOP bcf INDF,SCL bcf INDF,SDA bsf INDF,SCL Delay_600 bsf INDF,SDA return ; Гарантируем наличие НИЗКОГО уровня на ; линии тактового сигнала и данных ; Формируем на линии тактового сигнала ; ВЫСОКИЙ уровень на время не менее 0.6 мкс ; Формируем нарастающий фронт на линии данных
404 Часть III. Окружающий мир / f / f t * / ФУНКЦИЯ : Передает байт ведомому и проверяет подтверждение * ВХОД : 8 бит передаваемых данных в DATA_OUT * РЕСУРСЫ : Подпрограммы START и STOP * ВЫХОД : Байт передан. Если не было подтверждения, ERR = 1 * ВЫХОД : ИНАЧЕ ERR = 00. SCL - НИЗКИЙ уровень * I2C. I2C. .OUT bcf clrf movlw movwf _OUT_LOOP bcf rlf btfsc bsf Delay_ Delay_ bsf Delay_ bcf decfsz goto INDF,SCL ; На линии такт, сигнала - НИЗКИЙ уровень ERR ; Сбрасываем признак ошибки 8 ; Инициализируем счетчик цикла COUNT INDF,SDA ; Попробуем выдать 0 на линию данных DATA_OUT,f ; Сдвигаем исходный байт влево STATUS,С ; С = 0 или 1? INDF,SDA ; ЕСЛИ последнее, ТО выдаем на линию 1 600 ; Формируем требуемую задержку 600 INDF,SCL ; Выдаем на линию такт, сигнала ВЫСОКИЙ 600 ; уровень на время не менее 0.6 мкс INDF,SCL ; Выдаем на линию такт, сигнала НИЗКИЙ уровень COUNT,f ; Декрементируем счетчик цикла I2C_OUT_LOOP ; и повторяем восемь раз ; Теперь проверим наличие подтверждения от ведомого bsf INDF,SDA ; Высвобождаем линию данных Delay_ 600 ; Сохраняем на линии такт, сигнала НИЗКИЙ уровень Delay_ 600 ; на время, достаточное для ответа bsf INDF,SCL ; Выдаем на линию такт, сигнала ВЫСОКИЙ уровень btfsc INDF,SDA ; Проверяем наличие НИЗКОГО уровня на линии данных incf ERR, f ; ЕСЛИ нет, ТО ERR = 1 bcf INDF,SCL ; Переводим линию такт, сигнала в состояние НИЗКОГО уровня return После выхода из цикла линия данных высвобождается, а на линии SCL в тече- ние rLOw удерживается НИЗКИЙ уровень. Затем линия SCL высвобождается (на ней появляется ВЫСОКИЙ уровень) и проверяется состояние линии SDA, на ко- торую ведомый должен был выставить НИЗКИЙ уровень. Если это не так, зна- чит, подтверждения не было (NACK) и в регистр ERR заносится число h’01; в противном случае в этом регистре возвращается 0. Нашу программу нельзя считать полностью рабочей, поскольку в ней отсут- ствует обработка ошибок. А ошибки могут возникать, например, если какое-либо устройство будет удерживать на любой из линий НИЗКИЙ уровень, т.е. если ши- на будет занята. Мы не стали реализовывать в подпрограмме 12С_оит блок ведущего-прием- ника, поскольку в микросхеме МАХ518 не предусмотрена передача данных ведо-
Глава 12. Ох уж эти биты! 405 мому. Однако функция приема данных по шине 12С реализована в Программе 12.18 (подпрограмма I2C_IN). В качестве примера перешлем содержимое регистра h’40’ в 0-й канал, а содер- жимое регистра h’41 ’ — в 1-й канал. После этого обновим оба регистра ЦАП и, следовательно, одновременно сформируем напряжения на выводах Voutl и Vout2, эквивалентные содержимому регистров h’40’ и h’41 ’ соответственно. При этом предполагается, что оба вывода AD0 и AD1 подключены к общему проводу. Для выполнения указанных операций нам потребуется переслать по шине пять пакетов: 1. СТАРТ. 2. Пакет адреса: Ь’01011000’. Адрес ведомого Ь’01011(00), запись. 3. Управляющий байт 1: Ь’ОООООХХО’. Нет сброса, активный режим, канал 0. 4. Байт данных 1: Содержимое регистра h’40’. 5. Управляющий байт 2: Нет сброса, активный режим, канал 1. 6. Байт данных 2: Содержимое регистра h’41 ’. 7. СТОП; содержимое обоих регистров ЦАП обновляется. Ход выполнения Программы 12.10 в точности повторяет указанную последо- вательность операций. После каждого возврата из подпрограммы 12С_оит ре- гистр ERR проверяется на нулевое значение. Если он не равен нулю, то последо- вательность повторяется с самого начала — повторное формирование состояний СТАРТ допускается протоколом 12С. Однако если произошел аппаратный сбой самой шины или ведомого устройства, то этот процесс может продолжаться бес- конечно. Поэтому для увеличения надежности и предотвращения зависания сис- темы необходимо предусмотреть механизм тайм-аута. Программа 12.10. Работа с 12С-совместимой микросхемой двухканального ЦАП МАХ518 ANALOG call START ; Начинаем передачу movlw b' 01011000 ; Адрес ведомого, режим - ведущий-передатчик movwf DATA-OUT ; Копируем в промежуточный регистр call I2C_OUT ; Передаем movf ERR,f ; Проверяем на наличие ошибок btfsc STATUS,Z ; ЕСЛИ ноль, ТО продолжаем goto ANALOG ; ИНАЧЕ пробуем снова ; Управляющий байт 1 — movlw b' 00000000' ; Нет сброса, активный режим, канал 0 movwf DATA-OUT ; Копируем в промежуточный регистр call I2C_OUT ; Передаем movf ERR, f ; Проверяем на наличие ошибок
406 Часть III. Окружающий мир btfsc goto STATUS,Z ANALOG ; ЕСЛИ ноль, TO продолжаем ; ИНАЧЕ пробуем снова Байт данных 1 movf 20h,w ; Считываем значение канала 0 из памяти movwf DATA-OUT ; Копируем в промежуточный регистр call I2C_OUT ; Передаем movf ERR,f ; Проверяем на наличие ошибок btfsc STATUS,Z ; ЕСЛИ ноль, ТО продолжаем goto ANALOG ; ИНАЧЕ пробуем снова Управляющий байт 2 movlw Ь’ОООООООГ ; Нет сброса, активный режим, канал 1 movwf DATA-OUT ; Копируем в промежуточный регистр call I2C_OUT ; Передаем movf ERR,f ; Проверяем на наличие ошибок btfsc STATUS,Z ; ЕСЛИ ноль, ТО продолжаем goto ANALOG ; ИНАЧЕ пробуем снова Байт данных 2 movf 2 lh, w ; Считываем значение канала 1 из памяти movwf DATA-OUT ; Копируем в промежуточный регистр call I2C_OUT ; Передаем movf ERR, f ; Проверяем на наличие ошибок btfsc STATUS,Z ; ЕСЛИ ноль, ТО продолжаем goto ANALOG ; ИНАЧЕ пробуем снова call STOP Любые варианты модулей SSP микроконтроллеров PIC поддерживают работу в режиме 12С. Ранние версии модулей позволяли использовать микроконтроллер только в качестве ведомого устройства, тогда как модуль ведущего синхронного пос- ледовательного порта MSSP автоматически обеспечивает работу микроконтрол- лера в качестве ведущего устройства при наличии на шине нескольких ведущих, на что, собственно, и указывает его наименование. Спецификация шины 12С раз- решает наличие нескольких ведущих устройств, но, разумеется, не одновремен- но. Предотвращение конфликтов на шине является довольно сложной задачей, поэтому использование модуля MSSP в качестве ведущего шины 12С в данной книге рассматриваться не будет. Использование модуля MSSP в этом качестве подробно описано в документе AN7578 «Use of the SSP Module in the I2C Multi- Master Environment». Мы же ограничимся изучением работы модуля MSSP в ка- честве ведомого устройства 12С.' На Рис. 12.18 приведена структурная схема модуля SSP, сконфигурированно- го для работы в качестве ведомого 12С-устройства. Для подключения к двунаправ- ленной линии данных SDA используется вывод RC4, а к линии SCL — вывод RC3. При работе модуля в режиме 12С оба вывода должны быть сконфигурирова- ны как входы. Собственно ввод и вывод данных производится посредством сдвигового ре- гистра SSPSR, который используется и для приема данных, и для их передачи.
Глава 12. Ох у ж эти биты! и 407 Рис. 12.18. Структурная схема модуля MSSP, сконфигурированного для работы в качестве ведомого 12С-устройства Передача При посылке ведомым устройством данных удаленному ведущему, который при этом находится в режиме ведущий-приемник, байт данных, помещенный в бу- ферный регистр SSPBUF, автоматически пересылается в регистр SSPSR (если тот пуст), из которого затем побитно выдается на линию SDA. Если регистр SSPSR полон, то данные на линию не выдаются, и устанавливается флаг конфликта за- писи. Прием Если ведомое устройство ожидает пакет от удаленного ведущего, то данные по- битно вдвигаются через вывод SDA и после приема всех восьми битов получен- ный байт пересылается в регистр SSPBUF. Если ошибки переполнения не было, то модуль MSSP автоматически формирует подтверждение (АСК) во время 9-го тактового импульса. Эта ошибка возникает в том случае, если ранее принятый байт не был в свое время считан из регистра SSPBUF. После обнаружения состояния СТАРТ все ведомые устройства на шине принимают первый пакет от ведущего и сравнивают его содержимое со значени- ем, записанным в регистре адреса SSPADD. При совпадении старших семи битов (0-й бит — бит направления передачи) соответствующее устройство формирует подтверждение (АСК) и готовится к обмену данными с ведущим. При этом уста- навливаются оба флага — BF и SSPIF, сигнализирующие о событии на шине. Как мы уже видели на Рис. 12.15, 8-й бит первого пакета адреса указывает ведомому устройству, принимать или передавать данные до появления на шине следующего состояния СТАРТ или СТОП.
408 Часть III. Окружающий мир Как и в SPI-режиме, для конфигурирования модуля MSSP нам необходимо записать определенные значения в регистры управления и состояния. Формат ре- гистров, приведенный на Рис. 12.19, соответствует четырем возможным режимам работы модуля SSP в качестве ведомого устройства 12С. В этом режиме использу- ются те же регистры SSPSTAT и SSPCON. К сожалению, названия некоторых би- SSPSTAT h'94' 7 6 5 SMP СКЕ D/A (R/WO) (R/WO) (RO) 4 3 P S (RO) (RO) 2 1 R/W UA (RO) (RO) 0 BF (RO) Регистр состояния модуля MSSP • • '.....Чтение: Буфер полон • ! Запись: Идет передача • '••••• Обновление 10-битного адреса ' 0: Обновление адреса не требуется : 1: Необходимо поместить младший байт адреса в SSPADD '•••• Чтение/Запись (ведомый) Ведомый: Соответствует принятому биту R/W Ведущий: Осуществляется (1) или не осуществляется (0) передача данных Последним было обнаружено состояние СТАРТ : : '......Последним было обнаружено • : состояние СТОП ; Адрес/Данные • 0: Последний байт является адресным : 1: Последний байт является информационным • • • Поддержка SMBus 0: Уровни соответствуют спецификации 12С 1: Уровни соответствуют спецификации SMBus Управление длительностью фронтов SSPCON2 — GCEN (R/W0) h’91’ Регистр SEN управления 2 (R/wo) модуля MSSP 0: Включено (в режиме 400 кГц) 1: Выключено SSPCON h’14’ 7 WOOL (R/W0) 6 SSPOV (R/WO) 5 SSPEN (R/WO) 4 CKP (R/WO) 3 SSPM3 (R/WO) 2 SSPM2 (R/WO) 1 SSPM1 (R/WO) 0 SSPMO (R/WO) • - • • Разрешение удержания тактового сигнала при приеме (ведомый) - • • Разрешение поддержки общих вызовов Регистр управления модуля MSSP / Режимы l2C модуля SSP (SSPM[3:0]) 0110: Режим ведомого, 7-битная адресация 0111: Режим ведомого, 10-битная адресация 1000: Режим ведущего, тактовая частота — Fosc/(4*(SSPADD+1)) 1011: Программная поддержка режима ведущего (режим ведомого выключен) 1110: Режим ведомого, 7-битная адресация, прерывания при обнаружении состояний СТАРТ и СТОП 1111: Режим ведомого, 10-битная адресация, к прерывания при обнаружении состояний СТАРТ и СТОП ! J Удержание тактового сигнала : ! 0: Ведомый удерживает на линии SCL НИЗКИЙ уровень • ; 1: Линия SCL свободна : .................................. Включение • Переполнение модуля SSP Обнаружен конфликт записи буфера приемника (предпринята запись в SSPBUF во время передачи предыдущего байта) Рис. 12.19. Регистры управления и состояния модуля MSSP при его работе в режиме ведомого 12С
Глава 12. Ох уж эти биты! 409 тов, используемых в этом режиме, например СКЕ, остаются прежними, хотя их назначение кардинальным образом меняется. По сравнению с более старыми мо- дулями SSP модуль MSSP имеет второй регистр управления SSPCON2. За исклю- чением 0-го и 7-го битов, этот регистр используется исключительно при работе модуля в качестве ведущего 12С. SSPEN Установка бита SSPC0N[5] разрешает работу синхронного последовательного порта. После любого сброса модуль MSSP отключен, а выводы RC3 и RC4 могут использоваться в качестве линий порта С. SSPM[3:0] К нашему обсуждению относятся четыре комбинации этих битов выбора ре- жима работы модуля SSP. Для простоты мы будем считать, что используется режим 7-битной адресации. При использовании 10-битной адресации сначала необходимо загружать в регистр SSPADD старший байт адреса b’l 110А9А8’, а после его совпадения с принятым значением заменять на младший байт адреса Ь’А7А6А5А4АзА2А1’. Режимы Ь’ОИО’ и b’lПО’ отличаются только тем, что в пос- леднем при обнаружении состояний СТАРТ и СТОП устанавливается флаг пре- рывания SSPIF. BF, SSPIF Установленный флаг BF свидетельствует о том, что с данными в регистре SSPBUF что-то произошло. Флаг SSPIF является флагом прерывания от модуля MSSP и устанавливается при любом событии на шине 12С. Ведомый-приемник При приеме кадра от ведущего и записи его содержимого в регистр SSPBUF устанавливается флаг BF, показывая тем самым, что новые данные доступны для обработки, а во время 9-го тактового импульса передается подтвержде- ние (АСК). Также при этом устанавливается флаг SSPIF (PIR[3]), который может использоваться для генерации прерывания. При считывании получен- ного байта из буфера бит BF автоматически сбрасывается (этот флаг досту- пен только для чтения), однако флаг SSPIF необходимо сбрасывать вручную, как и остальные флаги прерываний. В случае приема нового байта до считывания предыдущего значения, т.е. при установленном бите BF, он не пересылается в буфер SSPBUF. Вместо этого устанавливается флаг SSPOV, извещающий о возникновении перепол- нения. В этом случае подтверждение не посылается (NACK). Ведомый-передатчик В течение всего времени пересылки байта ведущему флаг BF остается уста- новленным, показывая, что идет передача. Если в этот момент попытаться записать в регистр SSBUF новый байт, то вместо его пересылки в сдвиговый регистр SSPSR будет установлен флаг WCOL, извещающий о возникновении конфликта записи.
410 Часть III. Окружающий мир SSPOV При работе в режиме ведомый-приемник попытка считывания регистра SSPBUF до приема нового байта индицируется установкой данного флага. При этом веду- щему передается NACK. Ведомый может намеренно передавать NACK, чтобы информировать ведущего о том, что тот может повторить попытку передачи поз- же. Это состояние сбрасывается при считывании регистра SSPBUF (при этом сбрасывается флаг BF) и ручном сбросе флага SSPOV. WCOL Этот флаг устанавливается при попытке записи в регистр SSPBUF во время пере- дачи, извещая о возникновении конфликта записи. Данный флаг должен быть сброшен вручную. S,P Эти флаги индицируют обнаружение на шине состояний СТАРТ и СТОП соот- ветственно. Обычно они имеют противоположные значения. Исключением из данного правила является их состояние после любого сброса микроконтроллера или при разрешении модуля (SSPEN -> 1) — в эти моменты оба флага сброшены. Установленный флаг Р показывает, что шина свободна. Эта информация может потребоваться следящему за состоянием шины устройству, которое намеревается начать работать в качестве ведущего. D/А, R/W, UA Эти флаги относятся к пакету (пакетам), передаваемым по шине после формиро- вания состояния СТАРТ и содержащим информацию об адресе ведомого уст- ройства и направлении передачи последующих пакетов. Бит D/А показывает, какой именно байт находится в регистре SSPBUF — дан- ные (D) или_ адрес (А). Бит R/W информирует программу о том, в каком направлении будут переда- ваться последующие пакеты данных — к ведущему (R/W= 1) или от ведущего (R/W= 0). Вообще говоря, значение данного бита соответствует значению 0-го бита (первого) пакета адреса. Бит UA используется только в режимах с 10-битной адресацией. В этих режи- мах сначала сравниваются семь старших битов первого байта адреса b’ 11110А9А80’. Младший бит является битом направления передачи и информи- рует о том, что следующий пакет адреса будет передаваться ведущим (R/W= 0). После приема 1-го байта адреса флаг UA автоматически устанавливается в 1, из- вещая программу о том, что в регистр SPPADD можно загружать младший байт адреса. После выполнения записи флаг UA автоматически сбрасывается. GCEN Если бит разрешения общего вызова GCEN равен 1, то флаг прерывания SSPIF будет устанавливаться при приеме адреса общего вызова Ь’0000000’ независимо от значения, находящегося в регистре адреса модуля. Прием этого адреса свиде- тельствует о том, что ведущий собирается приступить к широковещательной рас- сылке всем ведомым устройствам. В более ранних вариантах модуля SSP данная функция отсутствует.
Глава 12. Ох уж эти биты! 411 СКР, SEN При сброшенном бите СКР ведомое устройство удерживает линию SCL в состоя- нии НИЗКОГО уровня, запрещая, таким образом, ведущему генерацию тактовых импульсов. После установки бита СКР в 1, ведомый освобождает линию SCL, позволяя ведущему формировать тактовые импульсы для нового пакета. Хотя бит СКР может быть изменен программно в любой момент времени (т.е. вручную), растягивание тактового сигнала может выполняться автоматически. SEN = O При сброшенном бите SEN (состояние по умолчанию), а также в модулях SSP более ранних версий бит СКР сбрасывается автоматически в конце каж- дого пакета, отсылаемого модулем ведущему. Ведомый должен устанавливать бит СКР каждый раз после загрузки содержимого нового пакета в регистр SSPBUF для высвобождения линии тактового сигнала и разрешения переда- чи следующего пакета. Растягивание тактового сигнала в таких ситуациях осуществляется всегда, независимо от состояния бита SEN. Точно так же ра- ботают модули, в которых этот бит отсутствует. SEN = 1 В последних версиях модуля MSSP1J автоматическое растягивание тактового сигнала при установленном бите SEN разрешается как при передаче от ведо- мого устройства, так и при приеме. Последнее полезно в том случае, если ве- дущий посылает пакеты быстрее, чем ведомый успевает их обрабатывать. СКЕ Этот бит имеется только в модулях MSSP, и при его установке электрические па- раметры сигналов на линиях SDA и SCL будут соответствовать спецификации шины SMBus. Работа модуля MSSP в качестве ведомого представляет собой многоэтапный процесс, требующий от программы реакции на всёвбЗМОЖНЫб СОбЫТИЯ, возни- кающие на шине 12С. Хотя это можно реализовать с помощью простого опроса флага SSPIF регистра PIR1, в наших примерах мы будем использовать прерывания. Прежде чем перейти к рассмотрению этих событий и знакомству с учебными программами, необходимо разобраться с инициализацией модуля MSSP. Типич- ный инициализационный код для микроконтроллера PIC16F877A, который дол- жен работать в качестве ведомого с адресом h’06’, при частоте шины 100 кГц вы- глядит следующим образом: include "pl6f877a.inc" SETUP movlw b'00110110' ; Включаем MSSP, такт, линия свободна movwf SSPCON ; Режим ведомого с 7-битной адресацией (ОНО) п Например, в моделях линейки PIC16F87X бит SEN в режиме ведомого не используется, тогда как в моделях линейки PIC16F87XA он выполняет описываемые функции.
412 Часть III. Окружающий мир bsf STATUS,RPO ; Переключаемся в 1-й банк bsf SSPSTAT,SMP ; Скорость нарастания соответствует частоте 100 кГц bsf SSPCON2,SEN ; SEN = 1 для автоматического удержания линии ; тактового сигнала после приема данных movlw h'OC' ; Адрес h'06' сдвигаем влево на один бит, movwf SSPADD ; чтобы значение соответствовало содержимому пакета bsf PIE1,SSPIE ; Разрешаем прерывание от модуля SSP bsf INTCON,PEIE ; Разрешаем прерывания от периферийных устройств bsf INTCON,GIE ; Разрешаем прерывания bcf STATUS,RPO ; Возвращаемся в 0-й банк В этом фрагменте: 1. В регистры управления и состояния модуля MSSP заносятся значения, со- ответствующие заданию. 2. Адрес ведомого (число h’06’, сдвинутое влево для соответствия семи стар- шим битам пакета адреса) заносится в регистр SSPAD. 3. Установкой бита маски SSPIE совместно с битами PEIE и GIE разрешается прерывание от модуля MSSP (см. Рис. 7.5 на стр. 223). Теперь, когда у нас есть код для инициализации модуля MSSP и системы пре- рываний, можно приступать к написанию процедуры обработки прерывания, распознающей различные события, происходящие на шине 12С. Любое допусти- мое событие приведет к передаче управления из фоновой программы в обработчик прерывания. Даже если микроконтроллер будет находиться в «спящем» режиме, это событие вызовет установку бита SSPIF и «пробуждение» микроконтроллера. Вот эти события: 1. Ведущий-передатчик: принятый пакет был пакетом адреса S = 1 Последним состоянием на шине было состояние СТАРТ. R/W = 0 Ожидается передача данных от ведущего. D/А = 0 Это пакет адреса. BF = 1 Буфер полон. Для сброса флага BF необходимо прочитать регистр SSPBUF, даже если пакет адреса, присланный ведущим, будет проигнорирован. Если этого не сделать, сле- дующий байт, посланный ведущим, вызовет переполнение буфера (SSPOV -> 1) и модуль отошлет NACK. Если бит SEN установлен в 1, то бит СКР будет автоматически сброшен и на линию SCL будет выставлен НИЗКИЙ уровень. Когда это станет возможным, необходимо будет установить бит СКР в 1 для разрешения работы ведущего. 2. Ведущий-передатчик: принятый пакет был пакетом данных После передачи адресного пакета ведущий посылает один или более пакетов дан- ных. Чтобы избежать возникновения переполнения и гарантировать отсылку подтверждения АСК, ведомое устройство должно считывать каждый из этих па-
Глава 12. Ох уж эти биты! 413 кетов. Также при установленном бите SEN необходимо манипулировать битом СКР, как и при обработке предыдущего события. Содержимое регистра STATUS для этого события отличается от предыдущего только значением бита D/A. S = 1 Последним состоянием на шине было состояние СТАРТ R/W=0 Ожидается передача данных от ведущего. D/A=l Это пакет данных. BF = 1 Буфер полон. 3. Ведущий-приемник: принятый пакет был пакетом адреса Обмен на шине начинается с посылки ведущим пакета адреса с установленным битом R/W, извещающим ведомого, что от него ожидается передача пакетов дан- ных ведущему. После распознавания ведомым своего адреса или адреса общего вызова при установленном бите GCEN, состояния битов регистра SSPSTAT будут следующими: S = 1 Последним состоянием на шине было состояние СТАРТ. R/W= 1 Ожидается передача данных к ведущему. D/A = 0 Это пакет адреса. BF = 0 Буфер свободен для передачи. Заметьте, что в данной ситуации бит BF сброшен. Дело в том, что при работе модуля в режиме ведущего-приемника флаг BF используется для того, чтобы со- общать программе о готовности регистра SSPBUF к загрузке байта данных, посы- лаемого ведущему. Поэтому, в отличие от 1-го события, нам нет необходимости считывать содержимое регистра SSPBUF. После распознавания адреса ведомый может послать первый байт данных ве- дущему, загружая его в регистр SSPBUF и устанавливая бит СКР для высвобожде- ния вывода SCL. Расширение тактового сигнала производится автоматически при получении пакета от ведущего-приемника, независимо от состояния бита SEN. 4. Ведущий-приемник: принятый пакет был пакетом данных Это событие похоже на предыдущее, и бит СКР в этом случае действует аналогич- ным образом. Новый байт нельзя загружать в регистр SSPBUF до тех пор, пока не будет сброшен бит BF, в противном случае установится бит WCOL. Значения битов регистра SSPSTAT идентичны значениям для предыдущего события, за исключением установленного бита D/А, показывающего, что послед- ним принятым пакетом был пакет данных: S = 1 Последним состоянием на шине было состояние СТАРТ. R/W=l Ожидается передача данных к ведущему. D/А = 1 Это пакет данных. BF = 0 Буфер свободен для передачи. 5. Ведущий-приемник: ведущий послал NACK Эта ситуация обычно возникает, когда ведущий по какой-либо причине не хочет больше принимать данные от ведомого. Сигнал NACK указывает на завершение обмена, и при его приеме ведомый модуль сбрасывает логику 12С.
414 Часть III. Окружающий мир S = 1 Последним событием на шине было состояние СТАРТ. R/W = 0 Бит R/W сбрасывается ведомым. D/А = 1 Это пакет данных. BF = 0 Буфер свободен для передачи. Появление NACK определяется по сброшенному биту BF при нулевом бите R/W. Это конфликтное состояние, поскольку такое сочетание битов говорит о том, что был принят пакет данных от ведущего, но буфер при этом остался пустым! В качестве примера давайте представим, что ведомый микроконтроллер PIC16F877A с адресом h’06’ контролирует восемь температурных датчиков, под- ключенных к входам встроенного модуля АЦП (см. главу 14). Если ведущий хо- чет считать значение одного из этих оцифрованных каналов, то он сначала по- сылает ведомому номер канала (ведущий-передатчик) N, а затем, перейдя в ре- жим ведущего-приемника, принимает от ведомого запрошенные данные. Предположим, что подпрограмма get_analog (Программа 14.1, стр. 516) уже написана. Последовательность операций может быть следующей: 1. Ведущий формирует на шине состояние СТАРТ, после чего адресует ведо- мого с адресом h’06’, приказывая ему принять следующий пакет данных (ведущий-передатчик). 2. Ведущий посылает пакет данных, содержащий номер канала 0...7. 3. Ведущий формирует состояние ПОВТСТАРТ для ведомого с адресом h’06’, требуя на этот раз, чтобы последний передал ему следующий пакет данных (ведущий-приемник). 4. Ведомый удерживает линию тактового сигнала, дожидаясь завершения преобразования по выбранному каналу. 5. Ведомый посылает требуемые значения. 6. Ведущий отвечает NACK, сообщая о завершении обмена. Для ясности разобьем нашу программу на две отдельные процедуры. Кроме того, предположим, что для сохранения контекста используются ячейки, отобра- женные на все банки памяти. В случае микроконтроллера PIC16F877A эти ячей- ки располагаются по адресам h’60’...h’7F’. Переменные, используемые в програм- ме, располагаются в 0-м банке. В Программе 12.11 приведен код процедуры обработки прерывания, сохране- ние и восстановление контекста в которой осуществляются в соответствии с принципами, обсуждавшимися ранее (см. стр. 216). Перед восстановлением кон- текста бит СКР устанавливается в 1 для разрешения формирования тактового сигнала, а флаг SSPIF сбрасывается.
Глава 12. Ох уж эти биты! 415 Программа 12.11. Процедура обработки прерывания 12С-совместимого регистратора температуры f • * ФУНКЦИЯ : Обработчик для передачи значения N-ro канала * ; * по шине 12С * ; * ВХОД : Произошло событие на шине * ; * ОКРУЖЕНИЕ : Использует п/п GET_ANALOG, I2C_HANDLER * / ; Сначала сохраним контекст ------------------------------------- ISR movwf _work ; Сохраняем W swapf STATUS,w ; и регистр STATUS movwf _status ; Проверяем, установлен ли флаг SSPIF? -------------------------- bcf STATUS,RPO ; : Переключаемся на 0-й банк bcf STATUS,RP1 btfss PIR1,SSPIF ; ; Это прерывание от MSSP? goto ISR—EXIT ; : ЕСЛИ нет, ТО выходим bsf STATUS,RP1 ; : ИНАЧЕ переключаемся на 1-й банк movf SSPSTAT,w ; : Считываем состояние из SSPSTAT bcf STATUS,RP1 i ; и возвращаемся в 0-й банк andlw b'00101101’ ; ; Обнуляем все биты, кроме S, D/А, R/W и BF, movwf I2C_STATUS ; ; и копируем полученное значение : во временный регистр clrf I2C_ERROR ; ; Обнуляем признак ошибки call I2C_HANDLER ; ; Теперь обработаем событие на шине I2C ; Восстановим контекст ISR_EXIT bcf PIR1,SSPIF ; Сбрасываем флаг прерывания bsf SSPCON,CKP ; Освобождаем линию такт, сигнала swapf _status,w ; Восстанавливаем исходное значение STATUS swapf _work,f ; Восстанавливаем исходное значение W, swapf _work,w ; не изменяя битов регистра STATUS, retfie ; и возвращаемся в фоновую программу Собственно, обработчик просто проверяет состояние флага SSPIF и, если тот не установлен, процедура завершается. В реальной жизни прерывания могут ге- нерироваться несколькими источниками, что повлечет за собой переделку этого фрагмента программы для проверки флагов всех используемых прерываний. Если флаг SSPIF установлен, то содержимое регистра состояния SSPSTAT копи- руется из 1-го банка в регистр I2C_STATUS, расположенный в более удобном 0-м банке (перед этим сбрасываются не интересующие нас биты). Регистр I2C_ERROR также обнуляется. При обнаружении ошибочной ситуации в этот регистр будет за- несено ненулевое значение для информирования фоновой программы. После инициализации управление передается в подпрограмму, которая, соб- ственно, и анализирует события, происходящие на шине 12С. Код данной под- программы приведен в Программе 12.12. Эта программа состоит из пяти блоков,
416 Часть Ш. Окружающий мир каждый из которых соответствует одному из состояний шины 12С, перечислен- ных на стр. 412. Выбор требуемого блока осуществляется с использованием ко- манды xorlw (см. стр. 146), с помощью которой проверяется равенство копии ре- гистра SSPSTAT и константы, соответствующей тому или иному состоянию. При обнаружении равенства выполняются действия в соответствии с нашим алгорит- мом или же просто вспомогательные операции, которые позволят модулю MSSP продолжить работу с корректного состояния. Так, нам ничего не нужно делать при возникновении 5-го состояния, при котором ведущий отсылает ведомому NACK, поскольку модуль MSSP будет сброшен автоматически. Если соответ- ствий не обнаружено, декрементируется переменная I2C_ERROR, сигнализируя об ошибке. Программа 12.12. Обработчик событий шины 12С регистратора температуры • it******************************************************************* ! ; * ФУНКЦИЯ : Анализирует события шины 12С и реагирует ★ . * требуемым образом ★ ; * ВХОД : Копия SSPCON в I2C_STATUS * ; * ВЫХОД : Выполняются требуемые действия ★ . * / • I2C_ERROR = -1, ЕСЛИ событие не было распознано ★ . ******************************************************************** , I2C_HANDLER; 1- -е событие? (пакет адреса, ведущий-передатчик) movf I2C_STATUS,w; Берем копию содержимого SSPSTAT xorlw b'00001001' ; Проверяем наличие S=1, D/A=0, R/W=0, BF=1 btfss STATUS,Z ; Равно? goto STATE2 ; ЕСЛИ нет, ТО проверяем 2-е событие movf SSPBUF,w ; ИНАЧЕ читаем буфер для сброса флага BF ; 2-е событие? (пакет данных, ведущий-передатчик) STATE2 movf I2C_STATUS,w; Берем копию содержимого SSPSTAT xorlw b'00101001' ; Проверяем наличие S=1, D/A=l, R/W=0, BF=1 btfss STATUS,Z ; Равно? goto STATE3 ; ЕСЛИ нет, ТО проверяем 3-е событие movf SSPBUF,w ; ИНАЧЕ считываем номер канала call GET_ANALOG ; Оцифровываем сигнал N-ro канала movwf TEMP ; и сохраняем результат в регистре TEMP ; 3-е событие? (пакет адреса, ведущий-приемник) STATE3 movf I2C_STATUS,w; Берем копию содержимого SSPSTAT xorlw b'00001100' ; Проверяем наличие S=l, D/A=0, R/W=l, BF=0 btfss STATUS,Z г Равно? goto STATE4 ; ЕСЛИ нет, TO проверяем 4-е событие movf TEMP,w ; ИНАЧЕ берем значение температуры movwf SSPBUF ; и помещаем в буферный регистр для передачи ; 4-е событие? (пакет данных, ведущий-приемник) STATE4 movf I2C_STATUS,w; Берем копию содержимого SSPSTAT xorlw b'00101100' ; Проверяем наличие S=l, D/A=l, R/W=l, BF=0 btfss STATUS,Z ; Равно? goto STATES ; ЕСЛИ нет, TO проверяем 5-е событие ; Ничего не делаем!!!
Глава 12. Ох уж эти биты! 417 ; 5-е событие? (ведущий отослал ведомому NACK) -------------------------- STATE5 movf I2C_STATUS,w; Берем копию содержимого SSPSTAT xorlw b'00101000' ; Проверяем наличие S=l, D/A=l, R/W=0, BF=0 btfss STATUS,Z ; Равно? decf I2C_ERROR,f ; ЕСЛИ нет, TO сообщаем об ошибке return Еще один пример использования модуля SSP приведен в документе AN734 «Using the PIC Microcontroller SSPfor Slave I2C Communications». Как и в случае с протоколом SPI, большинство Си-компиляторов для микро- контроллеров PIC имеют встроенные функции для реализации протокола 12С, что позволяет отказаться от написания собственных функций, манипулирующих различными битами регистров специального назначения. В качестве примера рассмотрим Программу 12.13, написанную для компиля- тора CCS и выполняющую действия, аналогичные ассемблерной программе, фрагменты которой были приведены в Программах 12.9 и 12.10. Программа 12.13. Взаимодействие с ЦАП МАХ518 на Си #include <16F84.h> /* 0-й бит порта А - SCL, 1-й бит порта А - SDA, режим ведущего, протокол Fast */ # use i2c(master, scl=PIN_A0, sda=PIN_Al, fast) # byte DATA_X = 0x20 # byte DATA_Y = 0x21 void MAX518(unsigned int channel_0, unsigned int channel_l); void main(void) { /* Различный код */ МАХ518(DATA_X, DATA_Y); /* Передаем два байта данных */ /* Остальной код • */ } void МАХ518(unsigned int channel_0, unsigned int channel_l) { i2c_start(); /* Формируем состояние СТАРТ */ i2c_write(0x58); /* Передаем адрес ведомого */ i2c_write(0); /* Посылаем 1-й управляющий байт */ i2c_write(channel_0); /* Посылаем данные 0-го канала */ i2c_write(0x01); /* Посылаем 2-й управляющий байт */ i2c_write(channel_l); /* Посылаем данные 1-го канала */ /* Обновляем оба канала */ i2c_stop(); /* Формируем состояние СТОП */ } В этой программе используются следующие встроенные функции компилято- ра CCS:
418 Часть III. Окружающий мир i2c_start(); Формирует состояние СТАРТ. i2c_stop(); Формирует состояние СТОП. i2c_read() ; Считывает один байт с шины. Если необязательный параметр равен 0, то в ответ на принятые данные будет возвращен NACK. При работе в режиме ведущего так- же генерирует тактовый сигнал. i2c_write(value); Передает по шине один байт. При работе в режиме ведущего также генерирует тактовый сигнал. #use i2c(master, scl=PIN_A0, sda=P!N_Al, fast) С помощью этой директивы программист сообщает компилятору о том, какие выводы будут использоваться для подключения к линиям шины 12С, тип исполь- зуемого протокола (стандартный или высокоскоростной), а также режим работы модуля (ведущий или ведомый). К моменту написания книги компилятор не под- держивал возможности модуля MSSP по работе в качестве ведущего, поэтому та- кие функции реализованы программно. Функции ведомого могут быть реализо- ваны аппаратно модулем MSSP, если в директиве #use i2c () указать опцию FORCE_HW. Отличительной особенностью всех последовательных протоколов, рассмот- ренных нами до настоящего момента, является то, что ведущее устройство гене- рирует тактовый сигнал, благодаря которому ведомое устройство может прини- мать и передавать данные в синхронном режиме. Альтернативный подход к пере- даче данных базируется на допущении, что передатчик и приемник работают примерно с одной и той же скоростью. Такой асинхронный протокол использовал- ся в системах передачи данных на протяжении более ста лет для передачи алфа- витно-цифровых символов по телеграфу, телефону и радиоканалу. Одной из особенностей ранних компьютеров, создававшихся в 40-х и 50-х го- дах, было интенсивное заимствование существовавших к тому времени техноло- гий. А одним из основных элементов любой машинно-ориентированной системы является терминал данных. В то время в области телекоммуникаций широко ис- пользовались телетайпы (TTY)1). Последовательные данные преобразовывались в параллельный формат самим терминалом, который также выполнял функции клавиатуры и печатающего устройства. До начала 80-х годов телетайпы были исключительно электромеханическими устройствами, управляющимися синхронными электродвигателями. То есть син- хронность работы удаленных терминалов гарантировалась только в течение ко- Образовано от греч. tele — вдаль, далеко и от англ, type — печатать на машинке.
Глава 12. Ох уж эти биты! 419 роткого интервала времени. Для устранения этой проблемы каждому передавае- мому слову предшествовал старт-бит, а после него передавался один или более стоп-битов. Типичный пример показан на Рис. 12.20. В свободном состоянии ли- нии на ней присутствует лог. 1 (обрыв). Появление сигнала лог. 0 сигнализирует о начале слова. Завершает передачу слова сигнал лог. 1. Электромеханические тер- миналы обычно печатали со скоростью десять символов в секунду и требовали не менее двух стоп-битов. Для 8-битных слов данных это соответствует скорости пе- редачи, равной 110 бит в секунду или 110 бод{\ Рис. 12.20. Передача строки сообщения «Р1С» по асинхронному последовательному каналу с проверкой четности и, как минимум, одним стоп-битом Первый полностью электронный терминал требовал наличия всего одного стоп-бита и мог печатать со скоростью 300 символов в секунду, обеспечивая ско- рость передачи 300 бод. По традиции, для каналов передачи данных используют- ся скорости, кратные 300, например 1200, 2400, 4800, 9600 и т.д. Последователь- ный порт ПК может работать на скоростях до 115 200 бод. Однако придерживать- ся этих значений, кратных 300, вовсе не обязательно — главное, чтобы приемник и передатчик работали с одинаковой номинальной скоростью. Обычно приемник при обнаружении входящих данных пытается прочитать значение каждого бита примерно в середине интервала его передачи. То есть на интервале передачи 10 бит будет допустимым уход частоты в пределах ±0.5 бита. Соответственно частоты приемника и передатчика должны отличаться друг от друга не более чем на ±5%, а их ресинхронизация будет производиться в начале каждого слова данных. Несмотря на не самую большую эффективность, описанный асинхронный протокол имеет огромное преимущество, заключающееся в том, что он является международным стандартом. Существует несколько его вариантов, к примеру, размер слова может варьироваться от 5 до 9 бит. В нашем примере длина слова равна 8 бит, причем восьмой бит используется для контроля ошибок. Значения Собственно говоря, скорость передачи данных, выражаемая в бодах (baud rate), является мерой измерения потока информации. В простых узкополосных системах она равна скорости передачи в бит/с (bit rate). Однако в более сложных системах это соотношение, как правило, нарушается. К примеру, модем использует двухбитовую модуляцию, при которой каждой паре битовых значений (00,01, 10,11) соответствует определенный сдвиг фазы несущего сигнала (0°, 90°, 180°, 270°). В этом случае скорость передачи информации в бодах будет в 2 раза больше битовой скорости передачи.
Глава 12. Ох уж эти биты! и421 случае малых скоростей в тело цикла может потребоваться добавить дополни- тельные команды пор. При использовании этой макрокоманды формирования задержки базовые подпрограммы ввода/вывода, код которых приведен в Программе 12.14, похожи на аналогичные подпрограммы для протокола SPI. Подпрограмма putchar просто выдает на вывод ТХ сигнал НИЗКОГО уровня в течение двух периодов Baud_delay, а затем изменяет состояние вывода восемь раз в соответствии с со- держимым регистра DATA_OUT, начиная с младшего бита, т.е. в обратном поряд- ке по сравнению с протоколами SPI/I2C. В конце на вывод ТХ выдается сигнал ВЫСОКОГО уровня для формирования сигнала СТОП. Программа 12.14. Подпрограммы асинхронного приема и передачи данных • ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ / ; * ФУНКЦИЯ : Передает 8-битное значение в асинхронном режиме * ; * ФУНКЦИЯ : Скорость передачи: 1200...9600 для XTAL 1...20 МГц * ; * РЕСУРСЫ : Макрокоманда BAUD_DELAY, формирующая задержку * ; * 0.5 битового интервала; COUNT * ; * ВХОД : 8-битное слово данных в DATA_OUT, предопределенные * ; * константы XTAL и BAUD * ; * ВЫХОД : Содержимое DATA_OUT обнуляется, байт передан * . ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ / PUTCHAR movlw 8 ; Восемь битов данных movwf COUNT bcf PORTA,ТХ ; Старт-бит Baud_delay ; Задержка 2x0.5 бита Baud_delay ; Теперь выдвигаем байт данных, начиная с младшего бита PUTCHAR_LOOP rrf DATA_OUT, f STATUS,C ITS_A_0 PORTA,TX PUTCHAR_NEXT ; Сдвигаем вправо через перенос ; Проверяем флаг переноса ; ЕСЛИ 0f ТО передаем 0 ; ИНАЧЕ передаем 1 ; и продолжаем btfss goto bsf goto ITS_A_0 PUTCHAR_ .NEXT bcf PORTA,TX ; Выдаем 0 Baud_delay ; Задержка на 1 бит Baud_delay decfsz COUNT,f ; Повторяем восемь раз goto PUTCHAR_LOOP bsf PORTA,ТХ ; Стоп-бит Baud_delay Baud_delay return
422 Часть Ш. Окружающий мир ****************************************************************** • * ФУНКЦИЯ : Принимает 8-битное значение в асинхронном режиме * ; * : Скорость передачи: 1200...9600 для XTAL 1...20 МГц * ; * РЕСУРСЫ : Макрокоманда BAUD_DELAY, формирующая задержку * ; * 0.5 битового интервала; COUNT * ; * ВЫХОД : Принят байт в DATA_IN * ; * ВЫХОД : Если нет ошибки кадрирования, ERR = 0, ИНАЧЕ ERR = -1* ****************************************************************** GETCHAR movlw 8 ; Восемь битов данных movwf COUNT clrf ERR ; Обнуляем байт признака ошибки GETCHAR.START btfsc PORTA,RX ; Ожидаем появления 0 goto GETCHAR.START Baud_delay ; Ждем в течение 0.5 бита btfsc PORTA,RX ; Все еще 0? goto GETCHAR.START Baud.delay Baud.delay GETCHAR.LOOP bcf STATUS,C rrf DATA_IN,f btfsc PORTA,RX bsf DATA-IN,7 Baud.delay Baud.delay decfsz COUNT,f goto GETCHAR—LOOP btfss PORTA,RX decf ERR, f return ; ЕСЛИ да, TO ждем в течение 1 бита ; Сбрасываем флаг переноса ; Вдвигаем 0 в байт данных ; На входе ВЫСОКИЙ уровень? ; ЕСЛИ да, ТО устанавливаем бит ; Повторяем восемь раз ; Проверяем приход стоп-бита (1) ; ЕСЛИ 0, ТО сообщаем об ошибке Подпрограмма приема GETCHAR более сложна. Появление на выводе RX НИЗКОГО уровня расценивается как приход старт-бита. Однако если осу- ществлять выборку значений последующего потока данных с периодичнос- тью, равной длительности битового интервала (два включения макроса Baud_delay), то, поскольку этот момент может соответствовать моменту окончания битового интервала, уход любой из двух частот может привести к появлению ошибок. Чтобы избежать этого, состояние вывода RX считывается повторно после задержки, равной половине битового интервала, чтобы еще раз убедиться в наличии старт-бита. Если это окажется так, то последующие вы- борки осуществляются с периодом, равным двум битовым интервалам. При этом моменты выборок будут приходиться примерно на середину интервала. Лучших результатов можно достичь, считывая состояние вывода с большей
Глава 12. Ох уж эти биты! 423 частотой (передискретизация) и принимая мажоритарное решение на основа- нии считанных значений. После приема восьми битов данных стоп-бит проверяется на равенство лог. 1. Если стоп-бит равен 0, значит, произошла ошибка кадрирования. Эта си- туация сигнализируется возвратом —1 в ERR. При использовании других, более развитых схем могут возвращаться сообщения об ошибках разных типов. Напри- мер, при использовании контроля четности может быть возвращена ошибка четности. В качестве примера рассмотрим фрагмент кода, осуществляющий передачу 3-символьного сообщения «Р1С». К счастью, ассемблер предоставляет програм- мисту возможность вместо ASCII-кодов символов записывать сами символы в одинарных кавычках, как описано на стр. 267. ; Передадим строку "PIC" movlw ' P • ; Аналогично movlw h*50’ (ASCII-код символа «Р») movwf DATA-OUT ; Помещаем в память данных call PUTCHAR ; Передаем movlw I’ ; Аналогично movlw h’491 (ASCII-код символа «I») movwf DATA-OUT ; Помещаем в память данных call PUTCHAR ; Передаем movlw ’C ; Аналогично movlw h143' (ASCII-код символа «С») movwf DATA-OUT ; Помещаем в память данных call PUTCHAR ; Передаем На самом деле такая реализация асинхронного обмена по последовательному каналу годится только в самых простых случаях. Например, если не отслеживать непрерывно состояние вывода RX, можно пропустить передачу или потерять синхронизацию. Кроме того, при таком подходе трудно реализовать дуплексную связь, не говоря уже о том, что большая часть вычислительной мощности микро- контроллера в данном случае тратится на выполнение циклов задержки. Эту си- туацию можно несколько улучшить, используя для формирования задержек внут- ренний таймер и работая по прерываниям. Однако в большинстве микроконт- роллеров PIC, выпускающихся в «многовыводных» корпусах (более 28 контактов), для реализации асинхронного обмена данными имеется встроенный коммуникационный порт. Одним из первых применений новых технологий производства БИС, появив- шихся в конце 60-х годов, было создание отдельной микросхемы асинхронного последовательного порта, называемого универсальным асинхронным приемопере- датчиком (Universal Asynchronous Receiver Transmitter — UART). К тому времени, когда начались разработки процессоров, эта микросхема UART1* уже производи- лась вовсю. В большинстве ПК, даже выпущенных в 70-х годах, имелся последо- вательный порт на базе микросхемы UART, также как и в большинстве современ- ных систем. Помимо узлов, отвечающих за побитовую передачу данных, конт- роль ошибок и обработку прерываний, большинство микросхем имели Иногда называемая также микросхемой адаптера интерфейса асинхронной связи (ACIA).
424 Часть III. Окружающий мир встроенный контроллер скорости передачи, который можно было конфигуриро- вать программно для задания требуемой скорости. Базовая структура микросхемы UART показана на Рис. 12.21. В любой подоб- ной микросхеме можно выделить три основные части. Сдвиговый регистр пере- датчика преобразует исходные данные из параллельных в последовательные для выдачи через вывод ТХ, обрамляя их старт- и стоп-битами. С этим регистром свя- зан буферный регистр, хранящий данные для последующей передачи. Регистр со- стояния содержит флаг (TBUF на рисунке), показывающий, что буфер пуст и го- тов для записи новых данных. Сдвиговый регистр приемника удаляет из принятой посылки старт- и стоп- биты, перегружая принятые данные в тот или иной буферный регистр. Одновре- менно с этим устанавливается флаг (RBUF в нашем случае), благодаря чему про- грамма может определить наличие новых данных. Эти данные необходимо про- читать из буферного регистра до сборки следующего пакета, в противном случае возникнет переполнение, и данные будут потеряны. Прием и передача кадра не взаимосвязаны, т.е. могут перекрываться, однако скорости обмена обычно делаются одинаковыми. Реальные приемопередатчики UART являются более сложными устройствами, позволяющими, например, передавать данные различной разрядности, а также обеспечивающие обнаружение различных ошибок. При этом, разумеется, услож- няется структура соответствующих регистров управления и состояния. Тем не ме- нее в основе модуля последовательного коммуникационного интерфейса (SCI), реали- зованного в микроконтроллерах PIC и более известного под названием USART (Universal Synchronous-Asynchronous Receiver Transmitter — универсальный синхрон- но-асинхронный приемопередатчик) (см. Рис. 12.22), лежит все та же архитектура UART. Этот модуль поддерживает два вида последовательного обмена: описанный
Контроллер скорости обмена последовательного порта SPBRG ГГ99' Тактовый сигнал Исходящие L h’19’ TXREG > Сдвиговый регистр приемника --------4— ---------1— Регистр данных приемника Регистр управления и состояния передатчика последовательные данные Входящие последовательные данные h’1A’ RCREG RC6/TX RC7/RX 7 Размер 6 переда* 5 Разрешение 4 3 2 Высоко- скоростной режим Сдвиговый 1 регистр 9-й бит 0 Й переда- X ваемого слова передачи 0 передатчика пуст ваемых Н данных Н ТХ9 TXEN SVNC BRGH TRMT TX9D I (R/W 0) (R/W 0) (R/W 0) (R/W 0) (R0) (R/W 0) (R 1) (W | h’98’ TXST£ г,- 7 Разрешение последова- тельного порта Размер 6 принима- емого слова 5 X 4 Разрешение приема Разрешение3 детектиро- : вания адреса Л в 2 Ошибка кадриро- вания Ошибка перепол- нения 9-й бит принятых данных 1 SPEN RX9 CREN ADDEN FERR OERR RX9D I (R/W0) (R/W 0) (R/W 0) (R/W 0) (R/W 0) (R0) (R 0) (RX) J h'18’ RCSTA Регистр управления и состояния приемника Рис. 12.22. Модуль синхронного последовательного интерфейса SCI, сконфигурированный для работы в асинхронном режиме Глава 12. Охуж эти биты! 425
426 Часть III. Окружающий мир выше асинхронный, осуществляемый при сброшенном бите SYNC регистра TXSTA[4] (состояние по умолчанию после сброса), и синхронный (SYNC = 1), при котором старт- и стоп-биты не используются. В последнем случае под линию так- тового сигнала задействуется дополнительный вывод RC6/CK — выход при пере- даче данных и вход при приеме. Вывод RC7/DT используется в качестве линии вво- да/вывода данных. При работе в синхронном режиме данные могут посылаться ли- бо побайтно, либо сплошным потоком. Именно из-за возможности работать в синхронном режиме этот модуль и называется USART, а не UART. Сейчас мы все свое внимание уделим асинхронному режиму, поэтому на Рис. 12.22, где показан формат обоих регистров состояния, обозначены только биты, соответствующие этому режиму работы. Основными элементами модуля USART являются сдвиговые регистры при- ема и передачи, а также связанные с ними буферные регистры и регистры состоя- ния, Для разрешения работы всего модуля USART необходимо установить бит SPEN регистра состояния приемника RCSTA (RCSTA[7J) в 1. Оба вывода RC6 и RC7, использующиеся соответственно для передачи и приема данных, должны быть сконфигурированы как входы1). Передача Работа передатчика разрешается установкой бита TXEN регистра состояния пере- датчика TXSTA (TXSTA[5]). Для передачи слова данных необходимо записать его в регистр передатчика TXREG, откуда он будет перегружен в сдвиговый регистр и побитно передан с вывода ТХ. Если требуется работать с 9-битными данными, то битТХ9 (TXSTA[6]) должен быть установлен в 1, а девятый бит данных необходи- мо записать в 0-й бит того же регистра перед загрузкой младших восьми битов в ре- гистр TXREG. Если сдвиговый регистр передачи не пуст, т.е. передача предыдуще- го слова еще не закончена, то новое значение останется в буфере TXREG и будет перегружено в сдвиговый регистр только после завершения передачи. Первый бит регистра состояния TXSTA отображает состояние сдвигового ре- гистра передатчика, тогда как флаг прерывания TXIF, расположенный в регистре PIR1, автоматически устанавливается в 1 при опустошении буфера TXREG (при его готовности к загрузке новых данных). Если это прерывание требуется в про- грамме, необходимо установить соответствующий бит маски TXIE регистра PIE1 (PIE1 [4]); см Рис. 7.5 на стр. 223. ФлагТХ1Е автоматически сбрасывается при за- писи в регистр TXREG, поэтому нет необходимости вручную обнулять его в про- цедуре опроса или в обработчике прерывания. Прием После обнаружения на выводе RX старт-бита последующие восемь или девять би- тов задвигаются в сдвиговый регистр приемника, откуда после завершения при- ема перегружаются в 2-уровневый буферный регистр RCREG, независимо от то- го, что в этот момент происходит в секции передатчика. Причем принятые данные сохраняются в регистре верхнего уровня, а содержимое последнего авто- ]) В более «миниатюрных» моделях обычно используются линии порта В (RB1 и RB2 соот- ветственно). В этом случае последний должен быть сконфигурирован как выход.
Глава 12. Ох уж эти биты! 427 матически перегружается в регистр нижнего уровня при условии, что в нем от- сутствуют данные, ожидающие считывания. При появлении в этом регистре дан- ных устанавливается флаг прерывания от приемника RCIF, который может использоваться для генерации прерывания при установленном бите маски RCIE регистра PIE1 (а также установленных битах GIE и PEIE). При чтении регистра флаг RCIF автоматически сбрасывается. Если при этом в регистре верхнего уров- ня находились очередные данные, они перегружаются в регистр младшего уров- ня, и флаг RCIF устанавливается снова. Если в момент приема очередного слова данный 2-уровневый буфер приемни- ка окажется полон, то устанавливается флаг ошибки переполнения OERR (RCSTA[1]), а принятое значение теряется. При этом оба слова, находящиеся в буфере, по-прежнему доступны для чтения. Однако для сброса флага OERR необ- ходимо сбросить логику приемника, сбросив бит CREN (RCSTA[4]), а затем уста- новив его снова. Флаг ошибки кадрирования FERR в RCSTA[2] устанавливается в 1, если после приема битов данных не было обнаружено стоп-бита. Флаг FERR (как и девятый бит принятых данных) буферизуется вместе с принимаемыми данными. Поэтому значение указанного флага необходимо проверять перед считыванием содержи- мого RCREG, поскольку во время этой операции изменяется состояние буфера и, соответственно, значение указанных битов. Все версии модуля USART позволяют работать с 8- или 9-битными данными независимо при передаче и при приеме. Для последнего необходимо сбросить бит RX9 регистра RCSTA (RCSTA[6]). Обычно этот дополнительный бит исполь- зуется для контроля четности. Другим применением 9-битных данных является реализация сети из асинхронных устройств. В этом случае девятый бит исполь- зуется в качестве селектора, показывающего содержимое пакета — данные или адрес устройства. Примитивная сеть, в которой используется этот принцип, изображена на Рис. 12.23. При 8-битном адресе можно адресовать до 255 ведо- мых устройств (один адрес при этом резервируется для широковещательных вызовов). Для облегчения работы в подобной сети последние версии модуля USART можно сконфигурировать таким образом, чтобы при приеме пакета с установ- ленным девятым битом автоматически устанавливался флаг RCIF. Эта функция включается установкой в 1 бита ADDEN (RCSTA[3]). При одновременно уста- новленных битах ADDEN и RX9 любой кадр со сброшенным старшим битом бу- дет игнорироваться, а принимаемые данные не будут загружаться в буфер при- емника. Если же старший бит окажется равным 1, то принятый байт будет ско- пирован из сдвигового регистра приема в буфер приемника с одновременной установкой флага RCIF. Ведомое устройство может прочитать этот адрес из RCREG, при этом флаг RCIF будет сброшен. Если адрес верен, то ведомый мо- жет сбросить бит ADDEN и принимать все последующие кадры данных обыч- ным образом. При этом ведомый может продолжать контролировать значение девятого бита в RX9D, прекращая прием при обнаружении кадра с установлен- ным 9-м битом.
428 Часть III. Окружающий мир Ведущий Ведомый 3 Рис. 12.23. Локальная сеть, использующая асинхронный последовательный протокол Контроллер скорости обмена SPBRG Этот узел представляет собой программируемый 8-битный счетчик, на выходе ко- торого имеется отключаемый делитель на 4. Конфигурация данного счетчика мо- жет задаваться пользователем для получения частот выборки и сдвига, соответ- ствующих желаемой скорости обмена. Отталкиваясь от значения частоты кварцевого генератора микроконтроллера, получаем: Скорость обмена = XTALxl°6 — низкоскоростной режим (BRGH = 0), 64х(% + 1) Скорость обмена = XTALxlQ — высокоскоростной режим (BRGH = 1), 16х(Х + 1) где X— 8-битное число, находящееся в регистре SPBRG. При использовании низ- коскоростного режима значение X определяется из выражения XTALx 106 X =--------------1 • Так, если при частоте резонатора 20 МГц нам потребуется 64 х (BAUD) скорость обмена, равная 9600 бод, то при Х= 31 действительное значение скорости будет равно 9766, т.е. ошибка составит ±1.73%. При частоте резонатора, равной 20 МГц, максимальная скорость обмена составляет 312 500 бод, а минимальная — 1221 бод. Скорость передачи, равную 1.25 Мбод, можно получить с резонатором частотой 20 МГц, используя высокоскоростной режим модуля при Х= 1. В действительности контроллер скорости обмена формирует тактовый сигнал с частотой в 16 раз больше заданной скорости, чтобы модуль мог за время переда- чи бита сделать три выборки в окрестностях середины битового интервала и при-
Глава 12. Ох уж эти биты! 429 нять мажоритарное решение о значении этого бита. Такая схема увеличивает на- дежность передачи данных в системах с высоким уровнем помех. Чтобы проиллюстрировать использование модуля USART, перепишем наши подпрограммы getchar и putchar таким образом, чтобы они использовали ап- паратные возможности модуля. Первым делом (в основной программе) мы долж- ны сконфигурировать контроллер скорости обмена, а также регистры управления и состояния приемника и передатчика. Предполагая, что константы XTAL и BAUD уже определены программистом, возложим вычисление числа X, которое мы впоследствии запишем в регистр SPBRG, на ассемблер. С учетом всего сказанно- го инициализационный код будет выглядеть следующим образом: include "pl6f877а.inc" #define #define #define BAUD d'4800' XTAL d'8' X ((XTAL*d' ; Скорость обмена 4800 бод ; 8-МГц резонатор 1000000')/(d164'*BAUD))-1 START bsf STATUS,RPO ; Переключаемся в 1-й банк movlw X ; Загружаем X в контроллер скорости обмена movwf SPBRG movlw b'00100000 ; 8 битов данных, передатчик включен, movwf TXSTA ; низкоскоростной режим bcf STATUS,RPO ; Возвращаемся обратно в 0-й банк movlw b ’ 10010000' ; USART включен, 8-битные данные movwf RCSTA ; Приемник включен Заметьте, что для корректной работы модуля USART в микроконтроллерах линейки PIC16F87X требуется, чтобы оба вывода RX и ТХ были сконфигурирова- ны как входы. Поскольку после сброса микроконтроллера выводы находятся в этом состоянии по умолчанию, в приведенном фрагменте отсутствуют команды для конфигурирования регистра TRISC. Как было отмечено в примечании на стр. 426, другие члены семейства могут потребовать других настроек для выводов ИХиТХ. Код самих подпрограмм приведен в Программе 12.15. Подпрограмма putchar просто опрашивает флаг TXIF, ожидая его установки, а затем копирует байт данных в регистр передачи TXREG. Подпрограмма приема символа GETCHAR будет немного сложнее из-за наличия контроля ошибок. Подпрограмма постоянно опрашивает состояние флага RCIF, который устанавливается при наличии доступных для чтения дан- ных. При отсутствии каких-либо проблем в переменной ERR возвращается число h’00’, при возникновении ошибки кадрирования возвращается —1, при переполнении-----2, а при одновременном обнаружении обеих ошибок-----3. В последних двух случаях осуществляется сброс бита OERR посредством сброса логики приемника. После проверки на наличие ошибок данные считываются из буфера приемника RCXREG. Контроль ошибок всегда выполняется перед считы- ванием данных, чтобы избежать непреднамеренного изменения этих флагов ре- гистра состояния приемника.
430 Часть III. Окружающий мир Программа 12.15. Подпрограммы ввода/вывода с использованием модуля USART ; * ФУНКЦИЯ : Передает 8-битное значение по асинхронному каналу ; * РЕСУРСЫ : Модуль USART ; * ВХОД : 8-битное значение в DATA-OUT ; * ВЫХОД : Содержимое DATA_OUT не изменяется, байт передан PUTCHAR btfss PIR1,TXIF goto PUTCHAR mo v f DATA_OUT,w movwf TXREG ; Проверим, полон ли буфер передатчика? ; ЕСЛИ нет, ТО проверим снова ; ИНАЧЕ считываем значение ; и копируем его в регистр передатчика return / ; * ФУНКЦИЯ : Принимает 8-битное значение по асинхронному каналу* ; * РЕСУРСЫ : Модуль USART * ; * ВХОД : Нет * ; * ВЫХОД : Принятый байт - в DATA_IN. * ; * ВЫХОД : ERR = 00, если не было ошибок. При ошибке * ; * кадрирования ERR = -1, при переполнении ERR = -2, * ; * при наличии обеих ошибок ERR = -3 * • ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ GETCHAR clrf ERR ; Обнуляем признак ошибки btfss PIR1,RCIF ; Проверим, есть ли символ? goto GETCHAR ; ЕСЛИ нет, ТО проверим снова ; Обработка ошибок btfss RCSTA,FERR ; Была ошибка кадрирования? goto CHECK_OERR ; ЕСЛИ нет, ТО проверим на переполнение movlw -1 ; ИНАЧЕ фиксируем ошибку CHECK_OERR btfss RCSTA,OERR ; Было переполнение? goto GET_EXIT ; ЕСЛИ нет, ТО завершаем обработку ошибок decf ERR, f ; Иначе фиксируем ошибку decf ERR,f bcf RCSTA,CREN ; и сбрасываем логику приемника bsf RCSTA,CREN GET_EXIT movf RCREG,W ; Читаем байт данных movwf return end DATA-IN ; и помещаем во временный регистр В некоторых системах нельзя позволить процессору тратить машинное время на ожидание символа, который придет неизвестно когда. Специально для таких случаев можно было бы написать альтернативную подпрограмму приема, назвав ее, скажем, get ch. Эта подпрограмма будет возвращать ERR = +1 при отсут- ствии данных в буфере. И все же наилучшим выходом из ситуации будет генера-
Глава 12. Ох уж эти биты! 431 ция прерывания при обнаружении входящего символа, а не простой опрос флага этого прерывания. В языке Си каналы асинхронного обмена могут использоваться в качестве стандартных потоков ввода/вывода. Что же касается конкретно компилятора CCS, то в нем имеется директива #use rs232 (), посредством которой можно сообщить компилятору, какие выводы будут использоваться для приема и переда- чи данных, а также какой должна быть скорость обмена. Стандартные Си-функ- ции ввода/вывода, такие как print f (), используют эти выводы для связи со стандартным каналом. С помощью данного компилятора можно реализовать множество не связанных между собой асинхронных каналов. В качестве примера, в Программе 12.16 приведена реализация на языке Си асинхронной дуплексной связи с терминалом (см. Рис. 12.25), работающим на скорости 9600 бод. К выводу RB0 подключена кнопка, и, когда оператор посыла- ет микроконтроллеру символ ‘G’, тот начинает опрашивать состояние этой кноп- ки. При ее замыкании (появлении на выводе сигнала НИЗКОГО уровня) терми- нал извещает оператора строкой «Кнопка 1 замкнута». Для ввода и вывода дан- ных воспользуемся стандартными функциями print f () >) и get ch () >). Программа 12.16. Использование дуплексного асинхронного канала в языке Си #include <16f877a.h> #use delay (clock = 20000000) /* Сообщаем компилятору частоту резонатора (20 МГц) */ /* Сообщаем компилятору о требуемой скорости обмена и используемых выводах */ #use rs232(baud=9600, xmit=PIN_Al, rcv=PIN_A2) #bit SWITCH1 =6.0 /* Кнопка подключена к RB0 */ void main(void) { while(TRUE) { if (getchO == 1G1) { while (SWITCH1) {;} /* Пока кнопка разомкнута (1), ничего не делаем */ printf("Кнопка 1 замкнута \п"); ) } Поскольку в качестве выводов приемника и передатчика используются вы- воды RA1 и RA2, компилятор сгенерирует код программно-реализованного UART, подобный использованному нами в Программе 12.14. Именно по этой причине компилятору необходима информация о частоте кварцевого резонато- ра микроконтроллера — для формирования требуемых задержек. Если же мы укажем выводы RC6 и RC7, то для реализации последовательного интерфейса компилятор автоматически воспользуется встроенным модулем USART. В на-
432 Часть III. Окружающий мир шем примере для реализации программного UART потребовалось 146 команд, тогда как при использовании модуля UART размер программы составил всего 74 команды. Однако для реализации полноценного соединения недостаточно одного толь- ко выбора подходящего протокола. При работе микроконтроллеров PIC исполь- зуются напряжения нормальных логических уровней и токи, которые предназна- чены для организации соединений на расстояниях не более 30 см (1 фут). Хотя при соблюдении определенных правил0 это расстояние можно значительно уве- личить, при относительно больших скоростях обмена должны использоваться принципиально другие методы формирования сигналов. В эпоху электромеханических терминалов широко использовался интерфейс «Токовая петля 20 мА», ставший стандартом де-факто. В этом интерфейсе для обозначения состояний лог. 0 и лог. 1 использовались разные значения тока: 0 мА и 20 мА соответственно. Привязка к току, а не напряжению позволяла избежать влияния потерь в линии (поскольку вытекающий ток должен быть равен втекаю- щему), и, кроме того, тока такой величины было достаточно для непосредствен- ного управления электромагнитным реле приемного устройства. Источники тока реализуются посредством источников высокого напряжения, последовательно с которыми включается большое сопротивление. Именно из-за последнего величины постоянных времени получаются настолько большими, что хотя они и удовлетворяли требованиям эпохи скоростей в 110 бод, но для использо- вания в электронных терминалах, UART и модемах не годятся. В качестве стандарт- ного интерфейса для подключения терминального оборудования (Data Terminal Equipment — DTE) к устройствам передачи данных (Data Circuit Equipment — DCE), как правило к модемам, в 1969 году был предложен интерфейс RS-232* 2). В специфи- кации этого интерфейса были определены не только различные уровни сигналов, как показано на Рис. 12.24, а, но и различные линии управления и квитирования, некоторые из которых показаны на Рис. 12.24, г и Рис. 12.25. Например, выдачей активного уровня на линию квитирования готовности к передаче (Clear То Send — CTS) модем может сообщить локальному терминалу о том, что удаленный терминал освободил телефонную линию. Для организации дуплексной линии связи необхо- димо две линии данных плюс общий провод как опция. Стандарт RS-232 рассчитан на дальность до 15 м (50 футов) при максимальной скорости 20 Кбод, что достигается использованием для передачи лог. 0 (это состоя- ние линии часто называется space) напряжения +12 В, а для передачи лог. 1 (mark) — напряжения —12 В. Минимальное же напряжение, при котором приемник может распознавать состояние линии, составляет ±3 В. Интерфейс стандарта RS-423 (1978 г.), показанный на Рис. 12.24, б, похож на RS-232, но позволяет управлять не- сколькими (до десяти) приемными устройствами на расстоянии 1.2 км (6000 футов) при скорости до 1 Кбод и на расстоянии до 12 м (40 футов) при скорости 100 Кбод. Интерфейсы RS-232 и RS-423 являются несимметричными (или небалансны- ми), поскольку приемник контролирует потенциал между сигнальной линией и Г) А иногда просто не обращая на это внимание! 2) Стандарт EIA 232-Е в США и МККТТ V.24 в Европе.
a) RS-232C Логические уровни UART _П_ Канал 1 Логические уровни^ 1070 Гц 1270 Гц 2025 Гц TX_DATA RX_DATA DCD CTS RTS г) FSK-сигнал (с частотной манипуляцией) Рис. 12.24. Некоторые варианты последовательной передачи данных Глава 12. Ох уж эти биты! 433
434 Часть III. Окружающий мир локальным общим проводом. И хотя «земли» передатчика и приемника, как пра- вило, объединяются между собой, импеданс этой линии при ее значительной протяженности может привести к появлению большой разности потенциалов на ее концах, в результате чего уменьшится помехоустойчивость. Более того, любая наведенная извне помеха вносит в различные сигналы неодинаковые искажения, что вызвано неидентичностью электрических характеристик сигнальных линий. Поэтому такие интерфейсы и называются несимметричными. Интерфейсы RS-422 (1978 г.) и RS-485 (1983 г.) относятся к классу симметрич- ных. В таких интерфейсах каждая линия связи состоит из двух проводников, обыч- но свитых между собой, называемых витой парой. Логические уровни в такой ли- нии представляются разностью потенциалов между проводниками, а не относи- тельно общего провода. Обозначим проводники буквами А и В, тогда логическому нулю будет соответствовать соотношение А < В, алогической единице — А > В. На стороне приемника разницы потенциалов, превышающей значение ±200 мВ, будет достаточно для устойчивого распознавания логического уровня, при том, что пере- датчик обычно формирует сигналы ДИ= ±5 В. Так как проводники А и В имеют одинаковые электрические характеристики и свиты друг с другом, они совершенно идентичны для наводимых помех. Поскольку один и тот же сигнал окажется при- ложенным к обоим проводникам, а приемник контролирует разность потенциалов, отсекая синфазное напряжение величиной до ±7 В, очевидно, что помехоустойчи- вость такой симметричной линии связи гораздо выше, чем несимметричной. Име- ющиеся в продаже кабели с витыми парами, используемые в локальных сетях (Local Area Network — LAN), обычно содержат три или четыре пары проводников, причем каждая пара имеет свой шаг скрутки. Это сделано для того, чтобы умень- шить уровень перекрестных помех между линиями. В шине USB, применяющейся в ПК, для передачи сигнала тоже используется симметричная линия связи. Основным отличием между стандартами RS-422 и RS-485 является возмож- ность использования в последнем нескольких передатчиков, так же как и прием- ников, что позволяет реализовать многоабонентскую сеть. Поскольку в каждый момент времени может быть активен только один передатчик, буфер передатчика должен иметь вход разрешения для выбора ведущего устройства. На линии RS-422 может быть только один передатчик, поэтому нет необходимости его запрещать. Интерфейс RS-232 изначально был разработан для организации соединения терминал-модем, однако в настоящее время сфера его применения намного шире (см. Рис. 12.25). На Рис. 12.24, г показана простая дуплексная система с частот- ной манипуляцией (Frequency Shift Keying — FSK), в которой состояния mark/space в одном канале представляются сигналами с частотами 1070/1270 Гц, а в другом — 2025/2225 Гц. Указанные частоты хорошо подходят для передачи по обычной телефонной линии, имеющей полосу пропускания 300 Гц...3.4 кГц. Ли- нии квитирования DCD (обнаружение несущей), CTS (готовность к приему) и RTS (готовность к передаче) используются для аппаратного управления потоком. В большинстве модемов в настоящее время используется фазовая манипуля- ция (Phase Shift Keying — PSK). При этом для кодирования 3-битных групп кодов в одном временном интервале обычно используется не менее восьми различных фаз сигнала одной и той же частоты, сдвинутых друг относительно друга на 45°.
Глава 12. Ох уж эти биты! 435 За счет этого можно увеличить скорость передачи данных при той же скорости передачи сигналов, хотя и ценой снижения помехоустойчивости. В качестве примера, на Рис. 12.25 показано соединение между микроконтрол- лером PIC и последовательным портом компьютера (или любым другим устрой- ством, имеющим порт RS-232). Микросхема МАХ233 компании Maxim является сдвоенным приемопередатчиком RS-232, осуществляющим двустороннее преоб- разование сигналов +12 В о О В (лог. 0) и -12 В о +5 В (лог. 1). Если линии кви- тирования не используются, что обычно имеет место при реализации простейших линий связи, ПК можно «обдурить», соединив выводы порта так, как показано на Рис. 12.25 (выход RTS соединен с входом CTS, а выход DTR — с входом DSR). В этом случае ПК будет считать, что последовательный интерфейс постоянно го- тов к приему данных. Микросхема МАХ232 имеет в общей сложности по два бу- фера на прием и на передачу, поэтому при необходимости ее можно будет исполь- зовать также для буферирования линий квитирования. На Рис. 12.25 тот же микроконтроллер управляет полудуплексной линией связи стандарта RS-485, используя преобразователь уровней МАХ485 фирмы Maxim. Оба буфера имеют собственные входы разрешения с противоположными активными уровнями (буфер передатчика — ВЫСОКИЙ, а буфер приемника — НИЗКИЙ). Микроконтроллер может включать соответствующий буфер в зави- симости от направления обмена. Также с помощью микросхемы МАХ485 можно реализовать дуплексный канал с использованием двух линий связи. Данные по интерфейсу RS-485 можно передавать и по синхронному протоко- лу. При этом, разумеется, необходимо будет выделить отдельный буфер для пере- дачи тактового сигнала. Примеры Пример 12.1 В Примере 11.2 мы написали подпрограмму, сравнивающую фиксированное значение TRIP с байтом, считанным из порта В. В ряде случаев может потребо- ваться подстройка программы под изменяющиеся условия работы путем модифи- кации порогового значения по командам извне. Вместо того чтобы использовать второй порт ввод/вывода, было предложено передавать новое значение в после- довательном виде на вывод RA4, используя вывод RA3 для подключения к линии тактовых сигналов. Предполагая, что состояние линии данных стабильно при ВЫСОКОМ уровне на линии тактового сигнала, напишите подпрограмму, счи- тывающую новое значение и записывающую его в ячейку TRIP. Решение Один из возможных вариантов решения этой задачи приведен в Программе 12.17. Эта подпрограмма отслеживает появление ВЫСОКОГО уровня на линии тактового сигнала, при котором, согласно условию задания, сигнал на линии данных стабилен. Изменяя значение бита переноса в соответствии с состо-
w О\ Часть III. Окружающий мир DCD — обнаружение несущей RI — индикатор вызова RXD — принимаемые данные CTS — готовность к приему Преобразователь уровней (логические — RS-232) TXD — передаваемые данные RTS — готовность к передаче DTR — готовность терминала DSR — готовность данных Рис. 12.25. Взаимодействие с ПК по интерфейсу RS-232 и с внешним миром по интерфейсу RS-422/485
Глава 12. Ох уж эти биты! 437 янием линии данных и выполняя операцию сдвига через перенос, осуществляет- ся побитовая загрузка нового значения в память. Причем очередная итерация цикла завершается только после того, как на линии тактового сигнала вновь по- является НИЗКИЙ уровень. Программа 12.17. Подпрограмма изменения порогового значения из Программы 11.6 f ; ФУНКЦИЯ : Задвигает значение порога TRIP, которое затем * ; вход ; ВЫХОД используется в качестве операнда п/п СОМР * : Изменение значения битов данных на RA4 происходит* при НИЗКОМ уровне на RA3 * : COUNT = 00, принятое значение - в TRIP * SER_TRIP movlw 8 Счетчик битов movwf COUNT SER_TRIP_ LOOP btfss PORTA,3 Ждем 1 на такт. линии goto SER_TRIP_LOOP bcf STATUS,C Обнуляем флаг переноса btfsc PORTA,4 На линии данных 1? bsf STATUS,C ЕСЛИ да, ТО устанавливаем флаг переноса rlf TRIP,f Вдвигаем бит SER_TRIP_ LOOP2 btfsc PORTA,3 Дожидаемся появления 0 на такт. линии goto SER_TRIP_LOOP2 decfsz COUNT,f goto SER_TRIP_LOOP return Эта подпрограмма похожа на подпрограмму SPI_READ (см. Программу 12.3), за исключением того, что тактовый сигнал формируется внешним устройством, т.е. микроконтроллер PIC в данном случае выступает в роли ведомого. В реальных системах, где ведомый микроконтроллер должен обладать возможностью сооб- щать ведущему о необходимости передачи нового байта, такая схема может вы- звать определенные проблемы. Указанную возможность можно реализовать, ис- пользуя дополнительную линию порта ввода/вывода для передачи квитирующего сигнала CTS. Этот сигнал будет генерировать прерывание на стороне ведущего и инициировать обмен. Конечно же, этим ведущим может быть другой микроконт- роллер PIC, и в этом случае мы получим очень простой вариант объединения двух микроконтроллеров. При использовании микроконтроллера с встроенным пос- ледовательным портом прерывания могут генерироваться автоматически — такой подход наиболее часто используется для реализации многопроцессорных сетей. Пример 12.2 Напишите подпрограмму I2C_IN, обратную по своему действию подпрограмме I2C_OUT из Программы 12.9. Предполагается, что в вашем распоряжении имеются те же переменные, а принятое значение должно сохраняться в регистре DATA IN.
438 Часть III. Окружающий мир Решение Подпрограмма I2C_IN, код которой приведен в Программе 12.18, загружает принимаемое значение в регистр DATA_IN посредством восьми операций сдвига через флаг переноса; значение флага соответствует состоянию вывода SDA. Од- новременно на линии тактового сигнала SCL формируются импульсы в соответ- ствии со спецификацией шины 12С, как и в подпрограмме I2C_OUT из Программы 12.9. В соответствии с этим протоколом ведущий приказывает ведо- мому остановить посылку данных путем выдачи на линию SDA ВЫСОКОГО уровня во время 9-го тактового импульса (см. Рис. 12.13). Наличие во время этого временного интервала НИЗКОГО уровня на линии данных называется АСК (подтверждение), а наличие ВЫСОКОГО уровня — NACK (нет подтверждения). Наша подпрограмма может генерировать оба сигнала, в зависимости от значения переменной ACKNO, которое задается вызывающей подпрограммой. Если при вы- зове подпрограммы содержимое регистра ACKNO равно нулю, то после приема 8-го бита данных отсылается АСК. Соответственно, любое ненулевое значение регистра ACKNO приведет к отсылке ведомому сигнала NACK. После получения этого сигнала ведомый прекратит передачу и начнет отслеживать появление на шине состояний СТАРТ/СТОП. Программа 12.18. Подпрограмма получения байта по шине 12С ; * ФУНКЦИЯ : Принимает байт от ведомого, отсылая * ; * в ответ АСК или NACK * ; * ВХОД : ACKNO =00 для отсылки АСК, ИНАЧЕ NACK * ; * РЕСУРСЫ : п/п START и STOP, макрокоманда Delay_600 * ; * ВЫХОД : Байт данных, посланный ведомым - в DATA_IN, * ; * ведомому отослан АСК или NACK, на SCL - НИЗКИЙ уровень * I2C_IN bcf INDF,SCL ; Гарантируем наличие НИЗКОГО уровня на SCL movlw 8 ; Счетчик цикла = 8 movwf COUNT I2C_IN_LOOP bcf INDF,SCL Delay_600 Delay_600 bsf INDF,SCL bcf STATUS,C btfsc INDF,SDA bsf STATUS,C rlf DATA_IN,f decfsz COUNT,f goto I2C_IN_LOOP Теперь посмотрим, что надо bcf INDF,SCL ; Формируем на тактовой линии ; отрицательный импульс ; минимальной длительности ; Сбросим флаг переноса ; Проверяем значение принятого бита ; ЕСЛИ 1, ТО устанавливаем флаг С ; и вдвигаем его в регистр ; Декрементируем счетчик цикла ; и повторяем восемь раз отослать (АСК или NACK) ; Выставим на SCL НИЗКИЙ уровень
Глава 12. Ох уж эти биты! 439 bsf INDF,SDA movf ACKNO,f btfsc STATUS,Z bcf INDF,SDA Delay_600 Delay_600 bsf INDF,SCL Delay_600 bcf INDF,SCL ; Высвободим линию данных (NACK) ; Проверим регистр ; ЕСЛИ не равно О, ТО ничего не делаем ; ИНАЧЕ выставляем на линию данных ; НИЗКИЙ уровень (АСК) ; Удерживает на тактовой линии ; НИЗКИЙ уровень ; Теперь выставляем ВЫСОКИЙ уровень ; На линии SCL оставляем НИЗКИЙ уровень return Пример 12.3 Во многих микроконтроллерных устройствах требуется сохранять данные в энергонезависимой памяти для того, чтобы считывать их после повторного вклю- чения. В качестве примера можно указать счетчик суммарного пробега, пройден- ного автомобилем, значение которого должно сохраняться независимо от состоя- ния аккумулятора. Обычно такого рода данные хранятся в EEPROM-памяти, ко- торая была подробно описана на стр. 43. Хотя во многих микроконтроллерах PIC имеется встроенный модуль EEPROM, о котором мы поговорим в главе 15, его емкость ограничена в лучшем случае 256 байтами1*. При больших объемах необ- ходимо задействовать внешние микросхемы EEPROM. Большинство таких мик- росхем используют интерфейс SPI или 12С, как, например, микросхема 24LCXXX, применяющаяся в схеме на Рис. 12.26. Микросхемы EEPROM с после- довательным интерфейсом серии 24LCXXX, выпускающиеся в 8-выводных кор- пусах, имеют емкость от 1 Кбит (24LC01B) до 512 Кбит (24LC512), организован- ных побайтно; т.е. от 128 байт до 64 Кбайт. Рис. 12.26. Применение 12С-совместимых микросхем EEPROM серии 24ХХХ ’’ Некоторые наиболее развитые модели имеют EEPROM-память объемом 512 байт.
440 Часть III. Окружающий мир Микросхемы EEPROM серии 24ХХХ имеют следующие характеристики: • 12С-совместимый интерфейс с максимальной частотой 400 кГц (KDD = 5 В) и 100 кГц при KDD = 2.5 В. • Возможность защиты содержимого микросхемы от записи (режим ПЗУ), используя вывод WP. • Типичная длительность цикла записи — 2 мс. • Долговечность — не менее 1 000 000 циклов записи на ячейку. • Ток потребления — 3 мА в режиме записи, 1 мА в режиме чтения и 100 мкА в режиме ожидания. • Встроенный генератор высокого напряжения для программирования. На примере микросхемы 24LC01B покажем, какие операции необходимо вы- полнить для инкрементирования содержимого трех ячеек, расположенных в младших адресах, где хранится суммарный путь в милях или километрах (единица измерения меняется в зависимости от рынка, для которого предназначен автомо- биль). Предположим, что каждый километр/милю генерируется прерывание для микроконтроллера и что наш код является частью процедуры обработки преры- вания. Кроме того, в нашем распоряжении имеются ресурсы, используемые под- программами, коды которых приведены в Программах 12.9 и 12.18. Решение Прежде чем приступить к написанию собственно кода, необходимо познако- миться с протоколом обмена, поддерживаемым микросхемами серии 24ХХХ. Этот протокол в виде временных диаграмм сигналов на линии данных приведен на Рис. 12.27. 5 п IW 10 1 О XXX О 0 Байт адреса а) Запись трех байтов 5 б) Чтение трех байтов Рис. 12.27. Временные диаграммы операций чтения и записи микросхем EEPROM
Глава 12. Ох уж эти биты! 441 Инициирование обмена всегда осуществляется ведущим (микроконтролле- ром), который формирует на шине состояние СТАРТ, после чего передает управля- ющий байт. В этом байте содержится адрес ведомого 1010, адрес конкретной мик- росхемы А2А1 АО, а также бит R/W: | 1 | 0 | 1 | 0 |А2|А1 |АЗ | R/w|. Однако, хотя в управляющий байт и включены биты адреса (а соответствующие им выводы мик- росхемы показаны на Рис. 12.26), в последних версиях микросхем EEPROM малого объема возможность изменения адреса микросхемы не реализована. Так сделано потому, что в случае необходимости увеличения объема памяти гораздо проще и эффективнее заменить микросхему на другую, большей емкости, поскольку цоко- левка у всех микросхем серии одинакова. Так, заменив микросхему 24LC01B на 24LC08B, мы получим восьмикратное увеличение объема без вмешательства в схе- му самого устройства. Микросхемы же большей емкости, такие как 24LC256, ис- пользуют выводы адреса для расширения системы, так как в этом случае придется подключать к шине дополнительные микросхемы. Так, при использовании восьми микросхем 24LC512 мы получим энергонезависимую память объемом 512 Кбайт. Как правило, после управляющего байта передается значение адреса в памя- ти, куда будут записываться или откуда будут считываться данные. Если говорить конкретно о микросхеме 24LC01B, то в ней данные организованы в виде 128 од- нобайтных ячеек, каждая из которых может быть записана или считана независи- мо от других. То есть в ней используется 7-битный адрес, для передачи которого вполне достаточно одного байта. Эта схема годится также для микросхемы 24LC02B, однако для всех остальных микросхем требуется адрес, разрядность ко- торого больше 8 бит. В микросхемах от 24LC04 до 24LC16 для передачи трех стар- ших битов адреса используются биты А[2:0] управляющего байта, в результате че- го разрядность адреса увеличивается до 11 бит. Микросхемы EEPROM, имеющие объем более 16 Кбит (24LC32 и далее), требуют уже двух байтов адреса, которые передаются вслед за управляющим байтом. Байты адреса посылаются в EEPROM в пакетах записи, как показано на Рис. 12.27, а, т.е. со сброшенным битом R/W управляющего байта. Если в данную ячейку необходимо записать байт данных, то он будет передан сразу же после байта адреса, а затем на шине будет сформировано состояние СТОП. Если же до формирования состояния СТОП будет передано более одного байта, то они будут сохранены во внутреннем буфере небольшого объема, а реальное программиро- вание начнется только после появления состояния СТОП. Микросхема 24LC02B может запомнить до восьми байтов (одну страницу) данных, инкрементируя при получении очередного байта три младших бита адреса. При переходе через грани- цу страницы ранее загруженные значения перезаписываются. Размер страницы зависит от модели устройства. Например, в 24LC256 используются 64-байтные страницы. На Рис. 12.27, а показан процесс записи трех байтов в микросхему 24LC01B. Поскольку используемые нами ячейки расположены в младших адре- сах (h’00’, h’01 ’ и h’02’), то переполнения не произойдет. После обнаружения микросхемой состояния СТОП запускается процесс за- писи буферированных данных в заданные ячейки. Длительность процесса про- граммирования в микросхемах семейства 24LCXXX составляет от 2 до 5 мс. Если в течение этого времени ведущий попытается обратиться к микросхеме, то она
442 Часть HI. Окружающий мир при получении первого (управляющего) байта отошлет NACK, что мбжно ис- пользовать в качестве индикатора занятости. В Программе 12.19 этот бит прове- ряется при посылке управляющего байта. Процесс чтения содержимого EEPROM, показанный на Рис. 12.27, б, немно- го сложнее. Как и в предыдущем случае, транзакция начинается посылкой адреса устройству. Затем ведущий формирует состояние ПОВСТАРТ, после чего повтор- но передает управляющий байт с установленным битом R/W, свидетельствующий о том, что ведущий будет работать в качестве приемника данных. После этого микросхема EEPROM отсылает байт, расположенный по адресу, указанному ве- дущим, который при получении байта выставляет подтверждение. Процесс пере- дачи и приема байтов (с постоянным инкрементированием адреса) будет продол- жаться до тех пор, пока ведущий в ответ на очередной байт не выставит NACK. После этого ведомый освободит шину и ведущий сможет сформировать состоя- ние СТОП. Если первый пакет, содержащий адрес ячейки памяти, был опущен, то чтение начнется с адреса, на единицу большего того, к которому производи- лось последнее обращение. Программа, код которой приведен в Программе 12.19, в точности выполняет все операции, показанные на Рис. 12.27. После посылки начального адреса h’00’ микроконтроллер переходит в режим чтения и считывает три последовательно расположенных байта из EEPROM-памяти. Чтение завершается возвратом NACK с последующим формированием состояния СТОП. Поскольку все три байта расположены друг за другом, используется автоматическое инкрементиро- вание адреса. После инкрементирования 3-байтного значения оно передается об- ратно в EEPROM после повторной передачи адреса 1-й ячейки (h’00’). Процесс завершается формированием состояния СТОП. Программа 12.19. Инкрементирование значения одометра, хранящегося в энергонезависимой памяти EXTRA_MILE ; Считываем три байта, хранящиеся ; по адресам 00:01:02h call START ; Начинаем с формирования состояния СТАРТ ; 1-й управляющий байт - адрес микросхемы ----------------------------- movlw b'10100000'; Адрес ведомого, ведущий-передатчик movwf DATA_OUT ; Копируем в буферный регистр call I2C_OUT ; Передаем movf ERR,f ; Проверяем наличие подтверждения btfsc STATUS,Z ; ЕСЛИ ноль, ТО продолжаем goto EXTRA_MILE ; ИНАЧЕ пробуем снова ; Адрес 00 ------------------------------------------------------------ clrf DATA_OUT ; Формируем адрес call I2C_OUT ; Передаем ; 2-й управляющий байт для инициирования операции чтения -------------- call START movlw b'10100001'; Адрес ведомого, ведущий-приемник movwf DATA_OUT ; Копируем в буферный регистр
Глава 12. Ох уж эти биты! 443 call I2C_OUT ; : Передаем ; Теперь считываем три байта данных clrf ACKNO ; : Разрешаем формирование подтверждения call I2C_IN ; : Считываем старший байт из ячейки с адресом 00h movf DATA_IN,w ; : Берем байт movwf MSB ; : и сохраняем его в памяти call I2C_IN ; Считываем средний байт из ячейки с адресом Olh movf DATA-IN,w ; ; Берем байт movwf NSB ; ; и сохраняем его в памяти incf ACKNO,f ; ; Выставить NACK call I2C_IN ; ; Считываем старший байт из ячейки с адресом 02h movf DATA-IN,w ; ; Берем байт movwf LSB ; : и сохраняем его в памяти call STOP ; ; Завершаем операцию чтения ; Теперь инкрементируем 3-байтное число incf LSB,f ; ; Прибавляем единицу btfss STATUS,Z ; ; Проверяем на ноль goto PUT_BACK ; ; ЕСЛИ не 0, ТО продолжаем incfsz NSB,f ; ; Инкрементируем средний байт goto PUT.BACK ; ; ЕСЛИ не 0, ТО продолжаем incf MSB, f PUT_BACK call START ; Начинаем операцию записи movlw b'10100000' ; ; Пакет записи movwf DATA-OUT call I2C_OUT clrf DATA-OUT ; Адрес 00h call I2C_OUT movf MSB,w ; Берем новое значение старшего байта movwf DATA-OUT call I2C_OUT movf NSB,w ; Берем новое значение среднего байта movwf DATA-OUT call I2C_OUT movf LSB,w ; Берем новое значение младшего байта movwf DATA-OUT call I2C_OUT call STOP Пример 12.4 Взяв за основу базовый принцип асинхронной передачи данных и дополнив его некоторыми принципами, лежащими в основе синхронного протокола 12С, мы сможем организовать асинхронную передачу данных в обоих направлениях по одной-единственной линии связи (в полудуплексном режиме). Одним из приме-
444 Часть Ш. Окружающий мир ров такого скрещивания является интерфейс l-WireTM1), характеристики которого показаны на Рис. 12.28. В схеме, приведенной на Рис. 12.28, а, используется микросхема цифрового термометра DS18S20 (Maxim/Dallas), управление которой осуществляется пос- редством одной линии порта микроконтроллера, выступающего в качестве веду- щего шины 1-Wire. Рис. 12.28. Взаимодействие с микросхемой цифрового термометра DS18S20 (1-Wire) 1-Wire™ является зарегистрированной торговой маркой Maxim Integrated Products/Dallas Semiconductor.
Глава 12. Ох уж эти биты! 445 Микросхема DS18S20 имеет следующие характеристики: • Диапазон измеряемой температуры от —55 до +85°С с шагом 0.5°С; резуль- тат представляется в виде 16-битного числа со знаком. • Точность измерения ±0.5% в диапазоне —1О...+85°С. • Время преобразования — не более 750 мс. • Нулевой ток потребления в режиме ожидания. • Может питаться от линии данных, диапазон напряжения питания от +3 до +5.5 В. • Возможность работы в многоточечной сети. , Выполнение различных операций, поддерживаемых термометром, таких как «Преобразование» (h’44’) и «Чтение температуры» (h’BE’), инициируется веду- щим посылкой соответствующих 8-битных значений. Процесс передачи каждого из этих значений начинается с посылки старт-бита (—\_), за которым следуют восемь слотов записи, как показано на Рис. 12.28, б. Как и в случае шины 12С, со- стояние ВЫСОКОГО уровня на линии данных DQ формируется за счет ее под- тяжки к шине питания, соответственно ведущий имитирует посылку лог. 1 пере- ключением линии порта на вход (см. Рис. 12.14,6). В этом состоянии ведущий может контролировать линию на предмет данных, посылаемых ведомым, как по- казано на Рис. 12.28, в. Для нашего примера потребуется написать две подпрограммы, одна из кото- рых будет передавать байт в ведомое устройство, а другая — считывать один байт из него. Решение Из Рис. 12.28, б видно, что процесс записи одного бита в ведомое устройство состоит из следующих этапов: 1. Ведущий инициирует процесс обмена, выставляя на линию данных НИЗ- КИЙ уровень на время не менее 1 мкс. 2. Ведущий либо оставляет на линии НИЗКИЙ уровень (запись лог. 0), либо высвобождает линию (запись лог. 1) на время 60. ..120 мкс. 3. Ведомый считывает состояние линии через 15...45 мкс после начала слота. 4. Ведущий освобождает линию (если он записывал лог. 0) на время не менее 1 мкс для приведения системы в исходное состояние. В подпрограммах, код которых приведен в Программе 12.20, предполагается, что вывод порта, управляющий линией данных DQ, уже сконфигурирован так же, как и выводы, управляющие линиями шины 12С (см. стр. 396), — жесткий НИЗ- КИЙ уровень и открытый выход, подтянутый к линии питания для формирова- ния ВЫСОКОГО уровня. Также мы предполагаем, что в нашем распоряжении имеется макрокоманда Delay_us, которая формирует задержку длительностью Л^мкс, где К— параметр, передаваемый в макрокоманду. Delay_us macro local movlw К ; К - длительность задержки в мкс DELAY_US_LOOP (K*XTAL)/(4*3)+1 ; 1~
446 Часть III. Окружающий мир DELAY_US_LOOP addlw btfss goto endm -1 STATUS,Z DELAY_US_LOOP ; Декрементируем счетчик: N~ ; до нуля : N + 1~ ; : 2(N-1)~ Дополнительная операция прибавления единицы в выражении для вычисления количества итераций цикла предназначена для округления К в большую сторону. Выполнение обеих подпрограмм начинается с выставления на линию DQ НИЗКОГО уровня на время не менее 1 мкс, в результате чего на линии формиру- ется состояние СТАРТ. Запись каждого бита на шину происходит в слоте длитель- ностью 60...120 мкс и инициируется либо выдачей на линию НИЗКОГО уровня (0), либо ее высвобождением (1). Ведомый считывает состояние линии данных через 15 мкс после начала временного слота. Хотя длительность временного слота и не критична, необходимо быть внимательным, поскольку состояние НИЗКОГО уровня длительностью 480...960 мкс интерпретируется ведомым как команда сброса (см. Вопрос для самопроверки 12.3). Для передачи одного байта используются восемь временных слотов, разде- ленных паузами длительностью не менее 1 мкс. Значение, передаваемое в каждом слоте, соответствует значению флага переноса, которое формируется в результате циклического сдвига байта данных DATA_OUT. После восьми циклов сдвига/вы- дачи процесс завершается. Чтение байта из ведомого устройства происходит следующим образом: 1. Ведущий инициирует процесс чтения, выставляя на линию НИЗКИЙ уро- вень на время не менее 1 мкс. 2. Ведущий считывает значение бита, выдаваемое на линию ведомым, кото- рое должно быть корректным в течение 15 мкс после формирования фрон- та состояния СТАРТ. 3. Через 15 мкс ведомый высвобождает линию, на которой к моменту завер- шения 60-мкс слота в результате подтяжки должен появиться ВЫСОКИЙ уровень. 4. Ведущий ждет не менее 1 мкс перед формированием следующего слота. Указанный алгоритм реализуется подпрограммой чтения read_1W, которая считывает состояние линии данных до наступления 15 мкс с момента начала вре- менного слота, т.е. в тот момент, когда напряжение на линии, определяемое ведо- мым, уже установилось. Значение считанного бита используется для установ- ки/сброса флага переноса, который затем вдвигается в переменную DATA_IN. После восьми операций чтения/сдвига в этой переменной окажется принятый байт данных. В отличие от протокола 12С, протокол 1-Wire предполагает наличие только од- ного ведущего устройства. Однако все ведомые устройства имеют уникальный 64- битный адрес, хранящийся во внутреннем ПЗУ. Первые восемь битов обозначают код семейства 1-Wire, к примеру, микросхема DS18S20 имеет код h’ 10’. Следую- щие 48 бит являются уникальным серийным номером, а последние восемь битов представляют собой контрольную сумму всех предыдущих байтов.
Глава 12. Ох уж эти биты! 447 Программа 12.20. Подпрограммы чтения и записи по шине 1-Wire / ; * ФУНКЦИЯ : Передает 1-байтное значение ведомому 1-Wire * ; * РЕСУРСЫ : Макрокоманда Delay_us, формирующая задержку N мкс * ; * ВХОД : Передаваем# байт в DATA_OUT * ; * ВЫХОД : DATA_OUT обнуляется; W, STATUS изменяются * WRITE_1W movlw 8 ; Количество проходов цикла movwf COUNT W_LOOP bcf INDF,DAT ; Спадающий фронт - СТАРТ Delay_us 1 ; Ждем 1 мкс rrf DATA_OUT,f . ; Выдвигаем байт через перенос btfsc STATUS,C ; Бит равен 1? bsf INDF,DAT ; ЕСЛИ да, ТО выставляем ВЫСОКИЙ уровень Delay_us d'60' ; Удерживаем в течение 60 мкс bsf INDF,DAT ; Высвобождаем линию Delay_us 1 ; Ждем 1 мкс decfsz COUNT,f ; Повторяем восемь раз goto return W_LOOP / * ; * ФУНКЦИЯ : Принимает 1-байтное значение от ведомого 1-Wire * ; * РЕСУРСЫ : Макрокоманда Delay_us, формирующая задержку N мкс * ; * ВХОД : Нет * ; * ВЫХОД : Принятый байт в DATA_IN; W, STATUS изменяются * READ_1W movlw movwf 8 COUNT ; Количество проходов цикла R_LOOP bcf INDF,DAT ; Спадающий фронт - СТАРТ Delay_us 1 ; Ждем 1 мкс bsf INDF,DAT ; Высвобождаем линию Delay_us 8 1 Ждем 8 мкС/ чтобы дать возможность ; ведомому выставить данные bcf STATUS,C ; Сбрасываем флаг переноса btfsc INDF,DAT ; Проверяем состояние входа bsf STATUS,C ; ЕСЛИ 1, ТО устанавливаем флаг переноса rrf DATA_IN,f ; Задвигаем бит в регистр Delay_us d' 48' ; Ждем до конца слота decfsz COUNT,f ; Повторяем восемь раз goto R_LOOP return t ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ; * ФУНКЦИЯ : Сбрасывает ведомого 1-Wire * . ************************************************************** RESET_1W bcf INDF,DAT ; Выставляем НИЗКИЙ уровень Delay _us cl1140 ' ; Ждем 480... 960 мкс Delay_us d'140' ; С помощью макрокоманды можно получить
448 Часть III. Окружающий мир Delay_us d'140' ; величину задержки (3*0.2)*255, только Delay_us d' 80' ; если процессор работает на 20 МГц bsf INDF,DAT ; Высвобождаем линию Delay_us d'601 ; Ведомый выставляет НИЗКИЙ уровень ; через 15 ... 60 мкс RESET_LOOP btfss INDF,DAT ; А затем высвобождает линию goto R_LOOP ; Ждем, пока на линии не появится ВЫСОКИЙ ; уровень return Вопросы для самопроверки 12.1. Перепишите Программу 11.5 со стр. 351, но с использованием модуля SPI, показанного на Рис. 12.4. Подсказка: вместо проверки итоговых 1-байтных значений более эффективным решением может стать побитовая проверка вдвигаемого значения. 12.2. Покажите, как можно подключить четыре АЦП МАХ518 (см. Рис. 12.16) к одной шине 12С и как можно загрузить значение 1-го канала третьего АЦП. 12.3. Обмен по шине 1-Wire начинается с формирования ведущим импульса сброса, при котором ведущий выставляет на линию НИЗКИЙ уровень на время 480...960 мкс, после чего линия высвобождается. В ответ на это ведо- мый выставляет на линию НИЗКИЙ уровень с задержкой не более 60 мкс. Это состояние удерживается на линии в течение 60...240 мкс, после чего ве- домый высвобождает линию. Напишите подпрограмму, выполняющую опи- санные действия. Предполагается, что в вашем распоряжении имеются ре- сурсы Программы 12.20. 12.4. Контроль по четности представляет собой метод, при котором значение числа всегда является четным или же нечетным. Для этого на стороне пере- датчика к слову данных добавляется дополнительный бит, значение которо- го подобрано таким образом, чтобы удовлетворять указанному критерию. Например, при контроле по нечетности (odd) 8-битного числа Ь’ОНОШГ оно преобразуется в число b’l 0110111 Г. Приемник же проверяет получен- ное значение на предмет его нечетности. Если один (или любое нечетное количество) бит был поврежден из-за помех, возникнет ошибка четности (parity error). Используя модуль USART микроконтроллера PIC, напишите програм- му, переключающую модуль в режим передачи 9-битных слов и вычисляю- щую значение бита для контроля по нечетности содержимого DATA_OUT. Этот бит затем должен заноситься в бит TX9D регистра TXSTA перед загруз- кой байта данных в регистр TXREG и его передачей. 12.5. Перепишите подпрограмму GETCHAR из Программы 12.14 в виде процеду- ры обработки прерывания (назовем ее GETCH). Сравните эти два подхода к решению задачи приема символа.
Глава 12. Ох уж эти биты! 449 12.6. Система сбора данных считывает значение температуры каждые 15 мин. Потребляемый ток сведен к минимуму за счет использования низковольт- ной версии микроконтроллера, работающей при напряжении 3 В, и кварце- вого резонатора с частотой 32.768 кГц. В этих условиях ток потребления микросхемы с работающим Таймером 1 составляет не более 70 мкА (типовое значение — 45 мкА). Отсчеты запоминаются во внешней микросхеме EEPROM с интерфейсом 12С, однако питание на нее подается только на время записи — в качестве источника питания EEPROM используется от- дельный вывод порта. Устройство должно работать от одного комплекта ба- тарей в течение 6 месяцев, находясь при этом на дне озера. Можете ли вы подобрать подходящую микросхему EEPROM из семейства 24LCXXX и оце- нить требуемую емкость батареи в мА ч?
ГЛАВА ГЛАВНОЕ - ВРЕМЯ Во многих системах ключевые операции тем или иным образом связаны со вре- менем. Это может быть измерение длительности какого-либо события, подсчет чис- ла внешних событий или же управление внешним объектом в течение определенно- го периода времени. В качестве примера можно указать задачу измерения интервала между импульсами, формируемыми датчиком при прохождении мимо него зубцов маховика коленчатого вала двигателя. Впоследствии это значение может быть ис- пользовано для определения скорости вращения вала (см. Рис. 3.8. на стр. 78). В тех случаях, когда время становится одним из важнейших критериев, для реализации указанных функций обычно используются аппаратные счетчики, а не программно-формируемые задержки. В данной главе мы познакомимся с различ- ными модулями таймеров, встречающимися в микроконтроллерах PIC среднего уровня. После прочтения этой главы вы: • Узнаете, как можно увеличить надежность микроконтроллерной системы с помощью сторожевого таймера, а также научитесь использовать интегри- рованный модуль сторожевого таймера микроконтроллеров PIC. • Сможете использовать модуль базового 8-битного Таймера 0 как в режиме счетчика, так и в режиме таймера. • Познакомитесь с возможностями модуля 16-битного Таймера 1 и разбере- тесь, каким образом он взаимодействует с модулями захвата/сравне- ния/ШИМ (Capture/Compare/PWM - ССР). • Сможете использовать модуль 8-битного Таймера 2 совместно с модулями ССР для формирования сигнала с широтно-импульсной модуляцией. Многие системы на базе микроконтроллеров работают в сложной электро- магнитной обстановке, когда помехи наводятся как по сигнальным линиям, так и по линиям питания. Типичным примером такого устройства является система уп- равления приборной панелью автомобиля, на которую воздействуют помехи, со- здаваемые высоковольтными разрядами в блоке зажигания, и пульсации напря- жения питания, вызываемые работой генератора. Даже если поместить блок в эк- ран, а на всех линиях поставить фильтры, никто не сможет гарантировать, что в какой-нибудь момент времени программа не собьется с корректного положения в памяти программ и микроконтроллер не «сойдет с ума»1), что вполне может То же самое может произойти и из-за ошибок в программе.
Глава 13. Главное — время 451 привести к серьезным последствиям в работе системы управления. Иногда эти проблемы можно решить ручным сбросом системы1). Однако во многих случаях это невозможно, например в случае имплантированного кардиостимулятора или космического зонда. Один из способов решения данной проблемы заключается в использовании связки генератор/двоичный счетчик, которая будет сбрасывать процессор при переполнении счетчика2). Если программа будет периодически обнулять этот счетчик во избежание переполнения, то микроконтроллер никогда не сбросится. Если по какой-либо причине микроконтроллер выйдет из основного цикла, в ко- тором выполнялся сброс счетчика, то счетчик рано или поздно переполнится и микроконтроллер будет сброшен, а программа начнет выполняться с самого на- чала. Эта схема называется сторожевым таймером (watchdog timer), поскольку увеличивает безопасность системы. Чтобы исключить использование внешних сторожевых таймеров, все микро- контроллеры PIC, даже представители самой старой линейки начального уровня, имеют встроенный модуль сторожевого таймера, структурная схема которого приведена на Рис. 13.1. Встроенный генератор сторожевого таймера никак не свя- зан с основным тактовым генератором процессора и, если сторожевой таймер включен, постоянно генерирует сигнал с номинальным периодом 18 мс. В качест- ве времязадающего элемента этого генератора используется внутренняя 7?С-це- почка, поэтому в зависимости от конкретного экземпляра, температуры и напря- clrwdt Сброс по включению питания -128 Сброс PIC STATUS h’03' Х128 Х64 Постделитель -64 -32 -16 -8 Делитель частоты Х32 х1§ х8 Х2 Х1 о PS0 Коэффициент деления PS1 2 PS2 OPTION h’81 ’ Номинальный период — 18 мс Р Конфигура- \ ционная 0 ячейка WDT Генератор сторожевого таймера REG Х4 § Рис. 13.1. Встроенный в микроконтроллеры PIC модуль сторожевого таймера с подключенным к нему постделителем Как в ОС Windows™. 2) Другие решения обычно базируются на использовании перезапускаемого ждущего гене- ратора.
452 Часть III. Окружающий мир жения питания период генератора может изменяться от 7 мс (—40°С, KDD = 6 В) до 33 мс (+85°С, KDD = 2 В) — см. Рис. 15.8 на стр. 561. Генератор сторожевого таймера подключен к 8-битному постделителю (postscaler). С его помощью период тайм-аута сторожевого таймера можно увели- чить до 0.018 х 128 » 2.3 с (0.9...4.2 с). Конкретное значение периода тайм-аута оп- ределяется состоянием битов PS[2:0] регистра OPTION_REG (см. Рис. 13.2). Ге- нератор сторожевого таймера и счетчик постделителя (эту связку мы будем назы- вать блоком сторожевого таймера) сбрасываются при выполнении команды clrwdt (CLeaR WatchDoG Timer — сброс сторожевого таймера). Соответствен- но, для предотвращения наступления тайм-аута сторожевого таймера необходимо периодически вызывать эту команду. OPTION_REG h’81 ’ Подключение пред/постделителя О — Таймер 0,1 — сторожевой таймер Активный фронт внешнего тактового сигнала Таймера О О — спадающий, 1 — нарастающий Выбор тактового сигнала Таймера О О — внутренний, 1 — внешний с вывода T0CKI Рис. 13.2. Формат регистра OPTION_REG Постделитель сторожевого таймера является разделяемым ресурсом, посколь- ку также используется модулем Таймера 0 (см. Рис. 13.3). Очевидно, что этот узел не может использоваться одновременно обоими модулями. Бит PSA регистра OPTION_REG определяет, к какому из модулей подключен данный узел. По умолчанию после сброса микроконтроллера постделитель подключен к стороже- вому таймеру, а множитель периода сторожевого таймера равен 128. Компания Microchip рассматривает сторожевой таймер больше как систем- ный ресурс, нежели как периферийный модуль. Поэтому пользователь должен разрешить работу стррожевого таймера программированием бита конфигурации WDTE при записи кода в память программ (см. Рис. 10.6 на стр. 312). Например, так: _config _HS_OSC & _WDT_ON & _PWRTE_OFF & _CP_OFF или с помощью аналогичной директивы, поддерживаемой конкретным Си-ком- пилятором. К примеру, в компиляторе CCS используется следующая директива: #fuses HS, WDT, NOPUT, NOPROTECT В приведенных выше строках работа сторожевого таймера разрешается. Для отключения сторожевого таймера необходимо использовать константы _wdt_off и nowdt соответственно.
Глава 13. Главное — время 453 Если сторожевой таймер включен, то при сбросе по питанию выполняется инициализация модуля сторожевого таймера и деактивизируется (устанавливает- ся в 1) 4-й бит регистра STATUS — ТО (см. Рис. 4.6 на стр. 95). Через заданный промежуток времени произойдет переполнение сторожевого таймера и бит ТО будет сброшен, как указано в Табл. 10.4 на стр. 322. Большинство программ для микроконтроллеров представляют собой бесконечный цикл, в теле которого вызы- ваются различные подпрограммы. Если сделать так, чтобы при нормальном вы- полнении программы периодически выполнялась команда clrwdt, то независи- мо от возникающих событий можно будет 'Гарантировать, что тайм-аут сторожевого таймера не наступит. В случае же сбоя программы наступит тайм-аут, в результате чего микроконтроллер автоматически сбросится и начнет выполнять программу с адреса вектора сброса h’000’. Однако состояние флага ТО при этом не изменится, чем можно воспользоваться при необходимости отличить сброс по тайм-ауту сторожевого таймера от «нормального» сброса. Флаг ТО доступен толь- ко для чтения, т.е. он не может быть установлен обычной командой, как bsf status,not_to (not_to — символическое имя для бита ТО, определен- ное в стандартном заголовочном файле). Команда clrwdt деактивирует ТО (а так- же флаг PD, сбрасываемый командой sleep) и, разумеется, перезапускает блок сторожевого таймера. В качестве примера давайте рассмотрим систему, выполняющую подсчет кон- сервных банок, перемещаемых по конвейеру (см. Рис. 13.4), и накапливающую суммарное значение в регистре BEAN_COUNT. При сбросе по питанию в указан- ный регистр заносится нулевое значение. Если по какой-либо причине возник- нет сбой программы и в результате тайм-аута сторожевого таймера микроконт- роллер сбросится, то данное значение не должно измениться. Для этого нам при- дется написать код, проверяющий состояние бита ТО и выполняющий требуемые действия, например: ________config _WDT_ON org MAIN btfss clrf h'ООО’ STATUS,NOT_TO BEAN_COUNT ; Остальной инициализационный clrwdt ; Включаем сторожевой таймер ; Вектор сброса ; Сброс от сторожевого таймера? ; ЕСЛИ нет, ТО обнуляем счетчик код---------------- ; Устанавливаем NOT_TO и сбрасываем сторожевой ; таймер Строго говоря, при инициализации после сброса по питанию команду clrwdt вызывать не обязательно. Однако из-за этой секции инициализации уве- личивается время перехода к основной части программы и, соответственно, вре- мя до запланированного вызова команды clrwdt. Так что дополнительная ко- манда clrwdt в конце любого блока инициализации является полезной предо- сторожностью. Поскольку генератор сторожевого таймера полностью независим от систем- ного тактового сигнала, он продолжает работать даже после перевода микроконт- роллера в «спящий» режим. Для этого команда sleep сбрасывает сторожевой
454 Часть III. Окружающий мир таймер и деактивирует флаг ТО. К тому же она активизирует флаг PD (STATUS[3]), указывающий на то, что процессор находится в «спящем» режиме. Благодаря всем этим действиям между выполнением команды sleep и наступле- нием тайм-аута сторожевого таймера проходит время, равное одному периоду сторожевого таймера. Если тайм-аут наступит при нахождении микроконтролле- ра в спящем режиме, то микроконтроллер проснется и продолжит выполнение программы с команды, следующей за командой sleeps. Обычно этой командой является команда сброса сторожевого таймера clrwdt. При необходимости программа может определить, что выход из спящего ре- жима произошел по тайм-ауту сторожевого таймера, проверив флаги ТО и PD. В микроконтроллерах младшего уровня тайм-аут сторожевого таймера является единственным способом вывода микроконтроллера из спящего режима, не счи- тая аппаратного сброса. В микроконтроллерах среднего и верхнего уровней выход из спящего режима тоже может осуществляться по внешнему прерыванию, а так- же, в некоторых случаях, по сигналу от Таймера 1 и аналоговых модулей, которые могут работать от собственного тактового генератора. Следует иметь в виду, что при включенном сторожевом таймере ток потребления микроконтроллера в спя- щем режиме возрастает с 0.9 мкА (5 мкА шах) до 7.5 мкА (30 мкА шах) (цифры приведены для модели PIC16F87X при KDD = 3 В, 7\ = —4О...+85°С). Если требу- ется длительная работа устройства от батарей (см., например, Вопрос для самопроверки 12.6 на стр. 449), то это может оказаться серьезной проблемой. Для сравнения скажу, что процессор, работающий на частоте 32.768 кГц при напряже- нии ЗВ и выключенном сторожевом таймере, потребляет ток около 20мкА (35 мкА шах). Модели младшего уровня имеют базовый 8-битный счетчик/предделитель, который первоначально назывался счетчиком/таймером реального времени (Real-Time Clock/Counter — RTCC). Хотя этот термин до сих пор встречается в документации на старые микроконтроллеры линейки PIC16CXXX, появление до- полнительных таймеров привело к возникновению более логичного термина — Таймер 0. Тем не менее сокращение «RTCC» до сих пор встречается в старых кни- гах и программном обеспечении. Например, в компиляторе CCS задание внеш- него тактового сигнала Таймера 0 с активным спадающим фронтом и коэффици- ента деления предделителя, равного 4, осуществляется вызовом функции setup_counters (rtcc_ext_l_to_h, rtcc_div_4* 2)). Из Рис. 13.3 можно заметить, что Таймер 0 представляет собой 8-битный счетчик, расположенный в регистре с адресом h’01 ’, подключенный к 8-битному предделителю. Таким образом, с помощью трех битов PS[2:0] регистра OPTION_REG мы можем задавать восемь различных частот сигнала, подаваемо- После задержки длительностью 1024 такта системного генератора, о чем говорилось на стр. 309. 2) Эта же функция может использоваться для конфигурирования постделителя сторожевого таймера, например setup_counters (WDT_2 88MS).
Глава 13. Главное — время 455 го на вход счетного регистра таймера. Поскольку предделитель Таймера 0 являет- ся также постделителем сторожевого таймера1), для его подключения к Таймеру О необходимо сбросить бит PSA регистра OPTION_REG. По умолчанию после сброса предделитель подключен к сторожевому таймеру. В этом случае счетчик таймера может работать либо от внутреннего системного тактового сигнала частотой/osc/4 (т.е. с частотой, в четыре раза меньшей частоты на выводе XTAL1) или от внешнего сигнала, поступающего на вход RA4/T0CK.I (или GP5/T0CKI) микроконтроллера. Для переключения между внутренним и внешним тактовым сигналом служит бит TOCS регистра OPTION_REG (OPTION_REG[5]). При работе от внешнего тактового сигнала активный фронт, по которому происходит инкрементирование счетного регистра, задается битом T0SE регистра OPTION_REG (OPTION_REG [4]). Очевидно, что появление активного фронта на входе T0CKI никак не синхро- низировано с внутренним тактовым сигналом микроконтроллера. Чтобы можно было обычным образом считывать и изменять содержимое счетного регистра Таймера 0, потребовалось ввести узел синхронизации. Синхронизация осуществ- ляется с использованием 2-ступенчатого сдвигового регистра, подключенного к тактовому входу счетного регистра Таймера 0. Работа синхронизатора вызывает задержку длительностью 2 машинных цикла (1 мкс при резонаторе частотой 8 МГц). Такая же задержка возникает между операцией записи нового значения в счетный регистр Таймера 0 (Ь’ОГ) и реальным обновлением его содержимого при работе таймера непосредственно от внутреннего тактового сигнала. 11 В микроконтроллерах старшего семейства PIC18XXXX реализованы отдельные предде- лители для сторожевого таймера и Таймера 0.
456 Часть III. Окружающий мир При переполнении Таймера 0 (11111111 -> 00000000) устанавливается флаг прерывания TOIF (INTCON[2]). Если при этом установлен бит разрешения пре- рывания TOIE (INTCON[5]), то автоматически будет сгенерировано прерывание (см. Рис. 7.3 на стр. 213). Длительность каждого из интервалов ВЫСОКОГО и НИЗКОГО уровней внешнего сигнала, используемого для непосредственного тактирования Таймера 0, должна быть не менее 2/osc + 20 нс. Таким образом, при использова- нии 8-МГц резонатора (fosc = 125 нс), как Thigh, так и T10w должны быть не менее 270 нс, в результате чего максимальная частота счета составит 1.8 МГц. При ис- пользовании предделителя указанный минимальный суммарный период 4/qsc + 40 нс может быть уменьшен в заданное число раз. При этом к сигналу, по- даваемому на предделитель, предъявляется единственное требование — длитель- ность его импульсов должна быть не менее 10 нс. Таким образом, при использо- вании коэффициента деления 256 на вход T0CKI можно подавать сигнал часто- той 50 МГц. Предделитель недоступен для чтения, поэтому таймер не является 16-битным счетчиком в строгом смысле этого слова. Чтение счетного регистра таймера не влияет на предделитель, а вот любая команда, осуществляющая запись в счетный регистр (например clrf h' 011 , movwf h' 01'), наряду с изменением состоя- ния Таймера 0 сбрасывает как предделитель, так и узел синхронизатора тактового сигнала. Как уже говорилось, после сброса предделитель подключен к сторожевому таймеру и для подключения его к таймеру необходимо сбросить бит PSA. Однако в результате этого переключения может произойти сброс микроконтроллера от сторожевого таймера, даже если последний выключен. Поэтому компания Microchip рекомендует перед изменением бита PSA выполнять команду clrwdt, как это сделано в следующем фрагменте кода. В данном фрагменте осуществляет- ся инициализация предделителя следующими параметрами: коэффициент деле- ния 4, вход T0CKI, инкрементирование по фронту. clrwdt bsf STATUS,RPO movlw b'11110001' movwf OPTION_REG bcf STATUS,RPO ; Сбрасываем предделитель и сторожевой таймер ; Переключаемся в 1-й банк ; Внешний тактовый сигнал, активный фронт - спадающий ; Предделитель 1:4, подключенный к Таймеру 0 ; Возвращаемся в 0-й банк Разумеется, переключать предделитель между Таймером 0 и сторожевым тай- мером можно и в процессе выполнения основной программы. По уже указанным причинам перед изменением регистра OPTION_REG следует выполнить команду clrwdt во избежание непроизвольного сброса от сторожевого таймера. Таймер 0 используется главным образом либо для счета внешних событий, ли- бо для измерения времени между внешними событиями. Кроме того, он может использоваться для управления выводами порта, позволяя отказаться от подпро- грамм, формирующих временные задержки при помощи циклов. Проиллюстрируем использование Таймера 0 в качестве счетчика событий на двух примерах. В первом примере, где мы также задействуем сторожевой таймер,
Глава 13. Главное — время 457 производится подсчет консервных банок, перемещающихся по конвейеру (см. Рис. 13.4). После прохождения очередных 24 банок датчик должен сформировать импульс для упаковочной машины, чтобы заполненная коробка была заменена пустой. Длительность этого импульса должна составлять всего несколько микро- секунд. Кроме того, необходимо предусмотреть двухбайтный счетчик, в котором будет накапливаться общее количество упакованных коробок с момента послед- него сброса микроконтроллера. В конце смены это значение пересылается в цент- ральный компьютер завода для инвентаризации. Сначала разберемся с секцией инициализации. Код этой секции, приведен- ный ниже, начинается с проверки флага ТО. Если этот флаг сброшен, то фаза инициализации пропускается, поскольку сброс произошел из-за тайм-аута сто- рожевого таймера. В противном случае вывод T0CKI настраивается на вход, а вы- вод RB1 — на выход (на последнем формируется импульс управления упаковоч- ной машиной). include "pl6F877a.inc" __config _WDT_ON ; Разрешаем работу сторожевого таймера cblock h'20' _work:1, COUNT:2 endc _status:1 org btfss goto goto 0 ; Вектор сброса STATUS,NOT_TO ; Сброс от сторожевого таймера? MAIN_LOOP ; ЕСЛИ да, ТО пропускаем секцию инициализации MAIN ; ИНАЧЕ начинаем с нуля org goto 4 ; Вектор прерывания ISR ; Обработчик
458 Часть III. Окружающий мир MAIN bsf PORTB,1 ; Неактивное состояние линии управления ; упаковщиком bsf STATUS,RPO ; Переключаемся в 1-й банк bsf TRISA,4 ; Вывод T0CKI - вход, а вывод RB1, управляющий bcf TRISB,1 ; упаковочной машиной, - выход movlw b'00101111' ; Таймер 0 работает от внешнего сигнала, ; активный фронт - movwf OPTION_REG ; спадающий, предделитель подключен ; к сторожевому таймеру movlw b'0110 * ; Не забыть переключить порт А в цифровой режим movwf ADCON1 ; То есть выключить аналоговые входы bcf STATUS,RPO ; Возвращаемся в 0-й банк bsf INTCON,TOIE ; Разрешаем прерывание от Таймера 0 movlw -d'24' ; Загружаем в таймер -24 (E8h) movwf TMRO clrf COUNT+1 ; Сбрасываем 2-байтный счетчик коробок clrf COUNT bsf INTCON,GIE ; Разрешаем прерывания ; Фоновая процедура, которая выполняет различные операции MAIN. .LOOP clrwdt ; Периодически сбрасываем сторожевой таймер ; Остальной код фонового цикла Находясь в 1-м банке, мы также инициализируем регистр OPTION_REG — подключаем предделитель к сторожевому таймеру и увеличиваем его период тайм-аута в 128 раз. После этого конфигурируем Таймер 0, который должен так- тироваться по спадающему фронту сигнала, поступающего на вход T0CKL И на- конец, после возвращения в 0-й банк заносим в счетный регистр таймера значе- ние h’E8’ (т.е. -24), чтобы после поступления 24 импульсов отдатчика происхо- дило переполнение таймера и генерировалось прерывание. После этого для разрешения данного прерывания мы устанавливаем флаги T0IE и GIE регистра INTCON. Основная фоновая программа начинается с команды clrwdt. Если время вы- полнения одного прохода основного бесконечного цикла будет не более 7 х 128 = 0.8961 с, т.е. меньше цериода сторожевого таймера, то тайм-аут никогда не наступит. Теперь нам осталось только написать процедуру обработки прерывания, кото- рая будет автоматически вызываться после накопления 24 банок, т.е. после пос- тупления 24 импульсов на вход Таймера 0 и его переполнения. При этом устанав- ливается флаг T0IF и микроконтроллер переходит по адресу вектора сброса h’004’. В нашем инициализационном коде по указанному адресу мы разместили команду goto ISR. Сам код обработчика прерывания приведен в Программе 13.1.
Глава 13. Главное — время 459 Программа 13.1. Процедура обработки прерывания счетчика консервных банок ; * В обработчике формируется импульс управления упаковочной ; * машиной и реинициализируется Таймер 0 значением -24. Также ; * в COUNT:2 накапливается общее количество упакованных коробок * для анализа в фоновом цикле ; Сначала сохраняем контекст ISR movwf _work ; Сохраняем W swapf STATUS,w ; и регистр STATUS movwf -Status ; Основной код btfss goto INTCON,TOIF ISR,EXIT ; Было переполнение таймера? ; ЕСЛИ нет, ТО ложная тревога bcf PORTB,1 ; Формируем передний фронт импульса movlw -d'24 ; Реинициализируем Таймер 0 movwf TMRO incf COUNT+l,f ; Увеличиваем счетчик коробок на 1 btfsc STATUS,Z incf COUNT,f bcf INTCON,TOIF ; Сбрасываем флаг прерывания bsf PORTB,1 ; Формируем задний фронт импульса ISR_EXIT swapf _status,w movwf STATUS swapf _work,f swapf _work,w retfie ; Восстанавливаем регистр STATUS ; Восстанавливаем W, ; не затрагивая STATUS, ; и выходим из прерывания Основной код обработчика заключен в «обертку», выполняющую переключе- ние контекста согласно Программе 7.2 (стр. 226). Ядро обработчика выполняет следующие операции: • Формирует импульс управления упаковочной машиной на выводе RB1. • Заносит в счетный регистр таймера число —24. • Инкрементирует двухбайтную переменную общего количества коробок. • Сбрасывает флаг прерывания от Таймера 0 — T0IF. При входе в обработчик прерывания проверяется флаг T0IF, и если он не уста- новлен, то осуществляется выход из процедуры обработки прерывания. Если бы в устройстве использовались и другие источники прерывания, то вместо выхода из обработчика нам надо было бы перейти к другой его секции, как показано в лис- тинге, приведенном на стр. 219. Альтернативный подход с использованием внешнего прерывания можно пос- мотреть в Программе 7.2, приведенной на стр. 226.
460 Часть III. Окружающий мир Во втором примере демонстрируется работа модуля Таймера 0 в качестве собственно таймера, выполняющего измерение времени между событиями. В данном случае этими событиями являются всплески ЭКГ, изображенные на Рис. 7.1 (стр. 208). При обнаружении такого всплеска пиковый детектор прерыва- ет работу микроконтроллера, в котором организован 2-байтный счетчик, работа- ющий от внешнего генератора частотой 10 кГц. Таким образом, мы можем с диск- ретностью 100 мкс (назовем эту величину «тиком») определить интервал между событиями. Мы несколько изменим требования, предъявляемые к системе, что- бы избавиться от внешнего генератора, и воспользуемся для накопления числа тиков Таймером 0 (один тик в данном случае будет равен 1 мс). Для решения данной задачи нам потребуется так сконфигурировать Таймер 0, чтобы при его работе от основного тактового сигнал микроконтролле- ра (через предделитель) переполнение происходило бы каждую миллисекунду (1000 мкс). Если мы возьмем кварцевый резонатор с частотой 4.096 МГц, то из уравнения 4 Тайм-аут = 1000 мкс = & х 256 х Коэффициент деления предделителя получим, что коэффициент деления предделителя должен быть равен 4. Учитывая указанные требования, напишем секцию инициализации: org goto 0 MAIN ; Вектор сброса ; Фоновая программа org 4 ; Вектор прерывания goto I SR ; Обработчик MAIN clrwdt ; Сбрасываем сторожевой таймер bsf STATUS,RPO ; Переключаемся в 1-й банк movlw b'00000001' ; Прерывание по спадающему фронту, внутр, такт, сигнал movwf OPTION_REG ; Предделитель - 1:4, подключен к Таймеру 0 bcf STATUS,RPO ; Возвращаемся в 0-й банк clrf NEW ; Обнуляем флаг нового события bsf NTCON,T0IE ; Разрешаем прерывание от Таймера 0 bsf INTCON,INTE ; Разрешаем внешнее прерывание bsf INTCON,GIE ; Разрешаем работу системы прерываний clrf TMR0 ; Сбрасываем таймер clrf COUNT ; Обнуляем счетчик Тиков clrf COUNT+1 Помимо разрешения прерывания от Таймера 0, устанавливается также флаг INTE для разрешения внешнего прерывания с вывода INT, на который подается сигнал с пикового детектора. При этом нам не требуется обнулять ни Таймер 0, ни 2-байтный счетчик тиков, поскольку первый отсчет из серии всегда будет не- верным — ведь сердцебиение пациента не синхронизировано со сбросом микро- контроллера! Однако регистр NEW, в который заносится ненулевое значение при каждом обнаружении импульса ЭКГ, сбрасывается.
Глава 13. Главное — время 461 Основное ядро обработчика прерывания, код которого приведен в Программе 13.2, выполняет следующие действия: 1. По прерыванию от Таймера 0: • инкрементирует 2-байтный счетчик тиков; • сбрасывает флаг прерывания от таймера T0IF; • выходит из прерывания. 2. По внешнему прерыванию от пикового детектора: • копирует содержимое счетчика тиков в РОНы; • обнуляет Таймер 0; • устанавливает флаг NEW; • сбрасывает флаг внешнего прерывания; • выходит из прерывания. Программа 13.2. Измерение периода ЭКГ с разрешением 1 мс * По прерыванию от Таймера 0 в обработчике инкрементируется * 2-байтный счетчик COUNT ; * По внешнему прерыванию COUNT:2 копируется в DATA:2 ; * и устанавливается флаг NEW, извещающий фоновую программу ; * о готовности новых данных ; Сначала сохраним контекст ISR movwf swapf movwf _work STATUS,w „status ; Сохраняем W ; и регистр STATUS ; Основной код btfss INTCON,TOIF ; Сердечный импульс? goto HEART_BEAT ; ЕСЛИ да, TO обрабатываем его incf COUNT+l,f ; Регистрируем очередной 1-мс тик btfsc STATUS,Z ; ЕСЛИ перешли через ноль, incf COUNT,f ; ТО инкрементируем старший байт bcf INTCON,T0IF ; Сбрасываем флаг прерывания goto ISR_EXIT HEART_BEAT movf COUNT+l,w movwf DATUM+1 movf COUNT,w movwf DATUM clrf COUNT+1 clrf COUNT btfsc INTCON,INTF incf NEW,f ; Сюда попадаем при обнаружении импульса ЭКГ ; Берем младший байт периода ; Копируем в пользовательский регистр ; Берем старший байт периода ; Обнуляем счетчик тиков ; Сбрасываем флаг прерывания ; Сообщаем о наличии новых данных
462 Часть III. Окружающий мир ISR_EXIT swapf _status,w movwf STATUS swapf _work,f swapf _work,w retfie ; Восстанавливаем регистр STATUS ; Восстанавливаем W, ; не затрагивая STATUS, ; и выходим из прерывания По внешнему прерыванию оба байта из регистров COUNTCOUNT+1 копи- руются в пользовательские регистры DATUM:DATUM+1, после чего счетчик ти- ков и Таймер 0 обнуляются для регистрации следующего события. Фоновая про- грамма постоянно опрашивает регистр NEW, ненулевое содержимое которого го- ворит о том, что имеется новое значение. В дальнейшем это значение можно будет, например, записать в последовательную EEPROM, как в Примере 12.3 на стр. 439, или же передать по последовательному каналу в ПК для последующей обработки и отображения. Большинство PIC-микроконтроллеров среднего и старшего уровней имеют, по крайней мере, два дополнительных таймера/счетчика со следующими возможностями: Таймер 1 Этот 16-битный таймер имеет собственный вспомогательный генератор и про- граммируемый предделитель. Состояние этого таймера можно запомнить по внешнему событию. Кроме того, он может изменять состояние определенного вывода микроконтроллера при достижении некоторого предустановленного зна- чения. Таймер 2 Этот 8-битный счетчик имеет как программируемый предделитель, так и про- граммируемый постделитель. Модуль счета данного таймера может задаваться программистом. Кроме того, этот таймер можно использовать для аппаратной ге- нерации сигнала с широтно-импульсной модуляцией. Модуль захвата/сравнения/ШИМ Оба таймера могут работать совместно с модулем захвата/сравнения/ШИМ (Capture/Compare/PWM — ССР), который позволяет считывать текущее состоя- ние Таймера 1 («захват»), сравнивать состояние Таймера 1 с заданным значением («сравнение»), а также обеспечивает автоматическую генерацию ШИМ-сигнала на базе Таймера 2. Таймер 1 состоит из базового 16-битного счетчика, реализованного в виде па- ры РСН, которые называются TMR1L (младший байт) и TMR1H (старший байт). При переполнении этого счетчика устанавливается флаг прерывания TMR1IF в регистре прерываний от периферийных устройств PIR1 (PIR1 [0]). Для тактирования этого таймера может использоваться внешний сигнал — либо подаваемый на вход T1CKI микроконтроллера, либо от собственного гене-
Глава 13. Главное — время 463 ратора Таймера 1. Кроме того, в качестве тактового сигнала таймера может ис- пользоваться системный тактовый сигнал частотой/osc/4- Независимо от источ- ника тактового сигнала его частота может быть уменьшена с помощью счетчика предделителя. Внешние импульсы дополнительно могут синхронизироваться с системным генератором. Управляемая версия модуля таймера позволяет извне за- прещать подачу тактового сигнала на счетный регистр с помощью вывода T1G микроконтроллера. Регистр управления Таймера 1 T1C0N (см. Рис. 13.5) используется для зада- ния различных функций таймера. После сброса все биты этого регистра равны О, что соответствует следующим установкам: Таймер 1 и его внешний генератор от- ключены, коэффициент деления предделителя равен 1, для тактирования тайме- ра используется системный тактовый сигнал. Рис. 13.5. Функциональная схема Таймера 1 TMR1ON Установка бита T1CON[0] в 1 разрешает работу Таймера 1. В одних случаях1) свя- занные с Таймером 1 выводы микроконтроллера автоматически переключаются в режим входа независимо от установок регистров TRIS, а в других2) — программа должна принудительно переключить эти выводы на вход. ’) Как правило, в более новых моделях, в которых Таймер 1 задействует выводы порта С (например, PIC16F87X) или порта В (например, PIC16F62X). 2) Как правило, в старых моделях (например, PIC16C74) или в тех моделях, в которых Таймер 1 задействует выводы порта А (например, PIC16F684) или порта GP (например, PIC12F675).
464 Часть III. Окружающий мир TMR1CS, T10SCEN Если GhtTMRICS (T1CON[1]) установлен в 1, то Таймер 1 будет тактироваться от системного тактового сигнала. В противном случае используется внешний источ- ник тактовых импульсов. В последнем случае счет осуществляется либо по нарастающему фронту сиг- нала на выводе T1CKI, либо, если бит разрешения генератора Таймера 1 T1OSCEN (T1CON[3]) установлен в 1, по сигналу «собственного» генератора тай- мера, независимого от основного генератора микроконтроллера. Наличие такого генератора позволяет избежать подбора частоты основного кварцевого резонато- ра в соответствии с требованиями таймера, чем мы и воспользовались в нашем детекторе пиков ЭКГ на базе Таймера 0 (см. стр. 460). В качестве времязадающего элемента в этом генераторе используется кварцевый резонатор, подключаемый к выводам T1OSC0/T1CKI и T1OSC1. Максимальная частота такого резонатора со- ставляет 200 кГц, однако, как правило, используется часовой кварц с частотой 32.768 кГц (215 Гц). T1CKPS[1:0] Независимо от источника тактовых импульсов инкрементирование 16-битного счетного регистра может производиться как непосредственно по данному сигна- лу, так и по каждому второму, четвертому или восьмому импульсу. Это определя- ется установками битов T1CON[5:4], как показано на Рис. 13.5. Переполнение Таймера 1 и установка флага прерывания TMR1IF происходит после 216 = 65 536 событий, считая от нуля. Этот флаг, в свою очередь, может ис- пользоваться для прерывания работы процессора, если парный ему бит маски TMR1IE в регистре PIE1 (см. Рис. 7.5 на стр. 223) установлен в 1. Разумеется, никто не запрещает отслеживать это событие путем обычного опроса данного флага. В любом случае флаг TMR1IF должен быть сброшен вручную после обна- ружения переполнения. К примеру, при использовании резонатора с частотой 32.768 кГц переполне- ние Таймера 1 будет происходить каждые 2 с при T1CKPS[1:O] = 00 и каждые 16 с приТ1СКР8[1:0] = 11. T1SYNC По умолчанию сигнал с выхода программируемого предделителя синхронизиру- ется с системным тактовым сигналом, что приводит к появлению задержки, рав- ной двум машинным циклам. Однако в отличие от Таймера 0 в этом таймере сиг- нал может передаваться в обход сдвигового регистра синхронизатора при установке 6wraTlSYNC (T1CON[2]) в 1. Наличие асинхронного режима позволя- ет использовать Таймер 1 с внешним источником тактового сигнала при нахожде- нии микроконтроллера в «спящем» режиме. Поскольку сдвиговый регистр синх- ронизатора тактируется системным тактовым сигналом Jose, который в «спящем» режиме отключается, возможность обхода данного регистра просто необходима. Также асинхронный режим необходимо использовать, когда частота внешнего тактового сигнала в 4 раза больше частоты системного резонатора (в этом случае наличие синхронизатора приведет к пропуску некоторой части событий).
Глава 13. Главное — время 465 За исключением указанных случаев, бит T1SYNC должен быть сброшен, пос- кольку отсутствие синхронизации может привести к непредсказуемому результату при попытке записи в счетный регистр Таймера 1 в тот момент, когда осуществ- ляется его инкрементирование по внешнему событию. Если требуется изменить состояние Таймера 1 в асинхронном режиме, то его необходимо остановить сбро- сом бита TMR1ON. Так, для записи в Таймер 1 константы h’8000’: movlw h'80’ ; Новое значение старшего байта bcf T1CON,TMR1ON ; Останавливаем таймер movwf TMR1H ; Загружаем в Таймер 1 число 8000h Clrf TMR1L bsf T1CON,TMR1ON ; Перезапускаем таймер Изменение состояния Таймера 1 всегда вызывает сброс счетчика предделителя. Если Таймер 1 работает в синхронном режиме, его значение можно изменять «на лету». Необходимо только быть аккуратным при изменении обоих байтов счетчика, поскольку может случиться так, что после изменения старшего байта переполнение младшего произойдет до записи в него нового значения, в резуль- тате чего старший байт изменится нежелательным образом. Чтобы избежать это- го, перед записью нового значения следует обнулить младший байт. Вот как, на- пример, можно записать в Таймер 1 «налету» число h’9FFF’: movlw h'9F' ; Новое значение старшего байта clrf TMR1L ; Предохраняемся от переполнения младшего байта movwf TMR1H ; Обновляем старший байт счетного регистра movlw h1FF' ; Новое значение младшего байта movwf TMR1L ; Обновляем младший байт счетного регистра Содержимое Таймера 1 можно прочитать в любой момент времени даже при работе в асинхронном режиме. Однако, поскольку одновременно можно считать только один байт1), возможна ситуация, при которой между двумя последователь- ными операциями чтения произойдет переполнение таймера, например: ; Предположим, что текущее состояние Таймера 1 - h'80FF' movf TMRlL,w ; Читаем младший байт = h’FF' movwf TEMPL ; Запоминаем ; «« в этот момент состояние Таймера 1 изменилось на h’8100’ »» movf TMRlH,w ; Читаем старший байт = h'811 movwf TEMPH ; Запоминаем. Считанное значение равно h'81FF'!!! В результате выполнения предыдущего кода мы получим значение h’81FF’ вместо h’80FF’. Возникновение такой ситуации наиболее вероятно при генерации пре- рывания от другого периферийного устройства между последовательными опера- циями чтения. ’) В моделях линейки PIC18XXXX при чтении одного из байтов счетного регистра Тайме- ров 1...3 второй автоматически копируется во временный регистр. Таким образом, за одно обращение к таймеру осуществляется считывание полного 16-битного значения.
466 Часть III. Окружающий мир Чтобы результат считывания был предсказуемым, можно использовать два способа. Первый способ заключается в остановке таймера перед считыванием его состояния. Второй же способ заключается в том, что сначала считывается стар- ший байт, а после считывания младшего байта проводится проверка, не измени- лось ли состояние старшего байта, как показано в следующем примере: ,_СЕТ movf TMRlH,w ; Считываем старший байт movwf TEMPH movf TMRlL,w movwf TEMPL Теперь проверим старший movf TMRlH,w ; Запоминаем ; Считываем младший байт ; Запоминаем байт на предмет изменения ; Снова считываем старший байт subwf TEMPH,w ; Проверим на равенство с предыдущим значением btfss STATUS,Z ; ЕСЛИ не отличается, ТО пропускаем goto T1_GET ; ИНАЧЕ выполняем чтение повторно Так или иначе, но при необходимости считывания точных значений крайне желательно запрещать прерывания сбросом бита GIE до окончания чтения обоих байтов. Альтернативный способ, позволяющий считывать два байта за один цикл, обсуждается на стр. 469. При работе Таймера 1 от внутреннего тактового сигнала (TMR1CS = 0) синх- ронизация не требуется. В этом случае значение бита Т1 SYNC игнорируется. TMR1GE, T1GINV Некоторые последние модели, такие как PIC12F675, имеют управляемую версию модуля Таймера 1 (см. Рис. 13.5). При установленном бите TMR1GE (T1CON[6]) изменение состояния таймера может быть приостановлено подачей на вывод T1G ВЫСОКОГО уровня. В других вариантах управляемого Таймера 1 для задания активного уровня сигнала на выводе T1G используется бит T1GINV (T1CON[7]). Такой модуль тай- мера (реализованный, к примеру, в микроконтроллере PIC16F684) может управ- ляться не только сигналом с вывода T1G, но и сигналом с выхода 2-го компара- тора (см. Рис. 14.6 на стр. 497). Указанная возможность позволяет измерять вре- менные параметры аналоговых сигналов. В качестве примера предположим, что нам требуется разработать экономич- ную систему сбора данных о температуре, которая будет считывать состояние дат- чика и передавать данное значение на базовый компьютер каждые 15 мин. Пред- положим, что для этого мы воспользуемся Таймером 1, работающим от кварцево- го резонатора с частотой 32.768-кГц. Поскольку максимально возможный период переполнения таймера составля- ет всего 16 с (см. стр. 464), для отсчета интервала в 900 с нам потребуется хранить число переполнений. Задав период переполнения таймера, равный 4 с, получим, «к 900 пос что для отсчета требуемых 15 мин нам потребуется отсчитать — = 225 перепол- нений. Соответственно, процедура инициализации и общая структура програм- мы будут похожи на код, приведенный в Программе 13.3. В данном случае
Глава 13. Главное — время 467 Таймер 1 конфигурируется для использования внешнего генератора с коэффици- ентом предделителя 2, что даст нам значение тика, равное 4 с. Программа 13.3. Формирование 15-минутного интервала include "pl6f877a.inc" __________config _WDT_OFF & _CP_OFF cblock h’20' JIFFY:1 endc org 0 MAIN movlw b'00011111' , ; Таймер включен, внешний тактовый сигнал, ; асинхронный режим movwf T1CON , ; Внешний генератор включен, предделитель - 1:2 clrf JIFFY ; ; Обнуляем счетчик тиков bsf STATUS,RPO , ; Переключаемся в 1-й банк bsf PIE1,TMR1IE ; ; Разрешаем прерывание от Таймера 1 bcf STATUS,RPO ; ; Возвращаемся в 0-й банк DOOZE sleep ; Ждем прерывания bcf PIR1,TMR1IF ; ; Сбрасываем флаг прерывания incf JIFFY,f ; ; Запоминаем очередной тик movlw d'2251 ; ; Уже 225 тиков =15 мин? subwf JIFFY,w btfss STATUS,Z ; • ЕСЛИ да, ТО делаем что-нибудь полезное goto DOOZE ; ; ИНАЧЕ ждем еще 15 с ; Делаем выборку clrf JIFFY ; ; Сбрасываем счетчик тиков call SAMPLE ; ; Считываем температуру и передаем ее goto DOOZE ; : Начинаем отсчет следующего интервала Для снижения энергопотребления микроконтроллер будет большую часть времени находиться в «спящем» режиме, пробуждаясь каждые четыре секунды. Для этого бит маски TMR1IE (PIE 1 [0]) устанавливается в 1. После выхода микро- контроллера из «спящего» режима флаг прерывания TMR1IF сбрасывается и ин- крементируется значение счетчика тиков. Затем оно сравнивается с константой 225. В случае равенства счетчик обнуляется и вызывается подпрограмма, осу- ществляющая передачу нового значения температуры на базовый компьютер. Следует отметить, что включенный Таймер 1 увеличивает потребление мик- роконтроллера на величину порядка 20 мкА. Особенно следует обращать на это внимание, если Таймер 1 используется для вывода микроконтроллера из режима пониженного потребления, в котором потребление микроконтроллера с выклю- ченной периферией составляет всего 0.9 мкА (все цифры приведены для моделей PIC16F87X).
468 Часть III. Окружающий мир Совместно с Таймером 1 (а также, как мы увидим позже, и с Таймером 2) ис- пользуется один (например, в PIC16F62X) или два (например, в PIC16F87X) мо- дуля захвата/сравнения/ШИМ (Capture/Compare/PWM — ССР). Каждый модуль ССР, по существу, состоит из 16-битного регистра (поскольку счетный регистр Таймера 1 является двухбайтным) и 16-битного цифрового компаратора для срав- нения состояния Таймера 1 и содержимого регистра модуля ССР. Поскольку мо- дули ССР1 и ССР2 практически идентичны (они используют один и тот же зада- ющий Таймер 1!)), отличаясь только различными контактами ввода/вывода ССР1 и ССР2, мы будем рассматривать модуль ССР1. Там, где это необходимо, разли- чия между модулями будут указаны отдельно. Модуль ССР выполняет три основных функции: • При его работе в режиме захвата (Capture) появление заданного события на выводе, связанном с модулем ССР, вызывает копирование состояния счет- ного регистра Таймера 1 в регистр ССР. Эту возможность можно использо- вать для определения момента наступления данного события или его дли- тельности с разрешением вплоть до 12.5 нс. • При работе в режиме сравнения (Compare) в случае равенства счетного ре- гистра Таймера 1 и содержимого регистра модуля ССР изменяется состоя- ние соответствующего вывода модуля или осуществляется сброс Таймера 1. Эта возможность может использоваться для аппаратного формирования сигналов с разрешающей способностью 200 нс. • При работе в режиме ШИМ (PWM) модуль ССР совместно с Таймером 2 может использоваться для аппаратного формирования сигнала с широтно- импульсной модуляцией разрядностью до 10 бит (разрешение 0.1%) с про- граммируемыми периодом и скважностью. Во всех случаях, когда используется Таймер 1, он должен работать в синхрон- ном режиме, т.е. бит Т1 SYNCH должен быть сброшен в 0. Каждый модуль ССР имеет свой регистр управления, используемый для зада- ния конфигурации модуля. Во всех случаях используемые модулями выводы не- обходимо вручную конфигурировать как вход или выход в соответствии с режи- мом работы модуля. Работа модуля в режиме захвата показана на Рис. 13.6. В этом режиме имеют- ся следующие подрежимы: 0000 При сбросе по питанию или по снижению напряжения питания все биты регист- ра обнуляются. При этом модуль ССР отключается, а предделитель сбрасывается. Чтобы избежать непредусмотренных прерываний при изменении режима работы модуля, его рекомендуется выключать перед сменой режима. В микроконтроллерах старшего семейства PIC18XXXX с одним модулем ССР может использоваться Таймер 1, а с другим — Таймер 3.
Глава 13. Главное — время 469 0100 По спадающему ~\_ фронту на выводе ССР1 оба байта счетного регистра Таймера 1 одновременно копируются в пару регистров CCPR1H:L. При этом ус- танавливается флаг прерывания CCP1IF (PIR1[2]) и, при установленном бите маски ССРПЕ (PIE1 [2]), генерируется прерывание. Модуль ССР2 функционирует точно так же, за исключением того, что соот- ветствующие биты флага CCP2IF и маски CCP2IE прерывания расположены в регистрах PIR2 (PIR2[0J) и PIE2 (PIE2[0]) соответственно. Причем во многих мо- делях среднего уровня эти биты являются единственными задействованными в указанных регистрах. 0101 Описанный выше процесс захвата производится по нарастающему _J~ фронту на выводе модуля ССР. 0110 Захват производится по четвертому нарастающему фронту на выводе ССРл. 0111 Захват производится по шестнадцатому нарастающему фронту на выводе ССРя. 0110 — По каждому 4-му нарастающему фронту 0111 — По каждому 16-му нарастающему фронту Рис. 13.6. «Захват» времени наступления события После наступления заданного события процессор может считать сохраненное значение (время) либо в обработчике прерывания, либо после установки опраши- ваемого флага CCPIF в 1. Если Таймер 1 после каждого события сбрасывается, то данное значение представляет собой время, прошедшее с момента наступления предыдущего события. Если же инкрементирование Таймера 1 не прекращается, то для определения времени между событиями достаточно вычесть новое значе- ние из значения, запомненного во время предыдущего прерывания. Поскольку режим модуля ССР допускается изменять «налету», мы можем измерять интервал между нарастающим и спадающим фронтами на входе модуля ССР1, переключая
470 Часть III. Окружающий мир между операциями захвата бит ССР1М[0]. При изменении режима возможна са- мопроизвольная установка флага прерывания ССР НЕ Чтобы предотвратить ге- нерацию ложного прерывания, необходимо перед изменением режима сбрасы- вать бит ССР НЕ, а после изменения режима — бит ССР НЕ Также можно ис- пользовать разные модули для захвата по каждому из фронтов, скажем, модуль ССР1 — для захвата по нарастающему фронту, а модуль ССР2 — для захвата по спадающему фронту (см. Пример 13.3). Как бы это ни казалось странным, но если вывод ССР сконфигурировать как выход, то захват состояния таймера можно будет осуществлять, программно из- меняя состояние данного вывода. Эта особенность позволяет использовать мо- дуль ССР для определения длительности какого-либо внутреннего события или же для «хитрого» одновременного считывания обоих байтов таймера. Позже ре- зультат можно будет считать из регистров модуля безо всяких проблем, связанных с раздельным чтением двух регистров. В качестве примера воспользуемся модулем ССР для измерения периода сиг- нала кардиограммы, подключив пиковый детектор, показанный на Рис. 7.1 (стр. 208), к выводу ССР1. Предполагая, что Таймер 1 работает в синхронном ре- жиме от собственного кварцевого резонатора частотой 32.768 кГц, инициализа- ционная часть программы может выглядеть следующим образом: movlw movwf b'OOOOlOll' ; T1CON ; Таймер включен, внешний такт, сигнал, синхр. режим : Генератор включен, предделитель - 1:1 movlw b'00000100' ; : Режим захвата по спадающему фронту movwf CCP1CON clrf NEW ; Обнуляем флаг NEW clrf TMR1H ; Обнуляем Таймер 1 clrf TMR1H bsf STATUS,RPO , ; Переключаемся в 1-й банк bsf PIE1,CCP1IE , ; Разрешаем прерывание от ССР1 bcf STATUS,RPO , ; Возвращаемся в 0-й банк bcf PIR1,CCP1IF , ; Сбрасываем флаг прерывания bsf INTCON,PEIE , ; Разрешаем прерывания от периферийных устройств bsf INTCON,GIE , ; Разрешаем работу системы прерываний В обработчике прерывания просто считывается содержимое регистра ССР, которое затем сохраняется в двух временных регистрах. Затем в регистр NEW за- носится ненулевое значение, извещающее фоновую программу о наличии нового значения. После этого Таймер 1 сбрасывается для регистрации следующего события. При использовании резонатора частотой 32.768 кГц и отключенном предде- лителе разрешающая способность считываемого значения составит 30.5 мкс. Пе- реполнение Таймера 1 при таких параметрах конфигурации будет происходить каждые 2 с, и этого достаточно для регистрации сердечного ритма частотой до 30 ударов в минуту (см. Программу 13.4).
Глава 13. Главное —время 471 Программа 13.4. Определение момента появления точки R на ЭКГ ; Сначала сохраняем контекст обычным образом ISR movwf _work ; Сохраняем W swapf STATUS,w ; и регистр STATUS movwf _status ; Основной код btfss goto PIR1,CCP1IF ISR_EXIT ; Было прерывание от CCP1? ; ЕСЛИ нет, ТО ложная тревога incf NEW,f ; Сообщаем о новом захвате bcf PIR1,CCP1IF ; Сбрасываем флаг прерывания movf CCPRlL,w ; Считываем младший байт movwf TEMP+1 ; Запоминаем его movf CCPR1H,W ; Считываем старший байт movwf TEMP ; Запоминаем его clrf TMR1L ; Обнуляем Таймер 1 clrf TMR1H / ISR_EXIT swapf _status,w ; Восстанавливаем регистр STATUS movwf STATUS swapf _work,f ; Восстанавливаем регистр W, swapf _work,w ; не затрагивая регистр STATUS, retfie ; и выходим из прерывания В системах с повышенной надежностью также может быть разрешено преры- вание по переполнению Таймера 1. Возникновение этого прерывания означает, что последующее захваченные данные будут некорректны, хотя можно подсчи- тать количество таких тайм-аутов и таким образом увеличить длительность опре- деляемого интервала. Однако в нашей системе это прерывание было бы логичнее использовать для включения сигнала тревоги! Режимы 1000...1011, указанные на Рис. 13.7, соответствуют четырем режимам сравнения. В этих режимах производится сравнение 16-битного счетного регистра Таймера 1 с содержимым пары регистров CCPR1H:L. При совпадении указанных значений устанавливается флаг прерывания CCP1IF (PIR1 [2]) и, при установлен- ном бите маски ССР НЕ (Р1Е1[2]), генерируется прерывание. Помимо установки флага CCP1IF при наступлении события «совпадение» может выполняться одно из четырех действий, определяемое состоянием битов ССР1М[3:0]: 1000: Установить вывод микроконтроллера при совпадении На вывод ССР1 выставляется ВЫСОКИЙ уровень. Защелка модуля ССР может быть сброшена только переключением модуля в режим 0000, т.е. при его выклю- чении.
472 Часть III. Окружающий мир 0000 1000 1001 1010 ССР1 выключен Установить вывод ССР1 при совпадении Сбросить вывод ССР1 при совпадении * Сгенерировать прерывание при совпадении (состояние вывода не изменяется) Сбросить Таймер 1 при совпадении Л Л ТТ Л| ССР 1 CON ССР1МЗ ССР1М2|ССР1М1 ССР1М0| h,17, TMR1l£ IPIR1 h'OC 1011 TMR1H Таймер 1 Цифровой компаратор о с О) 7| Совпадение Рис. 13.7. Модуль ССР1 в режиме «Сравнение» Регистр ССР1 Блок управления выводом Вывод сконфигу- рирован как выход ос Защелка ССР 1001: Сбросить вывод микроконтроллера при совпадении На вывод ССР1 выставляется НИЗКИЙ уровень. Защелка модуля ССР может быть установлена только выключением модуля. 1010: Генерировать прерывание при совпадении Состояние вывода ССР1 не изменяется, единственным действием является уста- новка флага ССР НЕ 1011: Сформировать специальное событие при совпадении Таймер 1 сбрасывается. При использовании модуля ССР2 (это единственное от- личие в функционировании модулей ССР1 и ССР2) можно запустить преобразо- вание АЦП (см. Рис. 14.11 на стр. 510). В режимах 1000 и 1001 выводы микроконтроллера, используемые модулями ССР1 и ССР2, должны быть сконфигурированы как выходы. При отключенном модуле ССР (после любого сброса) эти выводы будут отображать состояние соот- ветствующих битов порта. В качестве примера давайте рассмотрим выключение модуля ССР. Поскольку единственным способом переключения защелки модуля в ее исходное состояние является запись в регистр CCPCON режима 0000, логич- но будет записать это значение в соответствующий бит порта для исключения не- желательных выбросов. К примеру, при использовании режима 1001 бит регистра порта следует при инициализации установить в 1, чтобы после сброса на этом вы- воде присутствовал ВЫСОКИЙ уровень. В микроконтроллерах, выпускающихся в корпусах с количеством выводов более 28, вывод ССР1, как правило, задейству- ет линию RC2, а вывод ССР2 — линию RC1. В качестве примера предположим, что нам необходимо сконфигурировать Таймер 1 также, как и в предыдущем примере, — чтобы его переполнение проис-
Глава 13. Главное — время 473 ходило каждые 10 с. Для этого нам необходимо задать период тайм-аута, равный 16 с (коэффициент деления предделителя 8), а затем укоротить цикл. Соответ- ственно в регистр CCPR1 следует загрузить 10/i6 от максимального значения (216 х 10/1б), что составит h’AOOO’. При достижении таймером данного значения он будет автоматически сбрасываться, и если бит маски ССРПЕ (а также биты PEIE и GIE) установлен, то будет генерироваться прерывание. Инициализационный код для данной задачи выглядит следующим образом: movlw h'AO' ; Загружаем в CCPR1 число h’AOOO1 movwf CCPR1H / clrf CCPR1L / movlw b' 00001011' ; Режим CCP1 - 1011. Специальное событие movwf CCP1CON / movlw b'OOlllOir ; Таймер 1 вкл. (1), внешний такт. сигнал (1) movwf T1CON ; Синхр. режим (0), генератор вкл. (1), 1:8 (111) bsf STATUS,RPO ; Переключаемся в 1-й банк bsf PIE1,CCP1IE ; Разрешаем прерывание от ССР1 bcf STATUS,RPO ; Возвращаемся в 0-й банк bsf INTCON,PEIE ; Разрешаем прерывания от таймера/ССР bsf INTCON,GIE ; Разрешаем работу системы прерываний После выполнения этих команд микроконтроллер будет автоматически пре- рываться каждые 10 с. Поскольку в режиме 1011 вывод ССР1 отключен от модуля ССР, указанный вывод может использоваться как обычный вывод порта ввода/вывода независимо от модуля ССР1. Таймер 2 представляет собой 8-битный счетчик с программируемым предде- лителем и постделителем, как показано на Рис. 13.8. Этот счетчик всегда тактиру- ется от системного тактового сигнала. В отличие от двух предыдущих таймеров, выходным сигналом является сигнал не с выхода счетчика, а с выхода компарато- ра Таймера 2. Этот блок сравнивает состояние Таймера 2 с содержимым регистра периода PR2. При равенстве указанных значений на выходе формируется им- пульс, который сбрасывает Таймер 2 в момент прихода следующего счетного им- пульса. Эта возможность, в частности, может использоваться для задания скоро- сти передачи по интерфейсу SPI модуля SSP, как показано на Рис. 12.9 (стр. 385). В соответствии с установками постделителя по истечении заданного числа этих сбросов (от 1 до 16) будет устанавливаться флаг прерывания от Таймера 2 TMR2IF в регистре PIR1 (PIR1 [1]), а при установленном бите маски прерывания TMR2IE также будет генерироваться прерывание. Значения коэффициентов деления пред- и постделителя, а также управление Таймером 2 осуществляется с использованием регистра управления T2CON, как описано ниже. После сброса микроконтроллера все биты этого регистра сбрасы- ваются, выключая Таймер 2 и устанавливая коэффициенты деления, равные 1.
474 Часть III. Окружающий мир T2C0N Fosc/4 Счетный вход ------ TMR2ON V------- \ Предделитель —"у 4-1,4-4,4-16 С1 1D Совпадение Цифровой компаратор =“----------- TMR2 зздержка в один такт Выход Таймера 2 (к модулю SSP для управления скоростью передачи данных) ! 00 1:1 >01 1:4 ; 1Х 1:16 h’OC’ —V—т TMR2IF I о T2CKS1 T2CKS0 T2C0N h'12’ PR2 Постделитель 4-1...4-16 PIR1 -------П--------П--------«I-------з! TOUTPS3 TOUTPS2 TOUTPS1 TOUTPSO] h’92’ T2C0N Рис. 13.8. Упрощенная функциональная схема Таймера 2 TMR2ON Включение Таймера 2 осуществляется установкой бита T2CON[2] в 1. T2CKPS[1:0] Инкрементирование счетного регистра таймера может осуществляться либо с частотой тактового сигналаУозс/4, или же с частотой, меньшей в 4 или 16 раз. Три возможные установки битов T2CON[1:0] показаны на Рис. 13.8. TOUTPS[3:0] Число периодов Таймера 2, после которых устанавливается флаг прерывания TMR2IF, может быть задано с помощью битов TOUTPS[3:0] (T2CON[5:2]). Этот 4-битный код п соответствует коэффициенту 1:(л+1); отЬ’0000’ = 1:1 до b’l 111’ = 1:16. Преимуществом такой архитектуры является то, что подстройку точного зна- чения периода тайм-аута можно осуществить без использования модуля ССР, просто записью требуемого значения в регистр периода. Длительность интервала до установки флага TMR2IF будет определяться выражением ^/fosc х Предделитель х (PR+ 1) х Постделитель. В качестве примера предположим, что нам необходимо формировать преры- вание 100 раз в секунду. Если предположить, что микроконтроллер работает от резонатора с частотой 4 МГц, то, задав коэффициент деления предделителя рав- ным 4, мы получим период тактового сигнала Таймера 2, равный 4 мкс. Если в ре- гистр периода загрузить число 249, то период импульсов на выходе компаратора Таймера 2 составит 250 х 4 = 1 мс. А задав коэффициент деления постделителя, равный 10 (1001), получим период прерывания Юме (частота 100 Гц). Изменяя коэффициент деления постделителя от 1 до 16, мы сможем регулировать период генерации прерывания от 1 до 16 мс. Для точной подстройки периода с шагом, равным 4 х Постделитель [мкс], можно изменять содержимое регистра PR2.
Глава 13. Главное — время 475 Инициализационный код для этого примера выглядит следующим образом: movlw b' 010011011 ; Постделитель 1:10 (1001), Таймер 2 вкл. (1) movwf T2CON ; Предделитель 1:4 (01) bsf STATUS,RPO ; Переключаемся в 1-й банк movlw d’249' ; Задаем период, равный 249 movwf bsf PR2 PIE1,TMR2IE ; Разрешаем прерывание от Таймера 2 bcf STATUS,RPO ; Возвращаемся в 0-й банк bsf INTCON,PEIE ; Разрешаем все прерывания от таймеров и модуле ССР bsf INTCON,GIE ; Разрешаем прерывания В компиляторе CCS имеется своя функция для инициализации Таймера 2 — setup_timer_2(<режим>, <период>, <постделитель>): setup_timer_2(T2_DIV_BY_4,249,10); enable_interrupts(INT_TIMER2); enable_interruptg(GLOBAL); Содержимое счетного регистра таймера можно прочитать с помощью функ- ции get_timer2 (), а изменить — с помощью функции set_timer_2 (). В качестве одного из наиболее распространенных применений микроконт- роллерных устройств можно назвать задачу управления силовыми цепями, таки- ми как нагревательные элементы, осветительные приборы, а также управление скоростью электродвигателей. В принципе для этого можно было бы использо- вать цифро-аналоговый преобразователь, подобный изображенному на Рис. 12.16 (стр. 399), управляющий мощным усилителем. Однако такая схема ли- нейного управления дорога и крайне неэффективна из-за большой мощности, рассеиваемой на усилителе. Гораздо эффективнее и удобнее будет быстро вклю- чать/выключать нагрузку с достаточно высокой частотой. Мощные ключевые элементы, такие как тиристоры, рассеивают относительно небольшую мощность, поскольку в выключенном состоянии ток через них не протекает, а падение на- пряжения на открытом ключе практически равно нулю. Примеры таких сигналов показаны на Рис. 13.9. Средняя амплитуда вычисля- ется какЛ х N, где N— коэффициент заполнения. При изменении Not 0 до 100%, средняя мощность, выделяемая на нагрузке, будет изменяться пропорционально — и все это без использования аналоговых элементов. Такой метод преобразования цифрового сигнала в аналоговую форму называется широтно-импульсной модуляци- ей (ШИМ). Тепловая или механическая инерция большинства мощных нагрузок такова, что даже при относительно низкой частоте переключения (обычно не менее 100 Гц) «выбросы» будут сглаживаться. Низкие частоты переключения более эф- фективны, поскольку энергия рассеивается при каждом переключении. Если же ШИМ используется для более привычного цифро-аналогового преобразования, например, в аудиотехнике, то для снижения уровня высокочастотных гармоник полученный сигнал следует пропустить через фильтр нижних частот. При этом для отсечения нежелательных гармоник и уменьшения необходимой степени
476 Часть III. Окружающий мир Период ► Длительность рабочего импульса Среднее значение^. 25 А Период________: Длительность рабочего импульса а) Коэффициент заполнения 25% б) Коэффициент заполнения 75%> Рис. 13.9. Широтно-импульсная модуляция фильтрации частота выборок должна быть как минимум в 10 раз больше макси- мальной частоты аналогового сигнала (см. Рис. 14.3 на стр. 494). Формирование ШИМ-сигнала обычно осуществляется с использованием счетчика и схемы сравнения. Выход управляется защелкой, которая устанавлива- ется при каждом переполнении таймера. Сброс защелки производится при до- стижении счетчиком числа, соответствующего коэффициенту заполнения. Чем больше это число, тем больше доля времени, в течение которого на выводе будет присутствовать ВЫСОКИЙ уровень. В качестве примера рассмотрим 3-битный счетчик, в котором длительность рабочего импульса задана равной b’011’: Выставить Сбросить ВЫСОКИЙ (НИЗКИЙ уровень уровень) ООО -> 001 -> 010 -> 011 -> 100 -> 101 -> 110 -> 111 «Совпадение» В этом примере ВЫСОКИЙ уровень на выводе будет присутствовать в тече- ние трех тактов, что даст нам коэффициент заполнения, равный 3/8 или 37.5%. Изменяя это число, можно регулировать среднюю мощность от 0 до 87.5% с ша- гом */8. В микроконтроллерах PIC для формирования таких сигналов используются модули ССР. При работе модуля в режиме ШИМ в качестве основного счетчика используется Таймер 2, а число, определяющее коэффициент заполнения, загру- жается через регистр с двойным буферированием в 10-битный компаратор. На Рис. 13.10 показана структурная схема модуля ССР1 в режиме ШИМ, формирую- щего сигнал на соответствующем выводе. При наличии в микроконтроллере мо- дуля ССР2 он может использоваться аналогичным образом, только сигнал в этом случае будет формироваться на выходе ССР2. Любой из выводов, используемый для формирования ШИМ-сигнала, должен быть сконфигурирован как выход сбросом соответствующего бита регистра TRIS. Несмотря на то что оба модуля ССР могут работать параллельно с различными значениями коэффициента за-
Глава 13. Главное — время 477 полнения, период формируемых этими модулями ШИМ-сигналов будет одина- ковым, поскольку они используют один и тот же Таймер 2. h’17’ h’15’ Ведущий Регистры управления скважностью Ведомый (только для чтения) Регистры управления периодом Рис 13.10. Таймер 2 и модуль ССР в режиме ШИМ Период Временная Рис. 13.8. Величина периода переполнения зависит от длительности машинного цикла 4 х rosc, коэффициента деления предделителя и содержимого регистра пе- риода PR2. Учитывая, что Таймер 2 сбрасывается по следующему тактовому сиг- налу после достижения равенства с PR2, суммарный период повторения вычис- ляется следующим образом: развертка осуществляется Таймером 2, как было показано на (4xrOjC) х Коэфф.деления предделителя х (PR2 + 1). Например, при 16-МГц резонаторе, коэффициенте деления 16 и значении h’63’ = d’99’ в регистре PR2 получим х 16 х (99 + 1) = 400 мкс.
478 Часть III. Окружающий мир При каждом переходе Таймера 2 через значение, записанное в регистре пери- ода, происходит три события: 1. Таймер 2 сбрасывается в 0 (если PR2 не равен 0). 2. Защелка модуля ССР устанавливается, и на выводе ССР1 появляется ВЫ- СОКИЙ уровень. 3. 10-битное значение, представляющее собой длительность рабочего им- пульса в следующем периоде ШИМ-сигнала, копируется из ведущего ре- гистра в ведомый. Скважность Значение, подаваемое на 10-битный компаратор ШИМ, хранится в двухуровне- вом 10-битном регистре. Значение, загружаемое программой, хранится в регистре CCPR1L (8 старших битов) и в битах CCP1CON[5:4] (два младших бита) — на Рис. 13.10 все эти биты вместе названы ведущим регистром. Содержимое ведуще- го регистра можно изменить в любой момент времени, выполнив две команды movwf. Это значение продвигается по конвейеру и поступает на компаратор толь- ко в конце каждого периода. Такое решение уменьшает вероятность появления выбросов в середине периода из-за произвольного характера изменений содер- жимого ведущего регистра относительно состояния Таймера 2. Роль ведомого ре- гистра выполняет регистр CCPR1H совместно с 2-битной внутренней защелкой. При работе в режиме ШИМ регистр CCPR1H доступен только для чтения. Это сделано специально, чтобы исключить прямой доступ к значению, определяюще- му скважность сигнала. Счетный регистр Таймера 2 является 8-битным. Для увеличения его разряд- ности до 10 бит в соответствии с разрядностью значения, задающего скважность сигнала, добавляется два младших бита. Эти биты берутся либо от счетчика предделителя, который используется для снижения частоты системного тактово- го сигнала перед подачей его на счетный регистр таймера, либо, если коэффици- ент деления предделителя равен единице, от 2-битного счетчика, формирующе- го внутренние тактовые сигналы (см. Рис. 4.4 на стр. 92). В том и другом случае максимальное разрешение длительности рабочего импульса получается равным 10 бит (1:1024) при частоте счета, в 4 раза превышающей частоту тактирования 8-битного счетного регистра Таймера 2. При равенстве 10-битного значения счетчика числу, определяющему длитель- ность импульса (скважность), защелка ШИМ сбрасывается, и на выводе ССР1 появляется НИЗКИЙ уровень. В этом состоянии вывод удерживается до начала формирования следующего периода сигнала в момент переполнения Таймера 2, после чего описанный цикл повторяется. Во всех случаях число в регистре CCPRL1 должно быть меньше, чем в регистре PR2, ведь в противном случае за- щелка ШИМ никогда не сбросится! Если в регистре PR2 находится число h’FF’, то разрешение системы максимально и равно 10 бит. Меньшие значения в регист- ре периода приводят к уменьшению разрешающей способности сигнала. Напри- мер, если PR2 = h’3F’, то разрешающая способность сигнала будет равна 8 бит (шесть битов счетного регистра Таймера 2 и два дополнительных).
Глава 13. Главное — время 479 В качестве примера предположим, что нам необходимо формировать сигнал с периодом 400 мкс (частота 2.5 кГц) при частоте системного резонатора, равной 16 МГц. При этом коэффициент деления предделителя Таймера 2 равен 16, а в ре- гистре PR2 находится число h’63’. Для получения сигнала с коэффициентом за- полнения, равным 25% (как на Рис. 13.9, а), можно написать следующий инициа- лизационный код: bsf STATUS,RPO ; Переключаемся в 1-й банк movlw h’ 63 ’ ; Загружаем в регистр периода d'99' movwf PR2 bcf TRISC,2 ; Переключаем ССР1 на выход bcf STATUS,RPO ; Возвращаемся в 0-й банк movlw h' 19 ’ ; Устанавливаем ведущий регистр на 1/4 от полной ; шкалы (h'63/4') movwf CCPR1L ; То есть Ь'0001 1001' movlw b'00001100' ; Модуль ССР1 в режим ШИМ (1100) movwf CCP1CON ; с CCP1CON[5:4] (00) movlw b1 00000110 ; Предделитель Таймера 2 - 1:16 (10) movwf T2CON ; Включаем Таймер 2 (1). Начинаем генерацию сигнала Постделитель Таймера 2 при формировании ШИМ-сигнала не используется, однако влияет на установку флага TMR2IF, как и обычно. Флаг CCP1IF в данном режиме не изменяется. Во многих силовых приложениях необходимо формировать два или четыре сигнала для управления нагрузкой, включенной по мостовой схеме. Некоторые модели микроконтроллеров, такие как PIC16F684 с усовершенствованным моду- лем ССР, специально предназначены для управления такими мостовыми схема- ми. В этих микроконтроллерах также автоматически формируется задержка между включением соседних каналов (так называемое «мертвое время»). Эта задержка необходима для того, чтобы исключить появление сквозных токов, которые могут возникнуть при одновременном переключении ключевых элементов схемы. Примеры Пример 13.1 Покажите, как можно использовать Таймер 0 для формирования на выходе RA0 ШИМ-представления байта, находящегося в регистре DATUM. Полагая, что частота резонатора равна 8 МГц, рассчитайте период ШИМ-сигнала. Решение Время наступления переполнения Таймера 0 будет зависеть от значения, за- груженного в счетный регистр таймера в начале периода. Если мы загрузим в этот регистр дополнительный код значения (отрицательное число), то длительность периода будет пропорциональна этому числу — чем оно больше, тем больше вре- мени пройдет до переполнения таймера. И, наоборот, при загрузке в счетный ре-
480 Часть III. Окружающий мир гистр таймера самого числа DATUM период переполнения таймера будет обратно пропорционален этому значению. Загружая поочередно в счетный регистр тайме- ра обратное значение DATUM (и выставляя при этом на выход ВЫСОКИЙ уро- вень) и собственно значение DATUM (выставляя НИЗКИЙ уровень), мы полу- чим сигнал, период которого будет приблизительно равен периоду переполнения Таймера 0 при его нормальной работе (256 тактов). В Программе 13.5 Таймер 0 конфигурируется для работы на частоте 2 МГц (/osc/4) без использования предделителя. Таким образом, итоговая частота 2 ШИМ-сигнала составит — МГц = 7.8125 кГц. В обработчике прерывания, кото- 256 рое генерируется при переполнении Таймера 0, проверяется состояние бита PORTA[0], и если он сброшен, то его состояние изменяется и вычисляется допол- нительный код заданного значения (инвертирование плюс единица). Однако из- за наличия синхронизатора между записью в счетный регистр Таймера 0 и реаль- ным СГО изменением проходит 2 такта, поэтому для компенсации этой задержки дополнительно прибавляется двойка. Если же в бите порта уже присутствует еди- ница, то он сбрасывается, а в счетный регистр таймера заносится исходное значе- ние, увеличенное на 2. Наличие такой компенсации может вызвать проблемы при крайних значени- ях коэффициента заполнения. Почему это происходит и что можно предпринять для улучшения данной ситуации? Программа 13.5. Широтно-импульсная модуляция с использованием Таймера 0 MAIN bsf movlw movwf bcf bcf bsf bsf STATUS,RPO b'00001000' OPTI0N_REG TRISA,0 STATUS,RPO INTCON,TOIE INTCON,GIE ; Переключаемся в 1-й банк ; Внутренний такт, сигнал, предделитель выкл. ; RA0 - выход ; Возвращаемся в 0-й банк ; Разрешаем прерывание от Таймера 0 ; Разрешаем все прерывания ; «« Остальной код фоновой программы »» ; * Обработчик прерывания формирует ШИМ-сигнал на выводе RA0 * ; * Значение периода в DATUM. PORTA[0] - текущее состояние ШИМ * ; Сначала сохраним контекст > ISR movwf _work ; Сохраняем W swapf STATUS,w ; и регистр STATUS movwf „status ; Основной код btfss INTCON,TOIF ; Было переполнение Таймера 0? goto ISR_EXIT ; ЕСЛИ нет, TO ложная тревога
Глава 13. Главное —время 481 bcf INTCON,TOIF DATUM,w PORTA,0 MAKE-LO ; Сбрасываем флаг прерывания ; Берем значение ; Сейчас на выходе НИЗКИЙ уровень? ; ЕСЛИ нет, ТО выставляем НИЗКИЙ movf btfsc goto MAKE_HI bsf xorlw addlw goto PORTA,0 b'llllllir 1 SET_UP ; ИНАЧЕ выставляем ВЫСОКИЙ уровень, ; вычисляем дополнительный код ; (инвертируем и прибавляем 1) ; и загружаем значение в Таймер 0 MAKE_L0 bcf PORTA,0 ; Выставляем на вывод НИЗКИЙ уровень SETJJP addlw movwf 2 TMRO ; Компенсация задержки синхронизатора ; Инициализируй счетный регистр таймера . ************************************************************* ISR_EXIT swapf _status,w movwf STATUS swapf _work,f swapf _work,w retfie ; Восстанавливаем регистр STATUS ; Восстанавливаем регистр W, ; не затрагивая регистра STATUS, ; и выходим из прерывания Пример 13.2 Некий тахометр предназначен для регистрации скорости вращения двигателя в диапазоне 0...120 ООО об/мин. При каждом обороте вала двигателя генерируется один импульс. Для подсчета числа этих импульсов в секунду и вычисления соот- ветствующего значения в об/мин предполагается использовать микроконтроллер PIC16F877. Используя два или три имеющихся в этой модели таймера, можете ли вы разработать схему подключения микроконтроллера и написать соответствую- щую программу для решения данной задачи? Решение Скорость в 12 000 об/мин соответствует 200 оборотам в секунду. Таким обра- зом, в качестве счетчика импульсов мы можем использовать Таймер 0, тактируе- мый непосредственно с вывода T0CKI, без предделителя. Таймер 1 совместно с модулем ССР 1, работающим в режиме сравнения, будет использоваться для формирования секундного интервала. Этот таймер тактирует- ся от собственного генератора с часовым кварцем, а его состояние изменяется от h’0000’ до h’7FFF’. Однако для облегчения перевода единиц (об/мин = 60 х об/с) предлагается уменьшить интервал счета в 60/б4 Раз> чтобы реализовать эквивален- Гоб/с1х60 х. гл тное соотношение 1——-----х64 • Это можно сделать, уменьшив модуль счета до 64 h’7FFF’ х 60/64 = h’77FF’. Итоговое умножение на 64 можно выполнить либо сдвигом результата на шесть разрядов влево (<<6), либо, что более эффективно,
482 Часть III. Окружающий мир копированием полученного значения в об/с в старший байт результата в об/мин и сдвигом его на два разряда вправо, т.е. [об/мин] = ([об/с] х 256)» 2. Очевидно, что такой подход намного эффективнее, чем использование секунд- ного интервала и умножения на 60. Возможный вариант программы, реализующей описанный алгоритм, приве- ден в Программе 13.6. В секции инициализации выполняются следующие опера- ции: • Таймер 0 переключается в режим счета по спадающему фронту сигнала на входе T0CKI. • Модуль ССР1 переключается в режим сравнения 1011 для сброса Таймера 1 по событию «совпадение». • Разрешается прерывание по этому событию. • В регистры CCPRIHiL заносится значение, соответствующее интервалу 6%4 С- В процедуре обработки прерывания число оборотов в секунду, считанное из Таймера 0 и расширенное до двухбайтного значения, сохраняется во временных регистрах. После этого Таймер 0 обнуляется, а сохраненное значение преобразу- ется к об/мин, как было описано выше. После двукратного сдвига вправо два старших бита регистра RPM сбрасываются, чтобы исключить воздействие флага переноса. Итоговое 14-битное число в регистрах RPM:RPM+1 является искомым результатом, который впоследствии может использоваться в фоновой программе для выдачи на дисплей или, быть может, для передачи в компьютер по последова- тельному каналу. Для повышения надежности программы в обработчике прерывания необхо- димо также проверять состояние флага прерывания по переполнению Таймера 0, который может использоваться для включения индикатора, предупреждающего о превышении допустимой скорости. Программа 13.6. Программное обеспечение тахометра MAIN movlw movwf movlw movwf h’ 77' CCPR1H h’FF' CCPR1L ; Загружаем число h'77FF', чтобы сформировать ; интервал длительностью 60/64 с bsf STATUS,RPO ; Переключаемся в 1-й банк movlw b'00111000' ; Таймер 0 - внешний сигнал, спад, фронт movwf OPTION_REG ; Без предделителя bsf PIE1,CCP1IE ; Разрешаем прерывание от ССР1 movlw b 00000110' ; Все выводы порта А - цифровые movwf ADCON1 bcf STATUS,RPO ; Возвращаемся в 0-й банк movlw b' 00001011' ; Модуль ССР в режиме сравнения (1011)
Глава 13. Главное — время 483 movwf CCP1CON ; сбрасывает Таймер 1 movlw b'OOOOlOll' ; Таймер 1 - предделитель 1:1, собств. генератор, movwf T1CON ; синхронный режим clrf NEW ; Сбрасываем флаг bsf INTCON,PEIE ; Разрешаем прерывания от Таймера/ССР bsf INTCON,GIE ; Разрешаем все прерывания clrf TMRO ; Обнуляем счетчик импульсов clrf TMR1H clrf TMR1L ; <<<< Остальной код фоновой программы >>>> ; Сначала сохраним контекст ISR movwf _work ; Сохраняем W swapf STATUS,w ; и регистр STATUS movwf _status ; Основной код btfss goto PIR1,CCP1IF ; Сброс Таймера 1 от CCP1? ISR_EXIT ; ЕСЛИ нет, TO ложная тревога incf movf clrf movwf NEW, f ; Индицируем наличие нового значения TMR0,w ; Берем подсчитанное число импульсов TMR0 ; Обнуляем счетчик RPM ; Сохраняем результат во временном регистре ; Теперь умножим clrf rrf rrf rrf rrf bcf bcf на 64 RPM+1 ; Обнуляем младший байт RPM,f ; об/м - старший бит, т.е. х256 RPM+l,f ; >>2 для преобразования об/с в об/мин RPM,f RPM+1,f RPM,7 ; Сбрасываем два старших бита RPM,6 bcf PIR1,CCP1IF ; Сбрасываем флаг прерывания ISR_EXIT swapf _status,w movwf STATUS swapf _work,f swapf _work,w retfie ; Восстанавливаем регистр STATUS ; Восстанавливаем регистр W, ; не затрагивая регистра STATUS, ; и выходим из прерывания
484 Часть III. Окружающий мир Пример 13.3 Необходимо с помощью микроконтроллера PIC16F877 измерить длитель- ность некоторого события. Этим событием является ВЫСОКИЙ уровень сигна- ла, как показано на Рис. 13.11. Предполагается, что частота системного резонато- ра равна 8 МГц, а длительность измеряемого импульса не превышает 100 мс. Длительность события Захват 1 Захват 2 Рис 13.11. Длительность импульса в качестве длительности события Решение Один из возможных вариантов решения этой задачи заключается в одновре- менной подаче отслеживаемого сигнала на выводы ССР1 и ССР2. Используя один из модулей для захвата нарастающего фронта, а другой — спадающего фронта, можно будет вычислить интервал между событиями, равный разности между двумя сохраненными значениями. В Программе 13.7 по нарастающему фронту импульса Таймер 1 обнуляется, соответственно состояние Таймера 1, за- хваченное по спадающему фронту, представляет собой искомую длительность. Если таймер будет работать от системного тактового сигнала с коэффициентом деления предделителя, равным 4, то инкрементирование счетного регистра будет происходить с частотой 500 кГц, т.е. временное разрешение составит 2 мкс. Мак- симальная длительность, которая может быть измерена при такой конфигурации, равна 216 х 2 мкс = 131.077 мс. Этого достаточно для работы с нашим сигналом, длительность которого не превышает 100 мс. Обработчик прерывания, код которого приведен в Программе 13.7, просто проверяет по очереди флаги прерывания от каждого модуля ССР и выполняет со- ответствующие блоки программы. Если установлен флаг прерывания от модуля ССР1 (обнаружен нарастающий фронт _Г~ сигнала), то Таймер 1 обнуляется для запуска нового счета. Инкрементирование этого таймера осуществляется с частотой 500 кГц и при появлении спадающего фронта V_ сигнала его состоя- ние считывается модулем ССР2 и помещается в 16-битный регистр CCPR2H.L. Затем в обработчике прерывания это значение, представляющее длительность импульса в 2-мкс тиках, копируется в два пользовательских регистра — Т1МЕ:Т1МЕ+1. Вообще говоря, операция сброса Таймера 1, осуществляемого по первому со- бытию, вносит некоторую погрешность, поскольку занимает определенное вре- мя. В нашем случае это не принципиально, однако при измерении более корот- ких интервалов с большим разрешением могут возникнуть проблемы. В этих слу- чаях можно оставить Таймер 1 работать в непрерывном режиме, а длительность импульса определять как разность между двумя захваченными 16-битными зна- чениями.
Глава 13. Главное — время 485 Программа 13.7. Измерение длительности импульса MAIN movlw b'00000101' ; Модуль CCP1 - захват по нарастающему фронту movwf CCP1CON movlw b'OOOOOlOO' ; Модуль CCP1 - захват по спадающему фронту movwf CCP2CON bsf STATUS,RPO ; Переключаемся в 1-й банк bsf PIE1,CCP1IE ; Разрешаем прерывание от ССР1 bsf PIE2,CCP2IE ; Разрешаем прерывание от ССР2 bcf STATUS,RPO ; Возвращаемся в 0-й банк movlw b'00100001 ; Таймер 1 включен (1), внутренний генератор movwf T1CON ; Синхронный режим (0), предделитель 2:1 (10) clrf NEW ; Сбрасываем признак нового значения bsf INTCON,PEIE ; Разрешаем прерывания от Таймера/ССР bsf INTCON,GIE ; Разрешаем работу системы прерываний ; <<<< Остальной код фоновой программы »» ; Сначала сохраним контекст ISR movwf _work swapf STATUS,w movwf „status / * * * * *********************** ; Основной код btfsc PIR1,CCP1IF goto CAPTURE1 btfss PIR2,CCP2IF goto ISR_EXIT CAPTURE2 movf CCPR2L,w movwf TIME+1 movf CCPR2H,w movwf TIME bcf PIR2,CCP2IF incf NEW,f goto ISR_EXIT CAPTURE1 clrf TMR1L clrf TMR1H bcf PIR1,CCP1IF ; Сохраняем W ; и регистр STATUS ; Прерывание от CCP1 (нараст. фронт)? ; ЕСЛИ да, ТО обработаем его! ; Прерывание от ССР2 (спад, фронт)? ; ЕСЛИ нет, ТО ложная тревога! ; Берем младший байт захваченного значения ; и сохраняем его ; Берем старший байт захваченного значения ; и сохраняем его ; Сбрасываем флаг прерывания ; Сообщаем фоновой программе о наличии нового ; значения ; Обнуляем счетный регистр таймера ; Сбрасываем флаг прерывания
486 Часть Ш. Окружающий мир . **************** ******************************************* ISR_EXIT swapf „status,w movwf STATUS swapf _work,f swapf _work,w retfie ; Восстанавливаем регистр STATUS ; Восстанавливаем регистр W, ; не затрагивая регистра STATUS, ; и выходим из прерывания Вопросы для самопроверки 13.1. Используя Таймер 1 совместно с модулем ССР1, напишите программу, фор- мирующую на выходе ССР1 меандр с периодом 20 мс. Частоту кварцевого резонатора примите равной 8 МГц. Подсказка: помните, что состояние вы- хода модуля ССР изменяется только при событии «совпадение», поэтому режим сравнения потребуется переключать «налету» каждые 10 мс. 13.2. В схеме ультразвукового дальномера, приведенной на Рис. 7.9 (стр. 236), ис- пользуется внешний генератор частотой 17.2 кГц, который прерывает работу микроконтроллера каждые 58 мкс, т.е. с периодом, соответствующим време- ни прохождения звуковой волной расстояния в один сантиметр в воздухе. Полагая, что микроконтроллер работает на частоте 20 МГц, покажите, как можно использовать Таймер 2 для генерации прерывания с такой периодич- ностью и точностью, составляющей более 0.1%. 13.3. Микроконтроллеры PIC среднего уровня имеют только один вход внешнего прерывания, INT. Предложите вариант использования Таймера 0 для симу- ляции дополнительного внешнего прерывания на выводе T0CKI. 13.4. При программной реализации асинхронного канала последовательной пе- редачи данных со скоростью 300 бод, необходимо формировать задержки длительностью 3.33 мс. Предполагая, что микроконтроллер работает на час- тоте 8 МГц, покажите, как можно использовать таймер для генерации пре- рывания с периодичностью, равной длительности битового интервала. Усо- вершенствуйте процедуру таким образом, чтобы она поддерживала скорости передачи до 19 200 бод (каждое последующее значение скорости получается удвоением предыдущего). 13.5. Покажите, как можно использовать Таймер 1, работающий от собственного генератора с резонатором 32.768 кГц, для реализации часов реального вре- мени (регистры HOURS:MINUTES:SECONDS) системы центрального отопления из Примера 7.3 (стр. 231). 13.6. В Си-компиляторе CCS имеются встроенные функции для работы с тайме- рами и модулями ССР. Например, запись в счетный регистр Таймера 1 мож- но осуществить вызовом функции set_timerl (<значение>). Для считы- вания состояния таймера предназначена функция get_timerl () >). Функция setup_timerl (<режим>) используется для инициализации тай- мера. Аналогично, функция setup_ccpl(<режим>) предназначена для
Глава 13. Главное — время 487 инициализации регистра CCP1C0N. При задании конфигураций Таймера 1 и модуля ССР1 используются следующие константы: T1_DISABLED T1JNTERNAL T1_EXTERNAL_SYNCH T1_CLK_OUT T1_DIV_BY_2 T1_DIV_BY_4 CCP_COMPARE_RESET_TIMER T1_EXTERNAL T1_DIV_BY_1 T1_DIV_BY_8 Значение, передаваемое в подпрограмму, получается объединением указан- ных констант с помощью оператора ИЛИ «|». Покажите, как можно переписать ответ на Вопрос для самопроверки 13.5 с использованием языка Си. В компиляторе CCS функ- цию можно объявить в качестве обработчика прерывания от модуля ССР1, поставив перед ней директиву #int_ccpl (см. Программу 9.6 на стр. 293 для дополнительной информации). При этом в вашем распоряже- нии имеется зарезервированная переменная ССР_1, представляющая содер- жимое 16-битного регистра CCPR1H:L. 13.7. Широтно-импульсная модуляция может использоваться для управления скоростью вращения электродвигателя постоянного тока за счет изменения среднего тока, протекающего по его обмотке. Однако запуск такого элект- родвигателя представляет известную проблему, поскольку ток обмотки при пуске в несколько раз превышает ток, протекающий в установившемся ре- жиме. Для предотвращения выхода из строя силового управляющего тран- зистора предлагается постепенно увеличивать скважность ШИМ-сигнала с О до максимального значения в течение нескольких секунд. Покажите, как это можно осуществить с помощью микроконтроллера PIC, работающего на частоте 4 МГц, и его модуля ССР. 13.8. Дорожные светофоры на регулируемых пешеходных переходах в Англии при нажатии на любую из кнопок разрешения перехода работают по следующе- му алгоритму: 1. Зеленый свет (нормальный режим). 2. Оранжевый свет в течение 3 с. 3. Красный свет, сопровождающийся звуковым сигналом в течение 15 с. 4. Мигающий оранжевый свет — пять вспышек длительностью по 3 с с трехсекундными паузами между вспышками. 5. Возврат в нормальный режим. Используя подходящий микроконтроллер PIC с модулем Таймера 1, на- пишите программу, управляющую сигналами светофора и звуковым излуча- телем. Хотя световые сигналы расположены по обе стороны дороги, можете считать, что они соединены параллельно и включаются ВЫСОКИМ уров- нем на соответствующем выводе порта. Управляющие кнопки CROSS_REQUESTO и CROSS_REQUEST1 при нажатии формируют лог. О на входе микроконтроллера. Звуковой излучатель включается НИЗКИМ уровнем на соответствующем выводе порта микроконтроллера.
ГЛАВА 14 ЭТОТ БЕЗУМНЫЙ АНАЛОГОВЫЙ МИР Принимая во внимание тот факт, что основной задачей цифровых микрокон- троллеров является отслеживание и управление состоянием реального окруже- ния, которое по своей природе имеет аналоговый характер, нам придется рас- смотреть методы взаимодействия между аналоговым и цифровым миром. Часто все, что нам требуется, — это сравнить уровни двух аналоговых сигналов. Однако в более сложных случаях входной аналоговый сигнал необходимо преобразовы- вать в его цифровой эквивалент, т.е. выполнять аналого-цифровое преобразование (АЦП). В дальнейшем полученный двоичный код можно будет обработать при- вычным образом. И наоборот, если выходной сигнал должен быть аналоговым, необходимо выполнять цифро-аналоговое преобразование (ЦАП). Из этих операций, схематично изображенных на Рис. 14.1, наиболее сложной является операция аналого-цифрового преобразования. Во многих микроконт- роллерах PIC имеется встроенный модуль многоканального АЦП. А вот для фор- мирования аналогового выходного сигнала, как правило, приходится использо- вать дополнительные внешние элементы. Рис. 14.1. Аналоговый мир — цифровая обработка В данной главе мы с вами познакомимся с характеристиками аналоговых и цифровых сигналов, а также рассмотрим преобразование этих сигналов в обоих направлениях применительно к микроконтроллерам PIC. После прочтения этой главы, вы: • Поймете взаимосвязь между аналоговыми и цифровыми сигналами. • Осознаете причину, по которой выборку аналогового сигнала необходимо производить с частотой, превышающей максимальную частоту этого сигна- ла, по крайней мере, в 2 раза.
Глава 14. Этот безумный аналоговый мир 489 • Узнаете, как с помощью метода последовательного приближения можно преобразовать аналоговое напряжение в его двоичный эквивалент. • Разберетесь в работе модулей аналогового компаратора, источника опорно- го напряжения и АЦП, а также научитесь конфигурировать эти модули. • Узнаете, как следует конфигурировать линии ввода/вывода микроконтрол- лера, чтобы они могли работать с аналоговыми или цифровыми сигналами. • Сможете писать ассемблерные программы, считывающие значения анало- говых сигналов с использованием опроса, прерываний, «спящего» режима, а также опрашивающие состояние аналогового компаратора. • Сможете писать программы на языке высокого уровня Си, осуществляю- щие инициализацию различных аналоговых модулей и взаимодействие с ними. • Узнаете, как можно управлять микросхемой внешнего ЦАП через парал- лельный порт. Информация, передаваемая при помощи аналогового сигнала, содержится в определенных параметрах, таких как амплитуда, частота или фаза, которые могут принимать любые значения из непрерывного диапазона величин. Хотя такое оп- ределение подразумевает изменение аналоговых значений в диапазоне ±оо, на практике этот диапазон обычно ограничен. Так, ртутный термометр может изме- рять температуру в диапазоне, скажем, от —10 до +180°С. При температуре, мень- шей нижней границы, вся ртуть окажется спрятанной в колбе. А при температуре, превышающей верхнее значение, термометр просто взорвется! Теоретически квантовая природа вещества подразумевает наличие некоторого нижнего предела, после которого изменения любых параметров приобретают дискретный характер. Однако на практике максимальное значение разрешающей способности, необходимое для обработки, определяется шумами и ограниченной точностью источников сигналов. В цифровых сигналах информация представляется в виде совокупности диск- ретных символов. В зависимости от числа и типа этих символов возможно пред- ставление только конечного числа значений. Так, в двоичной системе л-битное число может в лучшем случае представлять 2" уровней. Хотя такое грубое пред- ставление может показаться несопоставимым с бесконечным числом значений, которые с равной вероятностью может принимать эквивалентный аналоговый сигнал, сетку (шаг) квантования можно подобрать таким образом, чтобы обеспе- чить точность, требуемую для решения каждой конкретной задачи. Так, в систе- мах передачи голоса по телефонным линиям вполне достаточно точности около 1%. В этом случае можно использовать 8-битное представление аналогового сиг- нала, которое даст нам 256 дискретных значений, что соответствует разрешающей способности около 0.5%. В музыкальном компакт-диске используется 16-битное представление (65 536 разрядов) — разрешающая способность около 0.0015%. Из сказанного можно понять, что любой процесс, включающий в себя преоб- разование между аналоговым и цифровым представлением, пройдет через этап квантования. Соответственно, нам необходимо рассмотреть, каким образом этот этап влияет на информационное содержимое соответствующих сигналов.
490 Часть III. Окружающий мир В качестве примера рассмотрим ситуацию, представленную на Рис. 14.2. В данном случае входной сигнал преобразуется в 3-битный код. Процесс кванто- вания (оцифровки) сигнала заключается в сравнении аналогового значения со значениями фиксированного числа уровней — в данном случае восемью. В качес- тве цифрового эквивалента исходного сигнала принимается ближайший по зна- чению уровень. Так, на Рис. 14.2 входное напряжение величиной 0.0536 из полно- го диапазона 0.4285 оказывается больше напряжения, соответствующего 3-му уровню. Соответственно, его квантованное значение принимается равным 3-му уровню и выражается числом Ь’01 Г. Получившаяся ошибка, равная -0.0536, называется шумом квантования, и пол- ностью ее избежать невозможно (см. также Рис. 14.3, г). Кривая распределения Рис. 14.2. Процесс квантования
Глава 14. Этот безумный аналоговый мир 491 ошибки квантования приведена в нижней части Рис. 14.2, и, как можно увидеть, она зависит только от числа уровней квантования. Эту ошибку можно легко определить, вычислив квадрат среднего значения интеграла вероятности ошибок. Взяв квадрат- ный корень от результата, мы получим среднеквадратичное значение шума: F(x) =---х+ — • X 2 Среднеквадратичное значение вычисляется по формуле L1 y[F^2dx 1 гхГ L2 2 L2 Z2L — —Тх-------х+—ах = X 4_ 1 L2 3 L2 2 L2 -----7-Х----X +—X X ЗХ2 2Х 4 = Г. » = 12 Таким образом, среднеквадратичное значение шума равно ь ь , где L — число уровней квантования. 2>/з Основной оценкой качества системы является отношение сигнал/шум (S/N). Если принять, что сигнал имеет синусоидальную форму с размахом 2И£, то сред- неквадратичное значение сигнала будет равно х 2 / , т.е. Дик^значение уаким V2 V2 образом, «-разрядная двоичная система имеет отношение сигнал/шум: или в децибелах: S/N = 20 logl.22x2n = (б.02л + 1.77)дБ. Динамический диапазон квантованной системы определяется отношением полной шкалы (2И£) к разрешающей способности L. То есть он равен 2й, или, в децибелах, 201og2" =20«log2 = 6.02л . Разрешающая способность может также выражаться в процентах — такой параметр называется процентной разрешающей способностью (см. Табл. 14.1). Из Табл. 14.1 четко виден экспоненциальный характер изменения этих пара- метров относительно разрядности двоичного значения. Однако сложность реали- зации этого преобразования и, соответственно, ее стоимость тоже подчиняется этому закону. Так, при использовании 20-битного преобразования на полной шкале 1 В, уровень квантования получится меньше 1 мкВ. В телефонных систе- мах с импульсно-кодовой модуляцией (ИКМ) используется 8-битное кодирова- ние, однако уровни квантования расположены неравномерно — более часто при
492 Часть III. Окружающий мир меньших значениях амплитуды. Такое решение позволяет снизить шипение в трубке во время пауз в разговоре! Линейное 8-битное преобразование подходит для большинства общих применений, обеспечивая разрешающую способность лучше ± х/^%. На самом деле видеоизображение имеет приемлемое качество уже при 4-битном разрешении, а для воспроизведения музыки вообще достаточно од- нобитного квантования, т.е. простого указания полярности сигнала! Таблица 14.1. Параметры квантования Разрядность, и Число уровней квантования (2я) Разрешающая способность [%] Динамический диапазон [ДБ] S/N [ДБ] 4 16 6.25 24.1 26.9 8 256 0.391 48.2 49.9 10 1024 0.097 60.2 61.9 12 4096 0.024 72.2 74.0 16 65 536 0.0015 96.3 98.1 20 1 048 576 0.00009 120.4 122.2 Величины отношения S/N, приведенные в Табл. 14.1, являются теоретически достижимыми максимальными значениями, поскольку ошибки преобразования между представлениями сигнала, а также эффект наложения спектров (мы обсу- дим это чуть ниже) вносят свой вклад в искажение сигнала. С точки зрения аналогового мира время является величиной непрерывной, тогда как в цифровых системах выборка значений происходит через дискретные промежутки времени. Теорема отсчетов Шеннона1* гласит, что при частоте отсче- тов, большей или равной удвоенному значению частоты самой высокочастотной составляющей в сигнале, потери информации не произойдет. Физический смысл этого нижнего предела, называемого частотой Найквиста (Котельникова), можно понять, рассмотрев спектр последовательности амплитудно-модулированных импульсов. Идеальные импульсы (импульсы нулевой длительности и единичной площади) представляются в частотной области бесконечной последователь- ностью гармоник одинаковой амплитуды, отстоящих друг от друга на величину, равную частоте следования импульсов. Реальные импульсы имеют похожий спектр, однако амплитуда гармоник снижается с ростом частоты. Если мы промодулируем эту импульсную последовательность узкополосным сигналом /IsincOfT, то в частотной области эта операция будет эквивалентна ум- ножению гармонического спектра (импульс) на величину >4sincofr, давая суммар- ную и разностную составляющие: ^sincOfT х Z?sina>]/ = (sin(coh +cof )/ + sin(coh -cof)0 для каждой из гармоник coh. В отечественной литературе она более известна как теорема отсчетов Котельникова (тео- рема Котельникова — Найквиста — Шеннона). — Примеч. пер.
Глава 14. Этот безумный аналоговый мир 493 Более сложные узкополосные сигналы можно представить в виде ограничен- ной по частоте (/,„) совокупности отдельных синусоидальных сигналов. Исходя из полученного соотношения, каждая из этих гармоник будет находиться как ниже (суммарная составляющая), так и выше (разностная составляющая) центральной частоты. Из Рис. 14.3, б можно увидеть, что для того, чтобы боковые полосы не перекрывались, гармоники (кратные частоте выборки) должны располагаться с интервалом не менее 2х/ . Для восстановления исходного узкополосного сигнала из импульсной после- довательности можно воспользоваться фильтром нижних частот, как показано на Рис. 14.3, г. Реальные фильтры будут пропускать определенные гармоники, хотя и ослабляя их. При более внимательном рассмотрении спектра сигнала на Рис. 14.3, г можно заметить остаток нижней боковой полосы первой гармоники, попавшей в полосу пропускания фильтра. Однако наибольшие искажения в вос- становленном аналоговом сигнале возникли из-за ошибок квантования, вызван- ных грубой 3-битной дискретизацией. Подобная система будет иметь отношение S/N на уровне 20 дБ. Чтобы снизить требования, предъявляемые к восстанавливающему фильтру, частота отсчетов выбирается, как правило, несколько выше частоты Найквиста. За счет этого появляется защитный промежуток между спектрами. Например, системы телефонной связи с ИКМ ограничивают входной аналоговый сигнал на уровне 3.4 кГц, однако частота выборки при этом составляет 8 кГц. Аналогично, в музыкальных компакт-дисках используется частота дискретизации 44.1 кГц, при этом максимальная частота сигнала составляет всего 20 кГц. Еще один пример дискретизации с частотой ниже частоты Найквиста показан на Рис. 14.4. В данном случае частота дискретизации составляет всего 0.75 от час- тоты узкополосного сигнала. Результат восстановления сигнала посредством фильтрации полученной импульсной последовательности, показанный на Рис. 14.4, б, мягко говоря, не очень похож на исходный сигнал. Этот ложный сиг- нал называется помехой дискретизации или ложной частотой (alias). В случае, ког- да во входном аналоговом сигнале присутствуют составляющие с частотой, кото- рая больше половины частоты дискретизации, скажем, из-за шумов, они приво- дят к появлению искажений в восстановленном сигнале. По этой причине аналоговые сигналы перед подачей на АЦП обычно пропускают через ФНЧ. Дан- ный процесс известен как защита от наложения спектров. При работе с аналоговыми сигналами во многих случаях достаточно просто знать, как соотносится контролируемое напряжение с опорным значением 7ref. Например, сигнал, изображенный на Рис. 14.5 (см. также Рис. 14.20), представля- ет собой ток разряда двухфазного дефибриллятора ЭКГ, формируемый датчиком тока (преобразователем ток — напряжение) на основе эффекта Холла. В режиме покоя (когда дефибриллятор не используется) напряжение на выходе датчика дер- жится на уровне 2.6 В. Когда дефибриллятор начинает разряжаться, это напряже- ние в течение нескольких десятков микросекунд резко увеличивается до 3.6 В.
494 Часть III. Окружающий мир Частотная область Временная область Частота Основная полоса частот а) Аналоговый сигнал б) Дискретизированный сигнал после аналого-цифрового преобразования в) Восстановленный аналоговый сигнал (цифро-аналоговое преобразование) г) Аналоговый сигнал после фильтрации; видны искажения, вызванные ошибками квантования Рис. 14.3. Процесс аналого-цифрового преобразования
Глава 14. Этот безумный аналоговый мир 495 а) Дискретизация с частотой, меньшей частоты Найквиста Рис. 14.4. Эффект наложения спектров Если микроконтроллеру необходимо отслеживать напряжение в течение последу- ющих нескольких десятков миллисекунд, скажем, для вычисления суммарной энергии разряда, то для запуска этого процесса ему необходимо знать, когда на- пряжение превысит пороговое значение. На Рис. 14.5 в качестве порогового вы- брано напряжение 3.4 В. Разумеется, можно просто с большой частотой считывать аналоговый сигнал с помощью встроенного модуля АЦП (если он есть), как опи- сано далее на стр. 511, однако на реализацию этой процедуры непрерывного счи- тывания и проверки уйдет большая часть вычислительных ресурсов процессора. Программа получилась бы более эффективной, если бы имелась возможность ав- томатической генерации прерывания при превышении входным напряжением порогового значения, а уже обработчик прерывания запускал бы процедуру счи- тывания и анализа сигнала в режиме реального времени. На Рис. 14.5 аналоговый сигнал Fdefb подается на неинвертирующий (+) вход аналогового компаратора. К инвертирующему входу компаратора подключен ис- точник опорного напряжения 3.4 В. Когда напряжение Pdefb становится больше напряжения Kref, сигнал на выходе компаратора меняется с лог. О на лог. 1, и, на- оборот, при Pdefb < Kefна выходе компаратора снова появляется лог. 0. По своей сути аналоговый компаратор является дифференциальным усилите- лем с высоким коэффициентом усиления без отрицательной обратной связи (ООС). Имея очень большое значение коэффициента усиления при разомкнутой цепи ООС, усилитель будет переходить в состояние насыщения с уровнем выход- ного сигнала, близким к отрицательному или положительному напряжению ис- точника питания, если напряжения на его входах отличаются даже на чрезвычай- но малую величину. Таким образом, в качестве компаратора может применяться обычный ОУ, однако лучше использовать специализированные микросхемы, формирующие на выходе стандартные логические уровни, а также мгновенно
496 и Часть III. Окружающий мир Рис. 14.5. Использование аналогового компаратора для определения начального момента разряда дефибриллятора ЭКГ срабатывающие при переходе медленно меняющегося сигнала через пороговое значение. Все три используемые нами модели микроконтроллеров PIC имеют встроен- ный модуль компаратора. В 8-выводной модели PIC12F675 реализован только один аналоговый компаратор. Однако для моделей в корпусах с большим коли- чеством выводов (в частности, для моделей серии PIC16F87XA) более типичным является наличие сдвоенного компаратора, различные варианты включения ко- торого приведены на Рис. 14.6. Регистр управления компаратора CMCON, обычно расположенный, по адресу h’9C’, используется для выбора одной из восьми возможных конфигураций моду- ля, показанных на Рис. 14.6. Конфигурация компаратора определяется битами режима СМ[2:0] (CMCON[2:OJ). В конкретном случае PIC16F987XA при сбросе микроконтроллера в эти биты заносится число b’l 1 Г, при котором модуль анало- гового компаратора полностью выключен. Во многих других устройствах режи- мом по умолчанию является режим Ь’000’, при котором компараторы тоже от- ключены, однако используемые ими выводы микроконтроллера сконфигуриро- ваны как аналоговые входы. . Запомните универсальное правило для всех микроконтроллеров PIC с анало- говыми модулями: все выводы микроконтроллера, которые могут работать как аналоговые (обычно выводы порта А, Е или GP), всегда после сброса по включе- нию питания становятся аналоговыми входами. Это сделано для того, чтобы пре- дотвратить повреждение входных цифровых буферов (см. Рис. 11.7 на стр. 340) на тот случай, если при включении микроконтроллера на выводе будет присутство- вать аналоговое напряжение величиной, скажем, 2.6 В. Если данный вывод будет сконфигурирован как цифровой вход, воспринимающий сигналы с напряжени-
Глава 14. Этот безумный аналоговый мир 497 Сброс компараторов: (в PIC16F62X/12F675 — состояние после сброса по питанию) I О 210 1 О “I CMCON 1 il I 1С1У(2:О] Компараторы выключены: (в PIC16F87X — состояние после сброса по питанию) I 121 1 'h “fcCMCON Ain 1 Г 1с1Д2:0] Два компаратора с общим входом опорного напряжения Два компаратора с общим входом опорного напряжения, выходы которых выведены на ножки микроконтроллера Четыре мультиплексированных входа, подключаемые к двум Один компаратор с выходом, выведенным на ножку микроконтроллера RA4/T0CKI/C1OUT компараторам I 12! 1 'I 0°1 CMCON 1 I I 1сМ[2:0] RA0/AN0 <±3—-------г • О Н—♦ <ZZ3 * • 1 RA3/AN3 RA2/AN2 От модуля опорного напряжения компаратора Рис. 14.6. Режимы работы модуля аналогового компаратора в микроконтроллерах PIC 16F87X
498 Часть III. Окружающий мир ем, близким к О В или к напряжению питания, то такое промежуточное напряже- ние может привести к одновременному открытию обоих входных транзисторов. В результате через них потечет сквозной ток, который способен вызвать тепловой пробой. Поскольку аналоговые напряжения не имеют каких-либо четко опреде- ленных значений, то даже в случае, когда вывод сконфигурирован как аналого- вый вход, в схему часто вводится внешний последовательный резистор, который служит для ограничения тока в том случае, если аналоговое напряжение превы- сит напряжение питания микроконтроллера или станет отрицательным, как по- казано на Рис. 14.20. В микроконтроллерах линейки PIC16F87XA для сохранения совместимости с более старыми моделями PIC16F87X, не имевшими модуля аналогового компара- тора, такая конфигурация (переключение) выводов полностью отключена по умолчанию. Однако все устройства данной линейки имеют модуль встроенного АЦП, который при сбросе по питанию переключает все связанные с ним выводы в режим аналоговых входов, выполняя, таким образом, описанное выше правило. При нахождении модуля компаратора в режиме Ь’ПГ его потребление мини- мально, поэтому этот режим следует использовать, если микроконтроллер не ра- ботает с аналоговыми сигналами и модуль компаратора не используется, особен- но в «спящем» режиме. По большому счету в зависимости от режима работы модуля в распоряжении пользователя оказываются либо два полностью независимых компаратора, либо два компаратора с объединенными неинвертирующими входами, которые могут использоваться для подачи общего опорного сигнала. Выходное значение любого активного компаратора можно считать в любой момент времени из 6-го (C1OUT) и 7-го (C2OUT) битов регистра CMCON. На выходе каждого компаратора имеется программируемый инвертор, управляемый битами C1INV и C2INV регистра CMCON (CMCON[4] и CMCON[5] соответственно). При Fin+ > Kin. и сброшен- ном бите инвертирования выходное значение компаратора будет равно 1, в про- тивном случае — 04 Как было указано на стр. 466, в некоторых исполнениях Таймера 1 выход 2-го компаратора может использоваться для блокирования счет- ных импульсов таймера. Используя эту возможность, можно измерять время, в те- чение которого уровень аналогового сигнала превышал пороговое напряжение. В режимах b’01Г и b’ 10 Г выходное значение компараторов также можно считать с выводов RA4/C1OUT и RA5/C2OUT микроконтроллера (в других моделях ис- пользуемые линии портов могут отличаться от указанных). Для этого данные вы- воды должны быть сконфигурированы как выходы при помощи сброса соответ- ствующих битов регистра TRIS.-Аналогично, любые выводы параллельных пор- тов, используемые в качестве аналоговых входов, должны быть сконфигурированы как входы. При изменении выходного сигнала компаратора устанавливается флаг преры- вания от компаратора CMIF, расположенный у микроконтроллера PIC16F687XA в регистре PIR[2], а при установленном бите маски CMIE (Р1Е2[6] для Существует небольшая область неопределенности этого разностного сигнала, составляю- щая не более ±10 мВ (±5 мВ typ), вызванная напряжением смещения компаратора.
Глава 14. Этот безумный аналоговый мир 499 PIC16F87XA) будет сгенерировано прерывание от компаратора, если, разумеется, бит глобального разрешения прерываний также установлен в 1. Поскольку эта линия прерывания используется обоими компараторами, программа должна хра- нить информацию о предыдущих значениях битов C1OUT и C2OUT, чтобы иметь возможность определить, состояние какого из компараторов действительно из- менилось. Эта информация может обновляться в обработчике прерывания. Пос- ле чтения регистра CM CON несоответствие между новым и предыдущим состоя- ниями компаратора, вызвавшее установку флага прерывания, будет устранено — точно так же, как и в случае прерывания по изменению состояния выводов порта В, описанного на стр. 347. Только после выполнения этой операции можно сбрасывать флаг CM IF. Если режим компаратора изменяется «на лету», то перед этим изменением следует запретить прерывание от компаратора. Выждав после изменения режима не менее 10 мкс (в течение этого времени стабилизируются значения сигналов), регистр CMCON необходимо повторно считать для сброса возможного несоответствия, а затем сбросить флаг CMIF перед повторным раз- решением работы системы прерываний. Поскольку модуль компаратора не использует системный тактовый сигнал, активный компаратор можно задействовать для вывода микроконтроллера из «спящего» режима при переходе внешнего сигнала через пороговое значение Fref, что вызывает установку флага CMIF. После «пробуждения» микроконтроллер должен убрать несоответствие (прочитать регистр CMCON) и сбросить флаг CMIF в теле основной программы (после команды sleep) или в обработчике прерывания, если было разрешено прерывание от компаратора. Необходимо отметить, что включенный компаратор потребляет ток, который намного больше базового значения потребления в «спящем» режиме. Например, типичный ток потребления микроконтроллеров PIC12F629/675 в «спящем» ре- жиме составляет 2.9 нА при напряжении 5 В (995 нА max), а модуль компаратора в среднем потребляет 11.5 мкА (16 мкА max). Так что если компараторы не ис- пользуются во время «сна» микроконтроллера, то они должны быть выключены. В режиме b’l 10’ каждый из компараторов может контролировать один из двух сигналов, определяемый состоянием бита входного ключа компаратора CIS (CMCON[3]), который при включении питания сбрасывается в 0. Неинвертиру- ющие входы обоих компараторов в этом режиме подключены к внутреннему ис- точнику опорного напряжения, формируемого модулем опорного напряжения компаратора (Comparator Voltage Reference — CVR). Этот модуль CVR имеется во всех моделях микроконтроллеров с модулем компаратора. Как видно из Рис. 14.7, данный модуль представляет собой анало- говый мультиплексор с подключенной к нему резистивной цепочкой, на выходе которого в соответствии со значениями битов CVR[3:0] регистра управления CVRCON (CVRCON[3:0]) может быть сформировано одно из 16 различных на- пряжений. Модуль опорного напряжения включается при установке бита разре- шения CVREN (CVRCON[7]). При этом цепочка последовательно соединенных резисторов, номинальное сопротивление каждого из которых равно 2 кОм, под- ключается к шине питания FdD.
500 Часть III. Окружающий мир Рис. 14.7. Модуль опорного напряжения компаратора В распоряжении пользователя имеется два диапазона опорного напряжения. Конкретный диапазон задается битом CVRR (CVRCON[5]), который подключает или отключает дополнительный резистор сопротивлением 87? в конец цепочки. Обозначив 4-битное значение CVR[3:0] как л, получим: CVRR Значение Минимум Шаг Максимум 0 (после сброса) KDD х (0.25 + «/32) 0.25 х KDD Kdd/32 0.71875 x rDD 1 HDD х «/24 OB Kdd/24 0.6255 x rDD где п изменяется в диапазоне от 0 до 15.
Глава 14. Этот безумный аналоговый мир 501 Погрешность установки напряжения составляет % шага, но в реальности аб- солютное значение выходного напряжения модуля прямо пропорционально на- пряжению питания, величина которого обычно задается не слишком точно. Кро- ме того, значение KDD может изменяться при уходе напряжения источника пита- ния или батареи из-за температуры или тока нагрузки. Даже любая помеха по шине питания отразится на опорном напряжении, хотя действие помех в какой-то степени можно ослабить посредством фильтрующих конденсаторов и корректной разводкой линий питания. Поэтому в тех случаях, когда требуется точное значе- ние напряжения, часто используются внешние прецизионные источники опорно- го напряжения. В частности, при работе модуля компаратора в режиме Ь’ 100’ этот источник подключается к выводу RA3 (см. Рис. 14.20). Предположим, что мы собираемся получить пороговое напряжение величи- ной 3.4 В (Рис. 14.5) при KDD = 5 В. Нам придется использовать верхний диапа- зон, т.е. CVRR = 0. Вычислим значение битов CVR[3:0]: 5х(0.25 +д/32) = 3.4 0.25+п /32 = 3.4/5 п = (3.4/5 - 0.25) х 32 = 13.76 Таким образом, наиболее близкое к заданному напряжение получится при л =14. Задав CVR[3:0] = b’lПО’, получим Kref= 3.4375 В. В некоторых моделях имеется дополнительный управляющий бит, подключа- ющий выход модуля опорного напряжения к выводу порта, что позволяет ис- пользовать его с внешними узлами схемы. Когда бит CVROE (CVRCON[6]) уста- новлен в 1, аналоговое напряжение Kef выдается на соответствующий вывод мик- роконтроллера. Из-за относительно высокого выходного сопротивления, которое к тому же зависит от выбранной величины опорного напряжения, компания Microchip рекомендует буферировать внутренний источник опорного напряже- ния — обычно с помощью операционного усилителя. При необходимости, зада- вая коэффициент усиления такого усилителя, можно более точно задавать напря- жение Kef- С помощью внешнего ОУ также можно реализовать фильтрацию этого сигнала для снижения уровня высокочастотных помех. При таком режиме работы модуль опорного напряжения может использоваться как простой 4-битный циф- ро-аналоговый преобразователь. Инициализационный код, осуществляющий настройку модулей компаратора и опорного напряжения для нашего примера с дефибриллятором (используется 1-й компаратор, вход которого подключен к RA3), будет иметь следующий вид: include "pl6f877a.inc" bsf STATUS,RPO ; Переключаемся в 1-й банк movlw b'00001110' ; Режим компаратора 110 movwf CMCON ; Подключен к RA3 (CIS = 1) movlw b'10001110' ; Модуль CVREF включен (1), наружу не выведен (0) movwf CVRCON ; Верхний диапазон (0), CVR[3:0] = 1110 bsf PIE2,CMIE ; Разрешаем прерывания от компаратора
502 Часть Ш. Окружающий мир call DELAY_10US ; Ждем 10 мкс, пока выходной сигнал модуля ; установится movf CMCON,f ; Читаем CMCON, чтобы сбросить признак изменения bcf STATUS,RPO ; Возвращаемся в 0-й банк bcf PIR2,CMIF ; Сбрасываем флаг прерывания от компаратора bsf INTCON,PEIE ; Разрешаем прерывания от периферийных устройств bsf INTCON,GIE ; Разрешаем работу системы прерываний Обратите внимание на то, что перед разрешением прерываний формируется задержка длительностью 10 мкс, необходимая для установления внутренних ана- логовых сигналов. Последующее чтение регистра CMCON сбрасывает возможное несоответствие между сохраненным и текущим состоянием компаратора, после чего сбрасывается флаг прерывания от компаратора CMIF. И наконец, как обыч- но, разрешается работа системы прерываний установкой битов маски PEIE и GIE регистра INTCON. В документации на некоторые модели, например PIC12F675, данный модуль называется просто модулем опорного напряжения. В таких моделях регистр уп- равления называется VRCON. Соответственно в названии различных битов этого регистра отсутствует первая буква «С», например VREN вместо CVREN. Во многих случаях необходимо иметь больше информации об аналоговом сигнале, нежели мы можем получить путем «тупого» сравнения сигнала с опор- ным напряжением. Возьмем, к примеру, ситуацию, показанную на Рис. 14.5. В данном случае для вычисления мощности импульса нам потребуется опреде- лять квадрат отклонения уровня сигнала от базового значения и интегрировать его по времени. В таких случаях входной сигнал после считывания необходимо преобразовывать в цифровую форму. Функция преобразования аналоговой величины в цифровой эквивалент мо- жет быть выражена следующим образом: Гь-»К„г^*,х2-'' , ,=1 где kj — z-й двоичный коэффициент, имеющий значение 0 или 1, a p<n < Kref (Fref— фиксированное аналоговое опорное напряжение). Таким образом, Fin представляется в виде двоичной доли Fref, а коэффициенты к, являются искомы- ми значениями разрядов двоичного числа. Чтобы понять, как можно реализовать подобные вычисления на практике, рассмотрим механическую аналогию метода последовательного приближения. Предположим, что у нас имеется объект неизвестной массы W (эквивалент Fin), безмен (эквивалент аналогового компаратора) и набор точных гирь известной массы 1, 2, 4 и 8 г (общая масса гирь эквивалентна величине опорного напряже-
Глава 14. Этот безумный аналоговый мир 503 ния Kref). Тогда для определения массы груза можно воспользоваться следующим алгоритмом: 1. Поместить 8 г на тарелку. Если груз слишком тяжелый, то убрать его (к^ = 0), в противном случае оставить (fa = 1). 2. Поместить 4 г на тарелку. Если груз слишком тяжелый, то убрать его (&2= 0), в противном случае оставить (к2 = 1). 3. Поместить 2 г на тарелку. Если груз слишком тяжелый, то убрать его (fa = 0), в противном случае оставить (£3 = 1). 4. Поместить 1 г на тарелку. Если груз слишком тяжелый, то убрать его (fa = 0), в противном случае оставить (fa = 1). В итоге мы получим ближайшее значение, не превышающее искомое, равнДр суммарной массе гирь, оставшихся на тарелке. Так, если Wбыло равно 6.2 г, то в случае 4-битной системы мы получим 4г + 2г = 6г (Ь’0110’)- В электронике для реализации метода последовательного приближения^ исполь- зуются наборы прецизионных резисторов или конденсаторов, объединенные таким образом, чтобы можно было последовательно уменьшать в 2 раза фиксированное на- пряжение ИгеГ, подаваемое на аналоговый компаратор, который играет роль весов. В большинстве микроконтроллеров для деления опорного напряжения ис- пользуются наборы конденсаторов, емкости которых пропорциональны степе- ням двойки (Рис. 14.8). Конденсаторы небольшой емкости можно легко реализо- вать в кремниевом кристалле интегральной микросхемы, и, хотя точное их значе- ние будет несколько отличаться от партии к партии, в каждом конкретном экземпляре микросхемы емкости всех конденсаторов будут соответствовать друг другу, причем это соответствие будет сохраняться при изменении температуры и напряжения питания. Емкость, кратная базовой, может быть реализована парал- лельным соединением конденсаторов. Как правило, роль этой емкости выполня- ет емкость перехода затвор-исток полевого транзистора, являющегося базовым элементом любой КМОП ИС. Перед запуском процесса преобразования все конденсаторы подключаются к неизвестному аналоговому входному напряжению Цп, как показано на Рис. 14.8, а. При осуществлении выборки (sampling) эти конденсаторы заряжают- ся через внутренние и внешние сопротивления с учетом времени установления внутренних аналоговых ключей. Возьмем в качестве примера модуль 10-битного АЦП, изображенный на Рис. 14.11. В этом случае к выводу AN подключается на- бор параллельно соединенных конденсаторов номинальной емкостью 0.12 пФ. Таким образом, их суммарная емкость равна 120 пФ (120 х 2-12Ф). Внутреннее сопротивление имеет величину порядка 7.5 кОм, которое, однако, сильно зави- сит от температуры и напряжения питания. Рекомендуется, чтобы внешнее со- противление составляло не более 2.5 кОм — в этом случае напряжение смещения, вызванное токами утечки ± % мкА, будет меньше уровня квантования (младшего значащего бита). ° АЦП, работающие по этому принципу, иногда также называют АЦП поразрядного урав- новешивания. — Примеч. пер.
504 Часть III. Окружающий мир а) Процесс выборки б) Процесс хранения Рис. 14.8. Инициализация набора конденсаторов 4-битного преобразователя Постоянная времени т (J?Q при указанных значениях равна 120 х 10-12 х 104 = 1.2 мкс для суммарного сопротивления 7.5 + 2.5 = 10 кОм. Чтобы получить точность не хуже 0.05% итогового напряжения, т.е. 'Л Ю-битно- го уровня квантования, возьмем 8 х т « 10 мкс. В документации максимальное время установления ключа указывается равным 10 мкс, но в нашем примере при- мем его равным 2 мкс. Таким образом, даже в наихудшем случае для полного за- ряда набора конденсаторов хватит 20 мкс. Наш последующий анализ строится на предположении, что к моменту преобра- зования конденсаторы должны быть заряжены до полного значения входного на- пряжения. Это замечание справедливо в случае осуществления выборки по одному из нескольких аналоговых каналов или в том случае, если с момента последней вы- борки прошло достаточно времени для полного стекания заряда. Меньшее выход- ное сопротивление источника сигнала приведет к уменьшению постоянной време- ни. Разумеется, для оценки максимально достижимой частоты выборки к указанно- му времени накопления следует прибавить время собственно преобразования. Во время выборки (S) верхние по схеме обкладки конденсаторов имеют нуле- вой потенциал, а нижние заряжаются до Kin. При переводе ключа в положение «хранение» (Н), как показано на Рис. 14.8, б, нижние обкладки конденсаторов оказываются соединенными с общим проводом, а верхние обкладки — ни к чему не подсоединены. Как известно, напряжение на конденсаторе может измениться только в том случае, если произойдет перенос заряда между обкладками, Л(2 = САК. Таким образом, изменение напряжения на нижних обкладках конден-
Глава 14. Этот безумный аналоговый мир 505 саторов на величину ди = -1/п приведет к появлению на верхних обкладках по- тенциала, равного 0-^п, поскольку заряд не может исчезнуть с обкладки, кото- рая никуда не подключена. Таким образом, в начале процесса преобразования на инвертирующем входе аналогового компаратора присутствует напряжение — Fin. Четырехбитный вариант схемы последовательного приближения, являющей- ся «сердцем» модуля АЦП, в упрощенном виде показан на Рис. 14.9. Поэтапное выполнение операций осуществляется сдвиговым регистром SRG (см. Рис. 2.22 на стр. 51) после установки бита GO/DONE регистра управления АЦП. При пос- туплении на этот регистр тактовых импульсов на каждом его выходе поочередно появляется лог. 1, активизируя каждый этап преобразования: Хранение 3-й бит 2-й бит 1 -й бит 0-й бит Преобразование завершено/ Выборка Набор конденсаторов переключается в положение «Хранение», и все конден- саторы, начиная с конденсатора, имеющего наибольшую емкость, по очереди подключаются к линии Kref. Выходной сигнал компаратора определяет состояние соответствующего бита регистра последовательного приближения (SAR). Под- робно этот процесс показан на Рис. 14.10. После четырех таких операций «установка — проверка — сброс» результат из SAR передается в регистр данных АЦП. При этом сбрасывается флаг GO/DONE, свидетельствуя об окончании процесса преобразования, и устанавливается флаг прерывания ADIF. И в завер- шение аналоговый вход снова подключается к конденсаторам (состояние «Вы- борка»), в результате чего они заряжаются для следующего преобразования, кото- рое можно будет выполнить после небольшой паузы. Суммарное время преобразования приблизительно равно шести периодам сигнала zAD, подаваемого на тактовый вход сдвигового регистра секвенсора (конт- роллера последовательности) — по одному периоду на каждый бит плюс один для каждого из интервалов выборки и хранения. В 10-битном модуле время преобра- зования приблизительно равно 12 периодам тактового сигнала АЦП. Если же го- ворить конкретно о микроконтроллерах PIC, то минимальный период тактового сигнала составляет примерно 1.6 мкс (« 600 кГц) для всех устройств, кроме самых старых моделей PIC16C71/711, в которых это значение равно 2 мкс. Нижняя гра- ница периода не нормируется, однако из-за постепенного стекания заряда из конденсаторов следует избегать тактовых частот с периодом zAD более 20 мкс (50 кГц). Из Рис. 14.11 видно, что в качестве тактового сигнала АЦП может ис- пользоваться сигнал от одного из четырех источников. Первые три сигнала полу- чают из системного тактового сигнала, прошедшего через предделитель, а четвер- тый формируется встроенным RC-генератором, период fAD которого составляет около 4 мкс. Процесс преобразования, при котором каждая последующая доля Kref добав- ляется и при необходимости исключается из начального значения, показан на Рис. 14.10. Как мы уже видели на Рис. 14.8, в конце этапа выборки верхние об- кладки конденсаторов заряжаются до уровня — ^п. В качестве примера предполо- жим, что Fin = 0.4285 Иге(-.
506 Часть Ш. Окружающий мир Рис. 14.9. Упрощенная схема 4-битного АЦП последовательного приближения 1. Процесс начинается с подключения источника опорного напряжения Иге1- к нижней обкладке конденсатора самой большой емкости, что определяется защелкой SAR8 (Рис. 14.9). Это вызывает инжекцию заряда величиной AQ = CtotalHref, который будет одинаков как для конденсатора С} емкостью 8 единиц, так и для остальных конденсаторов, суммарная емкость которых также равна восьми единицам (Рис. 14.10). Таким образом, напряжение на узле N возрастает на ИгеГ/2 до уровня —0.4285 + 0.5 = +0.0715 Href. В общем случае AKN = Kef^k/Ctotai- В результате на выходе компаратора появляется лог. 0 и защелка SAR8 соответственно сбрасывается, приводя напряжение на конденсаторах к значениям, которые были перед началом данного этапа.
Глава 14. Этот безумный аналоговый мир 507 а) Старший значащий бит ''ref _________________ SAR4 г) Младший значащий бит Рис. 14.10. Реализация метода последовательного приближения
508 Часть I II. Окружающий мир 2. SAR4 подключает источник Jzref к следующему конденсатору наибольшей емкости, в результате чего напряжение на узле N возрастает на VK{/4 (т.е. на4/1б). В итоге на инвертирующем входе компаратора появляется на- пряжение —0.4285 + 0.25 = —0.1785Иге(-, что приводит к появлению на выхо- де компаратора лог. 1. Защелка SAR4 остается установленной, при этом на- пряжение узла остается равным —0.1785 Kref. 3. SAR2 подключает источник ИгеГ к следующему конденсатору наибольшей емкости, в результате чего напряжение на узле N возрастает на 1^еГ/8 (т.е. на 2/16). Итоговое напряжение -0.1785 + 0.125 = -0.0535 Kref приводит к появлению на выходе компаратора лог. 1. Защелка SAR2 остается установ- ленной, при этом напряжение узла остается равным —0.0535 Fref. 4. SAR1 подключает источник Fref к конденсатору наименьшей емкости, в ре- зультате чего напряжение на узле А возрастает на ^ef/16 (т.е. на 71б)- Ито- говое напряжение -0.0535 + 0.0635 = +0.009Kref приводит к появлению на выходе компаратора лог. 0 и сбросу защелки SAR1. Таким образом, в регистре SAR окажется код b’0110’ или 0.375 В, представля- ющий 4-битное число, наиболее близко соответствующее напряжению = 0.4825 Иге1-. Остаток, равный 0.0535 Fref, представляет собой погрешность квантования. В большинстве микроконтроллеров используется 8- или 10-битная матрица конденсаторов. Теоретически этот метод можно легко применить и для преобра- зования с большей разрядностью, однако на практике при этом возникают про- блемы, связанные с согласованием конденсаторов большей емкости. Кроме того, наличие помех от работы внутренних логических узлов приводит к тому, что в по- давляющем большинстве процессоров разрядность модуля АЦП ограничивается 12 битами. Также выпускаются внешние быстродействующие АЦП последова- тельного приближения разрядностью больше 12 бит, но они обычно используют наборы резисторов, соединенные по лестничной схеме, и относительно дороги (по сравнению с 8-битными микроконтроллерами). Разброс емкостей конденсаторов, напряжения смещения, сопротивление внутренних ключей, токи утечки, а также нелинейность характеристики аналого- вого компаратора — все это является причиной погрешностей, возникающих при преобразовании. Анализ различных методик измерения указанных погрешностей выходит за рамки данной книги, однако в документации на любую микросхему АЦП (или модуль АЦП микроконтроллера) приводится список источников этих погрешностей и их величины, выраженные в единицах младшего значащего бита (LSB). Так, в справочных данных модуля 10-битного АЦП микроконтроллера PIC16F675 указано, что его суммарная абсолютная погрешность составляет ±1 LSB. Это гарантирует монотонность передаточной характеристики, т.е. что при любом приращении входного напряжения изменение двоичного кода никог- да не произойдет в обратном направлении. Эта ошибка нормируется при Vre{ = = Fdd. Если же Fref будет меньше FDD, то точность ухудшится, хотя в большинстве случаев приемлемый результат достигается при напряжении вплоть до 2 В.
Глава 14. Этот безумный аналоговый мир 509 В микроконтроллере PIC12F675, а также в моделях линейки PIC16F87X име- ется интегрированный модуль 10-битного АЦП. В более старых устройствах, та- ких как PIC16F73, использовался 8-битный вариант этого модуля, очень похо- жий по своей структуре и принципу работы на своего старшего 10-битного собра- та, показанного на Рис. 14.11, который мы и будем рассматривать. Модули АЦП во всех микроконтроллерах PIC используют наборы конденсаторов с параметра- ми, указанными выше. Однако, с точки зрения пользователя, подробности про- цесса преобразования гораздо менее важны, нежели вопросы практического ис- пользования этого модуля. Во всех микроконтроллерах с АЦП на входе последнего расположен аналого- вый мультиплексор. Это позволяет программе обрабатывать до восьми аналого- вых сигналов, по одному в каждый момент времени. Два регистра управления поз- воляют выбрать конкретный канал и определяют источник тактового сигнала. Кроме того, с помощью этих регистров можно сконфигурировать соответствую- щие выводы микроконтроллера как аналоговые (состояние по умолчанию после подачи питания) или цифровые, а также задать конфигурацию источника опор- ного напряжения. Преобразование инициируется установкой бита GO/DONE, который также служит для индикации завершения преобразования, а 10-битный результат затем можно считать из двух 8-битных регистров данных^. Разобьем наше описание модуля АЦП на две части. Сначала рассмотрим про- цесс инициализации и конфигурирования модуля, а уже только потом — соб- ственно процесс преобразования. Инициализация При конфигурировании модуля необходимо учитывать следующие моменты: 1. Каким образом можно включить модуль? 2. Как следует тактировать модуль? 3. Какие каналы требуется использовать? 4. Хватит ли 8-битного результата? Все эти опции задаются с помощью регистров управления АЦП ADCONO и ADCON1* 2). ADON (включение модуля АЦП) После подачи питания на микроконтроллер модуль АЦП находится в выключен- ном состоянии. Для его включения необходимо записать 1 в бит ADON (ADCONfO]). Включенный модуль потребляет в среднем 220 мкА (PIC16F87X), даже не осуществляя преобразований. Поэтому в тех случаях, когда энергопот- ребление микроконтроллера является критичным фактором, модуль АЦП следу- 0 В 8-битном модуле АЦП используется только один регистр данных. 2) В микроконтроллере PIC12F675 управление модулем АЦП осуществляется немного иначе — вместо регистра ADCONO в нем используется регистр ANSEL. Распределение управля- ющих битов по регистрам тоже отличается от описываемого. Однако общие принципы остаются теми же самыми.
510 Часть III. Окружающий мир Выбор формата результата преобразования АЦП 1: Выравнивание вправо, 6 старших битов регистра ADRESH читаются как 0 0: Выравнивание влево, 6 младших битов регистра ADRES читаются как 0 ADCON1 V'ref* 3 2 1 ’CFG3 PCFG2 PCFG1 PCFGOl h’9F’ I I ' Регистр 1 управления АЦП _j____________________________ < AN7/RE2 <‘ AN6/RE1 I I < AN5/RE0 I i AN4/RA5 I I < AN3/RA3 I I AN2/RA2 Преобрг зованиез; вершено Регистр О управления АЦП ADCONO F/2 F/8 F/32 CR PCFG AN7 AN6 AN5 AN4 AN3 AN2 AN1 ANO I4ref- [3 0]|RE2 RE1 REQ RA5 RA3 RA2 RAI RAO 0000 А А А А ААА А VDO 0001 А А А А 1'ref+ А А А RA3 0010 0 0 0 А ААА А VDD 0011 D 0 D А Vref+ А А А RA3 0100 0 D 0 D A D А А VDD 0101 0 D D 0 vret+ 0 А А RA3 011Х D 0 0 0 0 0 D 0 VDD 1000 А А А А V'ref+V'ref- А А RA3 1001 0 D А А ААА А VDD 1010 0 0 А А Vref+ А А А RA3 1011 0 0 А А Vref+l/ref- А А RA3 1100 0 0 0 А Vref+Vref* А А RA3 1101 0 0 0 0 Vref+l/ref- А А RA3 1110 D D D D ODD А VDD 1111 D D D D Vref^ref- 0 А RA3 Регистр результата АЦП, старший байт ADRESH Регистр результата АЦП, младший байт ADRESL _ у Внутренняя шина данных, к процессору Хан/ ^ref VSS VSS VSS VSS VSS VSS VSS RA2 VSS VSS RA2 RA2 RA2 VSS RA2 8/0 7/1 5/0 4/1 3/0 2/1 0/0 6/2 6/0 5/1 4/2 3/2 2/2 1/0 1/2 Рис. 14.11. Модуль 8-канального 10-битного АЦП микроконтроллеров PIC16F87X ет выключать (если, разумеется, он не используется в программе). Обратите вни- мание, что бит GO/DONE нельзя устанавливать той же командой, которая выполняет включение АЦП, во избежание запуска преобразования одновремен- но с включением модуля.
Глава 14. Этот безумный аналоговый мир 511 ADCS[1:0] (выбор тактового сигнала АЦП) Для работы модулю АЦП требуется тактовый сигнал для выполнения последова- тельности операций установки/проверки, проиллюстрированных на Рис. 14.10. Если частота этого сигнала будет слишком высока, то при переключении элемен- тов схемы уравновешивания требуемые значения напряжений не будут успевать устанавливаться. В справочных данных на микроконтроллер нормируется мини- мальное значение периода тактового сигнала АЦП rAD, равное 1.6 мкс (3 мкс при пониженном напряжении питания). Соответственно, максимальное значение частоты преобразования составляет примерно 600 кГц. Так, чтобы получить ?ad= 1.6 мкс (5/§МГц) при использовании 5-МГц резонатора, нам придется за- грузить в биты ADCS[1:0] число 01, соответствующее коэффициенту деления 8. В Табл. 14.2 приведены подходящие установки для пяти наиболее часто использу- емых значений частот кварцевых резонаторов. Таблица 14.2. Период тактового сигнала АЦП при различных частотах системного кварцевого резонатора Источник тактового сигнала АЦП Частота системного кварцевого резонатора ADSC[l:0] 20 МГц 8 МГц 4 МГц 1 МГц 333 кГц /osc/2 00 — — — 2 мкс 6 мкс /osc/8 01 — — 2 мкс 8 мкс — Josc/32 10 1.6 мкс 4 мкс 8 мкс — — CR0 11 2...6 мкс 2...6 мкс 2...6 мкс 2...6 мкс 2...6 мкс cr2) 11 3...9 мкс 3...9 мкс 3...9 мкс 3...9 мкс 3...9 мкс Примечания: 1. Стандартные модели, в среднем 4 мкс. 2. Модели с расширенным диапазоном температур и низковольтные исполнения, в среднем 6 мкс. Для обеспечения функционирования АЦП в системах с низкой тактовой частотой, в частности в тех, где системный генератор работает от часового кварца 32.768 кГц, в модуле предусмотрен отдельный 7?С-генератор. Поскольку этот генератор полностью независим от системного тактового сигнала, то при его использовании преобразование может выполняться при нахождении микро- контроллера в «спящем» режиме. В таком случае для «пробуждения» микроконт- роллера можно использовать прерывание по завершении преобразования. Вы- полнение преобразования при выключенном системном тактовом сигнале увели- чивает точность, поскольку при этом минимизируются наводки со стороны цифровых узлов микроконтроллера. Если при системной тактовой частоте более 1 МГц АЦП работает от встроенного 7?С-генератора, Microchip рекомендует вы- полнять преобразование в «спящем» режиме, поскольку отсутствие синхрониза- ции между двумя тактовыми сигналами увеличивает помехи, наводимые на ана- логовые узлы микроконтроллера. В отличие от других моделей в микроконтроллере PIC12F675 имеется три бита выбора тактового сигнала АЦП, что обеспечивает дополнительный коэффициент деления на 64. Эта опция полезна при работе с 20-МГц резонатором — ее исполь- зование позволяет получить минимально возможное значение периода rAD (3 мкс) при наибольшем допустимом напряжении питания для данного устройства.
512 Часть III. Окружающий мир CHS [2:0] (выбор канала) Микроконтроллеры с модулями АЦП имеют возможность оцифровывать напря- жение с нескольких аналоговых входов. Количество этих входов (каналов) может варьироваться от 4 (используются линии порта GP) в крошечном 8-выводном PIC12F675 до 8 (используются линии портов А и Е) в 40-выводных микроконт- роллерах PIC16F874/7. При сбросе по включению питания все разделяемые выводы портов по умол- чанию конфигурируются как аналоговые входы (см. стр. 496). Как можно увидеть из Рис. 14.12, у контакта ввода/вывода, работающего в качестве аналогового вхо- да, просто отключается входной цифровой буфер — сравните с Рис. 11.3 на стр. 333. Остальные элементы схемы при этом работают, как обычно. Из всего этого можно сделать следующие выводы: • При чтении бита порта, сконфигурированного как аналоговый вход, из-за отключенного входного цифрового буфера всегда будет возвращаться лог. 0. • Буфер TRIS работает, как обычно, поэтому соответствующий бит регистра TRIS должен быть установлен в 1. Таким образом, вывод порта, сконфигу- рированный как аналоговый, должен работать как вход для предотвраще- ния конфликта между аналоговым сигналом Kin и цифровым выходным сиг- налом триггера данных. • АЦП может считывать аналоговое напряжение с вывода микроконтролле- ра, даже если он не был сконфигурирован как аналоговый. Однако в этом случае находящийся в активном состоянии цифровой буфер может потреб- лять излишний ток, который превысит приведенный в спецификации. Vjn к аналоговому модулю Шина данных памяти^анных Запись в регистр PORTx Буфер TRIS 1D >С1 Защита от перена- пряжения Триггер данных 1D Запись в регистр TRISx >С1 Режим аналогового входа Триггер TRIS Цифровой входной буфер К защелке синхронизатора Рис. 14.12. Конфигурирование аналоговых входов портов А и Е PCFG[3:0] (конфигурация аналогового порта) Если в данном конкретном приложении требуется меньше аналоговых каналов, чем имеется в модуле, то некоторые неиспользуемые каналы могут быть задей- ствованы, как обычно, т.е. в качестве цифровых линий ввода/вывода. Для зада-
Глава 14. Этот безумный аналоговый мир 513 ния конфигурации аналогового порта предназначены биты PCFG[3:0] (ADCON1[3:0]). Возможные комбинации, число и положение этих битов зависит от модели микроконтроллера. Для микроконтроллеров линейки PIC16F87X воз- можные значения битов и соответствующие им конфигурации выводов приведе- ны на Рис. 14.11. К примеру, если в вашем проекте требуется только один анало- говый канал, то, загрузив в указанные биты значение b’lПО’, вы получите один аналоговый вход RA0/AIN0, а остальные выводы (RA5, RA[3:1 ] и RE[2:0]) сможе- те использовать для других целей. Даже если не требуется обработка аналоговых сигналов, регистр ADCON1 все равно необходимо конфигурировать — в этом случае используются значения Ь’ОИО’ или Ь’0111’, при которых все выводы, которые могут использоваться АЦП, конфигурируются как цифровые1). Невыполнение этого требования пред- ставляет собой одну из наиболее распространенных ошибок, поскольку боль- шинство современных моделей имеют аналоговые модули и, как было указано на стр. 496, при сбросе по включению питания все соответствующие выводы по умолчанию конфигурируются как аналоговые. Соответственно, при чтении со- стояния таких выводов будет всегда возвращаться 0. Как уже было отмечено, все выводы, используемые для считывания аналогового сигнала, должны быть скон- фигурированными как входы (1 в соответствующих битах регистров TRIS). Как мы видели из Рис. 14.10, операция последовательного приближения за- ключается в последовательном сравнении с долями фиксированного опорного на- пряжения, каждая последующая из которых в 2 раза меньше предыдущей. Соот- ветственно, точность данной операции зависит от качества этого опорного напря- жения. Как правило, указанный параметр (точность) определяется ценой единицы младшего бита (LSB), т.е. шагом квантования. В случае 10-битного преобразования эта величина составляет Kref/1024, или более 0.1% опорного напряжения. В качестве опорного напряжения можно использовать напряжение питания самого микроконтроллера, скажем, 5 В. Так, при значении битов PCFG[3:0] = = b’l 110’ вывод RA0 конфигурируется как аналоговый, а в качестве опорного ис- пользуется напряжение KDD. В этом случае значение, полученное в результате оцифровки, даст нам долю от напряжения питания, которой соответствует вход- ное аналоговое напряжение. Использование напряжения питания в качестве опорного является не самым лучшим выбором с точки зрения помехозащищенности. К тому же его значение может изменяться в некоторых пределах. Если требуется более высокая точность или опорное напряжение, отличное от напряжения источника питания, то для подключения внешнего источника опорного напряжения можно задействовать определенные аналоговые входы. Все модули АЦП позволяют использовать хотя бы одно внешнее напряжение. Что же касается PIC16F87X, то в этом микроконт- роллере можно использовать одно или два внешних опорных напряжения. В част- ности, при PCFG[3:0] = Ь’0101 ’ выводы RA[ 1:0] конфигурируются в качестве ана- логовых входов, а вывод RA3 используется для подключения внешнего прецизи- ” В некоторых моделях (не PIC16F87XA) необходимо также конфигурировать регистр управления модуля аналогового компаратора.
514 Часть 1П. Окружающий мир онного источника опорного напряжения Kref+ (см. Рис. 14.20)!). Величина Kref+ может находиться в пределах от KDD — 2.5 В до KDD + 0.3 В (при этом она не долж- на быть менее 2 В). В некоторых случаях может потребоваться измерение напряжений относительно уровня, отличающегося от Kss (0 В или земля). Модули АЦП в некоторых моделях, например в PIC16F87X, позволяют задать отдельное ниж- нее опорное напряжение Kref_. Скажем, при PCFG[3:0] = b’l 101’ тоже обеспечи- вается два аналоговых канала, а вывод RA3 используется для подачи опорного на- пряжения ИгеГ+. Только вывод RA2 в этом случае используется для подачи опорно- го напряжения Kef-, которое должно быть в пределах -0.3...2 В. А весь диапазон Kef+ ~ Kef- не может быть меньше 2 В. ADFM (формат результата преобразования) В рассматриваемом нами модуле АЦП используется два регистра для хране- ния 10-битного результата. Поскольку суммарная разрядность пары регист- ров ADRESH:ADRESL составляет 16 бит, то возможны два способа размеще- ния 10-битного результата в этих регистрах. В большинстве приложений вполне хватает 8-битных значений — в таких слу- чаях можно спокойно отбросить два младших бита результата преобразования. Из Рис. 14.13, а видно, что эту операцию проще всего осуществить, выравнивая ре- зультат преобразования влево и игнорируя содержимое регистра ADRESL. ADRESH Ь9 Ь8 67 66 65 64 63 62 ADRESL Ы ьо 0 0 0 0 0 0 а) Выравнивание полевой границе (поумолчанию) ADRESH ADRESL 0 0 0 0 0 0 69 68 67 66 hi 64 63. 62 61 60 б) Выравнивание по правой границе Рис. 14.13. Выравнивание 10-битного результата в 16-битном поле Если же необходимо полное 10-битное значение, то бит ADCON1[7] следует установить в 1 для выравнивания результата по правому краю. Как видно из Рис. 14.13, б, в этом случае результат представляет собой 10-битное число, расши- ренное до 16 бит заполнением старших битов нулями. Соответственно для обра- ботки этого значения можно использовать обычную 16-битную арифметику. В качестве источника опорного напряжения также можно использовать встроенный модуль опорного напряжения, если он реализован в данном конкретном микроконтроллере (см. Рис. 14.7). Однако это решение обладает практически теми же недостатками, что и вариант с использованием напряжения питания самого микроконтроллера.
Глава 14. Этот безумный аналоговый мир 515 Процесс преобразования После того как модуль АЦП сконфигурирован, оцифровка выбранного ана- логового канала, с точки зрения пользователя, выглядит достаточно просто. Предполагая пока, что прерывания не используются, можно выделить следую- щие этапы преобразования (включая, для полноты, этап инициализации), кото- рые в графическом виде изображены на Рис. 14.14: 1. Конфигурирование модуля АЦП: • Конфигурирование выводов портов как аналоговых входов и/или входов опорного напряжения (ADC0N1). • Выбор источника тактового сигнала АЦП (ADCONO). • Выбор входного канала АЦП (ADCONO). • Включение модуля АЦП (ADCONO). 2. Ожидание требуемого времени установления, около 20 мкс. 3. Запуск преобразования установкой бита GO/DONE. 4. Ожидание завершения преобразования (сброса бита GO/DONE). 5. Чтение регистров результата ADRES. 6. Переход к этапу 1 или 2 для выполнения следующего преобразования (за- висит от программы). Предположим в качестве примера, что нам необходимо поочередно считывать каждый из восьми аналоговых каналов микроконтроллера PIC16F874/7, выводя старшие восемь битов результата в порт В, а номер канала — в младшие три бита порта D. Частота основного резонатора составляет 20 МГц, в качестве опорного напряжения используется напряжение питания микроконтроллера. Код, приведенный в Программе 14.1, предполагает, что после сброса модуль АЦП был сконфигурирован следующим образом: include "pl6f877а.inc" bsf STATUS,RPO ; Переключаемся в 1-й банк clrf ADCON1 ; Все разделяемые линии порта А - аналоговые clrf TRISB ; Все выводы порта В - выходы movlw b’liniOOO' ; Младшие 3 бита порта D - выходы movwf TRISD bcf STATUS,RPO ; Возвращаемся в 0-й банк movlw b'10000001' ; fosc/32 (10), CH0 (000) movwf ADCONO ; Не запускать преобразование (0), включить АЦП (1) К В данном случае разрешается использование всех восьми аналоговых каналов с внутренним ИОН, результат преобразования выравнивается полевому краю. Ре- гистр ADCON1 инициализируется значением 11Q | QQQ | О'| 0 [ 1 |, при котором в качестве источника тактового сигнала используется/о sc/32 (20/32 = 625 кГц), что
516 Часть III. Окружающий мир соответствует периоду = 1.6 мкс, выбирается 0-й канал АЦП (что в принципе без разницы) и разрешается работа модуля. Поскольку бит GO/DONE сброшен, преобразование пока не запускается. Время преобразования процесс накопления Рис. 14.14. Временная развертка процесса преобразования Основная программа, код которой приведен в Программе 14.1, постоянно крутится в бесконечном цикле. В каждом проходе этого цикла из ADRESH счи- тывается оцифрованный результат преобразования очередного канала и копиру- ется в регистр данных порта В. Перед оцифровкой значение счетчика каналов CHANNEL выдается в порт D в качестве числа по модулю 3. Программа 14.1. 8-канальная система сбора данных MAIN clrf CHANNEL ; Используется в качестве счетчика каналов MAIN_ .LOOP movf CHANNEL,w ; Берем номер канала andlw b'00000111' ; Обнуляем старшие 5 бит movwf PORTD ; Копируем в порт D call GET_ANALOG ; Оцифровываем, результат возвращается в W movwf PORTB ; Копируем его в порт В incf CHANNEL,f ; Переходим к следующему каналу goto MAIN_LOOP ; и так без конца ; * ФУНКЦИЯ : Аналого-цифровое преобразование n-го канала ; * РЕСУРСЫ : Подпрограмма DELAY_17US, регистр TEMP ; * ВХОД : Номер канала в W ; * ВЫХОД : Оцифрованное 8-битное значение в W GET_ANALOG movwf TEMP ; Копируем номер канала в TEMP
Глава 14. Этот безумный аналоговый мир 517 bcf STATUS,C Сдвигаем на три бита влево, rlf TEMP,f rlf TEMP,f rlf TEMP,w ; помещая результат в W bcf ADCONO,CHSO Обнуляем биты выбора канала bcf ADCONO,CHS1 bcf ADCONO,CHS2 addwf ADCONO,f Заносим номер канала в ADCONO[5:3] call DELAY_17US ; Ждем 17 мкс для установления bsf ADCONO,GO ; Запускаем преобразование GET_ANALOG_LOOP btfsc ADCONO,GO Проверим завершение преобразования goto GET_ANALOG_LOOP movf ADRESH,w Считываем результат после сброса бита GO/NOT_DONE return ; * ФУНКЦИЯ : Формирует 17-мкс задержку при частоте 20 МГц (85 циклов) ; * РЕСУРСЫ : Нет ; * ВХОД : ; * ВЫХОД : Нет W обнуляется DELAY_17US movlw d'201 ; Параметр задержки DELAY_17US_L00P addlw btfss goto -1 ; Декрементируем STATUS,Z ; до нуля DELAY_17US_LOOP return Собственно считывание данных осуществляется в подпрограмме GET—analog, при вызове которой в младших трех битах рабочего регистра пере- дается номер требуемого канала. Это значение копируется во временный регистр TEMP, содержимое которого затем сдвигается на три бита влево, чтобы передан- ный номер канала оказался в позиции битов CHS« регистра ADCONO. После сброса битов CHS[2:0] полученное значение складывается с содержимым ADCONO, в результате чего в битах CHS[2:0] оказывается номер канала. После установки требуемого номера канала вызывается подпрограмма за- держки для формирования паузы, необходимой для установления (стабилизации работы) ключа. Поскольку нам достаточно 8-битного разрешения, для заряда конденсаторов с погрешностью до 0.25% финального (установившегося) значе- ния достаточно задержки всего 6т ~ 7 мкс (в худшем случае — 10 мкс), см. стр. 504. Затем для запуска преобразования устанавливается бит GO/DONE ре- гистра ADCONO^. Завершение процесса преобразования контролируется по ° Преобразование можно прервать в любой момент времени, сбросив бит GO/DONE.
518 Часть III. Окружающий мир сбросу этого бита. К этому моменту в регистре ADRESH будет находиться 8-бит- ный результат преобразования. В общем каждое преобразование занимает около 13 х 16 « 21 мкс, таким обра- зом, на оцифровку одного канала затрачивается 17 + 21 = 38 мкс. Соответственно, оцифровка всех восьми каналов (один проход) занимает 38 х 8 ~ 300 мкс, что дает нам скорость, примерно равную 3300 проходам в секунду. Вместо того чтобы опрашивать состояние бита, окончание преобразования можно определять по генерации прерывания. В частности, если преобразование выполняется в то время, пока микроконтроллер находится в «спящем» режиме, то прерывание может использоваться для его «пробуждения». Модуль АЦП мо- жет работать во время «сна» микроконтроллера, поскольку имеет собственный тактовый генератор, независимый от системного тактового генератора микро- контроллера. Основным положительным моментом в выполнении преобразова- ния во время «сна» микроконтроллера является то, что благодаря выключенному системному генератору оно выполняется в более спокойной электромагнитной обстановке. Отрицательной стороной можно назвать увеличение длительности преобразования, поскольку при выходе микроконтроллера из «спящего» режима формируется задержка длительностью 1024 такта, необходимая для перезапуска системного генератора (см. стр. 309). Этот собственный генератор может использоваться и при работе микроконт- роллера в нормальном режиме. Однако из-за отсутствия синхронизации между ним и системным тактовым генератором, возникают помехи от наложения такто- вых сигналов, представляющие достаточно серьезную проблему, особенно при тактовых частотах микроконтроллера выше 1 МГц. Для выполнения преобразования в «спящем» режиме необходимо выполнить следующее: 1. Выбрать в качестве источника тактового сигнала АЦП собственный RC-re- нератор модуля (ADCS 1:0 =11). 2. Сбросить флаг ADIF для предотвращения немедленной генерации преры- вания. 3. Установить биты масок ADIE и PEIE для разрешения прерывания от АЦП, которое будет использоваться для вывода микроконтроллера из «спящего» режима. 4. Если вы не хотите, чтобы после пробуждения микроконтроллера произо- шел переход к обработчику прерывания, необходимо сбросить бит общего разрешения прерываний GIE. 5. Для запуска преобразования сбросить бит GO/DONE регистра ADCON0, после чего сразу же выполнить команду sleep. 6. После «пробуждения» микроконтроллера считать оцифрованное значение из регистров ADRESH:L. В качестве примера напишем новый вариант подпрограммы GET_ANALOG из Программы 14.1, использующий «спящий» режим. На этот раз в секции инициа- лизации необходимо указанным выше образом сконфигурировать систему пре-
Глава 14. Этот безумный аналоговый мир 519 рываний, чтобы обеспечить вывод микроконтроллера из «спящего» режима при установке флага ADIF (которая происходит одновременно со сбросом бита GO/DONE) после завершения преобразования. include "pl6f877a.inc" bsf STATUS,RPO ; Переключаемся в 1-й банк clrf ADC0N1 ; Все разделяемые линии порта А - аналоговые clrf TRISB ; Все выводы порта В - выходы movlw movwf b'11111000' TRISD ; Младшие 3 бита порта D - выходы bsf PIE1,ADIE ; Разрешаем прерывание от АЦП bcf STATUS,RPO ; Возвращаемся в 0-й банк movlw b'11000001' ; Xtal/32 (10), СНО (ООО) movwf ADGQNQ i Не запускать преобразование (0), включить АЦП (1) bcf PIR1,ADIF ; Сбрасываем флаг прерывания bsf INTCON,PEIE ; Разрешаем прерывания от периферийных устройств bsf INTCON,GIE ; и прерывания вообще Помимо инициализации системы прерываний, еще одно изменение связано с установкой битов ADCONO[7:6], которые на этот раз равны Ь’1 Г, чтобы выбрать внутренний RC-генератор для тактирования АЦП. Код подпрограммы GET_ANALOG для работы в «спящем» режиме, приведен- ной в Программе 14.2, практически идентичен исходному варианту, за исключе- нием следующих моментов: 1. Если запрос на прерывание может генерироваться другими периферийны- ми устройствами, то бит GIE необходимо сбрасывать. 2. Перед запуском преобразования необходимо сбрасывать флаг ADIF для предотвращения преждевременного выхода из «спящего» режима. 3. Команда sleep расположена сразу после команды установки бита GO/DONE. При работе АЦП от собственного тактового генератора перед началом преобразования автоматически вставляется дополнительная за- держка длительностью ZDA, гарантирующая, что преобразование начнется только после исполнения команды sleep. 4. В данном случае нет необходимости опрашивать состояние флага GO/DONE, поскольку микроконтроллер возобновит выполнение про- граммы только после завершения преобразования. В нашем примере бит маски GIE сбрасывается, поэтому при наличии прерываний от других пе- риферийных устройств этот бит после «пробуждения» необходимо устано- вить повторно. Если перед входом в «спящий» режим не сбрасывать бит GIE, то после «пробуждения» процессор автоматически перейдет к выпол- нению обработчика прерывания.
520 Часть III. Окружающий мир Программа 14.2. Оцифровка канала в 8-канальной системе сбора данных ; * ФУНКЦИЯ : Аналого-цифровое преобразование n-го канала ; * РЕСУРСЫ : Подпрограмма DELAY_17US, регистр TEMP ; * ВХОД : Номер канала в W ; * ВЫХОД : Оцифрованное 8-битное значение в W GET_ANALOG movwf TEMP ; Копируем номер канала в TEMP bcf STATUS,C ; Сдвигаем на три бита влево, rlf TEMP,f rlf TEMP,f rlf TEMP,w ; помещая результат в W bcf ADCONO,CHSO ; Обнуляем биты выбора канала bcf ADCONO,CHS1 bcf ADCONO,CHS2 addwf ADCONO,f ; Заносим номер канала в ADCONO[5:3] call DELAY_17US ; Ждем 17 мкс для установления bcf INTCON,GIE ; Запрещаем все прерывания bcf PIR1,ADIF ; Предварительно сбрасываем флаг ADIF bsf ADCONO,GO ; Запускаем преобразование sleep ; Немного поспим bsf INTCON,GIE ; Разрешаем прерывания (если необходимо) movf return ADRESH,w ; Считываем результат после пробуждения В качестве заключительного примера давайте напишем на Си программу для микроконтроллера PIC16F874 (20 МГц), который должен работать как компаратор, наподобие устройства из Примера 11.2 (стр. 354). В данном случае мы будем сравнивать 8-битное слово N, подаваемое в параллельном виде на порт В, с цифровым представлением аналогового сигнала 1-го канала АЦП. Ре- зультат сравнения будет выставляться на выходы RC[2:0] в виде 3-битного ко- да: Ь’ООГ — при аналоговом сигнале, меньшем N, Ь’010’ — в случае равенства и b’l00’ — при аналоговом сигнале, большем N. Компаратор должен иметь гис- терезис величиной ±1 бит, названный в программе delta. Таким образом, если при предыдущем сравнении аналоговый сигнал оказался меньше N, то новый уровень будет равен N+ 1. В обратном случае уровень переключения становит- ся равным А — 1. Функция compare () из Программы 14.3 предполагает, что микроконтроллер уже инициализирован следующим образом: ttinclude <16f874.h> #byte P0RT_B = 0x06 #byte PORT_C - 0x07 ttdevice ADC=8 /* Результат преобразования - 8-битное число */
Глава 14. Этот безумный аналоговый мир 521 /* Объявляем функцию, в которую в качестве параметра передается гистерезис (+1 или -1) и которая возвращает новое значение гистерезиса */ unsigned int compare(unsigned int delta); void main(void) { unsigned int hysteresis = 0; set_tris_c(0xF8); setup_adc(ADC_CLOCK_DIV_32); setup_adc_ports(RAO_RA1_RA3_ANALOG); set_adc_channel(1); Ниже приведены основные функции компилятора CCS для работы с модулем АЦП. setup.adc(ADC_CLOCK_DIV_32) Эта функция загружает требуемое значение в биты ADCS 1 [1:0], определяющие источник тактового сигнала модуля; в данном случае используется деленный на 32 сигнал от тактового генератора процессора. Для выбора внутреннего 7?С-гене- ратора следует использовать константу adc_CLOCK_internal. setup_adc_ports(RAO_RA1_RA3_ANALOG) Эта функция конфигурирует биты PCFG[3:0] регистра ADCON1, определяющие, какие из выводов порта будут аналоговыми, какие — цифровыми и будет ли ис- пользовано внешнее опорное напряжение. Константа RAO_RA1_RA3_ANALOG соответствует такой конфигурации, при которой в качестве аналоговых входов используются линии порта RA3 и RA[1:0] (с внутренним источником опорного напряжения), тогда как остальные линии порта остаются цифровыми — PCFG[3:0] = Ь’ОЮО’ (см. Рис. 14.11). Если же мы хотим использовать вывод RA3 для подключения внешнего ИОН Fref+, то в качестве параметра функции следует указать константу RAO_RA1_ANALOG_RA3_REF. Эти константы, применимые для каждого конкретного устройства, определены в соответствующих заголовочных файлах, в нашем случае — в файле 16f874.h. Для всех устройств, имеющих в своем составе модули АЦП, определены, по меньшей мере, две константы: ALL_ANALOG И NO_ANALOGS. set_adc_channel(n); Эта функция используется для загрузки номера текущего канала в биты CHS[2:0] регистра ADCONO. read_adc(); Эта функция устанавливает флаг GO/DONE регистра ADCONO и возвращает со- держимое регистров ADRESH:L после сброса данного бита. ttdevice ADC=8 Этой директивой задается выравнивание 10-битного результата преобразования по левой границе (см. Рис. 14.13). В таком случае функция read_adc ()
522 Часть III. Окружающий мир возвращает 8-битное целое число, считываемое из регистра ADRESH. При нали- чии в тексте программы директивы #device ADC=10 эта же функция возвраща- ет 2-байтное значение типа long int. В функцию compare () из Программы 14.3 в качестве параметра передается значение гистерезиса, названного delta, который может быть равен +1 или -1 (h’FF’). Результат преобразования сохраняется в локальной переменной analog, которая затем сравнивается с содержимым порта В плюс delta. По результату сравнения на линии RC[2:0] порта С выдается соответствующий код. Программа 14.3. Цифро-аналоговый компаратор с гистерезисом unsigned int compare(unsigned int delta) { unsigned int analog; analog = read_adc(); i£(analog > Ж.В + delta) {PORT_C = 0x04; delta = Oxff;} else if(analog == PORT_B) {PORT_C = 0x02;} else {PORT_C = 0x01; delta = 1;} return delta; } В соответствии с результатом сравнения также обновляется значение пере- менной delta, т.е. delta = +1, если analog < (PORTJB + delta), и delta =-1, если analog > (PORT_B + delta). Новое значение delta воз- вращается функцией в вызывающую программу, что позволяет той обновить зна- чение своей локальной переменной (назовем ее hysteresis). Таким образом, для одновременного формирования выходного сигнала компаратора и обновле- ния значения переменной hysteresis в вызывающей программе должно при- сутствовать следующее выражение: hysteresis = compare(hysteresis); В качестве альтернативы можно было бы объявить переменную hysteresis вне функции main (). Тогда эта переменная стала бы глобальной, т.е. ее значение было бы доступно всем функциям программы и его не потребовалось бы переда- вать между функциями. Преобразование цифровой величины в эквивалентное аналоговое напряже- ние является более простой операцией, нежели рассмотренное нами аналого- цифровое преобразование, да и требуется не так часто. Наверное, именно по этим причинам цифро-аналоговые преобразователи (ЦАП) достаточно редко встреча- ются в составе большинства микроконтроллеров.
Глава 14. Этот безумный аналоговый мир 523 Мы с вами уже знаем, что одним из методов цифро-аналогового преобразования является управление коэффициентом заполнения импульсной последователь- ности, имеющей фиксированную частоту, как показано на Рис. 13.9 (стр. 476). Чем меньше в данном случае исходное значение, тем меньше длительность им- пульсов и тем меньше напряжение на выходе ФНЧ, который выполняет усредне- ние или, иначе, выделяет постоянную составляющую. И, наоборот, большому значению соответствует большой коэффициент заполнения, который в свою оче- редь приводит к появлению высокого напряжения. Цифро-аналоговое преобразование с помощью ШИМ может иметь очень вы- сокую точность и быть простым в реализации. Однако для удаления из выходного сигнала гармоник, кратных частоте импульсов, требуется очень хорошая фильт- рация, что приводит к увеличению времени отклика на изменение цифрового значения. Обычно ШИМ используется для управления мощными нагрузками, такими как электродвигатели или нагревательные элементы, в которых сглажива- ние осуществляется за счет инерционности самих исполнительных устройств. Более того, импульсный характер сигнала как нельзя лучше подходит для управ- ления мощностью с помощью тиристорных схем. Другой способ формирования аналогового сигнала заключается в коммута- ции отводов многозвенного резисторного делителя, каждая ступень которого из- меняет выходное напряжение на величину, соответствующую младшему биту. Этот принцип использовался в модуле опорного напряжения компаратора, пока- занном на Рис. 14.7. Однако для осуществления цифро-аналогового преобразова- ния требуется намного больше резисторов. Так, для 10-битного ЦАП требуется цепочка из 1024 резисторов. В продаже имеется очень много микросхем ЦАП, управляемых извне. Две такие микросхемы были показаны на Рис. 12.3 и Рис. 12.5 (стр. 374 и 379 соответственно). Передача цифрового значения в эти микросхемы осуществля- лась последовательно. Теперь же для полноты картины давайте познакомимся с микросхемой, имеющей параллельный интерфейс для ввода цифровых данных. Подавляющее большинство микросхем ЦАП основаны на многозвенной ре- зистивной цепи типа R-2R, подобной изображенной на Рис. 14.15, а. Напряже- ние, прикладываемое к каждому отводу при замыкании соответствующего ключа, передается на выходной узел после ослабления (деления). Как мы увидим чуть позже, каждая последующая ступень ослабляет это напряжение Ьх в 2 раза, фор- мируя для TV-битного значения следующую весовую функцию: N+\ и=ХАх2'- 1=0 Сопротивление в точке А схемы, показанной на Рис. 14.15,6, равно R (2/?||2/?), в результате чего напряжение ослабляется в 2 раза. По мере продвиже- ния к правому краю цепочки этот процесс повторяется, деля каждое из напря- жений на два. Так, в точке В напряжение Z>0/2 уменьшается в 2 раза, в результате чего мы получаем Ив = Z>0/4. Поскольку схема симметрична, сопротивление каж- дого узла с левой стороны также равно 2R. Это означает, что со стороны любого
524 Часть III. Окружающий мир две -------------------------------—iV'ref а) 3-битная резистивная цепь типа R-2R б) Эквивалентные преобразования схемы Рис. 14.15. Цифро-аналоговое преобразование с помощью многозвенной резистивной цепи типа R-2R цифрового ключа общее сопротивление равно 27? + 27?||27? = 37?. Это очень важ- но, поскольку характеристики транзисторного ключа, такие как его сопротивле- ние, зависят от тока, и поддержание их на одном уровне уменьшает ошибку пре- образования. Для простоты мы ограничились рассмотрением только для трех битов. Одна- ко данный пример можно расширить простым переносом левого оконечного ре- зистора и вставкой требуемого количества секций. Это не влияет на сопротивле-
Глава 14. Этот безумный аналоговый мир 525 ние узла с правой стороны и, соответственно, не изменяет режимов работы рас- положенных правее секций. Если мы еще раз взглянем на наши рассуждения, то увидим, что нигде в вычислениях не фигурирует абсолютное значение сопротив- ления. На самом деле точность преобразования зависит только от соотношения R.'IR. Дело в том, что резисторы с точным соотношением сопротивлений изгото- вить на кремниевой подложке относительно легко, в отличие от резисторов с точными абсолютными значениями сопротивлений. По этой причине в большинстве интегральных микросхем ЦАП используются многозвенные цепи R-2R. В качестве примера возьмем широко распространенную микросхему МАХ506 компании Maxim, изображенную на Рис. 14.16. Это устройство в 20-выводном корпусе содержит четыре независимых ЦАП, использующих одно внешнее опор- ное напряжение Kref. Цифровые данные подаются на выводы D[7:0], а один из че- тырех регистров-защелок выбирается с помощью адресных входов А[1:0]. После защелкивания байт данных перегружается в выбранный регистр и появляется на соответствующем выходе VOUTn. Это выходное напряжение будет находиться в диапазоне от нуля (аналоговая земля — AGND) — для входного кода h’00’ и до — для входного кода h’FF’. Когда вывод Vss подключен к общему проводу, напряжение Fref может иметь лю- бое значение от О В до FDD (+5 В). Однако напряжение на выводе Vss может до- стигать значения -5 В, и в этом случае Fref может лежать в диапазоне ±5 В. Если Kref отрицательно (в случае двухполярного источника питания), то выходное на- пряжение также будет отрицательным. В любом случае выходное напряжение оп- ределяется выражением D х 7ref, где D — входной цифровой код, соответствую- щий долям из диапазона 0...1 (h’00’...h’FF’). Микросхема МАХ505 представляет собой 24-выводную модификацию преды- дущей микросхемы, которая позволяет использовать с каждым из четырех ЦАП отдельный источник опорного напряжения. Кроме того, в этой микросхеме за- щелки ЦАП отделены от резистивной цепи преобразования дополнительным уровнем защелок, управляемых одним и тем же сигналом LDAC. Такая двойная буферизация позволяет программисту обновлять выходное значение всех четырех ЦАП одновременно после загрузки регистров каждого канала. Для примера предположим, что выводы адреса МАХ506 подключены к выво- дам RA[ 1:0] микроконтроллера, а вывод RA2 микроконтроллера управляет вхо- дом WR для защелкивания адресованного байта данных, формируемого на выво- дах порта В. Тогда для формирования на выходе DACD пилообразного сигнала, показанного на Рис. 14.17, можно написать следующую процедуру: movlw b'Olll' ; DACD - 3-й канал (b'll'), WR = 1 movwf PORTA ; Выдаем на выводы WR и Al:0 MAX506 LOOP movwf PORTB ; Данные передаем на выводы D7:0 МАХ506 bcf PORTA, 2 ; WR = 0; Защелкиваем данные, bsf PORTA, 2 ; WR = 1; формируя импульс на входе WR addlw 1 ; Инкрементируем счетчик 'goto LOOP ; и так без конца
526 Часть III. Окружающий мир Рис. 14.16. Счетверенный 8-битный ЦАП МАХ506 компании Maxim
Глава 14. Этот безумный аналоговый мир 527 Предполагается, что все линии порта В и линии RA[2:0] порта А уже сконфи- гурированы как выходы. Рис. 14.17. Формирование пилообразного сигнала с использованием ЦАП МАХ506 Пилообразный выходной сигнал ЦАП, изображенный на Рис. 14.17, форми- руется при использовании микроконтроллера с 12-МГц резонатором. При дли- тельности каждой итерации цикла, равной шести машинным циклам, период пи- лообразного сигнала получится равным (256 х 6)/3 » 0.5 мс. Примеры Пример 14.1 Диапазон входного напряжения аналоговых каналов в большинстве модулей АЦП1) ограничен положительным диапазоном 0...^ef+, где в качестве Kref+ может выступать либо напряжение питания KDD, либо внешнее напряжение, подавае- мое на вход RA3 и лежащее в диапазоне 3 B...KDD. Однако во многих случаях воз- никает необходимость оцифровки биполярных аналоговых сигналов. Сконструи- руйте простую резистивную цепочку для сдвига биполярного напряжения из диа- пазона ±10 В в однополярный диапазон 0...5 В, полагая, что Kref+ равно +5 В. Доработайте конструкцию, добавив фильтр, устраняющий эффект наложения спектров, и полагая, что выборка осуществляется с частотой 5000 отсчетов/с. *) Микроконтроллеры линейки PIC16C77X можно сконфигурировать таким образом, чтобы они воспринимали биполярное входное аналоговое напряжение.
528 Часть III. Окружающий мир Решение Один из возможных вариантов решения этой задачи представлен на Рис. 14.18. Сопротивления трех резисторов должны быть такими, чтобы при входном напряжении О В на входе AN формировалось бы напряжение, равное по- ловине шкалы (ИгеГ+/2 = 2.5 В). Кроме того, входное напряжение должно быть ос- лаблено в четыре раза. В общем виде это соотношение можно выразить следую- щим образом: Hin = ±G х Href+. Сопротивления резисторов определяются из следующих соображений: 1. Когда Hin = 0, напряжение на суммирующем узле равно половине диапазо- на, что соответствует выходному значению b’l0000000’. Для этого сопро- тивление параллельно соединенных резисторов R{ и R2 должно быть равно сопротивлению 7?3, т.е. ^3= Л111Л2- 2. Ослабление сигнала осуществляется делителем напряжения, составленным из резисторов R{ и /?2Н^з- Соответственно значение С определяется из выра- жения 2G=(R. + (R2\\R3))/(R2\\R3), в нашем случае G = 2. После ряда преобразований получим R} = (G-l)xR2 R2 = GxR3 Рис. 14.18. Резистивная цепочка для сдвига уровня напряжения
Глава 14. Этот безумный аналоговый мир 529 '2'kCR С = Понятно, что у нас имеется три неизвестных и всего два уравнения, поэтому для начала мы должны выбрать значение для одного из параметров. Задав сопро- тивление 7?з равным 5 кОм, получим R2 = 2 х 5 = 10 кОм и R} = 10 кОм. Со стороны входа микроконтроллера все три резистора оказываются соеди- ненными параллельно, поэтому выходное сопротивление нашей схемы равно 2.4 кОм. Это значение удовлетворяет требованию, предъявляемому модулем АЦП, по сохранению ошибки, вызванной токами утечки, в пределах младшего значащего бита для 10-битного преобразования. При 8-битном преобразовании значения резисторов следует увеличить в 4 раза. С помощью конденсатора небольшой емкости, подключенного к суммирую- щему узлу, можно реализовать простейший ФНЧ первого порядка для ослабле- ния высокочастотных составляющих, наводимых внешними источниками, таки- ми как тактовый генератор микроконтроллера. Этот же ФНЧ выполняет роль фильтра, устраняющего эффект наложения спектров, как было показано на Рис. 14.4. При частоте выборок, равной 5000 отсчетам в секунду, частота среза фильтра не должна превышать 2.5 кГц — половины частоты выборок. Поскольку ослабление в таком фильтре составляет всего 6 дБ/октаву, то лучше выбрать час- тоту среза 1 , равную 1 кГц. Таким образом, получаем atzCR = 1000 10~б 4.8хд 66 нФ Чтобы еще больше снизить шумы, конденсатор фильтра должен иметь хоро- шие высокочастотные параметры (на высоких частотах конденсаторы становятся индуктивностями) и вместе с резистором должен быть размещен как можно бли- же к входу, а рядом с ним не должно проходить никаких цифровых линий. Хоро- шей практикой является развязывание опорного напряжения и напряжения пи- тания с помощью танталовых электролитических конденсаторов малой емкости и/или керамических конденсаторов емкостью 0.1 мкФ для уменьшения помех, вызванных работой микроконтроллера и других устройств, питающихся от того же источника. Используя отдельные линии питания и земли для подключения микроконтроллера к источнику питания, можно еще больше снизить уровень по- мех от этого источника. Пример 14.2 Одной из задач интеллектуального биомедицинского монитора является пе- риодическое измерение пикового напряжения сигнала ЭКГ. Значение, соответ- ствующее данной точке R (см. Рис. 7.1 на стр. 208), должно выводиться через порт В, и при обновлении этого значения на выводе RA5 должен формироваться положительный импульс. Предполагая, что для реализации указанного устрой-
530 Часть III. Окружающий мир ства используется микроконтроллер PIC16F87X, а сигнал ЭКГ поступает на вход RA1, разработайте возможную методику решения указанной задачи. Для преры- вания процессора 2000 раз в секунду мы будем использовать Таймер 0 (см. Программу 13.2 на стр. 461). Напишите процедуру обработки прерывания, соответствующую разработанному алгоритму. Решение Как и любой биомедицинский параметр, сигнал ЭКГ отличается от такта к такту амплитудой, формой и периодом. Даже если бы это было и не так, несовер- шенство элементов системы сбора данных, в особенности кожных электродов, может привести к медленному дрейфу базовой линии (постоянной составляющей сигнала). Поэтому величину порогового напряжения, начиная с которого мы бу- дем отслеживать появление пикового (точка R) значения сигнала, необходимо во время каждого периода принимать равной некоторой части амплитуды предыду- щего пика. Один из возможных вариантов реализации этого метода приведен на Рис. 14.19. В данном случае после каждого импульса порог слегка уменьшается, чтобы исключить пропуск последующего пика с меньшей амплитудой. Примем минимальную частоту ЭКГ равной 40 ударам в минуту (период 1.5 с). Тогда если при каждой выборке мы будем уменьшать пороговое значение на У64 бита, то при частоте 2000 выборок/с максимальное уменьшение составит ~ 47. Для этого поро- говое значение THRESHOLD в Программе 14.4 хранится как двухбайтное значе- ние в формате целоегдробное, и после каждой выборки, в которой пиковое значе- ние MAXIMUM не обновляется, из порогового значения вычитается ’/б4 Целого (т.е. дробное, равное Ь’00000100’). Изменяя вычитаемое, можно управлять скоростью изменения порогового значения. Рис. 14.19. Стратегия определения пикового значения сигнала ЭКГ Данная программа работает по следующему алгоритму: 1. ВЫПОЛНИТЬ преобразование для получения значения ANALOG. 2. ЕСЛИ (ANALOG > THRESHOLD) • MAXIMUM = ANALOG • THRESHOLD = ANALOG • PORTB = ANALOG • RA5=1
Глава 14. Этот безумный аналоговый мир 531 3. ИНАЧЕ • THRESHOLD = THRESHOLD - ’/64 • RA5 = О При изменении порогового значения THRESHOLD (в случае, если ANALOG > THRESHOLD) в регистр, содержащий целую часть числа, заносится новое значение MAXIMUM, а регистр с дробной частью обнуляется. Если интер- претировать эту пару байтов как 16-битное слово, то пороговое значение можно вычислить как MAXIMUM х 256 или, иначе, THRESHOLD = MAXIMUM « 8. Предполагается, что значение THRESHOLD было обнулено фоновой програм- мой на этапе инициализации и что мы используем 8-битное аналого-цифровое преобразование. Если оцифрованное значение меньше порогового, то из младшего байта, рас- положенного в регистре THRESHOLD+1, вычитается h’04’ = b’00000100’, и, если при этом возникает заем, декрементируется старший байт THRESHOLD. При ра- венстве порогового значения нулю эта операция вычитания пропускается — та- ким образом, предотвращается потеря значимости. В Программе 14.4 используется подпрограмма GET_ANALOG из Программы 14.1, а также необходимая для ее работы подпрограмма формирова- ния 17-мкс задержки. Однако, поскольку в данном случае интервал между вызо- вами подпрограммы достаточно велик, длительность задержки при необходимос- ти можно уменьшить до 10 мкс. Программа 14.4. Определение пикового значения ЭКГ ; * ФУНКЦИЯ : Обработчик для обновления параметров ЭКГ * ; * ВХОД : По прерыванию от Таймера 0 * ; * ВЫХОД : Обновляет MAXIMUM и THRESHOLD:THRESHOLD+1 * ; * РЕСУРСЫ : П/п GET_ANALOG, возвращающая 8-битное значение * ; Сначала сохраняем контекст ; Сохраняем W ; и регистр STATUS EKG_ISR movwf _work swapf STATUSzw movwf _status btfss goto INTCON,TOIF EKG.EXIT ; Это было прерывание от Таймера 0? ; ЕСЛИ нет, ТО выходим bcf INTCON,TOIF ; Сбрасываем флаг movlw 1 ; Запускаем преобразование call GET_ANALOG ; по 1-му каналу movwf TEMP ; Сохраняем оцифрованное значение subwf THRESHOLD,w ; THRESHOLD - ANALOG btfsc STATUS,C ; ЕСЛИ нет заема, ТО goto BELOW ; не обновляем MAXIMUM movf TEMP,W ; ИНАЧЕ берем оцифрованное значение
532 Часть III. Окружающий мир movwf MAXIMUM которое становится новым MAXIMUM movwf PORTB Выдаем наружу bsf PORTA,5 Сообщаем об этом movwf THRESHOLD Теперь обновляем 2-байтный clrf THRESHOLD+1 порог goto EKG_EXIT и выходим ; Сюда попадаем, если входной сигнал ниже порога BELOW bcf PORTA,5 Сообщаем об отсутствии обновления ; Теперь уменьшаем порог на 1/64 до нуля movf THRESHOLD,f Целая часть порога равна нулю? btfsc STATUS,Z ЕСЛИ нет, ТО пропускаем goto EKG_EXIT ЕСЛИ да, ТО выходим movlw h'04' 1/64 = Ь'000001000' subwf THRESHOLD+1,f ; Вычитаем из байта дробной части btfss STATUS,C Пропускаем, если нет заема decf THRESHOLD,f ИНАЧЕ декрементируем целую часть / EKG_EXIT swapf _status,w ; Восстанавливаем STATUS movwf STATUS swapf _work,f ; Восстанавливаем W, swapf _work,w ; не затрагивая регистр STATUS, retfie / и выходим из прерывания В Программе 14.5 приведена программа на языке Си, реализующая тот же са- мый алгоритм. Директива #int_rtcc указывает компилятору интерпретировать описанную после нее функцию как процедуру обработки прерывания от часов реального времени (Таймера 0). Переменные threshold и maximum в функции ecg_isr () объявлены как статические (static). Это означает, что их значения будут сохранены после выхода из функции и доступны при последующем входе в функцию. По умолчанию локальные переменные функций сохраняют свое зна- чение только на время выполнения функции. В качестве альтернативного вари- анта можно было бы объявить такие переменные вне всех функций. В этом случае переменные становятся глобальными, и их значения сохраняются на протяжении всего времени работы программы. Программа 14.5. Определение пикового значения ЭКГ на Си #byte PORT_B - 6 #bit RA5 - 5.5 /* Вывод RA5 - 5-й бит порта А /* Порт В - регистр 06 #int_rtcc void ecg_isr(void) { unsigned int analog; static unsigned long int threshold = 0; static unsigned int maximum;
Глава 14. Этот безумный аналоговый мир 533 analog = read_adc(); if (analog > threshold»8) { maximum = analog; /* Новое максимальное значение */ PORT_B - analog; /* Выдаем наружу */ threshold = maximum « 8; /* Новый 2-байтный порог */ RA5 =1; /* Сообщаем об этом */ } else if(threshold >= 0x0004) /* ЕСЛИ порог не менее h'0004', */ { threshold = threshold - 0x0004; /* ТО уменьшаем на 1/64 */ RA5 - 0; /* Сообщаем об отсутствии обновления */ } } Переменная threshold имеет тип long int, ПОЭТОМУ КОМПИЛЯТОР CCS бу- дет интерпретировать ее как 16-битное число. Обнуление переменной threshold выполняется единожды при запуске программы, поскольку перемен- ная объявлена как static. И опять же, такое поведение отличается от поведения автоматических переменных, создаваемых по умолчанию. При занесении в переменную threshold нового максимального значения последнее умножается на 256 путем сдвига на восемь разрядов влево. Хороший компилятор автоматически преобразует выражение N*256 к виду N«8 или же, что еще лучше, просто возьмет в качестве результата операции старший байт пары. Пример 14.3 Микроконтроллер используется для вычисления энергии разряда двухфазного дефибриллятора, приведенного на Рис. 14.5. Когда микроконтроллер обнаружи- вает начало разряда, он должен сделать 256 выборок с частотой 20 000 выборок в секунду. При этом энергия определяется как сумма квадратов отклонений сигнала от базового уровня — предполагается, что сопротивление цепи «грудная клетка пациента — электроды» остается постоянным на протяжении всего разряда. Для получения опорного напряжения используется внешний ИОН с выход- ным напряжением 4.096 В, что при 8-битном преобразовании даст нам разреше- ние, равное 16 мВ. После начала разряда на выводе RA4 необходимо сформиро- вать импульс для запуска развертки запоминающего осциллографа, позволяюще- го сохранить осциллограмму сигнала для архивных целей. А после окончания разряда старший байт значения энергии должен быть выведен в порт В для отоб- ражения на дисплее. Покажите, как можно с помощью микроконтроллера PIC16F87XA с 20-МГц резонатором реализовать указанную измерительную систему. Можете считать, что на ИОН можно подавать смещение наподобие стабилитрона. На практике для достижения более точных результатов может использоваться дополнитель- ный переменный резистор, с помощью которого осуществляется подстройка вы- ходного напряжения ИОН в небольших пределах.
Глава 14. Этот безумный аналоговый мир 533 analog = read_adc(); if (analog > threshold»8) { maximum = analog; /* Новое максимальное значение */ PORT_B = analog; /* Выдаем наружу */ threshold = maximum « 8; /* Новый 2-байтный порог */ RA5 =1; /* Сообщаем об этом */ } else if(threshold >= 0x0004) /* ЕСЛИ порог не менее h'0004', */ { threshold = threshold - 0x0004; /* ТО уменьшаем на 1/64 */ RA5 - 0; /* Сообщаем об отсутствии обновления */ } } Переменная threshold имеет тип long int, ПОЭТОМУ КОМПИЛЯТОР CCS бу- дет интерпретировать ее как 16-битное число. Обнуление переменной threshold выполняется единожды при запуске программы, поскольку перемен- ная объявлена как static. И опять же, такое поведение отличается от поведения автоматических переменных, создаваемых по умолчанию. При занесении в переменную threshold нового максимального значения последнее умножается на 256 путем сдвига на восемь разрядов влево. Хороший компилятор автоматически преобразует выражение N*256 к виду N«8 или же, что еще лучше, просто возьмет в качестве результата операции старший байт пары. Пример 14.3 Микроконтроллер используется для вычисления энергии разряда двухфазного дефибриллятора, приведенного на Рис. 14.5. Когда микроконтроллер обнаружи- вает начало разряда, он должен сделать 256 выборок с частотой 20 000 выборок в секунду. При этом энергия определяется как сумма квадратов отклонений сигнала от базового уровня — предполагается, что сопротивление цепи «грудная клетка пациента — электроды» остается постоянным на протяжении всего разряда. Для получения опорного напряжения используется внешний ИОН с выход- ным напряжением 4.096 В, что при 8-битном преобразовании даст нам разреше- ние, равное 16 мВ. После начала разряда на выводе RA4 необходимо сформиро- вать импульс для запуска развертки запоминающего осциллографа, позволяюще- го сохранить осциллограмму сигнала для архивных целей. А после окончания разряда старший байт значения энергии должен быть выведен в порт В для отоб- ражения на дисплее. Покажите, как можно с помощью микроконтроллера PIC16F87XA с 20-МГц резонатором реализовать указанную измерительную систему. Можете считать, что на ИОН можно подавать смещение наподобие стабилитрона. На практике для достижения более точных результатов может использоваться дополнитель- ный переменный резистор, с помощью которого осуществляется подстройка вы- ходного напряжения ИОН в небольших пределах.
534 Часть III. Окружающий мир Решение Подходящая схема показана на Рис. 14.20. Собственно сигнал, изменяющий- ся в диапазоне +1.8...+3.6 В (см. Рис. 14.5), подается на вход 0-го аналогового ка- нала RA0/AIN0. Резистор сопротивлением 10 кОм защищает аналоговый вход от перегрузки, одновременно выполняя, совместно с конденсатором 3300 пФ, роль фильтра, устраняющего эффект наложения спектров, с частотой среза около 450 кГц. Поскольку реальные дефибрилляторы работают с очень большими на- пряжениями (порядка 25 кВ), для защиты микроконтроллера от высоковольтных выбросов используется два диода 1N4004, дополняющие внутренние защитные диоды (см. Рис. 14.12). Рис. 14.20. Измерение энергии разряда дефибриллятора Внешний ИОН напряжением 4.096 В подключен непосредственно к выводу RA3, который является входом внешнего опорного напряжения (конфигурация АЦП — Ь’0101’). Чтобы снизить уровень помех на линиях VDD и Vref+, к ним под- ключены развязывающие танталовые конденсаторы емкостью 1 мкФ. Для определения начального момента разряда используется внутренний ана- логовый компаратор, как было показано на Рис. 14.5. При работе компаратора в режиме Ь’ШО’ для формирования внутреннего опорного напряжения может ис- пользоваться модуль CVREF, как было описано на стр. 501. При сброшенном би- те CIS (см. Рис. 14.6) компаратор 1 может использовать 0-й аналоговый канал совместно с модулем АЦП.
Глава 14. Этот безумный аналоговый мир 535 И, наконец, вывод RA4 и все выводы порта В должны быть сконфигурирова- ны как выходы. Первый из указанных выводов используется для формирования синхроимпульса, а порт В — для вывода полученного результата. include "pl6f877a.inc" org 0 ; ; Вектор сброса goto SET_UP ; ; Переход на фоновую программу org 4 ; : Вектор прерывания goto ECG_ISR ; ; Обслуживаем прерывание от компаратора SET_UP bsf STATUS,RPO ; ; Переключаемся в 1-й банк movlw b’OOOOOHO' ; ; Режим работы компаратора = 110, CIS = 0 movwf CMCON call DELAY_17US ; г Ждем 17 мкс для установления напряжений movf CMCON, f ; ; Читаем CM^QN, чтобы СбрОСИТЬ ПрИЗКйК ИЗМбНеНИЯ bsf PIE2,CMIE ; : Разрешаем прерывания от компаратора movlw b'10001110' ; : Модуль CVREF вкл. (1), внутр. ИОН (0) movwf CVRCON ; : Верхний диапазон (0), CVR[3:0] = 1110 movlw b'00000101' ; ; RA0/1 - аналоговые входы movwf ADCON1 ; ; RA3 - вход опорного напряжения movlw b’101111' ; RA4 - выход movwf TRISA clrf TRISB ; : Все выводы порта В - выходы bcf STATUS,RPO ; ; Возвращаемся в 0-й банк movlw Ь'ЮОООООГ ; ; Включаем модуль АЦП (fosc/32) movwf ADCONO bcf PIR2,CMIF ; Сбрасываем флаг прерывания от компаратора bsf INTCON,PEIE ; ; Разрешаем прерывания от периферийных устройств bsf INTCON,GIE ; ; Разрешаем работу системы прерываний Выше приведена секция инициализация для нашей программы. В данном случае модули конфигурируются следующим образом: 1. Модуль аналогового компаратора работает в режиме b’l 10’, при этом CIS = 0. Для выдерживания интервала, требуемого для установления внут- ренних сигналов модуля, используется подпрограмма DELAY_17US (чтобы не писать отдельный код для формирования 10-мкс задержки). После этого выполняется чтение регистра CMCON для сброса признака изменения со- стояния компаратора. Затем сбрасывается флаг CMIF и разрешаются пре- рывания.
536 и Часть III. Окружающий мир 2. Модуль CVREF работает в режиме b’l 110’ и использует верхний диапазон. Таким образом, он формирует опорное напряжение 3.4375 В. 3. Модуль АЦП работает в режиме Ь’0101’, при котором выводы RA0 и RA1 используются в качестве аналоговых входов, а вывод RA3 — для подачи внешнего опорного напряжения Fref+. Результат преобразования выравни- вается по левой границе для упрощения получения 8-битного результата. В качестве тактового сигнала АЦП используется системный сигнал, делен- ный на 32. При этом в соответствии с Табл. 14.2 частота преобразования получается равной 625 кГц. 4. PORTA[4] конфигурируется как выход. Остальные выводы порта А остают- ся входами для обеспечения функциональности аналоговых входов AIN0, AIN1 и AIN3. Все выводы порта В конфигурируются как выходы. Код собственно программы приведен в Программе 14.6. В процедуре MAIN микроконтроллер просто переключается в «спящий» режим до момента измене- ния состояния аналогового компаратора, по которому генерируется прерывание. После возврата управления в фоновую процедуру старший байт трехбайтной пе- ременной, в которой находится вычисленное значение мощности, копируется в порт В, а затем описанный процесс повторяется. Программа 14.6. Измерение энергии разряда дефибриллятора MAIN sleep ; Ждем nop movf POWER,w ; Берем старший байт значения энергии movwf PORTB ; и выводим его в порт В goto MAIN I ; * ФУНКЦИЯ : Обработчик прерывания, в котором вычисляется * ; * энергия разряда дефибриллятора * ; * ВХОД : По прерыванию от модуля компараторов * ; * ВЫХОД : Обновляется значение POWER:3 * ; * РЕСУРСЫ : П/п GET_ANALOG, возвращающая 8-битное значение, * ; * РЕСУРСЫ : п/п SQUARE, выполняющая умножение 8x8 битов * / ; Сначала сохраняем контекст ECG_ISR movwf _work - ; Сохраняем W swapf STATUS,w ; и регистр STATUS movwf _status btfss PIR2,CMIF ; Это прерывание от компаратора? goto ECG_EXIT ; ЕСЛИ нет, ТО выходим clrf POWER ; Обнуляем регистры результата clrf POWER+1
Глава 14. Этот безумный аналоговый мир 537 clrf P0WER+2 ; Младший байт clrf COUNT ; Обнуляем счетчик (256 итераций) bcf PORTA,4 ; Формируем на RA4 bsf PORTA,4 ; синхроимпульс bcf PORTA,4 ACQUIRE clrw ; Канал 0 (W = h'00') call GET_ANALOG ; Запускаем преобразование addlw -BASELINE ; Определяем разность с базовым напряжением btfsc STATUS,C ; ЕСЛИ заем (С==0), ТО обходим, goto ECG_CONTINUE ; так как разность положительна xorlw b’llllllir ; ИНАЧЕ инвертируем и прибавляем addlw 1 ; единицу для вычисления модуля разности ECG_CONTINUE call SQR ; Возводим в квадрат movf SQUARE+l,w ; Берем младший байт квадрата напряжения addwf POWER+2,f ; Прибавляем к младшему байту результата btfss STATUS,C ; Проверяем перенос goto NEXT_BYTE ; ЕСЛИ нет, ТО складываем след, байты movlw 1 ; Инкрементируем средний байт результата addwf POWER+1,f btfsc STATUS,C ; Был перенос? incf POWER,f ; ЕСЛИ да, ТО инкрементируем старший байт NEXT_BYTE movf SQUARE,w ; Берем старший байт квадрата напряжения addwf POWER+1,f ; Прибавляем к среднему байту результата btfsc STATUS,C ; Проверяем перенос incf POWER,f ; ЕСЛИ нет, ТО инкрементируем старший байт call DELAY_470US ; Ждем перед следующей выборкой incfsz COUNT,f ; Инкрементируем счетчик цикла и повторяем goto ACQUIRE ; операции, если он не равен 0 ECG_EXIT bsf STATUS,RPO ; Сначала сбрасываем признак изменения movf CMCON,f ; состояния компаратора, bcf STATUS,RPO ; читая CMCON из 1-го банка, bcf PIR2, CMIF ; и сбрасываем флаг прерывания swapf _status,w ; Восстанавливаем регистр STATUS movwf STATUS swapf _work,f ; Восстанавливаем W, не затрагивая swapf _work,w ; регистр STATUS, retfie ; и выходим из прерывания После сохранения контекста обработчик прерывания сначала проверяет ис- точник прерывания, а затем сбрасывает счетчик итераций цикла и три регистра,
538 Часть III. Окружающий мир используемые для накопления суммы из 256 квадратов отсчетов напряжения. После этого на выводе RA4 формируется короткий импульс, извещая внешние устройства о начале разряда. Для получения 8-битного оцифрованного значения используется подпро- грамма GET_ANALOG из Программы 14.1. Затем вычисляется отклонение от базо- вого уровня, которое принимается равным 2.6 В (см. Рис. 14.5). Если эта величи- на отрицательна (входное напряжение меньше 2.6 В), что определяется по фор- мированию признака заема при вычитании, то вычисляется дополнительный код байта разности (см. стр. 22) для перевода отрицательного значения в положитель- ное. Полученный модуль напряжения затем возводится в квадрат с использова- нием подпрограммы SQR, код которой был приведен в Программе 8.3 (стр. 258). Затем содержимое 2-байтной глобальной переменной SUM: SUM+1 прибавляется к общей сумме, хранящейся в регистрах POWER: POWER+l: POWER+2. Этот процесс повторяется 256 раз, причем перед каждой последующей итера- цией цикла формируется задержка длительностью 470 мкс. В итоге считывание сигнала с вывода RA0 осуществляется каждые 500 мкс, что соответствует задан- ной частоте дискретизации (2 кГц). После завершения серии выборок, на что ухо- дит примерно 128 мс, производится чтение модуля компаратора для сброса при- знака изменения. Это делается в конце цикла, а не в начале, потому что если входное напряжение перейдет через порог срабатывания компаратора (3.3475 В), то будет зафиксировано новое изменение! Затем флаг CMIF сбрасывается, и вос- станавливается контекст программы. Разумеется, наша программа очень примитивна. Например, базовый уровень может изменяться с течением времени, что влечет за собой необходимость его оп- ределения перед запуском преобразования. И, наоборот, при высокой стабиль- ности этого значения его можно запомнить в энергонезависимой памяти, как бу- дет описано в следующей главе. Использование фиксированного числа отсчетов может оказаться ограничивающим фактором. Вместо этого можно выполнять операции считывания до тех пор, пока полученное значение не окажется меньше некоторого порога. Пример 14.4 Используя язык Си, покажите, как можно в микроконтроллере PIC16F874 считать 10-битный результат оцифровки 3-го аналогового канала при нахожде- нии микроконтроллера в «спящем» режиме. Решение В компиляторе CCS имеется функция sleep () для перевода микроконтрол- лера в «спящий» режим; эта функция просто транслируется в команду sleep. Для преобразования во время «спящего» режима нельзя применять функцию read_adc (), которую мы использовали в Программе 14.3, поскольку в «спя- щем» режиме процессор остановлен. Вместо этого нам потребуется перед входом в «спящий» режим вручную изменить состояния определенных битов, относя-
Глава 14. Этот безумный аналоговый мир 539 щихся к прерываниям, наподобие того, как это было сделано в ассемблерной Программе 14.2. При «пробуждении» можно по отдельности прочитать содержи- мое регистров ADRESH:L и объединить эти значения для формирования 10-бит- ного результата. Код для решения этой задачи приведен в Программе 14.7. Биты PEIE, ADIF и GO/DONE определены с помощью директивы #bit. На этот раз функция setup_adc () вызывается с параметром ADC_CLOCK_INTERNAL, соответствую- щим работе модуля АЦП от собственного RC-генератора, что необходимо для осуществления преобразования в «спящем» режиме. Программа 14.7. Преобразование в «спящем» режиме на Си #include <16f874.h> ttdevice ADC=10 #use delay(clock=8000000) /* Используем 10-битное преобразование */ /* Частота резонатора - 8 МГц */ #bit ADIF = OxOQ.6 #bit PEIE = OxOB.6 INTCON[ 6 ] * / #bit GO = OxlF.2 /* Фдаг прерывания от модуля АЦП в PIR1[$] */ /* Бит разрешения прерываний от ПУ - /* Бит GO/NOT_DONE - ADCONO[2] */ #byte ADRESH = 0x1e #byte ADRESL = 0x9e /* Регистры результата */ void main(void) { unsigned long int result; /* 1б-битная переменная для хранения результата */ set_tris_a(ОхОЕ); setup_adc(ADC_CLOCK_INTERNAL); setup_adc_ports(RAO_RA1_RA3_ANALOG); set_adc_channel(3); delay_us(17); /* Ждем для установления напряжений */ disable_interrupts(GLOBAL);/* Запрещаем все прерывания (GIE и PEIE=1) */ ADIF = 0; enable_interrupts(INT_AD); PEIE = 1; /* Основной код GO = 1; /* Разрешаем прерывания от ПУ */ */ sleep(); result = ((long)ADRESH < < 8) + ADRESL;/* После «пробуждения» считываем оба байта */ } Встроенная функция di sabl e_int er rupts (GLOBAL) сбрасывает оба бита— GIE и PEIE. Обратная по смыслу функция enable_interrupts (GLOBAL) уста- навливает оба бита, однако нам необходимо установить только PEIE, a GIE оста- вить сброшенным. Поэтому мы «напрямую» устанавливаем бит PEIE = 1. Анало-
540 Часть I II. Окружающий мир гично, для сброса флага ADIF используется выражение ADIF = 0. Перед вызовом функции sleep () мы запускаем преобразование, вручную устанавливая бит GO/DONE. После возврата из функции sleep () сначала считывается регистр ADRESH и преобразуется к типу long int, чтобы компилятор интерпретировал его как 16-битный объект. Затем это значение умножается на 256, поскольку оно является старшим байтом 16-битного объекта. В результате сложения с получен- ным 16-битным числом регистра ADRESL его содержимое помещается в младший байт результата. Вопросы для самопроверки 14.1. В Примере 14.2 пороговое значение уменьшалось по линейному закону. Не- смотря на то что такой подход достаточно эффективен при априори извест- ном периоде сигнала, который к тому же изменяется в незначительных пре- делах, в остальных случаях лучшие результаты можно получить, используя экспоненциальное изменение порога. Для получения такой характеристики из результата выборки необходимо вычитать не константу, а некоторую фиксированную долю полученного значения. Покажите, как можно моди- фицировать Программы 14.4 и 14.5, чтобы при каждой выборке пороговое значение уменьшалось примерно на 0.025% (-1/4096), и определите посто- янную времени в пересчете на количество отсчетов. 14.2. В реальных аналоговых сигналах обязательно присутствуют шумы. На прак- тике это часто приводит к необходимости фильтрации или сглаживания. В любом случае шумы, наводимые извне, не должны иметь составляющих значительной амплитуды, с частотой, которая больше половины частоты дискретизации, поскольку при восстановлении эти составляющие окажутся сдвинутыми в полосу пропускания, как показано на Рис. 14.4. Сигнал необ- ходимо пропускать через фильтр нижних частот перед аналого-цифровым преобразованием, как показано на Рис. 14.20. Хотя такой внешний фильтр, устраняющий эффект наложения спект- ров, должен быть, по определению, реализован аппаратно (например, на 7?С-цепочке), помехи, попадающие в полосу пропускания системы, можно сгладить, используя программную фильтрацию. Один из простейших вари- антов цифровой фильтрации заключается в считывании нескольких выбо- рок с последующей выдачей усредненного результата. Например, при сум- мировании 16 отсчетов и последующем четырехкратном сдвиге результата вправо (4-16) случайный шум уменьшится в </16 = 4 раза. Другим методом, хорошо известным тем, кто занимается статистикой, является метод скользящего среднего, который часто используется, напри- мер, для вычисления среднего курса акций в течение месяца. Достаточно эффективный алгоритм такого рода получается при усреднении по трем точкам'.
Глава 14. Этот безумный аналоговый мир 541 Array[z] = , 4 2 4 где Sn — п-й отсчет, полученный аналоговым модулем. Покажите, как можно модифицировать подпрограмму GET_ANALOG, чтобы она запоминала значение двух последних отсчетов и возвращала ус- редненное значение. 14.3. Предположим, что ЦАП МАХ506 используется для реализации автомати- ческой регулировки усиления (АРУ) сигнала, подаваемого на аналоговый вход микроконтроллера в ЭКГ-мониторе из Примера 14.2. Задача АРУ за- ключается в том, чтобы максимальное значение сигнала на аналоговом вхо- де лежало в диапазоне 3Д —7/s полной шкалы. Как бы вы реализовали такую подсистему? Подсказка: вспомните, что выходной сигнал каждого канала ЦАП МАХ506 определяется собственным аналоговым входом и общим опорным напряжением Fref, которое может лежать в диапазоне O...Pdd. 14.4. Входной синусоидальный сигнал необходимо подвергнуть двухполупериод- ному выпрямлению, т.е. отрицательные полуволны должны поменять свой знак. Напишите подпрограмму, выполняющую данное действие, полагая, что 8-битное оцифрованное значение находится в регистре ADRESH, а вы- ходное значение должно быть передано через порт В на внешний ЦАП. 14.5. В основе схемы, изображенной на Рис. 14.21, лежит Fig. 10 из документа AN546 «Using the Analog-to-Digital (A/D) Converter». Такая схема используется для формирования внешнего опорного напряжения для экономичных уст- ройств. Можете ли вы объяснить, как она работает, и из каких соображений выбирается номинал токоограничивающего резистора? Рис. 14.21. Управляемый источник опорного напряжения
ГЛАВА ХРАНИТЬ ВЕЧНО! В большинстве микроконтроллеров PIC среднего и верхнего уровней имеется отдельная EEPROM-память небольшого объема, управление и доступ к которой осуществляются посредством регистров специального назначения, как и для дру- гих периферийных устройств. Наличие встроенной энергонезависимой памяти дает программисту возможность считывать и модифицировать различные стати- ческие данные, такие как показания автомобильного одометра, которые должны сохраняться при отсутствии питания (см. Пример 12.3 на стр. 439). Разумеется, для этой цели можно воспользоваться и внешней микросхемой EEPROM, напри- мер из линейки 24ХХХ (см. Рис. 12.26 на стр. 439). Однако при небольшом объеме данных, требующих хранения, использование внутренней EEPROM увеличивает надежность устройства и уменьшает его стоимость, габаритные размеры и энер- гопотребление. Так что приступим к изучению возможностей этой энергонезависимой памя- ти. После прочтения этой главы вы: • Ознакомитесь с характеристиками модуля EEPROM. • Узнаете, как выполняется чтение/запись данных из/в модуль EEPROM. • Поймете, каким образом в некоторых моделях микроконтроллеров FLASH- память программ можно использовать для хранения долговременных дан- ных. • Сможете сравнить модуль EEPROM и FLASH-память программ в качестве устройства хранения долговременных данных. Устаревший к настоящему моменту микроконтроллер PIC16C84, выпущен- ный в 1994 году, был первым микроконтроллером PIC, у которого память про- грамм была сделана по технологии EEPROM. Как мы уже видели на Рис. 2.13 (стр. 42), электрически стираемое ППЗУ похоже на обычное СППЗУ (EPROM), однако для его стирания не требуется источник ультрафиолетового излучения. Не- смотря на то что технология EEPROM более дорога, чем EPROM, ее использова- ние для реализации памяти программ было оправдано повышенным удобством при разработке опытных образцов устройств, а также при использовании микро- контроллера в учебных целях и радиолюбительской практике. В то же время поя- вился и модуль EEPROM-памяти данных, в котором, отдельно от обычной памяти данных микроконтроллера, могло храниться до 64 байт долговременных данных.
Глава 15. Хранить вечно! 543 Микроконтроллер PIC16C84 и аналогичная ему модель с FLASH-памятью программ PIC16F84 долгое время оставались единственными представителями в линейке микроконтроллеров PIC, имеющими память EEPROM-типа, — до появ- ления в 1998 году микроконтроллера PIC16F87X. В большинстве своем все мик- роконтроллеры среднего уровня, выпущенные после 2000 года, были либо совер- шенно новыми моделями с FLASH-памятью программ, либо аналогами своих предшественников линейки PIC16CXXX с памятью программ EPROM-типа. Все используемые в данной книге модели имеют FLASH-память программ и модуль EEPROM. Прежде чем приступить к изучению этого модуля, будет полезно ознакомить- ся с приложениями, требующими использования энергонезависимой памяти. Хорошим примером такого приложения является смарт-карта (см. Рис. 12.1 на стр. 369). В этой карте должны храниться, помимо всего прочего, номер счета, PIN-код, даты начала и конца срока действия карты. Некоторые из этих данных, такие как номер счета, являются по сути дела фиксированными. А защищенные данные могут изменяться пользователем в любой момент времени с помощью терминала. Если карточка используется в качестве банковской, то должна быть предусмотрена возможность записи на счет посредством банкомата информации о доступном кредите, а также изменение этой информации после совершения оп- латы. Размеры смарт-карт и требования, предъявляемые к стоимости используе- мых в них процессоров, таковы, что наличие интегрированной EEPROM-памяти данных жизненно необходимо. На Рис. 15.1 показана логическая организация модуля EEPROM моделей PIC16F62X1). Матрица памяти модуля не имеет никакого отношения к обычным областям памяти программ и памяти данных. Доступ к ней осуществляется пос- редством четырех РСН, которые используются для указания адреса интересую- щего нас байта, для хранения считываемого или записываемого байта данных, а также для управления процессами чтения и записи. Матрица EEPROM-памяти Модуль EEPROM микроконтроллеров среднего уровня2) поддерживает до 256 8-битных ячеек. В моделях PIC16F627/8, PIC16F873/4 и PIC12F629/75 реа- лизовано только 128 младших ячеек. В моделях PIC16F648 и PIC16F876/7 реализованы все 256 ячеек. Вот основные параметры этого модуля: • Не менее 1 000 000 (107 typ) циклов стирания/записи на ячейку при напря- жении 5 В и температуре 25°С3). • Максимальная длительность цикла стирания/записи — 8 мс (4 мс typ). • Срок сохранности данных более 40 лет (100 лет для микроконтроллеров ли- нейки PIC16F62X). п Модуль EEPROM в более старой модели Р1С16Е84имел аналогичную архитектуру. Правда, размер памяти модуля составлял всего 64 байт, флаг прерывания EEIF был расположен в регистре EECON1, а бит маски EEIE — в регистре INTCON. 2) Модули EEPROM в микроконтроллерах старшего уровня практически такие же, хотя существуют и расширенные варианты модулей, поддерживающие память больших объемов. 3) Сравните с 10 000... 100 000 циклами перезаписи FLASH-памяти программ.
544 Часть III. Окружающий мир 7 (U0) Прерывание по завершении цикла записи 6 (U0) 4 <ио> Регистр 2 управления EEPROM 01010101 10101010 EECON2 h’9D’ EECON1 3 WRERR (R/WX) 2 WREN (R/W0) WR (R/S0) Блокирование RD I h’9C’ (R/SX) I (Состояние после сброса по питанию) R — доступен для чтения W — доступен для записи S — доступен для установки U — не реализован. Читается как 0 X — неизвестно Puc. 15.1. Модуль EEPROM модели PIC16F62X
Глава 15. Хранить вечно! 545 EEADR (регистр адреса EEPROM) Восьмибитный регистр может адресовать до 256 (28) байтов. Если в конкретной модели реализована EEPROM-память меньшего объема, то старшие биты адреса должны быть сброшены, чтобы обращение всегда происходило в пределах физи- ческого адресного пространства. В микроконтроллерах PIC16F627/8 допустимы- ми адресами являются адреса из диапазона h’00’...h’7F’. EEDATA (регистр данных EEPROM) Регистр данных либо содержит 8-битное число, считанное из EEPROM, либо значение, которое пользователь собирается записать в адресованную ячейку EEPROM-памяти. EECON1 (регистр управления 1 EEPROM) Модуль EEPROM может работать в двух режимах — чтение и запись. Управление и контроль процессов чтения и записи осуществляются с помощью регистра EECON1 (см. Рис. 15.2). Регистр управления 1 7 6 5 4 3 2 WRERR WREN (R/WX) (R/W0) 1 WR (R/S0) О RD (R/SX) EECON1 h’9C’ • • • Инициирование операции чтения : '........Инициирование операции записи • Разрешение записи .........................Ошибка записи R — доступен для чтения W — доступен для записи S — доступен для установки () — состояние после сброса X — неизвестно Рис. 15.2. Регистр EECON1 модели PIC16F62X EECON2 (регистр управления 2 EEPROM) Этот регистр физически не реализован и при его чтении всегда возвращается ну- левое значение. Однако он используется для разрешения инициирования опера- ции записи в EEPROM. Для этого в него необходимо загрузить непосредственно друг за другом два числа: сначала b’01010101 ’ (h’55’), а потом b’10101010’ (h’AA’). Эта «пляска с бубном» введена специально, чтобы исключить случайное измене- ние информации в EEPROM.
546 Часть III. Окружающий мир Чтобы прочитать данные из EEPROM, необходимо выполнить следующие операции: 1. Загрузить адрес интересующей нас ячейки в регистр EEADR. 2. Установить бит RD для запуска цикла чтения. 3. Бит RD сразу же автоматически сбрасывается, а искомое 8-битное число можно будет в следующем машинном цикле считать из регистра EEDATA. Подпрограмма EE_GET, код которой приведен в Программе 15.1, реализует опи- санный алгоритм и возвращает значение из EEPROM в рабочем регистре. Причем это значение остается в регистре EEDATA до повторного использования регистра. Программа 15.1. Чтение байта из EEPROM ; * ФУНКЦИЯ : Читает один байт из модуля EEPROM ; * ВХОД : Адрес в EEADR ; * ВЫХОД : Значение байта в W и в EEDATA EE_GET bsf STATUS,RPO ; Переключаемся в 1-й банк movlw b'00000001' ; Устанавливаем RD для запуска movwf EECON1 ; Считываем байт в EEDATA movf EEDATA,w ; Копируем его в W bcf STATUS,RPO ; Возвращаемся в 0-й банк return ; перед возвратом цикла чтения Процесс записи данных в EEPROM намеренно сделан более запутанным, что- бы уменьшить вероятность случайного запуска цикла записи из-за ошибки в про- грамме или неправильной работы процессора, скажем, по причине сбоя в питании. Запись значения в заданную ячейку осуществляется по следующему алгоритму: 1. Скопировать адрес искомой ячейки в регистр EEADR. 2. Установить бит WREN (EECON1[2]) для разрешения операции записи. 3. Запретить все прерывания. 4. Записать в регистр EECON2 число h’55’. 5. Записать в регистр EECON2 число h’AA’. 6. Установить бит WR для инициирования цикла записи. 7. Сбросить бит WREN. • 8. Разрешить прерывания. 9. Дождаться сброса бита WR, свидетельствующего о завершении процесса записи, и выйти из подпрограммы. Цикл записи не запустится, если операции с номерами 4...6 не будут выполне- ны непосредственно друг за другом. Так, если во время записи этой кодовой пос- ледовательности произойдет прерывание, то операция записи будет прервана. Поэтому перед загрузкой кодовой последовательности необходимо запрещать прерывания, сбрасывая бит GIE. При необходимости, по завершении цикла записи может генерироваться преры- вание. Это прерывание разрешается установкой бита маски EEIE (PIE1 [7]). После установки флага прерывания EEIF (PIR1 [7]) прерывание генерируется обычным образом. Флаг EEIF должен сбрасываться вручную в обработчике прерывания.
Глава 15. Хранить вечно! 547 Бывает так, что процессор сбрасывается, скажем, по тайм-ауту сторожевого таймера до завершения цикла записи. В этом случае данные в EEPROM могут оказаться поврежденными. Если операция записи была преждевременно прекра- щена из-за сброса микроконтроллера, то будет установлен флаг WRERR (EECON[3]). В остальных случаях для повышения надежности данные после за- вершения цикла записи можно считать обратно и убедиться в их целостности. К этому времени бит WREN можно уже сбросить, чтобы исключить несанкцио- нированную запись. Сброс этого бита до завершения цикла записи не оказывает влияния на операцию. Приведенный выше алгоритм реализован в Программе 15.2. Значения байта данных и его адреса заносятся в регистры EEDATA и EEADR вызывающей програм- мой. Возврат из подпрограммы происходит только после завершения цикла записи, который длится около 4 мс. Такое решение гарантирует, что указанные РСН не бу- дут изменены во время цикла, что может привести к неверным результатам. Программа 15.2. Запись байта в EEPROM ; * ФУНКЦИЯ : Пишет один байт в модуль EEPROM ; * ВХОД : Байт данных в EEDATA, адрес байта в EEADR ; * ВЫХОД : Прерывания запрещены в течение 9 маш. циклов ; * ВЫХОД : Используется 0-й банк памяти > ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ f EE_PUT bsf bcf bsf bcf STATUS,RPO ; STATUS,RP1 EECON1,WREN ; INTCON,GIE ; : Переключаемся в 1-й банк : Разрешаем запись : Запрещаем все прерывания movlw h'551 ; ; Загружаем кодовую последовательность movwf EECON2 movlw h'AA* movwf EECON2 bsf EECON1,WR ; : Инициируем цикл записи bcf EECON1,WREN ; : Запрещаем дальнейшие операции записи bsf INTCON,GIE ; : Разрешаем прерывания EE_EXIT btfsc EECON1,WR ; ; Проверяем, запись завершена? goto EE_EXIT ; : ЕСЛИ нет, ТО проверяем снова bcf STATUS,RPO ; ; Возвращаемся в 0-й банк return ; и выходим из подпрограммы по окончании ; цикла записи Чтобы проиллюстрировать работу с EEPROM, вернемся к Примеру 12.3 (стр. 439), в котором мы сохраняли 3-байтные показания одометра во внешней последовательной EEPROM. Однако на этот раз мы воспользуемся встроенной EEPROM-памятью. Предположим также, что показания одометра хранятся в ячейках EEPROM с адресами h’ 10’...h’ 12’.
548 Часть III. Окружающий мир В новой программе, код которой приведен в Программе 15.3, для чтения и последующей записи 3-байтного значения одометра из/в модуль EEPROM ис- пользуются подпрограммы EE_GET и ee_put. Адрес первого (старшего) байта в начале подпрограммы копируется в регистр EEADR, а по ходу выполнения под- программы для указания на требуемые ячейки этот регистр инкрементируется и декрементируется. Программа 15.3. Инкрементирование значения одометра, хранящегося в модуле EEPROM ; ФУНКЦИЯ : Инкрементирует 3-байтное значение одометра ; РЕСУРСЫ : Подпрограммы EE_GET и EE_PUT ; ВХОД : Текущее значение в EEPROM по адресам 10:11:12h ; ВЫХОД : Измененное значение в EEPROM по тем же адресам, ; ВЫХОД : а также находится в регистрах LSB:NSB:MSB EXTRA_MILE bsf movlw movwf call movwf bsf incf call movwf bsf incf call movwf STATUS,RPO h'10' EEADR EE_GET MSB STATUS,RPO EEADR,f EE_GET NSB STATUS,RPO EEADR,f EE_GET LSB ; Переключаемся в 1-й банк ; Адрес старшего байта показаний одометра ; Копируем в регистр адреса EEPROM ; Читаем байт из EEPROM ; и кладем его в регистр MSB ; Снова в 1-й банк ; Адрес среднего байта показаний одометра ; Читаем байт из EEPROM ; и кладем его в регистр NSB ; Снова в 1-й банк ; Адрес младшего байта показаний одометра ; Читаем байт из EEPROM ; и кладем его в регистр LSB ; Теперь инкрементируем 3-байтное значение incf LSB,f ; Прибавляем 1 btfss STATUS,Z ; Равно нулю? goto PUT_BACK ; ЕСЛИ нет, ТО продолжаем incfsz NSB,f ; Инкрементируем средний байт goto PUT_BACK ; ЕСЛИ не ноль, ТО продолжаем incf MSB,f ; Помещаем обновленное значение одометра обратно в EEPROM PUT_BACK movf LSB,w ; Берем новое значение младшего байта bsf STATUS,RPO ; Переключаемся в 1-й банк movwf EEDATA ; Кладем его в регистр данных EEPROM call EE_PUT ; Пишем в EEPROM по адресу h'12' movf NSB,w ; Берем новое значение среднего байта bsf STATUS,RPO ; Снова в 1-й банк movwf EEDATA ; Кладем его в регистр данных EEPROM decf EEADR,f ; Адресуем средний байт call EE_PUT ; Пишем в EEPROM по адресу h'll'
Глава 15. Хранить вечно! 549 movf MSB,w ; Берем новое значение младшего байта bsf STATUS,RPO ; Снова в 1-й банк movwf EEDATA ; Кладем его в регистр данных EEPROM decf EEADR,f ; Адресуем старший байт call EE_PUT ; Пишем в EEPROM по адресу h'10' return После считывания и копирования 3-байтного значения показаний одометра в память оно инкрементируется точно так же, как и в Программе 12.19 (стр. 442). Обновленное значение затем повторно заносится в EEPROM в обратном поряд- ке, при этом значение регистра EEADR декрементируется. Подпрограмма EE_PUT проверяет завершение цикла записи перед выходом, поэтому в вызываю- щей программе эту проверку можно не выполнять. Помимо изменения содержимого EEPROM из программы, ее можно инициа- лизировать при программировании микроконтроллера (при занесении кода про- граммы в память программ), как показано на Рис. 10.6, а (стр. 312). Как мы уже говорили, в памяти данных микроконтроллера имеется специальная область, рас- положенная по адресам h’2000’...h’30FF’, доступ к которой может осуществлять- ся только в режиме программирования. Из Рис. 10.6, б и Рис. 10.6, в мы видели, что конфигурационный байт расположен по адресу h’2007’. Содержимое модуля EEPROM также находится в этом адресном пространстве по адресам h’2100’...h’21FF’. Поэтому для занесения в EEPROM-память значений функции sin(x) от 0° до 90° с шагом 10° в исходном коде программы должны присутствовать следующие строки: org h’2100 * ; Адресное пространство модуля EEPROM SINE de 0, h'2C, h’57' , h'7F’, h'A4', h'C4' de h'DD', h'FO', h'FB’, h’FF' в которых содержимое EEPROM задается посредством ассемблерной директивы de (Data EEPROM). После занесения программы в микроконтроллер содержи- мое модуля EEPROM будет выглядеть так, как показано на Рис. 15.3. 1 ЕЕ PROM Window __________ В] ----- 00 01 02 03 04 05 06 07 08 09 Ой 0В ОС 0D 0Е 0F Я 0000 00 2С 57 7F Й4 С4 DD F0 FB FF FF FF FF FF FF FF Й0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF М ^**l**w^ ...'.. . 4 z ': , ',',,, '*, Рис. 15.3. Первые 32 байта внутренней EEPROM-памяти, содержащей таблицу значений функции sin(x) Любые данные, занесенные таким образом в память, могут впоследствии быть считаны программой. Например, чтобы узнать значение sin(50), надо будет про- читать ячейку EEPROM с адресом h’05’ (—), в которой хранится число h’C4’ или 1Q6 196 десятичное (-— =0.76525). 256
550 и Часть III. Окружающий мир Несмотря на то что память программ можно инициализировать аналогичным образом, используя директиву dw, как это сделано в Программе 15.5, такая воз- можность используется довольно редко. Это связано с тем, что в соответствии с идеологией гарвардской архитектуры, базирующейся на разделении адресных пространств памяти программ и памяти данных, ни одна из команд не сможет считать эти данные. Команды могут обращаться только к памяти данных. Однако все более-менее современные PIC-микроконтроллеры с FLASH-памятью про- грамм позволяют программам косвенным образом читать и писать эти данные аналогично тому, как это делается при работе с модулем EEPROM. К таким мик- роконтроллерам, в частности, относятся все модели группы PIC16F87X. Причем между исходными моделями и более поздними версиями с суффиксом «А» име- ются определенные различия. Но сначала мы поговорим о первых. Обе модели PIC16F873/4 имеют FLASH-память программ объемом 4 Кбайт и модуль EEPROM объемом 128 байт, тогда как модели PIC16F876/7 имеют уже 8 Кбайт памяти программ и 256 байт EEPROM. В остальном эти модели пол- ностью идентичны. Основные характеристики модуля EEPROM микроконтроллеров группы PIC16F87XA: • Не менее 100 000 (максимум 106) циклов стирания/записи EEPROM-памя- ти на ячейку. • Не менее 10 000 (максимум 105) циклов стирания/записи FLASH-памяти программ. • Максимальная длительность цикла записи/стирания составляет 8 мс (4 мс typ) как для модуля EEPROM, так и для FLASH-памяти. Хотелось бы обратить внимание на максимальное число циклов перезаписи FLASH-памяти программ (10 ООО^). Несмотря на то что такого значения более чем достаточно при изменении программы устройства, оно накладывает определенные ограничения на использование памяти программ в качестве хранилища долговре- менных данных. По этой причине FLASH-память программ более пригодна для хра- нения неизменяющихся данных, таких как таблицы соответствия, нежели для хране- ния информации, требующей частого обновления, такой как показания одометра. FLASH-память занимает меньше места на кристалле, чем обычная EEPROM- память. Хотя это и ускоряет процесс записи, однако заряды, которые в конечном счете стекают через изоляцию плавающего затвора, оказывают отрицательное воздействие на механизм хранения и приводят к более раннему ухудшению пара- метров памяти. На Рис. 15.4 показан модуль EEPROM модели PIC16F87X вместе с памятью программ. Такое представление справедливо, поскольку регистры EEDATA и EEADR используются для работы с обеими областями памяти. Разумеется, па- мять программ имеет как больший объем (8 Кбайт против 256 байт), так и большую разрядность (Мбит против 8). Именно поэтому в микроконтроллерах были реализованы дополнительные РСН, использующиеся для хранения старше- го байта адреса (EEADRH) и данных (EEDATH). Первые образцы FLASH-памяти допускали всего 100 циклов стирания/записи.
Глава 15. Хранить вечно! 551 ;о' $ EECON1 h’18C’ Прерывание no завершении цикла записи Запись Чтение EECON2 h’18D’ Регистр управления 2 (Блокирование -------н----- 6 5 4 3 2 1 — — — WRERR WREN WR (U0) (U0) (110) (R/WX) (R/W0) (R/SO) RD (R/SO) £ >« S в FLASH-память программ 4/8 Кбайт EEPROM-память данных 128/256 байт di3 d8 EEDATH 1 h’1OE’ d- do 1 EEDATA ' h’lOC’ Старший регистр данных EEPROM Регистр данных EEPROM Pwc. 15.4. FLASH- и EEPROM-память моделей PIC16F87X как хранилище данных
552 Часть III. Окружающий мир Как мы скоро убедимся, процессы чтения и записи обеих областей памяти очень похожи. Память, к которой осуществляется обращение, задается управля- ющим битом EEPGD регистра EECON1 (EECON 1 [7]). За исключением появле- ния этого нового бита и переноса битов EEIF и EEIE в регистры PIR2 и PIE2 со- ответственно, регистр EECON1, показанный на Рис. 15.4, ничем не отличается от регистра базового варианта модуля из модели PIC16F62X, изображенного на Рис. 15.2. Виртуальный регистр EECON2 остался таким же. Чтение и запись модуля EEPROM производятся точно так же, как и в более простых моделях PIC16F62X. Единственное изменение, которое необходимо вне- сти в подпрограммы ee_get и ee_put, связано с тем, что в новых моделях ре- гистры EEDATA и EEADR находятся во 2-м банке, а регистры EECON1 и EECON2 — в 3-м банке. Процесс чтения из FLASH-памяти похож на процесс чтения из модуля EEPROM, только при этом используются 2-байтные регистры адреса и данных. Однако не забывайте, что мы работаем с той же памятью программ, откуда коды команд считываются в исполнительный блок процессора. Из-за этого после ко- манды установки бита RD (EECON 1 [0]) должны располагаться две пустые коман- ды пор. Одним словом, чтение FLASH-памяти программ осуществляется по сле- дующему алгоритму: 1. Скопировать адрес интересующей нас ячейки в регистры EEADRH-.EEADR. 2. Установить бит EEPGD, показывая, что мы обращаемся к памяти про- грамм. 3. Установить бит RD для запуска цикла чтения. 4. Бит RD сразу же автоматически сбрасывается, и искомое 14-битное значе- ние можно обычным образом считать из регистров 2-го банка памяти EEDATH.EEDATA. Этот алгоритм реализован в подпрограмме FLASH_GET, текст которой приве- ден в Программе 15.4. При этом предполагается, что при входе в подпрограмму адрес ячейки уже загружен в регистры EEADRH:EEADR. Программа 15.4. Чтение слова из FLASH-памяти программ * ФУНКЦИЯ : Считывает одно слово из FLASH-памяти программ PIC16F877 ; * ВХОД ; * ВЫХОД : Адрес в EEADRH:EEADR : Данные в EEDATH:EEDATA. Используется 0-й банк • ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ / FLASH_GET bsf bsf STATUS,RP1 STATUS,RPO ; Переключаемся в 3-й банк movlw b’10000000' ; Указываем на память программ, movwf EECON1 ; устанавливая EEPGD в EECON1[7] bsf EECON1,RD ; Устанавливаем RD для запуска цикла чтения nop / nop /
Глава 15. Хранить вечно! 553 bcf STATUS,RP1 ; Возвращаемся в 0-й банк bcf STATUS,RPO return Для примера напишем подпрограмму, которая будет возвращать квадрат це- лого числа от 0 до 100, загружаемого в регистр EEDATH:EEDATA. Разумеется, эту операцию можно выполнить путем умножения, однако в учебных целях мы реа- лизуем это вычисление при помощи таблицы преобразования, размещенной в па- мяти программ. Поскольку содержимым этой таблицы являются константы, мы можем загрузить данные в FLASH-память одновременно с занесением всего ос- тального кода программы. В Программе 15.5 таблица размещается по адресу h’300’ памяти программ. Директива dw похожа на директиву de, однако каждое из значений, указываемых в этой директиве, является 14-битным. Для удобства мы также воспользовались директивой radix, чтобы указать систему счисления констант. В нашем случае все константы интерпретируются ассемблером как десятичные числа. Сразу же за таблицей располагается исполняемый код. Это делает Программу 15.5 отчасти похожей на класс языка Си++, который содержит в себе как члены-данные, так и члены-функции (подпрограммы). Программа 15.5. Табличное возведение в квадрат целого числа radix decimal __config _CPD_OFF & _WRT_ENABLE_OFF org h'3001 ; Таблица начинается с адреса h'300' ; памяти программ ; * ФУНКЦИЯ : Возвращает квадрат целого числа ; * РЕСУРСЫ : Подпрограмма FLASH_GET ; * ВХОД : Целое в W (от 0 до 100) ; * ВЫХОД : 14-битное значение квадрата в SQRH:SQRL. ; * Рабочий банк памяти - 0-й TABLE_QF_SQUARES ; Таблица десятинных констант dw 0,1,4,9,16,25,36,49,64,81,100,121,144,169,196,225 dw 256,289,324,361,400,441,484,529,576,625,696,729,784,841 dw 900,961,1024,1089,1156,1225,1296,1369,1444,1521,1600,1681 dw 1764,1849,1936,2025,2116,2209,2304,2401,2500,2601,2704 dw 2809,2916,3025,3136,3249,3364,3481,3600,3721,3844,3969 dw 4049,4225,4356,4489,4624,4761,4900,5041,5184,5329,5476 dw 5625,5776,5929,6084,6241,6400,6561,6724,6889,7056,7225 dw 7396,7569,7744,7921,8100,8281,8464,8649,8836,9025,9216 dw 9409,9604,9801,10000 SQUARE bsf bcf movwf movlw movwf STATUS,RP1 STATUS,RPO EEADR 3 EEADRH ; Переключаемся во 2-й банк ; Формируем адрес
554 Часть III. Окружающий мир call FLASH_GET ; Считываем n-й элемент таблицы bsf STATUS,RP1 ; Снова идем во 2-й банк bcf STATUS,RPO movf EEDATA,w ; Берем младший байт результата bcf STATUS,RP1 ; 0-й банк movwf SQRL ; Копируем в SQRL (0-й банк) bsf STATUS,RP1 ; Снова идем во 2-й банк movf EEDATH,w ; Берем старший байт результата bcf STATUS,RP1 ; 0-й банк movwf SQRH ; Копируем в SQRH (0-й банк) returr 1 Адрес пп-то элемента таблицы формируется в подпрограмме загрузкой числа пп, переданного в рабочем регистре, в регистр EEADR и записью константы h’03’ в регистр EEADRH. В результате указанных действий мы получаем двухбайтный адрес вида h’3nn’. После этого вызовом подпрограммы flash_GET из таблицы считывается 14-битное число. Затем подпрограмма копирует содержимое регист- ров EEDATH:EEDATA в регистры SQRH.SQRL. Так как эти регистры расположе- ны в 0-м банке, то после копирования содержимого каждого из регистров данных модуля EEPROM, расположенных во 2-м банке, в рабочий регистр, нам прихо- дится переключаться в 0-й банк. Поскольку микроконтроллеры PIC16F874/7 имеют 16 РОН, отображенных на все четыре банка памяти, было бы неплохо раз- местить регистры SQRH:SQRL именно в этой общей области памяти. После занесения программы в FLASH-память микроконтроллера внешним программатором содержимое памяти программ начиная с адреса h’300’ будет вы- глядеть так, как показано на Рис. 15.5. 0 Program Memory Window Mil 0300 0000 0001 8004 0009 0010 0019 0024 0031X 0308 0040 0051 0064 0079 0090 0089 00С4 00Е1 ; 8310 0100 0121 0144 0169 0190 0189 01Е4 0211 Й 0318 0240 0271 0288 02D9 0310 0349 0384 03С1 ; 0320 0400 0441 0484 0409 0510 0559 0584 05F1 0328 0640 0691 06Е4 0739 0790 07Е9 0844 0881 : 0330 0900 0961 09С4 0А29 0890 08F9 0864 0BD1. л 0338 0С40 0СВ1 0024 0D99 0Е1О 0Е89 0F04 0F81 ; 0340 0FD1 1081 1104 1189 1210 1299 1324 1381 0348 1440 14D1 1564 15F9 1690 1729 17С4 1861 Ц 0350 1900 1981 1844 18Е9 1890 1С39 1СЕ4 1D91 у 0358 1Е40 1EF1 1F84 2059 2110 21С9 2284 2341 0360 2400 24С1 2584 2649 2710 1703 1283 008D : 0368 3003 008F 2375 1703 1283 08 ОС 1303 0081 ? 0370 1703 08 0Е 1303 0080 0008 1703 1683 3080 } 0378 008С 140С 0000 0000 1303 1283 0008 3FFFS| Рис. 15.5. Фрагмент FLASH-памяти программ, в котором записана таблица преобразования и подпрограмма SQUARE
Глава 15. Хранить вечно! 555 Несмотря на то что в данном примере положение таблицы было выровнено по 256-байтной границе, на практике она может быть размещена в любом месте памяти. В общем случае для адресации лл-ячейки таблицы к полному 14-битному адресу начала таблицы требуется прибавить смещение пп. Как это можно сделать, обсуждается в Вопросе для самопроверки 15.2. Процесс записи FLASH-памяти в микроконтроллерах линейки PIC16F87X также практически идентичен процессу записи в EEPROM, отличаясь, как и в случае операции чтения, только двумя командами пор. Правда, после запуска цикла записи выполнение программы приостанавливается примерно на 4мс. В течение этого времени производится стирание и последующая запись нового значения в адресованную ячейку памяти программ. Затем программа возобнов- ляет работу в нормальном режиме. Итак, запись в FLASH-память осуществляется по следующему алгоритму: 1. Загрузить адрес конечной ячейки в регистры EEADR:EEADRH. 2. Установить бит EEPGD, показывая, что мы обращаемся к памяти про- грамм. 3. Установить бит WREN в EECON[2] для разрешения операции записи. 4. Запретить все прерывания, если они используются. 5. Записать h’55’ в регистр EECON2. 6. Записать h’AA’ в регистр EECON2. 7. Установить бит WR для инициирования цикла записи. 8. Выполнить две пустые команды пор. 9. Сбросить бит WREN. 10. При необходимости разрешить прерывания. 11. Дожидаться сброса бита WR, свидетельствующего о завершении цикла за- писи, нет необходимости, поскольку на время записи работа процессора приостанавливается и возобновляется только по окончании записи. Подпрограмма flash_put, код которой приведен в Программе 15.6, написа- на в предположении, что при входе в подпрограмму адрес ячейки уже находится в регистрах EEADRH:EEADR, а 14-битное значение — в регистрах EEDATH: EEDATA. Программа 15.6. Запись в FLASH-память программ • ************************************************************ / ; * ФУНКЦИЯ : Записывает одно слово в FLASH-память программ * f * вход : Слово данных в EEDATH:EEDATA / к вход : Адрес ячейки в EEADDRH:EEADDR / * выход : На время записи прерывания запрещаются 7 * выход : Рабочий банк - 0-й FLASH_PUT bsf STATUS,RPO ; Переключаемся в 3-й банк bsf STATUS,RP1 bsf EECON1,EEPGD ; Пишем в память программ
556 Часть III. Окружающий мир bsf bcf EECON1,WREN INTCON,GIE ; Разрешаем операцию записи ; Запрещаем все прерывания movlw h' 55 ' ; Загружаем кодовую последовательность movwf EECON2 / movlw h'AA' movwf EECON2 bsf EECON1,WR ; Инициируем цикл записи nop / nop / bcf EECON1,WREN ; Запрещаем последующую запись bsf INTCON,GIE ; Разрешаем прерывания bcf STATUS,RP1 ; Возвращаемся в 0-й банк bcf STATUS,RPO returr 1 ; и выходим из п/п по окончании цикла записи Все устройства, имеющие память программ с возможностью электрического стирания, содержат в слове конфигурации биты защиты кода. Основной задачей функции защиты кода является предотвращение считывания содержимого памя- ти программ внешним программатором. Таким образом, обеспечивается защита от несанкционированного доступа к коду программы. Что касается моделей PIC16F87X, в них за защиту кода программы отвечают два бита слова конфигура- ции СР[ 1:0], расположенные в битах 13:12 и продублированные в битах 5:4. При СР — 00 защищена вся память программ, при СР = 01 — только старшая половина памяти, при СР = 10 — только старшие 256 байт, а при СР = 11 защита полностью отключена (состояние битов по умолчанию, см. Рис. 10.6, в на стр. 312). Если хоть какая-нибудь область памяти программ защищена, то внешний программа- тор не сможет выполнить запись ни в одну из ее ячеек. При этом чтение запреще- но только для защищенных областей. В процессе разработки и отладки устройств защита памяти программ обычно отключается, поскольку на этом этапе предпо- лагается частое изменение содержимого памяти программ. Если же потребуется снять защиту, то у моделей с FLASH-памятью можно стереть все содержимое па- мяти программ, используя внешний программатор, при этом в слово конфигура- ции будет записано значение по умолчанию (во всех битах — 1). Такая возмож- ность, по определению, отсутствует в микроконтроллерах PIC16CXXX. Защита кода также влияет и на внутренние операции записи в память про- грамм с помощью кода, подобного представленному на Рис. 15.6. Из самой про- граммы запись может осуществляться только в незащищенные участки памяти программ при условии, что бит WRT установлен в 1 (состояние по умолчанию). Запись 0 в этот бит (_WRT_ENABLE_OFF) запретит внутреннюю запись в память программ независимо от установок битов защиты кода. На операцию внутренне- го чтения биты защиты кода никак не влияют. Директива_config, присутству- ющая в Программе 15.5, используется для отключения защиты всей памяти про- грамм, что, вообще говоря, делать не обязательно, поскольку в таком состоянии биты находятся по умолчанию.
Глава 15. Хранить вечно! 557 Защита EEPROM-памяти (Защищает содержимое модуля EEPROM от доступа извне) h’2007’ 13 12 11 10 9 | 8 7 6 5 4 3 2 1 0 СР (debug WRT1 WRT0 | CPD LVP BODEN PWRTE WDTE FOSC1 FOSCO Защита кода 00 = От записи защищена младшая половина памяти программ 00 = LP (Защищает память 01 = От записи защищена младшая четверть памяти программ 01 = XT программ от доступа извне) 10 = От записи защищены младшие 256 ячеек памяти программ 11 = RC 11 = Защита кода выключена Рис. 15.6. Конфигурационное слово моделей PIC16F87XA В моделях с суффиксом «А» защита содержимого памяти программ осуществ- ляется несколько иначе, как можно увидеть из Рис. 15.6. В этих моделях имеется единственный бит защиты СР, предназначенный для защиты всей памяти про- грамм от считывания или от записи извне. Причем даже при включенной защите памяти программ внутренние операции записи и чтения FLASH-памяти разре- шены. Для предотвращения внутренней записи в указанные выше участки памя- ти программ используются два бита WRT[l:0]. Внутренние операции чтения па- мяти программ разрешены всегда. За исключением самой старой модели PIC16F84, во всех микроконтроллерах с модулем EEPROM имеется бит CPD, при записи 0 в который запрещается до- ступ извне к внутренней EEPROM-памяти данных. Модели группы PIC16F87X могут осуществлять запись в память программ от- дельными словами. Однако в микроконтроллерах PIC16F87XA внутренняя орга- низация FLASH-памяти программ была изменена. Вследствие этого запись в па- мять программ указанных моделей осуществляется блоками по 4 подряд идущих слова. Младшие биты адреса первого слова блока должны быть равны 00. К при- меру, если программист собирается записать новое 14-битное слово в память про- грамм по адресу h’500’, ему также придется выполнить запись по адресам Ь’50Г, h’502’ и h’503’. В процессоре имеется четыре внутренних 14-битных буферных ре- гистра, как показано на Рис. 15.7. При каждой операции записи данные просто копируются в соответствующий буфер, определяемый значением двух младших битов адреса. После записи в последний буферный регистр при EEADR[l:0] = 11 (в нашем примере это соответствует адресу h’503’) блок из четырех слов по адре- сам h’500’...h’503’ стирается, а затем содержимое всех четырех буферных регист- ров одновременно заносится в память программ. Внутренние буферные регистры недоступны из программы при помощи обычных команд пересылки данных. Вместо этого запись каждого слова блока осуществляется точно так же, как запись отдельного слова, реализованная в под- программе flash_put (Программа 15.6). При выполнении первых трех опера- ций записи данные просто сохраняются в буферных регистрах, а задержки дли- тельностью 4 мс не происходит. Вызов этой же подпрограммы с адресом, равным последнему адресу блока, запускает «реальную» запись с приостановкой работы процессора.
558 Часть III. Окружающий мир ё Запись 1: (00) Запись 2: (01) Память программ h-ь слово данным I Буферные регистры h-e слово данных I ШШШ Л ххххооА ХХХХ01 I Конечный ХХХХ10 / блок ХХХХ11/ ММММММ в» Запись 3: (10) Запись 4: (11) I EEADRH | EEADR Ц| При записи 4-го слова данных все четыре слова заносятся в память программ Рис. 15.7. Запись в FLASH-память программ в моделях PIC16F87XA Чтобы проиллюстрировать процедуру блочной записи, рассмотрим следую- щий пример. Имеется четыре 2-байтных значения, размещенные в РОН с назва- ниями DATA_ARRAY..DATA_ARRAY+7 в порядке от старшего байта к младше- му, которые необходимо записать в память программ микроконтроллера PIC16F877A. Подпрограмма, код которой приведен в Программе 15.7, написана в предпо- ложении, что данные уже находятся в ОЗУ и что адрес первой из ячеек памяти программ уже занесен в регистры EEADRH.EEADR. Запись каждого слова в бу- ферные регистры и инициирование цикла записи осуществляются в цикле. В ка- честве указателя для работы с массивом в памяти данных используется регистр FSR, как это было показано на Рис. 5.8 (стр. 126). Каждое двухбайтное значение по очереди копируется в регистры EEDATH:EEDATA, после чего подпрограмма flash_put из Программы 15.6 запускает цикл псевдозаписи. После каждого прохода цикла адрес ячейки памяти программ, находящийся в регистре EEADR, инкрементируется (EEADRH не трогаем). После четвертого прохода запускается реальный цикл записи. К сожалению, в регистре EECON1 отсутствует флаг, по которому можно было бы отличить этот цикл записи от трех предыдущих. Вместо этого мы проверяем состояние двух младших битов регистра EEADR. Когда они снова становятся равными Ь’ОО’, процесс завершается. Несмотря на то что записывать необходимо полный блок, можно изменять значение только одного, двух или трех слов блока. Для этого все данные, которые нужно оставить неизменными, сначала считываются из памяти программ в па- мять данных, а затем записываются вместе с изменяемыми данными. Программа 15.7. Блочная запись FLASH-памяти программ в моделях PIC16F87XA • ************************************************************ / ; ФУНКЦИЯ : Пишет блок из 4-х слов в память программ ; ВХОД : Начальный адрес блока в EEADRH :ADDR ; ВХОД : Четыре слова в массиве DATA_ARRAY:8 ; ВЫХОД : Четыре слова записаны в память программ * * *
Глава 15. Хранить вечно! 559 ; ВЫХОД : Рабочий банк - 0-й * ; РЕСУРСЫ : Подпрограмма FLASH_PUT * • ************************************************************ FLASH_BLAST bsf STATUS,RP1 ; Переключаемся во 2-й банк bcf STATUS,RPO movlw DATA_ARRAY ; Загружаем в FSR адрес movwf FSR ; младшего байта массива данных в ОЗУ ; Теперь выполняем 4 цикла записи FB_LOOP movf INDF,w ; Считываем старший байт слова и movwf EEDATH ; помещаем его в старший регистр данных incf FSR, f ; Указываем на младший байт movf INDF,w ; Считываем младший байт слова и movwf EEDATA ; помещаем его в младший регистр данных incf FSR,f ; Указываем на старший байт следующего слова call FLASH_PUT ; Пишем в буферный регистр bsf STATUS,RP1 ; Снова переключаемся во 2-й банк bcf STATUS,RPO incf EEADR,f ; Инкрементируем адрес в памяти программ movf EEADR,w ; Проверим младшие биты на равенство 00 andlw b'00000011' ; Выделяем эти биты btfss STATUS,Z ; ЕСЛИ оба равны нулю, ТО выходим goto FB_LOOP ; ИНАЧЕ пишем следующее слово bcf STATUS,RP1 ; Возвращаемся в 0-й банк return ; и выходим по окончании цикла записи Примеры Пример 15.1 В компиляторе CCS имеются следующие встроенные функции для работы с модулем EEPROM: read_eeprom(<адрес>); Возвращает байт, находящийся по указанному адресу EEPROM. write_eeprom(<адрес>, <данные>); Заносит значение, переданное во втором параметре, по указанному адресу EEPROM (первый параметр). Возврат из функции происходит только после за- вершения цикла записи. Напишите функцию на языке Си, которая бы обновляла показания одометра, хранящиеся в EEPROM, аналогично Программе 15.3.
560 Часть III. Окружающий мир Решение Как и в исходной ассемблерной программе, код которой приведен в Программе 15.3, новая функция (см. Программу 15.8) состоит из трех частей: 1. На этом этапе объявляется массив из 3 байт, названный odometer [ ], ко- торый служит в качестве временного хранилища показаний одометра, со- держащихся в EEPROM. Массив заполняется с помощью трех вызовов функции read_eeprom (). 2. После загрузки 3-байтного значения в память данных оно инкрементирует- ся с использованием оператора выбора if-else: а) Инкрементируется младший байт и проверяется на нулевое значение. Если он не равен нулю, операция инкрементирования завершается, в противном случае происходит переход к обработке среднего байта. б) Инкрементируется средний байт и проверяется на нулевое значение. Ес- ли он не равен нулю, операция инкрементирования завершается, в про- тивном случае происходит переход к обработке старшего байта. в) Инкрементируется старший байт. 3. В заключение каждый байт заносится обратно в EEPROM с помощью функции write_eeprom (). Программа 15.8. Инкрементирование показаний одометра на Си void odometer(void) { unsigned int odometer[3]; /* Объявляем 3-байтный массив */ odometer[0] - read_eeprom(0xl0); /* Считываем текущее значение */ odometer[1] = read_eeprom(0x11); odometer[2] = read_eeprom(0xl2); /* Инкрементируем число, находящееся в массиве */ if(++odometer[0] != 0) break; else if(++odometer[1] != 0) break; else odometer[2]++; /* Теперь заносим инкрементированное значение в EEPROM */ write_eeprom(0x10, odometer[0]); write_eeprom(0xll, odometer[1]); write_eeprom(0x12, odometer[2]); } Если сравнить размеры ассемблерного кода самостоятельно написанной Программы 15.3 и сгенерированного при компиляции Программы 15.8 (для PIC16F62X), то можно увидеть, что при ручном кодировании размер программы получается практически в 2 раза меньше (54 команды против 105).
Глава 15. Хранить вечно! 561 Пример 15.2 Рассмотрим контроллер сауны, построенный на базе микроконтроллера PIC. Задачей такого контроллера является контроль температуры и управление нагре- вателем и охладителем. Кроме того, в нем должна быть предусмотрена тревожная сигнализация и возможность экстренного отключения в случае перегрева. В принципе для построения такого контроллера можно использовать 8-вы- водной микроконтроллер с внешним датчиком температуры. Однако кто-то предложил в качестве дешевого, хотя и довольно любительского датчика темпе- ратуры использовать встроенный сторожевой таймер микроконтроллера, а точ- нее, зависимость его периода от температуры. Были исследованы 8 экземпляров микроконтроллеров из одной партии, при- чем при каждой контрольной температуре они выдерживались по 30 мин. В ре- зультате был построен график, показанный на Рис. 15.8. Каждая точка этого гра- фика была получена усреднением 500 периодов сторожевого таймера при данной температуре. Температура ["С] Рис. 15.8. Экспериментальная зависимость периода сторожевого таймера от температуры Данные, представленные на Рис. 15.8, базируются на документе AN720 «Measuring Temperature Using the Watch Dog Timer (WDT)». Два графика представляют максимальное и минимальное значение периода сторожевого таймера тестируемых устройств. Измерение периода сторожевого таймера производилось по числу пере- полнений Таймера 0, работающего от внутреннего сигнала 4 МГц. К сторожевому таймеру был подключен постделитель с коэффициентом деления 8. Из приведенных графиков можно заметить, что между величиной периода и температурой существует четкая корреляция. Однако, несмотря на предсказуе- мость общего характера зависимости, значения смещения и крутизны характе- ристики будут своими у каждой модели. К примеру, коэффициент пропорцио-
562 и Часть III. Окружающий мир нальности у всех восьми протестированных устройств колеблется от 2.28 до 2.42 отсчета на градус Цельсия. Поэтому перед использованием системы ее необходи- мо будет калибровать. Если для какого-либо конкретного устройства известно количество отсчетов при заданной температуре TQ и коэффициент пропорцио- нальности к, то величину отклонения от температуры То, соответствующую коли- честву отсчетов COUNT„, можно будет определить по формуле ДТ = (COUNT„ - COUNT0)xJl. Для калибровки этих устройств было решено выдерживать партию в холо- дильнике при 0°С и записывать 2-байтное значение отсчетов в модуль EEPROM. Затем устройства подвергались нагреву до 30°С, после чего разность между новым и исходным значением запоминалась в отдельном байте EEPROM. После этого осуществлялось перепрограммирование микроконтроллеров — вместо програм- мы калибровки в память программ заносилась рабочая программа, вычисляющая по значению периода сторожевого таймера COUNTn текущую температуру: T=(COUNT0 - COUNT,,)x(Diff/30), где COUNTo — 2-байтное значение из EEPROM, содержащее количество отсче- тов при 0°С, a Diff — 1-байтное значение из EEPROM, характеризующее измене- ние количества отсчетов при увеличении температуры до 30°С. Покажите, как можно написать программу калибровки, реализующую ука- занный алгоритм. Решение Можно выделить пять задач, которые необходимо выполнять при наступле- нии тайм-аута сторожевого таймера: • Усреднение текущего значения числа переполнений Таймера 0 с предыду- щими значениями, полученными при низкой температуре (0°С), за исклю- чением первого отсчета. • Усреднение текущего значения числа переполнений Таймера 0 с предыду- щими значениями, полученными при высокой температуре (30°С), за ис- ключением первого отсчета. • Запоминание значения, соответствующего низкой температуре, в EEPROM. • Вычисление разности между значениями для высокой и низкой температур и запоминание ее в EEPROM. • Вход в бесконечный пустой цикл. Для указания, какая из четырех активных задач должна выполняться, мож- но использовать линии порта ввода/вывода микроконтроллера. Так, наличие ВЫСОКОГО уровня на выводе GPIOO означает, что текущее число переполне- ний Таймера 0 необходимо прибавить к уже имеющемуся 2-байтному значению для низкой температуры. После каждого сложения, за исключением первого, результат необходимо усреднить делением на два. Этот ВЫСОКИЙ уровень
Глава 15. Хранить вечно! 563 должен удерживаться на выводах GPIOO всех микроконтроллеров партии в те- чение нескольких минут после установления температуры холодильника на уровне 0°С. Подача на GPIOO НИЗКОГО, а на GPIO1 — ВЫСОКОГО уровней на корот- кое время вызывает сохранение вычисленного значения в EEPROM. При подаче НИЗКОГО уровня на оба входа никаких действий не производится. Это состоя- ние соответствует времени перед стабилизацией температурного режима и време- ни после программирования EEPROM, Таймер 0 и сторожевой таймер инициализируются одновременно при сбросе по включению питания. Эта операция будет выполнена лишь один раз при пода- че питания на микроконтроллер, уже помещенный в термокамеру. Все последую- щие сбросы будут происходить по тайм-ауту сторожевого таймера. Для определе- ния причины сброса можно использовать флаг ТО регистра STATUS (см. стр. 453). Из текста Программы 15.9 видно, что переход к секции кода, в которой вы- полняется инициализации таймеров и переменных, производится только в том случае, если флаг ТО при сбросе установлен в 1, т.е. при включении питания. После этого программа входит в бесконечный цикл, организованный командой goto $ (ассемблер заменяет символ $ текущим адресом команды), которая прос- то переходит сама на себя. Программа 15.9. Процедура инициализации и обработчик прерывания для программы калибровки контроллера сауны include "pl2f629.inc" __config _WDT_ON & _CP_OFF & _INTRC_OSC_NOCLKOUT & _MCLRE_OFF cblock h'20' _work:l, -Status:1 FIRST—HI:1, FIRST_LO:1 ROLL_OVER:2, LO_TEMP:2, HI_TEMP:2 DELTA_TEMP:1 endc org 0 START goto MAIN org 4 goto ISR ; При каждом сбросе инициализируем Таймер 0, порт ввода/вывода и пр. MAIN movlw h’07’ ; Выключаем аналоговый компаратор movwf CMCON movlw b'11011010’ ; Предделитель - к WDT, коэффициент 1:8 bsf STATUS,RPO ; Переключаемся в 1-й банк movwf OPTION_REG ; Таймер 0 тактируется внутренним сигналом bcf STATUS,RPO ; Возвращаемся в 0-й банк clrf TMRO ; Обнуляем таймер bsf INTCON,TOIE ; Разрешаем прерывание от Таймера 0 bsf INTCON,GIE ; Разрешаем все прерывания
564 Часть Ш. Окружающий мир btfss STATUS,NOT_TO ; ЕСЛИ тайм-аут сторожевого таймера, goto READING ; ТО идем на обработку значений ; ИНАЧЕ это был сброс по питанию clrf ROLL_OVER+1 ; Обнуляем 2-байтный счетчик переполнений Таймера 0 clrf ROLL_OVER clrf FIRST_HI clrf FIRST_LO clrf LO_TEMP+1 ; Обнуляем регистры результата для низкой температуры clrf LO_TEMP clrf HI_TEMP+1 ; и для высокой температуры clrf HI_TEMP clrwdt ; Сбрасываем сторожевой таймер goto $ ; Ждем следующего сброса от сторожевого таймера В обработчике прерывания при прерывании от Таймера О инкрементируем 2-байтное число переполнений таймера ; Сначала сохраняем контекст ISR movwf _work ; Сохраняем W swapf STATUS,w ; и регистр STATUS movwf _status ; Основной код обработчика incf ROLL_OVER+1,f ; Регистрируем новое переполнение btfsc STATUS,Z ; Пропускаем, если не ноль incf ROLL_OVER,f ; Инкрементируем старший байт bcf INTCON,ТОIF ; Сбрасываем флаг прерывания swapf „status,w ; Восстанавливаем исходное значение movwf STATUS ; регистра STATUS swapf _work,f ; Восстанавливаем исходное значение W, swapf _work,w ; не меняя STATUS, retfie ; и выходим из прерывания В этой же программе приведен код процедуры обработки прерывания от Таймера 0. В обработчике осуществляется инкрементирование 2-байтной пере- менной, расположенной в регистрах ROLL_OVER:ROLL_OVER+1, которая представляет собой числовое выражение длительности периода сторожевого тай- мера, считываемое системой при сбросе по тайм-ауту сторожевого таймера. Во время работы программы процессор будет периодически сбрасываться по тайм-ауту сторожевого таймера. А поскольку при этом событии бит ТО сбрасыва- ется, будет выполняться переход к секции reading, код которой приведен в Программе 15.10. В этой секции проверяется состояние каждого из четырех вхо- дов GPIO[3:0]. При обнаружении на каком-либо входе ВЫСОКОГО уровня вы- полняется одна из четырех описанных выше задач. Если на всех выводах присут-
Глава 15. Хранить вечно! 565 ствует НИЗКИЙ уровень, то программа просто очищает регистры ROLL_OVER и ROLL_OVER+1, перезапускает Таймер 0 и сторожевой таймер, после чего входит в бесконечный цикл. Эти операции, обозначенные меткой reading_exit, вы- полняются в конце всех четырех задач. Программа 15.10. Считывание нового значения количества периодов / ******** ; * Сюда попадаем при сбросе по тайм-ауту сторожевого таймера * READING btfsc goto btfsc goto btfsc goto btfsc goto goto ******************************************************* GPIO,0 ; Новое значение при низкой температуре? NEW_LO ; ЕСЛИ да, ТО переходим на эту секцию! GPIO,1 ; Новое значение при низкой температуре? NEW_HI ; ЕСЛИ да, ТО переходим на эту секцию! GPIO,2 ; Обновление среднего для низкой температуры? UPDATE_LO ; ЕСЛИ да, ТО переходим на эту секцию! GPIO,3 ; Обновление разности для высокой температуры? UPDATE_HI ; ЕСЛИ да, ТО переходим на эту секцию! READING_EXIT ; ИНАЧЕ ничего не делаем NEW_LO movf addwf ROLL_OVER+1,w; Берем младший байт текущего числа переполнений LO_TEMP+1,f ; и прибавляем его к младшему байту ; накопленного значения btfsc incf movf addwf STATUS,С ; Проверяем перенос LO_TEMP,f ; ЕСЛИ был, ТО учитываем его ROLL_OVER,w ; Берем старший байт текущего числа переполнений LO_TEMP,f ; и прибавляем его к старшему байту ; накопленного значения movf btfsc goto rrf rrf FIRST_LO,f ; Это был 1-й отсчет? STATUS,Z FIRST_TIME_LO; ЕСЛИ да, ТО отметим это! LO_TEMP,f ; ИНАЧЕ делим сумму на два LO_TEMP+1,f goto FIRST_TIME_LO incf goto READING_EXIT ; и выходим ; При первом отсчете ничего не делаем FIRST_LO,f ; Первый отсчет уже был READING_EXIT NEW_HI movf addwf ROLL_OVER+1,w; Берем младший байт текущего числа переполнений HI_TEMP+1,f ; и прибавляем его к младшему байту ; накопленного значения btfsc incf movf addwf STATUS,С ; Проверяем перенос HI_TEMP,f ; ЕСЛИ был, ТО учитываем его ROLL_OVER,w ; Берем старший байт текущего числа переполнений HI_TEMP,f ; и прибавляем его к старшему байту ; накопленного значения movf btfsc goto FIRST_HI,f ; Это был 1-й отсчет? STATUS,Z FIRST_TIME_HI; ЕСЛИ да, ТО отметим это!
566 Часть III. Окружающий мир rrf HI_TEMP,f ; ИНАЧЕ делим сумму на два rrf HI_TEMP+1,f goto READING_EXIT , ; и выходим FIRST_TIME_HI ; При первом отсчете ничего не делаем incf FIRST_HI,f ; Первый отсчет уже был READING_EXIT Clrf TMRO ; ; Сбрасываем Таймер 0 clrwdt ; Перезапускаем сторожевой таймер clrf ROLL_OVER+1 ; ; Обнуляем число переполнений clrf ROLL_OVER goto $ ; : Ждем следующего сброса от сторожевого таймера Также в Программе 15.10 приведены секции кода, соответствующие первым двум задачам. В этих секциях 2-байтное количество переполнений Таймера 0 прибавляется к значению, хранящемуся в регистрах LO_TEMO:LO_TEMP+1 или Н1_ТЕМР:Н1_ТЕМР+1 соответственно, после чего для усреднения результат де- лится на два сдвигом на один бит вправо. Поскольку суммарное число перепол- нений достаточно скромное, двух байтов вполне достаточно, чтобы избежать пе- реполнения. Если мы будем в течение нескольких минут многократно выполнять указанные операции, то в результате получим усредненное значение. Если считывание результата производится в первый раз, то операция деления на два пропускается, а в соответствующую переменную-флаг first_lo или first_hi заносится ненулевое значение. Основная процедура, относящаяся к теме данной главы, приведена в Программе 15.11. При ВЫСОКОМ уровне на выводе GPIO2 2-байтное число пе- реполнений при низкой температуре LO_temP:LO_temp+1 заносится в два младших байта EEPROM с использованием подпрограммы ee_put из Программы 15.2. При ВЫСОКОМ уровне на выводе GPIO3 вычисляется разность между 2-байтными значениями, полученными при высокой и низкой температурах. Ес- ли посмотреть на график, приведенный на Рис. 15.8, то можно заметить, что раз- ность отсчетов при изменении температуры на 30°С в любом случае не превысит 256, так что для хранения этой разности достаточно будет одного байта. Данное значение сохраняется в EEPROM обычным образом. Программа 15.11. Обновление информации в EEPROM UPDATE_LO movf LO_TEMP,w ; Берем старший байт значения для низкой ; температуры bsf movwf clrf call movf STATUS,RPO ; Переключаемся в 1-й банк EEDATA ; Кладем в регистр данных EEPROM EEADR ; Адрес в EEPROM - h’00' EE_PUT ; Запоминаем значение LO_TEMP+l,w ; Берем младший байт значения для низкой ; температуры
Глава 15. Хранить вечно! 567 bsf STATUS,RPO ; ; Переключаемся в 1-й банк movwf EEDATA ; ; Кладем в регистр данных EEPROM incf EEADR,f ; г Адрес в EEPROM - h'01' call EE_PUT ; ; Запоминаем значение goto READING_EXIT UPDATE_HI ; Вычисляем HI_TEMP-LO_TEMP и сохраняем разность в EEPROM ; Достаточно вычесть только младшие байты, поскольку разность не может быть ; больше 256 movf HI_TEMP+1,W ; : Берем старший байт значения при высокой : температуре subwf LO_TEMP+1,W ; : Вычитаем младший байт значения при низкой : температуре movwf DELTA_TEMP ; ; Запоминаем разность bsf STATUS,RPO ; ; Переключаемся в 1-й банк movwf EEDATA ; : Разность - по адресу h'02' movlw 2 movwf EEADR call EE_PUT goto READING_EXIT Пример 15.3 В Примере 14.3 мы вычисляли энергию разряда дефибриллятора путем сум- мирования квадратов отклонений напряжения от базового значения. Причем после анализа графика в качестве базового было принято значение 2.6 В. Это среднее значение может изменяться от экземпляра к экземпляру прибора, а также с течением времени. Поэтому было решено доработать программное обеспече- ние, введя в него возможность самообучения, которое будет осуществляться, ска- жем, при замыкании кнопки, подключенной к выводу RA4. При нажатии на кнопку надо будет выполнить 256 выборок значений напряжения в режиме ожи- дания, с последующим их сложением для получения 2-байтной суммы. Взяв стар- ший байт ЭТОЙ суммы, МЫ получим усредненное значение напряжения (взятие старшего байта 2-байтного числа эквивалентно его делению на 256). Это значе- ние мы запишем в EEPROM по адресу h’00’ и впоследствии будем использовать в качестве базового уровня, периодически обновляя его при необходимости. Пред- полагая, что в вашем распоряжении имеется подпрограмма GET_ANALOG из Программы 14.1 (стр. 516), напишите соответствующую подпрограмму. Решение Из Рис. 14.20 на стр. 534 видно, что напряжение с датчика тока дефибрилля- тора подается на вывод RA0/AIN0 микроконтроллера. С учетом того что модуль АЦП уже инициализирован, как это было сделано в Программе 14.6 на стр. 536, нам останется только 256 раз считать оцифрованное значение с 0-го канала АЦП для накопления 16-битной суммы. Взяв старший байт этой суммы, мы получим
568 Часть III. Окружающий мир усредненное значение, т.е. частное от деления суммы на 256. Если во время взя- тия отсчетов дефибриллятор находился в режиме ожидания, то полученное сред- нее значение будет представлять собой базовое напряжение. После определения базового напряжения это однобайтное значение можно записать в EEPROM обычным образом. Впоследствии его можно будет считать из EEPROM и использовать вместо константы BASELINE (Программа 14.6). В Программе 15.12 регистр COUNT используется в качестве счетчика итера- ций цикла. В каждом проходе цикла новое значение АЦП прибавляется к общей сумме, накапливаемой в регистрах ACCUMULATOR:ACCUMULATOR+1. После выхода из цикла содержимое регистра ACCUMULATOR (старший байт суммы) заносится в EEPROM по адресу h’00’ с использованием подпрограммы ee_PUT. Программа 15.12. Определение базового напряжения ; * ФУНКЦИЯ Суммирует 256 выборок аналогового сигнала для * ; * ФУНКЦИЯ нахождения среднего значения, являющегося * ; * ФУНКЦИЯ базовым напряжением, которое запоминается в * ; * ФУНКЦИЯ модуле EEPROM * ; * РЕСУРСЫ Подпрограммы GET_ANALOG, EE_PUT * ; * вход Нет * ; * ВЫХОД Среднее значение 0-го канала - в EEPROM (h’00’)* LEARN clrf BASE ; Обнуляем старший байт суммы clrf BASE+1 ; Обнуляем младший байт суммы clrf COUNT ; Обнуляем счетчик цикла LEARN_LOOP clrw ; Работаем с 0-м аналоговым каналом call GET_ANALOG ; Оцифровываем addwf BASE+1,f ; Прибавляем к младшему байту суммы btfsc STATUS,C ; Был перенос? incf BASE,f ; ЕСЛИ да, ТО инкрементируем старший байт decfsz COUNT,f ; Уменьшаем счетчик цикла на единицу goto LEARN_LOOP ; Запоминаем среднее в EEPROM movf BASE,w ; Берем среднее значение bsf STATUS,RP1 . ; Переключаемся во 2-й банк clrf EEADR ; Будем писать по адресу h100' movwf EEDATA ; Загружаем байт данных call EE_PUT ; Запоминаем его return ; Все сделали В реальной ситуации лучшего результата можно достичь, беря 65 536 отсчетов и накапливая их в 3-байтной сумме. И опять же, старший байт этой суммы будет представлять собой усредненное значение.
Глава 15. Хранить вечно! 569 Вопросы для самопроверки 15.1. В соответствии с хорошим стилем программирования данные, записывае- мые в EEPROM, следует верифицировать. Как можно модифицировать под- программу ee_put из Программы 15.2, чтобы она возвращала в регистре ERROR число —1 в случае, если запись прошла неудачно? В противном слу- чае в этом регистре должен быть ноль. 15.2. В Программе 15.5 мы поместили таблицу преобразования в память программ, выровняв ее для упрощения вычисления ^байтного индекса по 256-байт- ной границе (а именно h’300’). В результате, чтобы обратиться к элементу ил таблицы, нам достаточно поместить адрес Ь’Зии’ в регистры EEADRH:EEADR. Размещение сегментов программы по адресам, заданным пользовате- лем, является не очень хорошей идеей, поскольку при последующих моди- фикациях программы может произойти наложение участков кода. Более на- дежным будет оставить размещение меток на совести ассемблера. Однако в нашем случае необходимо прибавлять значение пп к адресу, по которому ас- семблер разместил таблицу table. К сожалению, адрес памяти программ 13-битный, а микроконтроллеры PIC выполняют арифметические опера- ции только с 8-битными числами. В Microchip-совместимых ассемблерах предусмотрены директивы high и low, с помощью которых можно обра- титься соответственно к старшему и младшему байту адреса метки, напри- мер movlw low TABLE. Используя эти директивы, доработайте подпро- грамму square, чтобы она могла работать при отсутствии в программе директивы org h' 3 0 0 '. 15.3. В Microchip-совместимых ассемблерах имеется директива da, которая мо- жет использоваться для описания строк символов в памяти программ, на- пример: MESSAGE da "Hello world\n",0 Эта директива помещает символы, расположенные между кавычками, в па- мять программ, причем в каждое 14-битное слово заносится по два символа, представленных 7-битным кодом ASCII. Завершается строка словом с нуле- вым значением. Служебный символ \п означает «новая строка», его ASCII- код равен h’OA’. Полагая, что мы работаем с PIC16F87X, напишите подпрограмму, назы- ваемую PDATA, для выборки каждого символа из памяти программ и переда- чи его на терминал с помощью подпрограммы PUTCHAR из Программы 12.14 (стр. 421). 15.4. В некоторых системах безопасности гостиниц для электронных замков но- меров используются перепрограммируемые смарт-карты на базе микрокон- троллеров PIC. При регистрации в гостинице в эту карту заносятся следую- щие данные: 1. 4-разрядный номер комнаты, например 1311.
570 Часть III. Окружающий мир 2. Дата начала срока действия ключа, например 13072005. 3. Дата окончания срока действия ключа, например 15072005. Предположим, что микроконтроллер смарт-карты имеет встроенный модуль EEPROM, а для обмена информацией с терминалом на ресепшине используется подпрограмма обмена по последовательному порту, аналогич- ная реализованной в Программе 12.14 на стр. 421. Данные передаются в ука- занном порядке в кодировке ASCII, причем перед началом пакета передает- ся символ STX, после завершения пакета — ЕТХ, а сами данные внутри пакета разделяются символом SP (см. Табл. 1.1 на стр. 18). Напишите под- программу, выполняющую разбор принимаемых данных и записывающую их в EEPROM.
ГЛАВА ДАЛЬНЕЙШЕЕ РАЗВИТИЕ В 1994 году компания Microchip представила на суд общественности первого представителя старшей линейки микроконтроллеров PIC17C42, работающего на частоте 25 МГц. В основу этого микроконтроллера легла та же гарвардская RISC- архитектура, которая использовалась в предыдущих моделях, однако количество команд было увеличено до 55 (при этом система команд была совместима снизу вверх). Новая архитектура обеспечивала поддержку памяти больших объемов, а также имела расширенные возможности в части косвенной адресации, управле- ния прерываниями и стеком. В 1999 году было представлено семейство микроконтроллеров PIC18CXXX с максимальной тактовой частотой 40 МГц и 16-битным набором команд. Коли- чество команд в этих микроконтроллерах было увеличено до 75, причем боль- шинство из них были введены для поддержки языков высокого уровня. Также в этих микроконтроллерах была реализована более развитая система прерываний, косвенной адресации и управления стеком. Чтобы несколько сбалансировать наше обсуждение, посвященное большей частью микроконтроллерам среднего уровня, в этой главе приводится обзор рас- ширенного семейства на примере микроконтроллеров линейки PIC18FXX2. Эти модели специально предназначены для замены более ранних моделей среднего уровня линейки PIC16F87X, на примере которых мы и изучали микроконтролле- ры PIC. После прочтения этой главы вы: • Сможете критически сравнить архитектуры семейств среднего и расширен- ного уровня. • Познакомитесь с побайтно адресуемой структурой 16-битной памяти про- грамм. • Разберетесь в организации памяти данных, позволяющей при помощи ре- гистра выбора банка использовать до 16 банков памяти по 256 регистров каждый. • Узнаете, как можно использовать три 12-битных регистра FSR« для косвен- ной адресации с пред/пост инкрементом/декрементом, а также для относи- тельной адресации со смещением, хранящимся в рабочем регистре. • Поймете, каким образом реализована поддержка приоритетов прерываний.
572 Часть I 11. Окружающий мир • Познакомитесь с основными изменениями в стандартных периферийных модулях. • Узнаете, каким образом был расширен набор команд. Линейка микроконтроллеров PIC18FXX2 была представлена в 2001 году. Эти микроконтроллеры предназначались для замены моделей среднего уровня PIC16F87X, а также более старых PIC16C73/4. Всего в этой линейке было выпу- щено 4 микроконтроллера. PIC18F242 Этот микроконтроллер, выпускающийся в 28-выводном корпусе, имеет память программ объемом 8 Келов и память данных объемом 788 байт. PIC18F252 Эта модель идентична предыдущей, но имеет память программ объемом 16 Келов и память данных объемом 1536 байт. PIC18F442 Этот микроконтроллер представляет собой 40/44-выводный вариант модели PIC18F242. PIC18F452 Этот микроконтроллер представляет собой 40/44-выводный вариант модели PIC18F252. Кроме несколько урезанного набора периферийных модулей в 28-выводных моделях PIC18F2X2, эти микроконтроллеры практически идентичны соответ- ствующим моделям PIC18F4X2, архитектура которых изображена на Рис. 16.1. Чтобы сравнить эти микроконтроллеры с их предшественниками — PIC16F87X (см. Рис. 10.1 на стр. 303), мы рассмотрим блок выборки, исполни- тельный блок, периферийные устройства, а также отличие системы команд. Блок выборки Блок выборки команд, находящийся в верхней левой части Рис. 16.1, имеет гар- вардскую архитектуру с двухуровневым конвейером, в котором могут находиться две 16-битные команды, за счет чего циклы выборки и исполнения команд осущест- вляются параллельно. Команды хранятся в FLASH-памяти программ в виде 16-бит- ных слов. Как уже говорилось в главе 15, микроконтроллер может самостоятельно читать и записывать данные в свою память программ. В семействе PIC18XXXX со- держимое памяти программ рассматривается как совокупность 8-битных данных, передаваемых в обоих направлениях посредством регистра специального назначе- ния TABLAT. Адрес байта в памяти программ, к которому производится обращение, содержится в трех регистрах указателя TBLPTR. Для копирования адресованного байта в регистр TBLAT используется команда third. Соответственно команда tblwt используется для записи данных в память программ, однако реальная запись осуществляется 8-байтными блоками при помощи регистра EECON1, подобно то- му, как это было показано на Рис. 15.7 (стр. 558).
Глава 16. Дальнейшее развитие 573 Vdd Vss lh'FF5' | TABLAT | 21 j ‘Шинаданных) 8 .памяти I ,, программ I Память I программ I FLASH-ПЗУ 8/16Кх 16 21 MCLR 0SC1/CLKI OSC2/CLKO/RA6 RD1, RE1, PIC18F442/452 h'FF8* h’FF?’ h’FF6* FBLPTRU| TBLPTRH | TBLPTRu}* Указатель таблицы h’FFB’ h'FFA' PLATU | PLATH Шина адреса' . 16 памяти программ 16-битная шина данных памяти программ . .Конвейер Регистр команд 1 Регистр команд 2 h'FFC’ jSTKPTRj- 8 8' Память данных EEPROM h’FA9' КОП 9 Дешифратор — у команд и схема---•• управления 01 02 03 04 ________КН Счетчик команд ° - [ h’FFF’ h’FFE’ h’FFD' q TOSU I TOSH I TOSL~[* h'FF9’ Шина данных памяти данных т Вершина стека Память данных ОЗУ Регистры общего назначения 768/1536x8 EEPROM Память данных 256 x8 ► ~ Стек "• — (31 уровень) — Шина адреса 12--памяти данных / Мультиплексору / адреса \ 8 Непосредственный адрес Д2 h’FAS’ RESET Генератор/ ________ схема ФАПЧ SLfcEH Таймер включения питания Таймер запуска генератора Сброс по включению питания Сброс по снижению напряжения питания Сторожевой таймер Порт ввода/вывода В Е Порт ввода/вывода Е 8 Значение константы *------------------- \ Мультиплексор^ \ данных / hTEOj----н I BSR I FSR1 FSR2 |h’FDO A’ ГЗП1 h’FE1:2' FSRO lh’FEA:9' Таймер О 4-8 IND0:1:2 ^^-,8 h’FD8' 8 АЛУ ЛН OV N Z DC C|4—/— ~I Регистр STATUS g _______4. h’FE8' [Рабочий регистр |*-»[ Умножитель8x8 | , , C.I h’FF41 ______±WF3’ I PRODHl PRODL & н 18 1| И JI г & Порт ввода/вывода А 3 Порт ввода/вывода В (Л 3 I INTCON 1р^Й1 ---------1 h’FFI’ h'FFO' INTC0N2 INTC0N3 Порт ваода/выаода С 3 RAO/ANO /INT1 /INTO RCi/Tiosi/ссрг- — -nrS Рис. 16.1. Архитектура 40-выводных микроконтроллеров PIC18F442/52
574 Часть 111. Окружающий мир Большинство команд в микроконтроллерах с расширенным ядром — 16-бит- ные (см. Рис. 16.4), и только несколько команд занимают два слова памяти про- грамм. Однако для облегчения доступа к таблицам однобайтных данных и стро- кам память программ организована побайтно. Как можно увидеть из Рис. 16.2, все команды занимают по два байта памяти программ и размещаются только по нечетным адресам. Например, 6-я команда будет располагаться по адресу h’OOOA’, а 7-я команда — по адресу h’OOOC’. Такое размещение команд вызвано отсутстви- ем 0-го бита в 21-битном счетчике команд. Соответственно при линейном выпол- нении программы содержимое счетчика команд изменяется с шагом 2. Таким Память программ Старший байт 15 ____________ Команда 1:H Команда 2:Н Команда 3:Н Команда 4:Н Команда 5:Н Команда 6:Н Команда 7:Н Команда 8:Н Команда 9:Н Команда 10:Н Команда 11 :Н Команда 12:Н h'0001' Младший байт 8 7 h'0005' h’000/ h'0009' h'OOOB' ЮТЯТ W5F h'OOir h'0013* h’0015’ h’0bl7’ Командам Команда 21 Команда31 Команда 4'L Команда51 Командаб! Команда 71 Команда81 Команда 91 Команда 101 Команда 111 Команда 121 h'dbbO' ттаг h’0004' W Mot» Ь’ОЙА' W w тапг h'0012' hW h'OOie' Счетчик команд Указывает на команду, которая будет выбрана из памяти программ в следующем цикле. Инкрементируется дважды после каждой операции выборки Исполнение Рис. 16.2. Упрощенное представление памяти программ моделей PIC18FX42
Глава 16. Дальнейшее развитие 575 образом, данная архитектура поддерживает память программ объемом до 220 слов или 221 байт. Между тем в моделях, которые мы взяли в качестве образца, реали- зован 17-битный счетчик команд. Как и в микроконтроллерах среднего уровня, счетчик команд при сбросе об- нуляется. Младший байт счетчика команд РС[7:0] напрямую доступен через ре- гистр PCL, а регистр PCLATH выступает в качестве буфера, как показано на Рис. 4.8 (см. стр. 103), позволяя изменять старший байт счетчика команд одно- временно с записью нового значения в регистр PCL. Однако в отличие от микро- контроллеров среднего уровня при чтении регистра PCL в регистр PCLATH ко- пируется содержимое среднего байта счетчика команд РС[ 15:8]. Регистр PCLATU работает точно так же, но обеспечивает доступ к старшему байту счетчика команд РС[21:16]. Такое нововведение позволяет программе считывать и записывать все 21 бит счетчика команд. Глубина стека была увеличена с 8 до 31 уровня. При переполнении или, на- оборот, опустошении стека микроконтроллер автоматически сбрасывается. До- ступ к регистру указателя стека STKPTR осуществляется точно так же, как и к ос- тальным РСН, а 21-битное значение может считываться с вершины стека в три регистра TOS или заноситься в стек из указанных регистров. Все это обеспечивает большую гибкость при манипулировании содержимым стека и передаче данных через стек. Исполнительный блок Как и в микроконтроллерах младшего и среднего уровня, АЛУ обрабатывает данные побайтно. Поэтому, несмотря на усовершенствованное ядро, микроконт- роллеры старшего семейства тоже относятся к классу 8-битных. В АЛУ этого се- мейства появился аппаратный умножитель 8x8, для поддержки которого были введены команды mullw (умножить константу на рабочий регистр) и mulwf (пе- ремножить рабочий регистр и регистр данных). Результат умножения (16 бит) за- носится в два регистра специального назначения PRODH:PRODL (см. Программу 16.1, стр. 580). Изменения в ядре коснулись и рабочего регистра — в этом семействе к нему можно обращаться как к обычному регистру специального назначения, располо- женному в памяти данных. То есть он может выступать в качестве операнда ко- манд, напрямую оперирующих регистрами данных. Например, команда decfsz WREG, f декрементирует содержимое рабочего регистра как РСН с име- нем WREG. Регистр STATUS больше не используется для переключения банков памяти, а освободившееся место занято флагами N (флаг отрицательного значения) и OV (переполнение) для полноценной поддержки операций сложения и вычитания в дополнительном коде (см. стр. 22). В памяти данных старшего семейства, структура которой показана на Рис. 16.3, хранится большая часть данных, обрабатываемых АЛУ, а также регист- ры специального назначения. Как и во всех микроконтроллерах PIC, в каждой
576 Часть III. Окружающий мир ячейке памяти содержится один байт, однако схема адресации регистров рази- тельно отличается от той, которая использовалась в семействе среднего уровня (см. Рис. 4.7 на стр. 97). Память данных РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН (банк) РОН {банк} РОН (банк) ~ РОН (банк) РОН (банк) РОН/РСН (банк) h’000’ h’07F’ h’080' h’100' h'200’ h’300’ h’400’ h'500’ h'600' h’700’ h’800’ h'900' h’AOO' h’BOO' h’COO’ h'DOO' h'EOO’ h'FOO’ h'F7F’ h’F80’ h'FFF’ Puc. 16.3. Структура памяти данных старшего семейства Из Рис. 16.3 видно, что память данных разбита на 16 банков по 256 регистров каждый, что дает максимальный объем памяти, равный 4 Кбайт. Переключение этих банков осуществляется посредством регистра выбора банка BSR. Текущий банк задается битами BSR[3:0] и выбирается с помощью дешифратора 4x16. Пос- кольку сам регистр BSR расположен в 15-м банке, а указывает после сброса по питанию на нулевой банк, то для изменения регистра BSR, независимо от ис- пользуемого в данный момент банка памяти, была введена специальная команда Ibsr. Так, если нам необходимо переключиться на 5-й банк, достаточно выпол- нить команду Ibsr 5.
Глава 16. Дальнейшее развитие 577 Младшие 128 адресов 0-го банка, отведенные под пользовательские регистры общего назначения (РОН), и старшие 128 регистров 15-го банка, хранящие РСН, названы на Рис. 16.3 банком быстрого доступа (Access bank). К ячейкам банка быстрого доступа можно обращаться напрямую, игнорируя установки регистра BSR. Чтобы указать на то, каким образом будет произведено обращение к памяти данных, используется 8-й бит 16-битного слова команды, как показано на Рис. 16.4. Если сравнить этот рисунок с форматом слова команды среднего се- мейства, показанного на стр. 98, то можно заметить, что разрядность поля адреса регистра увеличилась с 7 до 8 бит, что обусловлено увеличением размера банка памяти с 27 = 128 байт до 28 = 256 байт. При сброшенном бите «а», обращение производится к банку быстрого доступа, в противном случае команда обратится к банку, заданному регистром BSR. Если после включения микроконтроллера оста- вить содержимое регистра BSR без изменений (h’00’), то команды смогут обра- щаться ко всем 256 регистрам 0-го банка и всем 128 РСН 15-го банка. Например, для копирования содержимого регистра h’026’ в рабочий регистр мы должны бу- дем выполнить команду movf h'02 6',w,0, а для копирования регистра h’O96’— команду movf h' 09 6' ,w,l. Схема доступа определяется последним числом (0 или 1). На практике такое явное указание способа обращения приме- няется достаточно редко, поскольку ассемблер автоматически использует банк быстрого доступа (т.е. команду формата , 0) для адресации регистров из диапазо- на h’000’...h’07F’ и h’F80’...h’FFF’. movf <file>,d,a Адресат 0-W --------- 1 — регистр данных Доступ к ОЗУ 0 — к банку быстрого доступа 1 — с учетом BSR КОП Адрес регистра 0 0 0 0 0 0 d а f fffffff а) Формат слова байт-ориентированной команды movf h’02б',w,О в WREG _____ _______из банка быстрого доступа h’026’ movf 010100 0 0 00100110 б) Пример пересылки (копирования) содержимого регистра h’026’ в рабочий регистр Рис. 16.4. Формат слова типичной команды, обращающейся к памяти данных В моделях PIC18FX52 для хранения регистров общего назначения предназна- чена область памяти вплоть до верхней границы 5-го банка, т.е. по адрес h’5FF’ включительно. В микроконтроллере PIC18FX42, оделенном памятью не так щед- ро, для этой цели используются банки 0...2, т.е. ячейки с адресами до h’2FF’ включительно.
578 Часть III. Окружающий мир Любые микропроцессоры и микроконтроллеры могут использовать косвен- ную или индексную адресацию для эффективной работы с массивами данных и таблицами, размещенными в ОЗУ. В младшем и среднем семействах косвенная адресация осуществляется с использованием регистра FSR, выступающего в ка- честве указателя на память данных, и специального механизма, включающего об- ращение к виртуальному регистру INDF. Точно такой же подход используется и в старшем семействе, но на более глубоком уровне. В этом семействе имеется три отдельных регистра косвенной адресации — FSRO, FSR1 и FSR2. Каждый из этих регистров-указателей реализован в виде двух РСН, как показано на Рис. 16.5, а. То есть в регистре косвенной адресации может храниться 12-битное число, что позволяет ему указывать на любой регистр в адресном пространстве памяти дан- ных, не обращая внимания на ее сегментированную структуру. Команда загрузки регистра косвенной адресации If sr позволяет за одно действие скопировать 12- битную константу в любой из трех регистров FSR. Так, чтобы регистр FSR2 ука- зывал на регистр h’500’, достаточно выполнить одну команду 1 f sr 2 , h' 50 0 '. Каждый из регистров косвенной адресации имеет несколько различных ре- жимов работы, показанных на Рис. 16.5, а. Конкретный режим определяется по тому, к какому из пяти виртуальных регистров производится обращение. Вот эти регистры (/ = 0,1 или 2): INDF/ (простая косвенная адресация) Команда clrf INDF2 обнулит регистр, адрес которого находится в регистре FSR2. POSTDEC/ (косвенная адресация с постдекрементом) Команда clrf POSTDEC2 обнулит регистр, адресованный 12-битным регистром FSR2, а затем декрементирует содержимое регистра косвенной адресации. POSTING/ (косвенная адресация с постинкрементом) Команда clrf POSTINC2 обнулит регистр, адресованный 12-битным регистром FSR2, а затем инкрементирует содержимое регистра косвенной адресации. PREINC/ (косвенная адресация с прединкрементом) Команда clrf PREINC2 сначала инкрементирует содержимое регистра FSR2, а затем обнулит регистр, адресованный регистром FSR. PLUSW/ (относительная косвенная адресация) Если в рабочем регистре находится число h’06’, то команда clrf PLUSW2 обну- лит регистр, адрес которого получается сложением содержимого FSR2 и констан- ты h’06’. Ни содержимое регистра FSR2, ни содержимое рабочего регистра при этом не изменяется. Содержимое рабочего регистра интерпретируется как число со знаком, представленное в дополнительном коде (—128...+127). Рассмотрим в качестве примера два массива по восемь байтов, обозначенных метками NUM1 и NUM2 (см. Рис. 16.5, б). Предположим, что нам необходимо пере- множить f-е элементы этих массивов для получения массива из восьми 16-битных чисел NUM3. Старший байт z-го элемента массива NUM3 будет располагаться по
Глава 16. Дальнейшее развитие 579 ст INDF0 в действительности FSROH передает. .................... виртуальный этот 12-битный регистр | адрес УКАЗАТЕЛЬ h’FEA’ FSROL [FSRO] h’FEF’ POSTDECO в действительности FSROH ............— передает. виртуальный этот 12-битный регистр I адрес УКАЗАТЕЛЬ h’FED’ h’FEA’ POSTINCO в действительности FSR0H — -.передает. виртуальный этот 12-битный регистр I адрес h’FEE’ УКАЗАТЕЛЬ h’FEA’ PREINC0 в действительности FSR0H .передает ________ виртуальный этот 12-битный регистр | адрес h'FEC’ УКАЗАТЕЛЬ h’FEA’ PLUSW0 в действительности FSROH передает.............. — виртуальный этот 12-битный регистр | адрес_____________________ h’FEB’ УКАЗАТЕЛЬ h’FEA’ h’FE9’ FSROL [FSRO]— h’FE9' FSROL [FSROJ++ h'FE9' FSROL ++[FSRO] h’FE9' FSROL [FSROJ+W h’FE9’ а) Пять режимов косвенной адресации на примере указателя FSRO NUM3 NUM3+7 NUM3+8 NUM3+15 ф FSR2 б) Перемножение массивов с использованием косвенной адресации Рис. 16.5. Косвенная адресация посредством регистра FSRO
580 Часть III. Окружающий мир смещению —8 относительно младшего байта. В Программе 16.1 для обращения к массиву NUM1 используется регистр FSR0, а для обращения к массиву NUM2 — регистр FSR1. Поскольку массив NUM3 представляет собой по существу два бай- товых массива, сдвинутых друг относительно друга на восемь байтов, регистр FSR2 используется для указания на массив младших байтов произведений. При инициализации в эти указатели с помощью команды If sr заносятся адреса пос- ледних элементов каждого массива, выделенные на Рис. 16.5, б серым цветом. Программа 16.1. Перемножение двух массивов однобайтных значений •★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ / ; ФУНКЦИЯ : Перемножает NUM1[8] x NUM2 [8] = NUM3[16] * ; ВХОД : Глобальные массивы NUM1[8], NUM2[8] * ; ВЫХОД : Глобальный массив NUM3[16] * ARRAY_MUL Ifsr 0,NUMl+7 ; Указываем на последний элемент NUM1 Ifsr l,NUM2+7 ; Указываем на последний элемент NUM2 Ifsr 2,NUM3+d'15' ; Указываем на младший байт последнего ; элемента NUM3 movlw 8 ; Инициализируем счетчик цикла movwf COUNT M_LOOP movf POSTDECO,w ; Берем NUM1[п] mulwf POSTDEC1 ; Умножаем на NUM2[п] movlw -8 ; Задаем смещение для обращения к ; старшим байтам NUM3 movf f PRODH,PLUSW2 ; Сохраняем старший байт произведения movf f PRODL,POSTDEC2 ; Сохраняем младший байт произведения decfsz COUNT,f ; Возвращаемся к началу цикла goto M_LOOP return Основной в программе является команда mulwf. Эта команда формирует в регистрах специального назначения PRODH.PRODL 16-битное произведение, получаемое перемножением содержимого рабочего регистра и заданного регист- ра данных. Режим косвенной адресации с постдекрементом используется как для пересылки первого сомножителя в рабочий регистр, так и для указания второго сомножителя. Для копирования содержимого регистра PRODL в младший байт текущего элемента массива NUM3[], а регистра PRODH — в старший байт, в программе используется команда movf f, осуществляющая пересылку между двумя регист- рами данных. Эта команда, занимающая два слова памяти программ (см. стр. 589), использует для идентификации регистра-источника и регистра-прием- ника 12-битные адреса, что позволяет ей обращаться к любой ячейке памяти дан- ных, не используя механизм банков. Сначала содержимое PRODH копируется в старший байт элемента массива с использованием режима относительной кос-
Глава 16. Дальнейшее развитие и 581 венной адресации. Поскольку в рабочий регистр было предварительно записано число —8 (h’F8’), содержимое регистра PRODH будет скопировано в регистр, ад- рес которого на 8 меньше адреса, находящегося в регистре FSR2. А содержимое регистра PRODL копируется в младший байт элемента массива, адресуемый ре- гистром FSR2, с использованием режима косвенной адресации с постдекремен- том. Периферийные устройства Вообще говоря, модули периферийных устройств в рассматриваемых моделях практически ничем не отличаются от аналогичных модулей, имеющихся в мик- роконтроллерах среднего уровня. Укажем основные отличия. Параллельные порты С каждым из параллельных портов, изображенных на Рис. 16.1, теперь связа- но три регистра вместо двух, описанных в главе 11. Регистр PORTT все также уп- равляет состоянием контакта ввода/вывода, направление передачи данных через который все также определяется регистром TRISX Кроме того, у каждого порта появился регистр защелки LATX Несмотря на то что каждый из этих регистров защелки имеет уникальный адрес, например регистр LATB размещен по адресу h’F8A’, они физически не реализованы. Взаимосвязь между этими РСН можно понять из Рис. 16.6, который следует сравнить с аналогичным Рис. 11.3 на стр. 333. Единственное принципиальное отличие между рисунками — появление дополнительного тристабильного буфера LAT, выделенного серым цветом. При чтении регистра LAT, например с помощью команды movf LATB, w, возвращает- ся состояние самого триггера данных. Соответствующая команда movf PORTB, w считывает реальное состояние выводов порта В. Обычно обе эти операции дают одинаковый результат. Однако, как мы обсуждали на стр. 337, если величина вте- кающего или вытекающего тока превышает паспортные значения или если на- грузка имеет большую емкость, а период переключения достаточно мал, то ре- зультат выполнения команды типа «чтение-модификация-запись», примененной к регистру PORTT, будет непредсказуем. Манипулирование содержимым регист- ра защелки вместо содержимого соответствующего регистра порта даст нам опре- деленную независимость от условий работы схемы. Например, команда btfsc LATB,7 пропустит следующую команду, если 7-й бит порта В сброшен, даже если вывод RB7 окажется подтянутым к ВЫСОКОМУ уровню из-за слиш- ком большого вытекающего тока. Очевидно, что в этом случае надежность про- граммы будет гораздо выше, нежели при использовании эквивалентной команды btfsc PORTB,7. При записи в регистр LATA" или соответствующий ему регистр PORTT изме- няется состояние триггера данных. Таким образом, команда movwf LATB по своему действию идентична команде movwf PORTB.
582 Часть III. Окружающий мир Чтение из LATx 1D >С1 Шина данных памдти данных Запись в PORTx Запись в LATx Буфер TRIS Контакт ввода/вывод Запись в TRISx Чтение из TRISx Триггер данных PORT/LAT Защита от пере- напряжения 1D >С1 Входной буфер с триггером Шмитта Триггер TRIS Защелка синхронизатора Буфер данных 1D С1 о- Чтение из PORTx Рис. 16.6. Упрощенная схема одной линии порта ввода/вывода микр8к8ЙТР8ЛЛ8р8В СТЭРШОГО СОМеЙСТВЛ Таймер О Таймер 0, который практически в неизменном виде перешел из семейства младшего уровня в семейство среднего уровня, теперь стал 16-битным и обзавел- ся собственным предделителем, не связанным с постделителем сторожевого тай- мера. При необходимости этот таймер можно переключить в 8-битный режим с помощью нового регистра управления TOCON. Таймер 3 В микроконтроллерах старшего семейства реализован дополнительный 16-битный таймер, который похож по своей структуре на Таймер 1. Таймер 3 может тактироваться от внешнего низкочастотного генератора Таймера 1, ко- торый также может использоваться в качестве системного при необходимос- ти уменьшения потребляемого тока. Каждый из модулей ССР может рабо- тать как с Таймером 1, так и с Таймером 3, что дает нам возможность исполь- зования двух независимых временных шкал.
Глава 16. Дальнейшее развитие 583 Некоторые представители старшего семейства имеют более крупные корпуса и больший ассортимент периферийных модулей, чем рассматриваемые модели. Так, 80-выводной микроконтроллер PIC18F8720 имеет память программ объемом 128 Кбайт, память данных объемом 3840 байт, EEPROM объемом 1024 байта и предоставляет пользователю до 68 линий ввода/вывода. На входе модуля 10-бит- ного АЦП в этой модели имеется 16-канальный мультиплексор. Кроме того, в данном микроконтроллере реализовано два модуля USART и пять модулей CCP/PWM, а также дополнительный 8-битный таймер. Обработка прерываний Как и в моделях среднего семейства, каждое периферийное устройство может генерировать запрос на прерывание. Вдобавок к внешнему прерыванию INT, ко- торое теперь называется INTO, появилось два новых внешних прерывания — INT1 (вывод RB1) и INT2 (вывод RB2). Для поддержки этих внешних прерыва- ний были введены три соответствующих регистра INTCON. Наиболее заметным отличием в системе прерываний стало появление двух уровней приоритета. В микроконтроллерах среднего уровня при обработке за- проса какого-либо разрешенного прерывания все прерывания от других источни- ков автоматически запрещались из-за сброса бита GIE регистра INTCON. При выходе из обработчика прерывания по команде ret fie бит GIE устанавливался снова, чтобы можно было обработать отложенные или последующие запросы прерываний. Несмотря на то что такой механизм необходим для предотвращения конфликтов между запросами прерываний, он может вызвать определенные про- блемы. Возьмем, к примеру, биомедицинский монитор, в котором обработчик прерывания используется для управления достаточно медленной линией связи, предназначенной для передачи телеметрических данных в центральный процес- сор с периодом в один час. Представьте, что у пациента произошла остановка сердечной деятельности. Приоритеты здесь очевидны, однако датчик, формиру- ющий последнее прерывание, окажется заблокированным! в микроконтроллерах расширенного семейства каждому источнику прерыва- ния сопоставлено три бита1). Так, у модуля АЦП имеется флаг прерывания ADIF (PIR1 [6]) для индикации запроса прерывания, бит маски ADIE (Р1Е[6]) для раз- решения прерывания от этого источника, а также бит приоритета ADIP (IPR1 [6]), определяющий приоритет прерывания от данного источника: ADIP = 1 — высо- кий приоритет (состояние после сброса по питанию), ADIP = 0 — низкий при- оритет. При возникновении запроса прерывания от источника с низким приорите- том, происходит переход по вектору низкоприоритетных прерываний, располо- женному по адресу h’018’, и сбрасывается бит GIEL (глобальное разрешение пре- рываний с низким приоритетом) в INTCON[6]. В результате обработка любого другого прерывания с низким приоритетом будет невозможна до завершения об- 11 За исключением внешнего прерывания INTO, которое всегда имеет высокий приоритет.
584 Часть Ш. Окружающий мир работки текущего прерывания. Однако если во время обработки низкоприори- тетного прерывания будет получен запрос прерывания от источника с высоким приоритетом, то выполнение текущего обработчика приостановится и процессор перейдет по вектору высокоприоритетного прерывания, расположенному по адре- су h’008’ (что соответствует адресу слова h’004’ вектора прерывания предыдущих семейств). Одновременно с этим сбрасывается бит GIEH (глобальное разреше- ние прерываний с высоким приоритетом) в INTCON[7], запрещая все прерыва- ния независимо от их приоритетов. При переходе к соответствующему вектору прерывания содержимое счетчика команд помещается на вершину стека, как и в семействе среднего уровня. Поми- мо этого, в трех скрытых регистрах, называемых иногда быстрым стеком (fast stack), сохраняется содержимое рабочего регистра, регистра STATUS и регистра BSL. Для восстановления сохраненного контекста одновременно со счетчиком команд необходимо, чтобы определенный бит слова команды ret fie был уста- новлен в 1. Для этого команда ret fie записывается с параметром: ret fie 1 или, более удобочитаемо, ret fie FAST. Если же команда будет записана без па- раметров, то этот бит окажется сброшен и при возврате из обработчика прерыва- ния будет восстановлен только счетчик команд. Необходимо очень аккуратно использовать этот механизм быстрого сохране- ния/восстановления контекста, поскольку в быстром стеке может храниться только одна копия этих системных регистров. Если запрос прерывания с высоким приоритетом прервет выполнение обработчика прерывания с низким приорите- том, то содержимое быстрого стека будет перезаписано новыми значениями! По- этому при наличии в программе прерываний с разными приоритетами команда ret fie FAST должна использоваться только в обработчиках прерываний с вы- соким приоритетом. Если же прерывания в программе вообще не используются, то быстрый стек можно использовать для сохранения контекста при вызове обычных подпрограмм. Так, по команде call FAST происходит вызов подпро- граммы с одновременным сохранением контекста, а по команде return FAST — возврат из подпрограммы с восстановлением контекста. В трех регистрах INTCON хранятся флаги прерываний, биты маски и приори- тета для прерывания от Таймера 0, прерывания по изменению состояния порта В и трех внешних прерываний. Управление остальными прерываниями осуществ- ляется с помощью регистров PIR1:2, PIE1:2 и IRP1:2. При включении питания схема обработки прерываний работает так же, как и неприоритетная схема сред- него семейства, т.е. состояние битов приоритета игнорируется. Кроме того, для разрешения работы приоритетной системы прерываний должен быть установлен в 1 бит IPEN регистра управления сбросом RCON (RCON[7]). Система команд В Табл. 16.1 перечислены все 75 команд, поддерживаемые ядром PIC18. Срав- нивая эту таблицу с приведенной в Приложении Г, можно заметить, что сохрани- лись все предыдущие команды, за исключением команды clrw. Эта команда ста-
Глава 16. Дальнейшее развитие 585 ла избыточной, поскольку рабочий регистр теперь может обрабатываться как обыкновенный регистр данных и, соответственно, его сброс может быть осущест- влен командой clrf WREG. Также была расширена функциональность ряда ста- рых команд, например, в части воздействия на флаги отрицательного значения (N) и переполнения (OV). У нескольких команд появились расширенные вариан- ты; например, команда addwf с прибавляет содержимое рабочего регистра к ука- занному регистру данных с учетом переноса (см. Программу 16.2). Таблица 16.1. Набор команд микроконтроллеров расширенного семейства 16-битная команда Мнемоника Адресат Изм. флаги Операции W F N V z D c Сложение константы с W addlw LL w <- w + #LL Сложение W и регистра addwf f,d,b d <- w + f Сложение W и регистра с учетом переноса addwfc f,d,b d <- w + f + c Побитовое И константы и W andlw LL • • • w <- w • #LL Побитовое И регистра и W andwf f , d, b • • • d <- w • f Сброс п-го бита в регистре bcf f,n,b • • • • • fn <- 0 Установка «-го бита в регистре bsf f,n,b • • • • • fn <- 1 Инверсия «-го бита в регистре btg f,n,b • • • • • fn <- fn Безусловный переход (относительный) bra offset • • • • • PC <- PCtoffset Переход (относительный), если перенос be offset • • • • • PC <- PCtoffset, если C==l Переход (относительный), если нет переноса bnc offset • • • • • PC <- PCtoffset, если C= = 0 Переход (относительный), если ноль bz offset • • • • • PC <- PCtoffset, если Z= = l Переход (относительный), если не ноль bnz offset • • • • • PC <- PCtoffset, если Z= = 0 Переход (относительный), если отрицательный результат bn offset • • • • • PC <- PCtoffset, если N==l Переход (относительный), если положительный результат bnn offset • • • • • PC <- PCtoffset, если N==0 Переход (относительный), если переполнение bov offset • • • • • PC <- PCtoffset, если V==l Переход (относительный), если нет переполнения bnov offset • • • • • PC <- PCtoffset, если V==0 Пропуск, если «-й бит регистра сброшен btfsc f,n,b • • • • • PC++, если fn == 0
586 и Часть III. Окружающий мир Таблица 16.1. Набор команд микроконтроллеров расширенного семейства (продолжение) 16-битная команда Мнемоника Адреса! Изм. флаги Операции W F N V z D c Пропуск, если л-й бит регистра установлен btfss f ,n,b • • • • • PC++, если fn == 1 Вызов подпрограммы0 call aaa • • • • • TOS <- PC, SP—, PC <- aaa Вызов подпрограммы с сохранением контекста0 call aaa,FAST • • • • • + сохран. W,STATUS,BSR Очистка регистра clrf f ,b • • a/ • • f <- 00 Сброс сторожевого таймера clrwdt • • • • • WDT <- 00 Инвертирование регистра comf f ,d,b • • • d <- f Сравнить регистр с W и пропустить, если равно cpfseq f ,b • • • • • PC++, если f == W Сравнить регистр с W и пропустить, если больше cpfsgt f ,b • • • • • PC++, если f (беззн.) > W Сравнить регистр с W и пропустить, если меньше cpfsit f ,b • • • • • PC++, если f (беззн.) < W Десятичная коррекция W daw • • • • V Коррекция суммы BCD-чисел Декрементирование регистра decf f ,d,b V d <- f— Декрементирование регистра и пропуск, если ноль decfsz f ,d,b • • • • • d <- f--, PC++, если f == 0 Декрементирование регистра и пропуск, если не ноль defsnz f ,d,b • • • • • d <- f--, PC++, если f != 0 Безусловный переход0 goto aaa • • • • • pc <- aaa Инкрементирование регистра incf f ,d,b d <- f++ Инкрементирование регистра и пропуск, если ноль incfsz f ,d,b • • • • • d <- f++, PC++, если f == 0 Инкрементирование регистра и пропуск, если не ноль infsnz f ,d,b • • • • • d <- f++, PC++, если f == 0 Побитовое ИЛИ константы и W iorlw LL • V • • w <- w v #LL Побитовое ИЛИ регистра и W iorwf f ,d,b a/ • a/ • • d <- w v f Загрузка регистра FSRn (л = 0...2)° Ifsr n,LLL • • • • • FSRn <- #LLL Пересылка константы в регистр выбора банка movlb L • • • • • BSR <- #L Пересылка регистра movf f ,d,b • • • • d <- f Пересылка одного регистра в другой0 movf f fs,fd • • • • • fd <- fS
Глава 16. Дальнейшее развитие 587 Таблица 16.1. Набор команд микроконтроллеров расширенного семейства (продолжение) 16-битная команда Мнемоника Адресат Изм. флаги Операции W F N V z D c Пересылка константы в W movlw LL • • • • • w <- #LL Пересылка W в регистр movwff V • • • • • f <- w Перемножение константы с W mullw LL • • • • • PRODH:PRODL <- W x #LL Перемножение W с регистром mulwf f,b • • • • • PRODH:PRODL <- W x f Отрицательное значение регистра negf f,b f <- -f Нет операции nop • • • • • - Чтение вершины стека pop • • • • • SP + + Запись в вершину стека push • • • • • (TOS) <- PC + 2, SP-- Вызов подпрограммы (относительный) rcall Offset • • • • • (TOS)<-PC, SP—, PC<-±Offset Программный сброс reset • • • • • Все регистры - со- стояние по умолчанию Возврат из подпрограммы return • • • • • PC <- TOS Возврат из подпрограммы с восстановлением контекста return FAST PC <- TOS; контекст Возврат из подпрограммы; константа в W retlw • • • • • w <- #LL, PC <- TOS Возврат из прерывания retfie • • • • • GIELIGIEH <- 1, PC <- TOS Возврат из прерывания с восстановлением контекста retfie FAST GIELIGIEH <- 1, PC <- TOS, контекст Циклический сдвиг регистра влево через перенос rlcf f,d,b • • b7 —| C [*-[ 7 Регистр 0 Циклический сдвиг регистра влево без переноса rlncf f,d,b • • • ** 17 Регистр 0 p-} Циклический сдвиг регистра вправо через перенос rrcf f,d,b • • bo ^-*4 7 Регистр 0 |~—[C |— Циклический сдвиг регистра вправо без переноса rrncf f,d,b • • • 7 Регистр 0 |~" ) Установка всех битов регистра setf f,b • • • • • f <- b'llllllll' Переход в спящий режим sleep • • • • • WDT <- 0, такт, сиг- нал выкл. Вычесть W из константы sublw LL w <- #LL - w Вычесть W из регистра subwf f,d,b d <- f - w
588 Часть Ш. Окружающий мир Таблица 16.1. Набор команд микроконтроллеров расширенного семейства (продолжение) 16-битная команда Мнемоника Адресат Изм. флаги Операции W F N V z D c Вычесть W из регистра с учетом заема subwfb f,d,b d <- f - (w + !C) Вычесть регистр из W с учетом заема subfwb f,d,b d <- w - (f + !C) Поменять местами полубайты регистра swapf f,d,b • • • • • d <- f[7:4] <-> f [3:0] Команды табличного чтения/записи из/в памяти программ ячейки, определяемой TBLPTR[20:0], в/из регистр TABLAT Табличное чтение third * • • • • • TABLAT <- (TBLPTR) Табличное чтение с постинкрементом third *+ • • • • • TABLAT <- (TBLPTR++) Табличное чтение с постдекрементом third *- • • • • • TABLAT <- (TBLPTR--) Табличное чтение с прединкрементом third +* • • • • • TABLAT <- (++TBLPTR) Табличная запись tblwt * • • • • • (TBLPTR) <- TABLAT Табличная запись с постинкрементом tblwt *+ • • • • • (TBLPTR++) <- TABLAT Табличная запись с постдекрементом tblwt *- • • • • • (TBLPTR--) <- TABLAT Табличная запись с прединкрементом tblwt +* • • • • • (++TBLPTR) <- TABLAT Проверка регистра и пропуск, если он равен нулю tstfsz f,b • • • • • Побитовое Исключающее ИЛИ константы и W xorlw LL • • • w <- w ® #LL Побитовое Исключающее ИЛИ регистра и W xorwf f,d • • • d <- w ® f ° Двухсловная команда Условные обозначения: Воздействует на флаг (TOS) Содержимое вершины стека LLL 12-битная константа ++ Инкрементирование fn «-й бит регистра # Константа w Рабочий регистр Offset Смещение ±1024 слова TOS Вершина стека LL 8-битная константа 1= Условие неравенства b Обращение к ОЗУ посредством BSR SP Указатель стека РС++ Пропуск следующей команды offset Смещение ±128 слов WDT Сторожевой таймер • Не воздействует на флаг == Условие равенства d Адресат: 0 = w, 1 = f — Декрементирование PC Счетчик команд ааа Адрес f Регистр данных GIE Бит глобального разрешения прерываний
Глава 16. Дальнейшее развитие * 589 Чтобы завершить главу, перечислим основные отличия нового набора ко- манд, используя то же деление по выполняемым функциям, что и в главе 5. Команды пересылки данных Наиболее важным нововведением в этой группе команд является команда movf f, которую мы уже использовали в Программе 16.1. Эта команда позволяет копировать содержимое любого регистра-источника в любой регистр-приемник, невзирая на сегментированную структуру памяти данных и не используя рабочий регистр. Поскольку для указания каждого из регистров требуется полный 12-бит- ный адрес, команда movf f занимает два слова в памяти программ и выполняется за два машинных цикла. Двоичный код этой команды выглядит следующим образом: |1100 | fs[ fs[ fs| fs| fs| fs[ fs| fs| fs| f8[ fg| fs| |11111 f<i| fd| fd| м f d| ч fd| fd| fd| td| M fdl Четыре старших бита второго слова b’ 111Г имитируют код операции команды пор1*. Это отличительная особенность всех двухсловных команд, позволяющая избежать проблем при переходе на середину такой команды (в качестве примера см. Программу 16.4). Команда Ifsr, также занимающая два слова в памяти программ, загружает 12-битную константу (значение адреса) в один из трех регистров косвенной адре- сации FSR, как показано в Программе 16.1. Команда Ibsr является однослов- ной. Команды арифметических операций Одним из назначений микроконтроллеров PIC18XXX является реализация приложений начального уровня* 2) для цифровой обработки сигналов (DSP, ЦОС) в реальном масштабе времени. Задачи ЦОС требуют значительной вычислитель- ной мощности, поэтому эта категория команд претерпела, наверное, самые боль- шие изменения. Одним из наиболее вопиющих недостатков набора команд младшего и среднего семейств является отсутствие в нем команд сложения с учетом переноса и вычитания с учетом заема. По этой причине операции сложения и вычитания многобайтных чисел получаются достаточно громоздкими и медленными. Команда addwf с прибавляет содержимое рабочего регистра W к содержимому указанного регистра данных плюс значение бита переноса. Результат, как и пре- жде, помещается либо в рабочий регистр, либо обратно в регистр данных. При этом значение бита переноса изменяется соответствующим образом. Для приме- ра в Программе 16.2 приведен код подпрограммы, выполняющей сложение двух Существует две разновидности команд пор: одна — с кодом b’000000000000’, а другая — с кодом Ь’ 111 Ixxxxxxxx’. 2) Специально для задач цифровой обработки сигналов, требующих большой вычислитель- ной мощности, было разработано семейство цифровых сигнальных контроллеров dsPIC™.
590 Часть III. Окружающий мир 3-байтных чисел и получающей 4-байтный результат. За исключением операции сложения младших байтов, при переходе к старшим байтам учет бита переноса осуществляется естественным образом, независимо от требуемой точности. Без такой команды каждое сложение пришлось бы сопровождать операцией условно- го инкрементирования. Программа 16.2. Сложение трехбайтных чисел TP_ADD clrf NUM3_V ; Обнуляем старший байт результата movf NUM1_L,w ; Берем младший байт 1-го числа addwf NUM2_L,w ; Прибавляем младший байт 2-го числа movwf NUM3_L ; Сохраняем младший байт результата movf NUMl_H,w ; Берем средний байт 1-го числа addwfc NUM2_H,w ; Прибавляем средний байт 2-го числа movwf NUM3_H ; Сохраняем средний байт результата movf NUMl_U,w ; Берем старший байт 1-го числа addwfc NUM2_U,w ; Прибавляем старший байт 2-го числа movwf NUM3_U ; Сохраняем старший байт результата btfsc STATUS,C ; Пропускаем, ЕСЛИ нет переноса incf NUM3_V,f ; ИНАЧЕ прибавляем 1 Аналогичным образом команда subwfb вычитает из заданного регистра со- держимое W плюс бит заема, сформированный предыдущими арифметическими операциями. Также появилась команда subfwb, которая выполняет вычитание в обратном порядке, т.е. рабочий регистр вычитается из регистра данных. Все команды сложения, вычитания, а также команды инкрементирования и декрементирования обеспечивают полную поддержку чисел со знаком, представ- ленных в дополнительном коде (в сочетании с флагами N и OV). Команды incf и decf теперь воздействуют на все флаги арифметических операций, упрощая, та- ким образом, выполнение многобайтных вычислений (напоминаю, раньше эти команды воздействовали только на флаг Z). Новая команда neg£ ВЫЧИСЛЯёТ до- полнительный код числа, находящегося в заданном регистре. Соответственно ес- ли в регистре находилось число со знаком, то значение знака меняется на проти- воположное. Одной из наиболее важных операций при цифровой обработке сигналов яв- ляется операция умножения. Поэтому в систему команд были добавлены две ко- манды умножения, вычисляющие за один машинный цикл 16-битное произведе- ние двух беззнаковых 8-битных чисел, как показано в Программе 16.1. На базе этих команд можно создавать относительно короткие подпрограммы для умно- жения многобайтных чисел, представленных в дополнительном коде. Команда десятичной коррекции daw упрощает реализацию сложения чисел, представленных в BCD-коде. Как мы уже говорили на стр. 111, при выполнении операций над такими числами с использованием обычной двоичной арифметики результат необходимо корректировать, чтобы исключить шесть избыточных зна-
Глава 16. Дальнейшее развитие 591 ченийЬ’1011’...Ь’111Г. Команда daw осуществляет такую коррекцию, будучи вы- полнена сразу же после команд сложения или инкрементирования упакованных BCD-чисел (два BCD-разряда хранятся в одном байте). Пример использования этой команды приведен в Программе 16.3, которая выполняет те же действия, что и Программа 4.1. Заметьте, что команда daw не преобразует двоичное число в BCD-формат, она просто осуществляет коррекцию после сложения данных, уже представленных в упакованном BCD-формате. Программа 16.3. Инкрементирование упакованного BCD-числа с использованием команды daw BCD_INC incf BCD,w ; Инкрементируем BCD-число по правилам двоичной ; арифметики daw ; Корректируем его для приведения к формату BCD movwf BCD ; и помещаем результат обратно в регистр К командам сброса и установки отдельных битов регистров данных была до- бавлена третья команда btg, позволяющая инвертировать значение заданного би- та регистра. Для примера в Программе 16.4 генерируется последовательность из 20 прямоугольных импульсов на выводе RA0, длительность каждого из которых составляет 8 машинных циклов. Причем формирование этих импульсов осущест- вляется по аналогии с кодом, относящимся к Рис. 5.19 (стр. 156). Обратите внима- ние на использование команды decfsz непосредственно с рабочим регистром, который в данном случае представляется в виде РСН с именем WREG. Программа 16.4. Переключение вывода RA0 ; Конфигурируем порт А movlw Ь'0110' ; movwf ADCON1 bcf LATA,0 bcf TRISA,0 ; Конфигурируем порт А как цифровой Начнем с НИЗКОГО уровня на выводе Делаем RA0 выходом RA0 ; Где- -TO В П] movlw d'40' Загружаем в W число 40 LOOP btg LATA,0 Изменяем состояние вывода RA0 1~ decfsz goto WREG,f LOOP Декрементируем до нуля ИНАЧЕ выходим из цикла 113~ 2~ ........... ; Далее В ядре PIC18 команда goto занимает два слова памяти программ. Поэтому при выполнении команды decfsz произойдет переход на второе слово команды goto. Именно по этой причине машинный код второго слова всех четырех двух- словных команд таков, что при непосредственном переходе на это слово оно ин- терпретируется как команда пор. Из-за этого время выполнения команды увели- чивается на один машинный цикл.
592 Часть Ш. Окружающий мир И наконец, команда setf предоставляет программисту возможность непос- редственно устанавливать все биты заданного регистра в 1, дополняя, таким обра- зом, команду clrf, имевшуюся в предыдущих семействах. Команды логических операций и операций сдвига * В данной категории изменения коснулись только группы команд циклическо- го сдвига. Как мы видели из Рис. 5.13 (стр. 148), команды rlf и rrf сдвигают со- держимое заданного регистра через бит переноса. Новые же команды осуществ- ляют сдвиг в обход этого бита. Эти команды имеют мнемоники гIncf (сдвиг вле- во, не через флаг переноса) и rrncf (сдвиг вправо, не через флаг переноса). Команды сдвига, доставшиеся в наследство от семейства среднего уровня, для единообразия были переименованы в rlcf и rrcf. Еще раз напоминаю, что, пос- кольку к рабочему регистру можно обращаться как к регистру данных, его содер- жимое можно сдвигать точно так же, как и содержимое любого другого регистра. Команды передачи управления Все команды, имевшиеся в предыдущих семействах, т.е. команды goto, call и три команды возврата, были сохранены. Однако первые две из них теперь зани- мают два слова в памяти программ и способны осуществлять переход в пределах 20 Мелов, позволяя, таким образом, забыть о страничной организации памяти программ, которая имела место в микроконтроллерах с ядром PIC 16. Кроме того, команда call может теперь сохранять контекст программы в теневом стеке (если прерывания не используются) при ее вызове с параметром fast. Возврат из под- программы, вызванной таким образом, осуществляется командой return FAST. Большинство переходов, осуществляемых с помощью команд goto, относи- тельно короткие. К примеру, команда goto LOOP в Программе 16.4 возвращает- ся назад всего на две команды. Новая однословная команда bra позволяет осу- ществлять переход в пределах —1023...+1024 слов. Таким образом, используя вместо goto LOOP команду bra LOOP, мы экономим одно слово памяти про- грамм, хотя время выполнения команды остается равным двум машинным цик- лам. Аналогичным образом команда относительного вызова подпрограмм rcall позволяет осуществлять вызов близлежащих подпрограмм, правда, она не имеет возможности сохранения контекста. К командам безусловного перехода добавились десять команд условного пере- хода, в которых переход осуществляется по определенному значению того или иного флага регистра STATUS, за исключением флага DC. В принципе проверить состояние этих флагов можно с помощью команд btfss и btfsc, однако все, что мы получим в результате их выполнения, — это пропуск единственного слова па- мяти программ. А команды условного перехода позволяют осуществлять переход в пределах +128...—127 слов при установке определенного флага регистра STATUS, как, например, команда Ьс (переход, если флаг С установлен), или его сбросе, например, Ьпс (переход, если флаг С сброшен). Предположим, что на порт С микроконтроллера подается значение от датчика температуры, представ-
Глава 16. Дальнейшее развитие 593 ленное в дополнительном коде. В следующем фрагменте кода при положитель- ной температуре выполняется сброс регистра, названного SIGN, а при отрица- тельной температуре все биты этого регистра устанавливаются в 1. Модуль значе- ния температуры помещается в регистр TEMPERATURE. clrf movf f SIGN PORTC,TEMPERATURE ; Обнуляем регистр признака знака и копируем ; принятое значение температуры movf TEMPERATURE,f ; Проверим на ноль или отрицательное число bnn NEXT ; Если > 0, ТО обходим инвертирование NEXT negf setf TEMPERATURE,f SIGN ; ИНАЧЕ меняем знак числа ; и устанавливаем биты регистра признака знака ; Продолжаем Команда bnn next выполняет переход к метке next при положительном значении. В противном случае меняется знак числа и с помощью команды setf в регистр SIGN заносится число h’FF’. На стр. 139 мы с вами видели, что для сравнения двух беззнаковых чисел, ска- жем W и содержимого регистра данных, необходимо вычесть одно число из дру- гого с последующей проверкой состояния флагов С и Z. Три новые команды срав- нения/пропуска выполняют эту проверку автоматически. Команда cpf seq про- пускает следующую команду, если беззнаковое число в заданном регистре равно числу в рабочем регистре, cpf sgt — если оно больше, чем число в W, a cpf sit — если это число меньше находящегося в W. Возьмем для примера код системы контроля уровня топлива, приведенный на стр. 140. Если в баке остается менее 20 л, то включается сигнальная лампочка, а если менее 5 л — звуковой сигнал. Теперь этот же код будет выглядеть следующим образом: ALARM bcf DISPLAY,BUZZER ; Выключаем пищалку bcf DISPLAY,LAMP ; Выключаем лампочку movlw 4 cpfsgt FUEL bsf DISPLAY,BUZZER movlw d'19’ cpfsgt FUEL bsf DISPLAY,LAMP ; Проверим уровень 5 литров ; Пропускаем, ЕСЛИ > 4 литров ; ИНАЧЕ включаем звуковой сигнал } Проверим уровень 20 литров ; Пропускаем, ЕСЛИ >19 литров ; ИНАЧЕ включаем лампочку NEXT ......................... ; Продолжаем Не забывайте, что данные команды сравнения корректно работают только с беззнаковыми числами. Для сравнения чисел, представленных в дополнительном коде, необходимо по-прежнему выполнять операцию вычитания и проверки фла- гов N, OV и Z.
594 Часть III. Окружающий мир И последняя новая команда в этой группе, t st f sz, проверяет содержимое за- данного регистра (не изменяя его) и пропускает следующую команду, если оно равно нулю. К командам incfsz и decfsz прибавились команды infsnz и dcfsnz, выполняющие пропуск команды, если результат инкрементирова- ния/декрементирования не равен нулю.
ГЛАВА УЧЕБНЫЙ ПРИМЕР До настоящего момента вся информация носила в какой-то степени фраг- ментарный характер. Чтобы придать процессу изучения логическую завершен- ность, давайте попробуем применить большую часть полученных знаний и разра- ботать реальное устройство (как схему, так и программу). Может показаться, что в одной короткой главе это сделать не так уж легко. Однако на данном этапе нам придется изучить совсем мало нового материала, большей же частью мы будем просто применять полученные знания. Всякая подобная работа начинается с составления подробных технических требований. Все без исключения студенты во время устных докладов говорят очень долго. Чтобы хоть как-то их ограничить, предполагается создать специали- зированное устройство на базе микроконтроллера, которое бы отслеживало за- данный интервал времени. По умолчанию рабочий период этого устройства (на- зовем его таймером) равен 10 мин, однако следует предусмотреть возможность изменения этого значения в пределах 1...99 мин. После запуска таймер должен выполнить следующие операции: 1. При нажатии кнопки СБРОС включается зеленый СИД, а на сдвоенном 7-сегментном индикаторе начинается обратный отсчет от заданного зна- чения до числа 03 с интервалом в одну минуту 2. Еще через минуту включается желтый СИД, на дисплее высвечивается чис- ло 02 и на одну секунду включается звуковой излучатель. 3. Еще через минуту включается красный СИД, на дисплее высвечивается число 0Q, а звуковой излучатель включается на две секунды. 4. И наконец, по истечении последней минуты на дисплее высвечивается число 00 и включается звуковой излучатель (красный СИД продолжает светиться) до тех пор, пока не будет нажата кнопка СТОП. При этом таймер сбрасывается, выклю- чив все индикаторы, светодиоды и звук. В принципе нажатие кнопки СТОП в любой момент времени приведет к останову таймера. Перезапуск системы с за- данным значением тайм-аута может быть осуществлен сбросом процессора. 5. В любой момент времени работу таймера можно приостановить нажатием и удерживанием кнопки ПАУЗА. При отпускании этой кнопки процесс счета продолжается с места остановки. 6. Чтобы изменить величину интервала, заданную по умолчанию (Q0), при перезапуске системы необходимо держать нажатой кнопку УСТ. В результа-
596 и Часть III. Окружающий мир те на дисплее появится значение 09, которое начнет медленно уменьшать- ся. Значение, которое будет находиться на дисплее в момент отпускания кнопки УСТ., станет новой длительностью интервала и будет сохранено до следующей операции установки временного интервала таймера. Итак, прежде всего нам необходимо выбрать подходящий микроконтроллер. В данном случае мы вынуждены будем ограничить наш выбор моделями, рас- сматриваемыми в книге, т.е. PIC12F675/29, PIC16F627/8 и PIC16F87X. Посколь- ку модуль АЦП нам не требуется, то для реализации описанного устройства мож- но спокойно выбрать любой из указанных микроконтроллеров. Использование 18-выводного PIC16F627/8 вместо 40-выводного PIC16F874/7 потребует допол- нительных ухищрений для реализации необходимого количества линий вво- да/вывода, однако в то же время на его примере можно будет проиллюстрировать выбор оптимального решения при разработке более сложных систем. Альтерна- тивный вариант таймера, в котором используется последний из указанных мик- роконтроллеров, можно найти на Web-сайте книги. В первой редакции книги1} был использован микроконтроллер PIC16F84, и, поскольку он по выводам сов- местим с PIC16F627, решено было использовать последний. Единственное изме- нение, которое потребовалось внести в программу, было связано с модулем ана- логового компаратора, отсутствовавшего в исходной модели. Окончательная схема на базе выбранного микроконтроллера приведена на Рис. 17.1. Ниже описывается, как используются выводы микроконтроллера. Органы управления Пять кнопок S2...S6, управляющие операциями ПУСК, УСТАНОВКА, СТОП, ДИАГНОСТИКА, ПАУЗА, подключены к линиям RB[4:0] порта В. Используя внутреннюю подтяжку (см. Рис. 11.9 на стр. 342), мы можем отказаться от внеш- них подтягивающих резисторов. Кнопка 51 вместе с подтягивающим резистором /?1 используется для ручного сброса системы, вызывающего перезапуск отсчета времени. Этот же сигнал MCLR используется в качестве сигнала сброса для остальных микросхем. В микроконт- роллере PIC16F627/8 4-й вывод может быть задействован в качестве дополнитель- ной линии порта А. Хотя этот вывод используется в качестве входа внешнего сбро- са MCLR по умолчанию, в Программе 17.3 такое функционирование вывода зада- ется явно посредством указания значения бита конфигурации MCLRE. В данной схеме можно использовать любые кнопки без фиксации с нормаль- но разомкнутыми контактами. Световая индикация Для формирования световых сигналов используются 10-мм светодиоды D3...D1 подходящих цветов с большой яркостью, подключенные к выводами RB[7:5] порта В. Резисторы сопротивлением 330 Ом, включенные последователь- но с СИД, ограничивают их ток на уровне 10 мА. ° Имеется в виду оригинальное издание книги на английском языке. — Примеч. ред.
Глава 17. Учебный пример 597 У1 3.2768 МГц 171 С1 --------------- I 16 +5 В Q зз 0SC1 RB7 RB6 13 D1 Красный [Я2 /7 Т 12 D2 1^. Желтый , J С2 0SC2 ± 33 GND RB5 // о4 11 D3 । . Зеленый , . 330 RB4 RB3 RB2 RB1 RB0/INT 10 9 8 7 6 // ПУСК УСТ- СТОП ДИАГ. ПАУЗА jGND Примечание: BhtRBPU (OPTION_REG[7J) должен быть установлен для включения подтяжки на входах порта В 3 --- RA4/TOCKI +5 В R1 33k 4 < MCLR RA3 RA2 RA1 RAO 2 1 18 17 \Reset 9 Clock 8 U2_____ SRG8 R BZ1 +5 В Излучатель PIC16F627 S1 СБРОС Data -----11 <►— 2 & R5 2_Гг 4 2 3 _5___3 6 4 10 5 11 6 12 7 13 8 174 16 1 15 14 14 12 13 10 12 4 11 2 10 13 9 9 a b c d e f 74НС164 6 330 g rhdp ihdp CON CON CON CON 3 —f—О 5 —о 16 —о 11 2 8 U3 SRG8 R 7-сегментный индикатор с общим анодом 2 74НС164 & 1D 3 1 16 1 4 2 15 14 5 3 14 12 6 4 13 10 10 5 12 4 11 6 11 2 12 7 10 13 13 8 9 9 1 1 6 а b с d е f 330 CON 3 g rhdp —— ihdp CON CON CON 5 16 7-сегментный индикатор с общим анодом Рис. 17.1. Схема таймера со звуковой и световой сигнализацией
598 Часть III. Окружающий мир Звуковая индикация Для звуковой индикации мы применим миниатюрный твердотельный излуча- тель. Типичный пьезоэлектрический излучатель может работать в диапазоне на- пряжений от 3 до 16 В, потребляя при напряжении 5 В чуть больше 1 мАЧ Управ- ление звуковым излучателем осуществляется с вывода RA2. Цифровой дисплей Цифровой дисплей, позволяющий отображать значения от 00 до 99, образован двумя 7-сегментными светодиодными индикаторами. Поскольку в нашем распоря- жении осталось всего 4 линии ввода/вывода, мы реализовали последовательный ин- терфейс. Этот интерфейс похож на применяющийся в схеме на Рис. 12.2 (стр. 370), однако в данном случае для каждого 8-битного сдвигового регистра с последова- тельным входом и параллельным выходом 74НС164 используется отдельная линия данных (RA0 — для десятков и RA3 — для единиц). Поэтому изображение на обоих индикаторах можно будет обновлять одновременно (за восемь тактов). Цоколевка 7-сегментных индикаторов, показанная на схеме, соответствует индикаторам с общим анодом, выпускающимся в 16-выводном DIP-корпусе и имеющим десятичные точки с обеих сторон знакоместа — Ihdp и rhdp. В нашем устройстве используется только правая точка для индикации паузы. Широко рас- пространены индикаторы в других 16-выводных и 14-выводных корпусах, в том числе и сдвоенные (с двумя знакоместами в одном корпусе). Однако даже для ин- дикаторов в 16-выводных корпусах цоколевка не стандартизирована. В малогабаритных индикаторах (высотой менее 20 мм/0.8 дюйма) для каждо- го сегмента используется один СИД, прямое падение напряжения на котором со- ставляет около 2 В* 2). Две резисторные сборки R5 и /?6 сопротивлением 330 Ом ог- раничивают ток через сегменты на уровне 10 мА. Общие аноды подключены не- посредственно к линии +5 В и должны быть развязаны с помощью танталового конденсатора небольшой емкости. Хотя яркость свечения индикаторов обычно нормируется при токе 20 мА, она будет вполне достаточной даже при выбранном нами токе. Кроме того, при этом исключается необходимость подключения буфе- ров к выходу сдвиговых регистров 74НС1643). В принципе можно сделать так, что при включении излучателя продолжать разговаривать станет просто невозможно. Достаточно мощный пьезоэлектрический излучатель, генерирую- щий звуковой сигнал с уровнем 110 дБ на расстоянии в 1 м, требует источника питания напря- жением 12 В, потребляя при этом 200 мА. 2) В индикаторах больших размеров, например 2.24 дюйма/56 мм, каждый сегмент состоит из двух или четырех последовательно включенных СИД. При применении последних придется использовать отдельный источник питания напряжением 12 В и буферы с повышенной нагру- зочной способностью. 3) Также выпускаются слаботочные (low-current) 7-сегментные индикаторы.
Глава 17. Учебный пример 599 Кварцевый резонатор В качестве времязадающего элемента тактового генератора используется кварцевый резонатор частотой 3.2768 МГц, в результате частота выполнения ко- манд составляет 819.21 кГц. Типичный резонатор с такой частотой имеет точность ±300 ppm и температурный коэффициент ±50 ppm в диапазоне рабочих темпера- тур. Такой необычный выбор частоты обусловлен тем, что при использовании Таймера 0 с коэффициентом деления предделителя, равным 64, мы сможем фор- мировать прерывания ровно 50 раз в секунду (см., далее). В принципе можно было бы снизить потребление микроконтроллера, взяв резонатор частотой 32.768 кГц и генерируя прерывание каждые две секунды с использованием 16-битного Таймера 1. Однако мощность, потребляемая микроконтроллером, в любом случае будет мизерной по сравнению с потреблением светодиодных индикаторов. В моделях PIC16F627/8 выводы OSC1 и OSC2 могут использоваться в качест- ве дополнительных линий порта А, при этом микроконтроллер будет работать от внутреннего ЛС-генератора с номинальной частотой 4 МГц (см. Табл. 10.2 на стр. 309). На то, что в нашей схеме используется внешний кварцевый резонатор, указывает наличие в Программе 17.3 опции конфигурации _XT_OSC. Теперь, когда схема разработана, мы можем сосредоточить все свои усилия на разработке программы. В общем виде модульная структура нашей системы изображена на Рис. 17.2. Прямоугольники с двойными линиями по краям соответствуют подпрограмме или процедуре обработки прерывания. На данном этапе можно выделить три обособленных процесса и две основных вспомогательных задачи. Рис. 17.2. Модульная структура программы таймера Задача формирования временных отсчетов Все процессы привязаны ко времени. Временная привязка реализована аппа- ратно, генерацией прерывания с периодичностью 50 раз в секунду. Изменение значений секунд и минут происходит после накопления определенного количест-
600 Часть III. Окружающий мир ва тиков. Эти значения в дальнейшем используются для выполнения требуемых процессов. При обнаружении нажатия на кнопку ПАУЗА декрементирование этих счет- чиков приостанавливается, за счет чего обратный отсчет времени можно «замо- розить» на сколь угодно длительный временной интервал. Задача отображения времени Состояние счетчика, а также вспомогательную информацию необходимо вы- водить на двухразрядный дисплей. Поскольку в процессе вывода осуществляется преобразование параллельного кода в последовательный, а также его передача по последовательному каналу, эту задачу лучше выделить в отдельный модуль. Фоновый (основной) процесс Фоновый процесс осуществляет в цикле вывод на индикатор значение счет- чика минут до тех пор, пока он не станет равным нулю. При замыкании кнопки СТОП происходит преждевременный выход из цикла. Процесс установки интервала Если в момент сброса микроконтроллера кнопка УСТ. находится в замкнутом состоянии, то вызывается подпрограмма sett. Эта подпрограмма постепенно уменьшает выводимое на дисплей число до тех пор, пока кнопка не будет отпуще- на. Последнее показанное число сохраняется в EEPROM и используется всеми запускаемыми впоследствии фоновыми процессами в качестве начального значе- ния для отсчета интервала. Процесс самодиагностики Если при сбросе кнопка ДИАГ. находится в замкнутом состоянии, то управле- ние передается в подпрограмму диагностики. Основной задачей этой подпро- граммы является проверка всех периферийных устройств, чтобы облегчить на- хождение неисправного узла. Все процессы зависят от задачи формирования временных отсчетов, которая предоставляет базовую информацию о реальном времени. Как показано в Про- грамме 17.1, эта задача реализована в виде обработчика прерывания от Таймера 0. Предделитель таймера сконфигурирован таким образом, чтобы при системной тактовой частоте 3.2763 МГц переполнение таймера происходило бы каждые 750 с. Поскольку прерывание от Таймера 0 разрешено (см. Программу 17.3), мик- роконтроллер будет переходить к обработчику прерывания при каждом перепол- нении таймера — каждые 256 импульсов с выхода предделителя. Учитывая, что частота внутреннего тактового сигнала составляет 7д от частоты резонатора, ко- эффициент деления предделителя, равный 64, позволит нам формировать отсче- ты времени 50 раз в секунду, т.е. -Зд2763х10 _ 4x64x256
Глава 17. Учебный пример 601 Итак, обработчик прерывания будет выполнять следующие задачи: 1. ЕСЛИ кнопка ПАУЗА отпущена, ТО а) Декрементировать счетчики на один тик. б) Если прошла секунда, то установить соответствующий флаг. 2. ИНАЧЕ а) Переключить флаг состояния ПАУЗА. б) ЕСЛИ он установлен, ТО показать, что работа таймера приостановлена. в) ИНАЧЕ отобразить время (нормальная работа). г) Ждать, пока не будет отпущена кнопка УСТ. 3. Выйти из прерывания. Программа 17.1. Обработка времени • ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ / ; * При каждом вызове обработчика прерывания внутренний * ; * счетчик увеличивается на 20-мс дискрет * ; * Каждую секунду в NEW_SEC заносится ненулевое значение * • ************************★★★**★★★★★★★★★★★★★★★★★★★★★★★★*****★** t ; Сначала сохраним контекст ISR movwf swapf movwf _work ; Сохраняем W STATUS,w ; и регистр STATUS _status ; Основной код btfss goto INTCON,TOIF ; Произошло переполнение Таймера 0? ISR_EXIT ; ЕСЛИ нет, ТО ложная тревога btfsc goto Pause,0 ; Проверяем флаг паузы ISR_EXIT ; ЕСЛИ нажата, не инкрементируем incf movlw subwf btfss goto clrf JIFFY,f ; Регистрируем очередные 1/50 с d'501 ; Досчитали до 50? JIFFY,w STATUS,Z ISR_EXIT ; ЕСЛИ нет, ТО выходим JIFFY ; ИНАЧЕ обнуляем счетчик дискретов movf btfsc goto decf incf SECOND,f ; Счетчик секунд равен нулю? STATUS,Z NEW_MIN ; ЕСЛИ да, ТО смотрим минуты SECOND,f ; ИНАЧЕ декрементируем счетчик секунд, NEW_SEC,f ; извещаем основную программу о прохождении ; секунды goto ISR_EXIT ; и выходим NEW_MIN movlw movwf d'59' ; Реинициализируем счетчик секунд SECOND
602 Часть III. Окружающий мир movf MINUTE,f ; Счетчик минут равен нулю? btfsc STATUS,Z goto ISR_EXIT ; ЕСЛИ да, ТО делать больше нечего decf MINUTE,f ; ИНАЧЕ декрементируем счетчик минут . ************************************************************ ISR_EXIT btfss call PORTB,PAUSE ; FREEZE ; ; Проверяем кнопку ПАУЗА : ЕСЛИ нажата, ТО обновляем флаг паузы bcf INTCON,T0IF ; : Сбрасываем флаг прерывания swapf _status,w ; ; Восстанавливаем регистр STATUS movwf STATUS swapf _work,f ; ; Восстанавливаем W, swapf _work,w ; ; не меняя состояния STATUS, retfie ; и возвращаемся из прерывания • ************************************************************* I ; * ФУНКЦИЯ : Инкрементирует флаг паузы. * ; * ЕСЛИ 1, ТО отображает десятичные точки * ; * ЕСЛИ 0, ТО отображает нормальный отсчет минут * ; * РЕСУРСЫ : П/п SPI_WRITE, переменная Pause * ; * ВХОД : Кнопка ПАУЗА нажата * ; * ВЫХОД : Кнопка ПАУЗА отжата; соответствующая индикация * • ************************************************************* / FREEZE incf Pause,f ; Обновляем 0-й бит флага паузы btfss Pause,0 ; Проверяем его состояние goto UNFREEZE ; Переход 1 -> 0, разблокируем ; Дисплей заблокирован movlw b'01111111' ; Код для десятичной точки movwf DATA_OUT_L movwf DATA_OUT_H call SPT_WRI№ goto FREEZE_EXIT UNFREEZE ; Сюда переходим, если 0-й бит флага изменился 1 -> 0. movf MINUTE,w ; Отображаем оставшееся количество минут call OUTPUT FREEZE_EXIT btfss PORTB,PAUSE goto FREEZE_EXIT clrf TMR0 ; Ждем отпускания кнопки ; Сбрасываем таймер/предделитель return Из Программы 17.1 видно, что время хранится в виде 3-байтного числа в ре- гистрах MINUTES, SECOND и JIFFY. Полагая, что 0-й бит регистра PAUSE сброшен в 0, значение счетчика тиков JIFFY увеличивается на единицу. Обычно после этого выполняется выход из обработчика прерывания, однако если JIFFY
Глава 17. Учебный пример 603 становится равным 50, то он обнуляется и декрементируется счетчик секунд SECOND. При этом в регистр NEW_SEC заносится ненулевое значение, извеща- ющее фоновый процесс о том, что прошла секунда. Когда счетчик секунд стано- вится равным нулю, в него снова загружается константа 59, а счетчик минут MINUTES декрементируется. Описанная процедура похожа на операцию инкре- ментирования счетчика, которую мы реализовали в Примере 7.3. Задача формирования временных отсчетов также поддерживает функцию приостановки счета. Самым простым решением был бы пропуск декрементиро- вания счетчиков при нажатой кнопке ПАУЗА. Однако если потребуется удержи- вать кнопку нажатой дольше нескольких минут, то эта операция может оказаться довольно утомительной. Формирование останова/пуска по последовательным нажатиям кнопки явля- ется более эргономичным и может быть достаточно легко реализовано програм- мно. И уж точно, это гораздо лучше, чем использовать кнопку какого-либо друго- го типа (скажем, кнопку с фиксацией). В Программе 17.1 код обработки нажатия кнопки ПАУЗА вынесен в отдельную подпрограмму freeze. Мы вполне можем вызвать подпрограмму из обработчика прерывания точно так же, как и из другой подпрограммы. Аппаратный стек позволяет использовать до 8 уровней вложен- ности. В нашем случае будет использовано только два уровня стека. Подпрограмма freeze вызывается только в случае замыкания кнопки ПАУ- ЗА. При каждом входе в подпрограмму изменяется состояние 0-го бита регистра PAUSE, что реализуется простым инкрементированием регистра. После переключения PAUSE[0] проверяется его состояние и, если он равен 1, то вызывается подпрограмма SPI_WRITE для вывода на индикаторы только двух десятичных точек. Разумеется, это только один из множества возможных спосо- бов отображения состояния паузы. Можно, например, выводить на индикатор символы ня. Если же PAUSE[0] равен 0, то вызывается подпрограмма output, в которую передается значение счетчика минут и убирается индикация паузы. Выход из подпрограммы осуществляется только после отпускания кнопки ПАУЗА. Это очень важно, так как в противном случае при входе в обработчик прерывания по следующему переполнению Таймера 0 могло бы произойти пов- торное (ложное) переключение флага паузы. Чтобы предотвратить влияние дре- безга контактов кнопки, после ее отпускания сначала обнуляется Таймер 0 со своим предделителем и только после этого сбрасывается флаг прерывания T0IF, благодаря чему повторная проверка состояния кнопки будет произведена только через Узо секунды. Вывод на индикаторы содержимого рабочего регистра в виде десятичного числа осуществляется подпрограммой output, код которой приведен в Программе 17.2. Эта подпрограмма выполняет следующие операции: 1. Преобразует двоичное число в 2-разрядное BCD-число. 2. Преобразует значения обоих разрядов в коды 7-сегментного индикатора. 3. Пересылает каждый байт в соответствующий индикатор по последователь- ному каналу.
604 Часть III. Окружающий мир Программа 17.2. Функция вывода на индикаторы . ************************************************************* / ; * ФУНКЦИЯ : Выводит на индикаторы 2-разрядное десят. число * ; * РЕСУРСЫ : П/п BIN_2_BCD, SPI_WRITE, SVN_SEG * ; * РЕСУРСЫ : Перем. DATA_OUT_L, DATA_OUT_H, NEW_SEC, NUMBER * ; * ВХОД : Число в W (от 0 до 99) * ; * ВЫХОД : Число выводится, NEW_SEC обнуляется * • ************************************************************* / OUTPUT bcf PORTA,SCK ; Инициализируем линию тактового сигнала call BIN_2_BCD ; Преобразовываем в BCD movwf NUMBER ; Сохраняем результат в NUMBER movf NUMBER,w ; Берем число, которое нужно вывести andlw b'OOOOUir ; Выделяем число единиц call SVN_SEG ; Преобразуем в 7-сегментный код movwf DATA_OUT_L ; Копируем в младший регистр swapf NUMBER,w ; Перегружаем число десятков в младший полубайт andlw Ь’ООООИИ' ; Выделяем число десятков call SVN_SEG ; Преобразуем в 7-сегментный код movwf DATA_OUT_H ; Копируем в старший регистр call SPI_WRITE ; Передаем значение обоих разрядов clrf NEW_SEC ; Обнуляем флаг NEW_SEC return . ******************************************************* / ; * ФУНКЦИЯ : Одновременно передает два байта по * ; * последовательному каналу * ; * ВХОД : Значения в DATA_OUT_L (младший разряд) * ; * ВХОД : и DATA_OUT_H (старший разряд) * ; * ВЫХОД : DATA_OUT_L и DATA_OUT_H изменяются * • ******************************************************* / SPI_WRITE PORTA,SCK ; Выставляем НИЗКИЙ уровень на SCK bcf movlw 8 ; Инициализируем счетчик цикла movwf COUNT LOOP bcf PORTA,SDOH ; Выставляем 0 на линию данных старшего разряда rlf DATA_OUT_H,f ; Выдвигаем младший бит в бит переноса btfsc STATUS,C ; ЕСЛИ С -- 0, ТО пропускаем bsf PORTA,SDOH ; ИНАЧЕ выставляем на линию данных 1 bcf PORTA,SDOL ; Выставляем 0 на линию данных младшего разряда rlf DATA_OUT_L,f ; Выдвигаем младщий бит в бит переноса btfsc STATUS,C ; ЕСЛИ С =- 0, ТО пропускаем bsf PORTA,SDOL ; ИНАЧЕ выставляем на линию данных 1 bsf PORTA,SCK ; Формируем тактовый импульс bcf PORTA,SCK
Преобразование двоичного кода в код 7-сегментного индикатора Подпрограмма SVN_SEG преобразует младший полубайт содержимого регист- ра W в соответствующий код 7-сегментного индикатора. Код подпрограммы пол- ностью эквивалентен приведенному в Программе 6.6 (стр. 184). Вывод по SPI Подпрограмма SPI_WRITE похожа на свою тезку, реализованную в Программе 12.1 на стр. 371, но формирует два потока последовательных данных. Число, находящееся в регистре DATA_OUT_L, передается по линии RA3, тогда как число, находящееся в регистре DATA_OUT_H, — по линии RA0. Оба канала используют общий тактовый сигнал. Прежде чем перейти к кодированию процессов, составляющих программу, вкратце рассмотрим используемую конфигурацию микроконтроллера, задавае- мую при его программировании, а также инициализационный код, выполняемый после сброса микроконтроллера (см. Программу 17.3). Программа 17.3. Инициализационный код include "pl6f627a.inc" SDOH equ 0 SCK equ 1 BUZ equ 2 SDOL equ 3 GREEN equ 5 YELLOW equ 6 RED equ 7 PAUSE equ 0 DI AG equ 1 STOP equ 2 SETT equ 3 GO equ 4 cblock 20h MINUTE:1, SECOND:1, JIFFY:1, NUMBER:1, NEW_SEC:1 DATA_OUT_L:1, DATA_OUT_H, COUNT:1, TEMP:1, TIME_OUT:1 Pause:1, _work:l, _status:l endc config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF & _MCLRE_ON org de 2100h d ’ 10 * ; Область EEPROM ; Значение по умолчанию - 10 мин RESET org 0 ; Вектор сброса
606 Часть Ш. Окружающий мир Преобразование двоичного кода в код 7-сегментного индикатора Подпрограмма SVN_SEG преобразует младший полубайт содержимого регист- ра W в соответствующий код 7-сегментного индикатора. Код подпрограммы пол- ностью эквивалентен приведенному в Программе 6.6 (стр. 184). Вывод по SPI Подпрограмма SPI_WRITE похожа на свою тезку, реализованную в Программе 12.1 на стр. 371, но формирует два потока последовательных данных. Число, находящееся в регистре DATA_OUT_L, передается по линии RA3, тогда как число, находящееся в регистре DATA_OUT_H, — по линии RA0. Оба канала используют общий тактовый сигнал. Прежде чем перейти к кодированию процессов, составляющих программу, вкратце рассмотрим используемую конфигурацию микроконтроллера, задавае- мую при его программировании, а также инициализационный код, выполняемый после сброса микроконтроллера (см. Программу 17.3). Программа 17.3. Инициализационный код include "pl6f627a.inc SDOH equ 0 SCK equ 1 BUZ equ 2 SDOL equ 3 GREEN equ 5 YELLOW equ 6 RED equ 7 PAUSE equ 0 DI AG equ 1 STOP equ 2 SETT equ 3 GO equ 4 cblock 20h MINUTE:1, SECOND:1, JIFFY:1, NUMBER:1, NEW_SEC:1 DATA_0UT_L:1, DATA_0UT_H, COUNT:1, TEMP:1, TIME_OUT:1 Pause:1, _work:l, _&tatus:l endc __config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF & _MCLRE_ON org de 2100h d'10' ; Область EEPROM ; Значение по умолчанию - 10 мин RESET org 0 ; Вектор сброса
Глава 17. Учебный пример 607 goto MAIN org 4 ; Вектор прерывания goto ISR MAIN bsf STATUS,RPO ; Переключаемся в 1-й банк movlw b'11100000' ; RA4:0 - выходы movwf TRISA movlw b'OOOlllll' ; RB7:5 - выходы; RB4:0 - входы movwf TRISB movlw b'00000101' ; Таймер 0: внутр. такт. сигнал, movwf OPTION_REG ; предделитель 1:64. Подтяжка вкл. bcf STATUS,RPO ; Возвращаемся в 0-й банк clrf Pause ; Обнуляем флаги паузы clrf NEW_SEC ; и секунды clrf TMR0 bcf INTCON,T0IF bsf INTCON,TOIE ; Разрешаем прерывание от Таймера 0 bsf INTCON,GIE ; Разрешаем все прерывания btfss PORTB,SETT ; Проверяем кнопку УСТ. call SET_TIME ; ЕСЛИ нажата, ТО устанавливаем интервал btfss PORTB,DIAG ; Проверяем кнопку ДИАГ. call DIAGNOSTIC ; ЕСЛИ нажата, ТО выполняем самодиагностику Конфигурирование кристалла С помощью директивы _config задается состояние конфигурационных ячеек в слове конфигурации кристалла. Сторожевой таймер отключен, генератор работает с внешним кварцевым резонатором, также задействован вход внешнего сброса MCLR. Запрещение низковольтного программирования высвобождает вывод RB3 для нужд ввода/вывода (см. стр. 312). При прошивке микроконтроллера в ячейку EEPROM с адресом h’00’ заносит- ся число 10. Это означает, что интервал счета только что запрограммированного микроконтроллера составляет 10 мин. Данное значение впоследствии можно из- менять посредством процедуры установки интервала. Эта ячейка расположена в адресном пространстве специальной области памяти по адресу h’2100’, а для указания значения, заносимого в эту область памяти на эта- пе программирования, используется директива de, как было описано на стр. 549. Выполнение программы Код, выполняемый при каждом сбросе микроконтроллера, используется для инициализации рабочего окружения.
608 Часть I II. Окружающий мир Вектора По адресу вектора сброса (h’000’) расположен переход к основной программе main, а по адресу вектора прерывания (h’004’) — команда перехода к процедуре обработки прерывания. Конфигурирование портов Линии PORTA[4:0] и PORTB[7:5] конфигурируются как выходы, а остальные линии портов используются как входы. Конфигурирование Таймера 0 Коэффициент деления предделителя задается равным 64, в качестве источни- ка тактовых импульсов Таймера 0 используется системный тактовый сигнал. Так- же разрешается прерывание от Таймера 0. Выбор текущего процесса Для выбора процесса, которому следует передать управление, проверяется со- стояние кнопок ДИАГ. и УСТ. Если ни одна из кнопок не нажата, то осуществля- ется переход к процессу MAIN. Если в момент сброса микроконтроллера нажата кнопка ДИАГ., то управле- ние передается в подпрограмму DIAGNOSTIC, код которой приведен в Программе 17.4. Задачей процесса диагностики является тестирование различ- ных периферийных устройств, подключенных к процессору, с целью проверки целостности цепей и исправности собственно устройств. Программа 17.4. Процедура самодиагностики . ************************************************************* ; * ФУНКЦИЯ : Проверяет состояние кнопок и включает соотв. * ; * ФУНКЦИЯ : СИД или звуковой излучатель. Поочередно включает* ; * ФУНКЦИЯ : по одному сегменту на каждом индикаторе * ; * РЕСУРСЫ : Подпрограмма SPI_WRITE * ; * РЕСУРСЫ : Переменные TEMP, DATA_OUT_H, DATA_OUT_L * ; * ВХОД : Кнопка ДИАГ. нажата * ; * ВЫХОД : Кнопка ДИАГ. отпущена * . **************************р********************************* DIAGNOSTIC movlw movwf b'111111101 TEMP ; Формируем начальное значение ; маски управления индикаторами D_LOOP movlw b'11111111' movwf PORTB bsf PORTA,BUZ ; Сканируем кнопки ; Выключаем все СИД и пищалку btfss PORTB,PAUSE ; ЕСЛИ нажата кнопка ПАУЗА, bcf PORTB,GREEN ; ТО включаем зеленый СИД
Глава 17. Учебный пример 609 btfss PORTB,STOP ; ЕСЛИ нажата кнопка СТОП, bcf PORTB,YELLOW ; ТО включаем желтый СИД btfss PORTB,SETT ; ЕСЛИ нажата кнопка УСТ., bcf PORTB,RED ; ТО включаем красный СИД btfss PORTB,GO ; ЕСЛИ нажата кнопка ПУСК, bcf PORTA,BUZ ; ТО включаем пищалку ; Теперь по очереди включаем все сегменты на обоих индикаторах movf TEMP,w ; Берем маску movwf DATA_0UT_L ; Копируем в регистры последовательной movwf DATA_0UT_H ; передачи call SPI_WRITE ; Передаем ее btfsc PORTB,DIAG ; ЕСЛИ кнопка ПАУЗА отпущена, return ; ТО выходим из процедуры самодиагностики clrf NEW_SEC ; Сбрасываем флаг секунды ; Теперь сдвигаем маску для индикаторов и ждем 1 секунду bcf STATUS,C ; Сбрасываем бит переноса btfsc TEMP,7 ; Проверяем старший бит маски bsf STATUS,C ; ЕСЛИ 1, ТО устанавливаем флаг переноса rlf TEMP,f ; Вдвигаем его в регистр D_LOOP2 movf NEW_SEC,f ; Ждем секунду btfsc STATUS,Z ; ЕСЛИ флаг не равен нулю, ТО пропускаем goto D_LOOP2 ; ИНАЧЕ пробуем снова goto D_LOOP ; Повторяем процедуру Кнопки Поочередно проверяются все пять кнопок, подключенных к порту В. При за- мыкании кнопки включается либо один из СИД, либо звуковой излучатель. Та- ким образом, проверяется состояние кнопок и соответствующих органов индика- ции. Разумеется, работоспособность кнопки ДИАГ. проверяется по переходу сис- темы в режим диагностики, а работоспособность кнопки СБРОС — по запуску процесса инициализации. Если число органов управления в устройстве больше числа органов индика- ции, то можно либо включать определенные комбинации индикаторов, либо за- действовать для этой цели один или более сегментов 7-сегментного индикатора. СИД и звуковой излучатель Устройства вывода статической информации проверяются совместно с кноп- ками, как описано выше. Разумеется, отсутствие свечения у СИД или звука у из- лучателя может быть обусловлено неисправностью как входного, так и выходного узла. Какой именно из узлов неисправен, достаточно легко выясняется при помо- щи вольтметра или логического пробника. Помните также, что светодиоды долж- ны светиться во время установки временного интервала.
610 Часть I И. Окружающий мир Дисплей Каждый из индикаторов дисплея проверяется путем поочередного включения одного из сегментов с периодом в одну секунду. Это реализуется формированием значения с «бегущим нулем» (b’l 1111110’ —> b’ 11111101 ’ —Ь’ОПППГ), которое передается подпрограммой SPI_WRITE при каждом ненулевом значении регист- ра NEW_SEC. Данный регистр инкрементируется в обработчике прерывания от Таймера 0 при каждом инкрементировании счетчика секунд и сбрасывается в процедуре диагностики. То есть он играет роль храпового механизма, обеспечи- вая вывод каждого нового символа не ранее чем через секунду. Процесс установки интервала запускается в том случае, если при выходе мик- роконтроллера из состояния сброса кнопка УСТ. оказывается замкнутой. Данный процесс предназначен для того, чтобы оператор мог изменить содержимое ячей- ки EEPROM с адресом h’00’ на любое значение от 1 до 99. В этой ячейке хранится начальное значение, используемое основным процессом для определения дли- тельности процедуры счета. В этой подпрограмме, код которой приведен в Программе 17.5, сначала в счетчик секунд записывается число 99, которое затем декрементируется с перио- дом в одну секунду в соответствии с логикой работы обработчика прерывания. Содержимое регистра SECOND передается в подпрограмму вывода на дисплей каждый раз, когда обработчик прерывания записывает в регистр-флаг NEW_SEC ненулевое значение, т.е. каждую секунду. В подпрограмме DISPLAY регистр NEW SEC обнуляется, благодаря чему обновление дисплея происходит опять же только раз в секунду. Кроме того, каждую секунду проверяется состояние кнопки УСТ. — при ее размыкании состояние счетчика секунд сохраняется в EEPROM в секции update вызовом подпрограммы низкого уровня ee_put из Программы 15.2 (стр.547). Программа 17.5. Процедура установки временного интервала . 'k'k-k-k'k'k'k-k-k-k'k'k-k-k-k-k-k'k-k-k-k-k-k'k'k'k-k-k-k-k'k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k-k ; * ФУНКЦИЯ : Медленно считает от 99 до 00. При отпускании * ; * кнопки УСТ. в EEPROM заносится новое значение * ; * интервала * ; * РЕСУРСЫ : П\п DISPLAY, EE_PUT, ISR; переменная TIME_OUT * ; * ВХОД : Кнопка УСТ. нажата * ; * ВЫХОД : Обновляется содержимое EEPROM по адресу 00 * . ************************************************************* г SET_TIME movlw movwf movlw movwf SET_LOOP movf d' 99 ’ SECOND b'00000000' PORTB • SECOND,w OUTPUT ; Начинаем счет co значения 99 ; Включаем все СИД ; Берем значение счетчика секунд ; и выводим его на дисплей call btfsc PORTB,SETT ; Проверяем, не надо ли прекратить счет? goto UPDATE ; ЕСЛИ да, ТО обновляем EEPROM и выходим
Глава 17. Учебный пример 611 movf movwf SECOND,w TIME_OUT ; Берем отображаемое число ; Делаем временную копию S_LOOP movf NEW_SEC,f ; Проверяем флаг секунды btfsc STATUS,Z ; ЕСЛИ не ноль, ТО пропускаем goto S_LOOP ; -IJFHA4E проверяем снова goto SET_LOOP ; Повторяем UPDATE movf TIME_0UT,w ; Берем значение movwf EEDATA ; Инициализируем EEPROM clrf EEADR call EE_PUT ; Пишем в EEPROM return ; и возвращаемся в основную программу Полная блок-схема алгоритма работы фоновой программы приведена на Рис. 17.3. На данной схеме показана процедура выбора требуемого процесса пос- ле сброса, а также подробная структура основной фоновой процедуры. Хотя эта блок-схема кажется довольно сложной, ее можно разбить на пять фаз, код кото- рых приведен в Программе 17.6. Программа 17.6. Основная фоновая процедура movlw b111000000' ; Включаем зеленый СИД movwf PORTB bsf PORTA,BUZ ; Выключаем звук ; Считываем начальное значение из EEPROM clrf call movwf movlw movwf clrf EEADR EE_GET MINUTE d'59' SECOND JIFFY ; Адрес в EEPROM - 00 ; Считываем начальное значение ; Начальное значение секунд ; равно 59 DISPLAY movf MINUTE,w ; Берем значение минут call OUTPUT ; Выводим его на дисплей ] *** 1^4. lYLKLtiy 1 MLJyl 1 U 1 xirlUC 1У1 ; За две минуты до конца включаем звук на одну секунду и включаем ; желтый светодиод TWO movf MINUTE,w ; Счетчик минут =2? addlw -2 btfss STATUS,Z goto ONE ; ЕСЛИ нет, ТО проверим след, фазу movlw b'10100000' ; ; Включаем желтый СИД movwf PORTB bcf PORTA,BUZ ; Включаем пищалку TWO_LOOP movf NEW_SEC,f ; ; Проверяем флаг NEW_SEC btfsc STATUS,Z ; ; ЕСЛИ не ноль, ТО пропускаем goto TWO_LOOP ; ; ИНАЧЕ проверяем снова
612 Часть III. Окружающий мир Сброс MAIN Рис. 17.3. Блок-схема основной фоновой процедуры
Глава 17. Учебный пример 613 bsf PORTA,BUZ ; Выключаем пищалку через 1 секунду goto REPEAT ; Выводим интервал на дисплей ; Фаза 1- -минутной готовности ; За одну минуту до конца включаем звук на две секунды и включаем ; красный светодиод ONE movf MINUTE,w ; Счетчик минут - 1? addlw -1 btfss STATUS,Z goto ZERO ; ЕСЛИ нет, ТО проверим след, фазу movlw b'01100000' ; Включаем красный СИД movwf PORTB bcf PORTA,BUZ ; Включаем пищалку ONE_LOOP movf NEW_SEC,f ; Проверяем флаг NEW_SEC btfsc STATUS,Z ; ЕСЛИ не ноль, ТО пропускаем goto ONE_LOOP ; ИНАЧЕ проверяем снова clrf NEW_SEC ; Сбрасываем флаг NEW_SEC UN_LOOP movf NEW_SEC,f ; Проверяем флаг NEW_SEC btfsc STATUS,Z ; ЕСЛИ не ноль, ТО пропускаем goto UN_LOOP ; ИНАЧЕ проверяем снова bsf PORTA,BUZ ; Выключаем пищалку через 2 секунды goto REPEAT ; Выводим интервал на дисплей ; Когда । счетчик минут становится равным нулю, включаем пищалку ; до тех пор, пока не будет нажата кнопка СТОП ZERO movf MINUTE,f ; Счетчик минут = 0? btfss STATUS,Z goto REPEAT ; ЕСЛИ нет, ТО повторим проверку через минуту bcf PORTA,BUZ ; Включаем пищалку ZERO_LOOP btfsc PORTB,STOP i Проверяем кнопку СТОП goto ZERO_LOOP ; и продолжаем, пока не будет нажата FINI movlw b’11100000’ ; Выключаем индикаторы movwf PORTB bsf PORTA,BUZ ; и пищалку movlw b'llllllll' ; Код для очистки индикаторов movwf DATA_OUT_L movwf DATA_OUT_H call SPI_WRITE ; Очищаем оба индикатора sleep ; и ждем следующего сброса REPEAT btfss PORTB,STOP ; Проверяем кнопку СТОП goto FINI ; ЕСЛИ нажата, ТО прекращаем работу movf SECOND,f ; Ждем обнуления счетчика секунд, btfss STATUS,Z ; т.е. наступления следующей минуты goto REPEAT ; ЕСЛИ нет, ТО ждем дальше clrf NEW_SEC ; ИНАЧЕ ждем еще секунду R_LOOP movf NEW_SEC,f ; Проверяем флаг NEW_SEC
614 Часть III. Окружающий мир btfsc STATUS,Z ; ЕСЛИ не ноль, ТО пропускаем goto R_LOOP ; ИНАЧЕ проверяем снова goto DISPLAY ; Повторяем вывод на дисплей Преамбула Если в момент сброса не нажата ни кнопка УСТ., ни кнопка ДИ АГ., то управ- ление переходит к основной программе, обозначенной меткой MAIN_PROC. В этой секции осуществляется считывание значения отсчитываемого периода из ячейки EEPROM с адресом h’00’ и инициализация счетных регистров. Зеленый СИД включается, а остальные световые индикаторы и звуковой излучатель вы- ключаются. Обратный отсчет В фазе обратного отсчета осуществляется периодический ВЫВОД на дисплей значения счетчика минут — обновление дисплея осуществляется в прерывании. Зеленый СИД остается во включенном состоянии до тех пор, пока на дисплее не появится число оа . Эта фаза завершается, когда до конца отсчитываемого интер- вала остается меньше 3 мин либо при нажатии на кнопку СТОП. В последнем случае все органы индикации выключаются, и микроконтроллер переходит в «спящий» режим. Всегда, за исключением времени обработки нажатия кнопки СТОП, на дисп- лей выводится значение счетчика минут. В секции repeat проверяется значение счетчика секунд, и, если он равен нулю, цикл повторяется, т.е. период повторе- ния равен одной минуте. Использование более простого решения, заключающе- гося в непрерывном обновлении 7-сегментных индикаторов, привело бы к ухуд- шению изображения, поскольку данные, постоянно передаваемые по последова- тельному интерфейсу, могли бы вызывать кратковременную засветку лишних сегментов, которые должны быть выключены. Кроме того, период повторения цикла, равный одной минуте, упрощает кратковременное включение звукового излучателя при равенстве счетчика минут двум и единице. Две минуты до конца Когда на дисплее появляется число 08, включается желтый СИД и подается звуковой сигнал длительностью в одну секунду. Длительность последнего контро- лируется с помощью регистра NEW_SEC. И опять же, цикл можно преждевре- менно завершить, нажав кнопку СТОП. Одна минута до конца Когда на дисплее высвечивается Q0, включается красный СИД и подается звуковой сигнал длительностью в две секунды (в программе это реализовано в ви- де двух включений излучателя по 1 с каждое).
Глава 17. Учебный пример 615 Тайм-аут Когда счетчик минут становится равным нулю, на дисплей выводится 02, а излучатель начинает непрерывно генерировать звуковой сигнал. Эта какофония может быть прекращена только нажатием кнопки СТОП либо сбросом и повтор- ным запуском таймера. Как и прежде, при нажатии на кнопку СТОП все органы индикации выключаются, и микроконтроллер переводится в «спящий» режим. После ассемблирования исходного кода программы и, быть может, даже пос- ле симуляции (см. Рис. 8.7 на стр. 265) полученный шестнадцатеричный код мож- но загрузить в память программ. На первом этапе достаточно реализовать только программу диагностики и связанные с ней подпрограммы, чтобы проверить пра- вильность монтажа устройства. Подробности процесса программирования в зна- чительной степени зависят от используемого программатора и его программного обеспечения. На снимке экрана, показанном на Рис. 17.4, изображено окно ИСР MPLAB при работе с программатором PICSTART Plus® компании Microchip (см. Рис. 17.5). К персональному компьютеру программатор подключается через пос- ледовательный порт RS-232, а установление соединения осуществляется из пунк- та меню Picstart Plus. Окно, показанное в правой части экрана, позволяет опера- тору задать требуемые значения битов конфигурации (см. левое нижнее окно). После этого оператор может выполнить команды Blank out (очистить), Read from (считать), Program (запрограммировать) или Verify (верифицировать). Последняя команда позволяет убедиться, что содержимое EEPROM или памяти программ идентично коду, сгенерированному в текущем проекте. Обращаю ваше внимание, что верификацию можно выполнить только в том случае, если защита кода вы- ключена. Если же при программировании контроллера была включена защита кода, то после завершения программирования нельзя будет выполнить ни коман- ду Verify, ни последующие команды Program. На среднем окне отображается состояние процесса программирования или верификации. В данном случае сообщается, что была запрограммирована память программ до ячейки с адресом h’03FF’ и что верификация прошла успешно. Весь этот процесс занимает меньше минуты, а размер нашей программы оказался ра- вен 254 словам памяти программ. При использовании микроконтроллеров PIC, имеющих в обозначении букву «F», процесс программирования можно повторить несколько тысяч раз без ухуд- шения параметров FLASH-памяти программ. Микроконтроллеры с индексом «С»’\ такие как PIC16C74, имеют память программ EPROM-типа. Если в корпусе микросхемы имеется кварцевое окошко (см. фотографию на стр. 15), то перед повторным программированием содержимое памяти программ необходимо сте- реть, подвергая кристалл действию ультрафиолетового излучения в течение при- мерно 20 мин. Хотя модели с кварцевым окошком необходимы для разработки устройств на базе микроконтроллеров С-серии, они все-таки достаточно дороги. ° Исключением из правила является устаревшая к настоящему моменту модель PIC16C84, память программ в которой была реализована по технологии EEPROM.
616 Часть III. Окружающий мир Н11И ЯНЫ «КЙН* ежей »** sr»m,«r« : HOUIM S' ШМ**»* Шй MMttw mvH ИМИ nnwlw «'MMHHtf* шл art j он M.S ан ими elrf taw eiw iojk art ii«s ш iHtwt.twr »sf йв«мЖ as# шгЕоилк btfss Mil btfss «all KSXt *1 •.•.•.•.г.:;-.-I'l.i.iartiJiiiltrilW W4Mt^4 1вМ1 rem,мп sfi rm rest» «aim 81КЖКК мод» I’tiwue1 ;*i*««.iHfaa nwvwf гаю» Mt mil.» : tam »ff 1мШ1 ЕнпЫН Й>**4 XT IM в» wtr «Mat t« <Hs$»o 1» Mt* 1 *♦»•»«« W4 start: vais* in tan*. • *«l«r far жчяммн Рис. 17.4. Программирование микроконтроллера из ИСР MPLAB 5-й версии Поэтому в производстве используются более дешевые варианты, называемые од- нократно-программируемыми (One-Time Programmable — OTP), поскольку их стереть невозможно. Изделия с кварцевым окошком в корпусе отличаются суф- фиксом «JW» в обозначении. Например, PIC16C74B—20/JW является микроконт- роллером PIC16C74B в керамическом корпусе с окошком, a PIC16C74B—4/Р — однократно-программируемым исполнением того же микроконтроллера с макси- мальной частотой 4 МГц в 40-выводном корпусе типа DIP. Удостоверьтесь, что вы заказываете правильное устройство! Устройство, схему и программное обеспечение которого мы только что разра- ботали, представляет собой достаточно простой пример, в котором мы попыта- лись собрать воедино различные методики, изучавшиеся нами на протяжении всей книги. Если вы решите изготовить это устройство, то на Web-сайте книги к вашим услугам имеются исходные файлы (в том числе и вариант на языке Си), сравнение с аналогичной конструкцией на микроконтроллере 68000 фирмы Motorola, а также много других идей для экспериментирования. Удачи!
Глава 17. Учебный пример и 617 Рис. 17.5. Фирменный программатор PICSTART Plus компании Microchip
Приложение А СПИСОК СОКРАЩЕНИЙ, СИМВОЛИЧЕСКИХ ИМЕН И АББРЕВИАТУР 1. Русская нотация АЛУ АЦП БИС Арифметико-логическое устройство Аналого-цифровое преобразование/преобразователь Микросхема высокой степени интеграции; большая интегральная схема ИОН ИС ИСР КМОП МЭК ОЗУ ОС ПЗУ ПК ППЗУ РОН РСН СБИС Источник опорного напряжения Интегральная микросхема Интегрированная среда разработки Комплементарная структура металл-оксид-полупроводник Международная электротехническая комиссия Оперативное запоминающее устройство Операционная система Постоянное запоминающее устройство Персональный компьютер Программируемое постоянное запоминающее устройство Регистр общего назначения Регистр специального назначения Микросхема сверхвысокой степени интеграции; сверхбольшая интегральная схема сид сис Светоизлучающий диод; светодиод Микросхема средней степени интеграции; средняя интегральная схема СППЗУ Стираемое программируемое постоянное запоминающее устройство ТТЛ ЦАП ЦОС ЦПУ ШИМ ЭСППЗУ Транзисторно-транзисторная логика Цифро-аналоговое преобразование/преобразователь Цифровая обработка сигналов Центральный процессор Широтно-импульсная модуляция/модулятор Электрически стираемое программируемое постоянное запоминающее устройство
Приложение А. Список сокращений, символических имен и аббревиатур 619 2. Английская нотация ADC (A/D) Analog-to-Digital Conversion см. АЦП ADCONO \ A/D CONtrolO Регистр управления 0 модуля АЦП ADCON1 A/DCONtroll Регистр управления 1 модуля АЦП ADCS« ADC Clock Select; ADCONO[7:6]' Выбор источника тактового сигнала модуля АЦП ADDEN ADDress ENable; RCSTA[3] Разрешение детектирования адреса ADFM ADC module outcome ForMat; ADCON1 [7] Формат результата модуля АЦП ADIE ADC Interrupt Enable; PIE1 [6] Бит разрешения прерывания по окончании преобразования модуля АЦП ADIF ADC Interrupt Flag; PIR1[6] Флаг прерывания от модуля АЦП ADIP ADC Interrupt Priority; IPR1 [6] (PIC18XXXX) Бит приоритета прерывания от модуля АЦП ADON ADC module ON; ADCONO[0] Запуск преобразования АЦП ADRES ADC RESult Результат преобразования АЦП ADRESH ADC RESult High byte Результат преобразования 10-битного АЦП (старший байт) ADRESL ADC RESult Low byte Результат преобразования 10-битного АЦП (младший байт) ALU Arithmetic Logic Unit см. АЛУ AN я ANalog input pin n Аналоговый вход n ANSI American National Standards Institution Американский национальный институт стандартов ASCII American Standard Code for Information Interchange Американский стандартный код обмена информацией AUSART Addressable USART Адресуемый USART (см. также USART) BSR/? Bank Select Register; BSR[3:0] (PIC18XXXX) Биты выбора банка
620 Приложения BCD BF С сюит C2OUT C1INV C2INV ССР CCPw CCPRnH CCPRnL CCPwCON CCP1IE CCP1IF CCPwMzn CCP2IE CCP2IF CHS« CIS CISC Binary Coded Decimal Двоично-десятичное число Buffer Full; SSPSTAT[0] Буфер модуля SSP полон Carry flag; STATUS[0] Флаг переноса Comparator 1 OUTput; CMCON [6] Выход компаратора 1 Comparator 2 OUTput; CMCON [7] Выход компаратора 2 Comparator 1 INVertor; CMCON [4] Инвертирование выхода компаратора 1 Comparator 2 INVertor; CMCON[5] Инвертирование выхода компаратора 2 Capture/Compare/PWM module Модуль «Захват/сравнение/ШИМ» CCPw input/output Вход/выход модуля CCP« ССР Register n High byte Регистр захвата модуля CCP« (старший байт) ССР Register п Low byte Регистр захвата модуля ССРя (младший байт) ССР« CONtrol register Регистр управления модуля ССРя ССР1 Interrupt Enable; PIE1 [2] Бит разрешения прерывания от модуля ССР1 ССР1 Interrupt Flag; PIR1 [2] Флаг прерывания от модуля ССР1 ССРя Mode control; CCPwCON[3:0] Биты управления режимом модуля ССРл ССР2 Interrupt Enable; PIE2[0] Бит разрешения прерывания от модуля ССР2 ССР2 Interrupt Flag; PIR2[0] Флаг прерывания от модуля ССР2 A/D CHannel Select; ADCONO[5:3] Биты выбора канала АЦП Comparator Input Switch; CMCON[3] Управление входами компараторов Complex Instruction Set Computer Процессор co сложным набором команд
Приложение А. Список сокращений, символических имен и аббревиатур 621 СК USART synchronous ClocK I/O Вывод тактового сигнала USART в синхронном режиме СКЕ ClocK Edge; SSPSTAT[6] Управление фронтами тактового сигнала модуля MSSP СКР \ ClocK Polarity; SSPCON[4] Выбор полярности тактового сигнала модуля SSP CMIE CoMparator change Interrupt Enable mask; PIE2[6] Выбор фронта тактового сигнала модуля SSP CMIF CoMparator change Interrupt Flag; PIR2[6] Флаг прерывания от компараторов СМд Comparator Mode; CMCON[2:0] Режим работы аналоговых компараторов CMOS Complimentary Metal-Oxide Semiconductor см. КМОП CMCON CoMparator CONtrol Регистр управления модулем аналоговых компараторов CPU Central Computing Unit см. ЦПУ CREN Continuous Receive ENable; RCSTA[4] Разрешение приема модуля USART CS Chip Select Вывод выбора кристалла CTS Clear То Send Готовность к приему (сигнал квитирования в стандарте RS-232) CVR/7 Comparator Voltage Reference mode; CVRCON[3:0] Биты режима работы ИОН аналогового компаратора CVREN Comparator Voltage Reference ENable; CVRCON[7] Разрешение работы ИОН аналогового компаратора CVRCON Comparator Voltage Reference CONtrol Регистр управления ИОН аналогового компаратора CVROE Comparator Voltage Reference Output Enable; CVRC0N[6] Управление выходом ИОН аналогового компаратора CVRR Comparator Voltage Reference Range; CVRC0N[5] Выбор диапазона ИОН аналогового компаратора D/A Data/Address; SSPSTAT[5} Бит данные/адрес модуля SSP (режим 12С) DAC (D/A) Digital-to-Analog Converter/Conversion module см. ЦАП DC Digit Carry; STATUS[1] Флаг десятичного переноса/заема
622 Приложения DClBw Duty Cycle 1 Bits; CCPwCON[5:4] Младшие биты значения скважности ШИМ DCE Data Circuit terminating Equipment Оконечное оборудование передачи данных DSP Digital Signal Processing см. ЦОС DSR Data Set Ready Готовность устройства передачи данных DT USART synchronous DaTa Вывод данных USART в синхронном режиме DTE Data Terminal Equipment Терминальное оборудование DTR Data Terminal Ready Готовность терминала ea Effective Address Исполнительный адрес EEADR EEPROM ADdress Регистр адреса EEPROM EEADRH EEPROM ADdress High Регистр адреса EEPROM (старший байт) EEC0N1 EEPROM CONtrol 1 Регистр 1 управления EEPROM EECON2 EEPROM CONtrol 2 Регистр 2 управления EEPROM EEDATA EEPROM DATA Регистр данных EEPROM EEDATH EEPROM DATa High Регистр данных EEPROM (старший байт) EEIE EEPROM Interrupt Enable; INTC0N[6] или PIE2[4] Бит разрешения прерывания от модуля EEPROM EEIF EEPROM Interrupt Flag; EECON1[4] или PIR2[4] Флаг прерывания от модуля EEPROM EEPGD EEPROM ProGram/Data; EECON1[7] Бит выбора памяти программ/данных EEPROM Electrical Erasable PROM см. ЭСППЗУ EPROM Erasable PROM см. СППЗУ FERR Framing ERRor; RCSTA[2] Бит ошибки кадрирования модуля USART
Приложение А. Список сокращений, символических имен и аббревиатур 623 FSR File Select Register Индексный(ые) регистр(ы) косвенной адресации GCEN General Call ENable; SSPCON2[7] Бит разрешения общего вызова модуля MSSP (режим 12С) GIE Global Interrupt Enable; INTCON [7] Бит глобального разрешения прерываний GIEH Global Interrupt Enable High-priority; INTCON [7] (PIC 18XXXX) Бит глобального разрешения высокоприоритетных прерываний GIEL Global Interrupt Enable Low-priority; INTCON[6] (PIC18XXXX) Бит глобального разрешения низкокоприоритетных прерываний GO/DONE ADC Start Convert (GO)/End Of Conversion (DONE); ADCONO[2] Бит запуска преобразования АЦП/признак завершения преобразования GPR General-Purpose Register см. РОН GPw General Purpose I/O pin w-й вывод порта ввода/вывода общего назначения GPIO HVP High-Voltage Programming Высоковольтное программирование IC ICSP™ см. ИС In-Circuit Serial Programming Внутрисхемное программирование 12С Inter-Integrated Circuit Протокол межсоединения ИС, протокол 12С IDE Integrated Development Environment см. ИСР IEC International Electrotechnical Commission см. МЭК INDF INDirect File Регистр косвенной адресации INT External INTerrupt Вход внешнего прерывания INTfl External INTerrupt п; (PIC18XXXX) Вход(ы) внешнего прерывания INTCON INTerrupt CONtrol Регистр управления прерываниями INTEDG External INTerrupt EDGe; OPTION_REG[0] Бит выбора активного фронта внешнего прерывания INTE INTerrupt Enable; INTC0N[4] Бит разрешения внешнего прерывания
624 Приложения INTF INTerrupt Flag; INTCONfl] Флаг внешнего прерывания I/O Input/Output Ввод/вывод IPRX Interrupt Priority Register A (PI Cl 8XXXX) Регистр X приоритета прерываний IRP Indirect addressing Register Page; STATUS[7] Бит выбора банка при косвенной адресации ISR Interrupt Service Routine Процедура обработки прерывания LATA LATch Х-, (PIC18XXXX) Регистр защелки параллельного порта ввода/вывода LED Light-Emitting Diode см. СИД LSB Least Significant Bit/Byte Младший значащий бит или байт LSI Large-Scale Integration см. БИС LSD Least-Significant Digit Младший значащий разряд LVP Low-Voltage Programming Низковольтное программирование MCLR Master CLear Reset Вход внешнего сброса микроконтроллера MCU Microcontroller Unit М и кроконтроллер MPU Microprocessor Unit Микропроцессор p.s Microsecond Микросекунда (10-6 с) ms Millisecond Миллисекунда (10-3 с) MSB Most Significant Bit/Byte Старший значащий бит или байт MSD Most Significant Digit Старший значащий разряд MSI Medium-Scale Integration см. СИС MSSP Master Synchronous Serial Port Ведущий синхронный последовательный порт
Приложение А. Список сокращений, символических имен и аббревиатур и 625 N Negative flag; STATUS[4] (PIC 18XXXX) Флаг отрицательного значения ns Nanosecond Наносекунда (10-9 с) ОЁ Output Enable Вывод разрешения выхода OERR Overflow ERRor; RCSTAfl] Бит ошибки переполнения буфера USART OS Operating System см. ОС OPTION_REG OPTION REGister Регистр опций OTP One-Time Programmable Однократно-программируемая память OSCAL OScillator CALibrate Регистр калибровки генератора OV Overflow flag; STATUS[3] (PIC18XXXX) Флаг переполнения P StoP condition; SSPSTAT[4] Бит обнаружения состояния СТОП PC Program Counter Счетчик команд PC Personal Computer см. ПК PCFGw ADC Port ConFiGuration; ADCON1 [2:0] Биты конфигурирования каналов АЦП PCL Program Counter Low byte Счетчик команд, младший байт PCLATH Program Counter LATch High byte Защелка счетчика команд, старший байт PCLATU Program Counter LATch Upper byte (PIC 18XXXX) Защелка счетчика команд, самый старший байт (PIC 18ХХХХ) PD Power Down; STATUS[3] Бит режима пониженного энергопотребления PEIE PEripheral Interrupt Enable; INTCON[6] Бит разрешения прерываний от периферийных устройств PIC Peripheral Interface Controller Контроллер периферийного интерфейса PIPO Parallel-In Parallel-Out Регистр с параллельным входом и параллельным выходом
626 Приложения PI EX Peripheral Interrupt Enable register X Регистр Хразрешения прерываний от периферийных устройств PIRX Peripheral Interrupt Register X Регистр Хфлагов прерываний от периферийных устройств PISO Parallel-In Serial-Out Сдвиговый регистр с параллельным входом и последовательным выходом PORTX PortX Регистр параллельного порта ввода/вывода X PR2 Period Register for Timer 2 Регистр периода Таймера 2 PRNG Pseudo Random Number Generator Генератор псевдослучайных чисел PRODH PRODuct High byte (PIC18XXXX) Регистр произведения (старший байт) PRODL PRODuct Low byte (PIC 18XXXX) Регистр произведения (младший байт) PROM Programmable ROM см. ППЗУ PS« Post/Prescale rate Select; OPTION_REG[2:0] Биты выбора коэффициента деления пост/предделителя PSA Post/Prescale Scaler Assign; OPTION_REG[3] Бит выбора подключения пост/предделителя PWM Pulse Width Modulation см. ШИМ RXw Register X pin п Вывод п порта ввода/вывода X RAM Random Access Memory см. ОЗУ RBIE Register portB Interrupt Enable; INTC0N[3] Бит разрешения прерывания от порта В RBIF Register portB Interrupt Flag; INTCON[0] Флаг прерывания от порта В RBPU Register portB Pull-Up; OPTION_REG[7] Бит включения подтяжки на входах порта В RCIE ReCeive Interrupt Enable; PIE1 [5] Бит разрешения прерывания от приемника USART RCIF ReCeive Interrupt Flag; PIR1[5] Флаг прерывания от приемника USART RCREG ReCeive REGister Буфер приемника USART
Приложение А. Список сокращений, символических имен и аббревиатур и 627 RCSTA ReCeive STAtus Регистр состояния приемника USART RD ReaD; EECON1[0] Бит запуска операции чтения FLASH/EEPROM R/W Read/Write; SSPSTAT[2] ' Бит типа пакета (чтение/запись) в модуле SSP RISC Reduced Instruction Set Computer Компьютер с сокращенным набором команд; см. также CISC ROM Read-Only Memory см. ПЗУ RP« Register Page; STATUS[6:5] Биты выбора страницы памяти данных rtl Register Transfer Language Язык регистровых передач RTS Ready То Send Готовность к передаче (сигнал квитирования в стандарте RS-232) RX Receive Вход приемника USART RX9 ReCeive 9-bit; RCSTA[6] Бит разрешения 9-битного приема USART RTCC Real Time Counter/Clock Часы/счетчик реального времени S Start condition; SSPSTAT[3] Бит обнаружения состояния СТАРТ SAR Successive Approximation Register Регистр последовательного приближения SCI Serial Communication Interface Последовательный коммуникационный интерфейс (USART) SCK Serial ClocK Линия тактового сигнала (протокол SPI) SCL Serial CLock Линия тактового сигнала (протокол 12С) SDA Serial DAta Линия данных (протокол 12С) SDI Serial Data Input Вход данных (протокол SPI) SDO Serial Data Output Выход данных (протокол SPI) SEN Stretch ENable; SSPCON2[0] Разрешение растягивания тактового сигнала 12С
628 Приложения SIPO Serial-In Parallel-Out Сдвиговый регистр с последовательным входом и параллельным выходом SISO Serial-In Serial-Out Сдвиговый регистр с последовательным входом и последовательным выходом SMP SaMPle; SSPSTAT[7] Фаза выборки бита SP Stack Pointer Указатель стека SPBRG Serial Port Baud-Rate Generator Регистр управления контроллером скорости передачи USART SPEN Serial Port ENable; RCSTA[7] Бит разрешения работы последовательного порта (USART) SPI Serial Peripheral Interface Протокол последовательного интерфейса; протокол SPI SPR Special-Purpose Register см. РСН SSP Synchronous Serial Port Синхронный последовательный порт SSPADD SSP ADDress Регистр адреса модуля SSP SSPBUF SSP BUFfer Буферный регистр модуля SSP SSPCON SSP CONtrol Регистр управления модуля SSP SSPCON2 MSSP CONtrol 2 Регистр управления 2 модуля SSP SSPEN SSP ENable; SSPCON[5] Бит включения модуля SSP SSPIE SSP Interrupt Enable; PIE1 [3] Бит разрешения прерывания от модуля SSP SSPIF SSP Interrupt Flag; PIR1 [3] Флаг прерывания от модуля SSP SSPMw SSP Mode; SSPCON[3:0] Биты выбора режима работы модуля SSP SSPOV SSP Overflow; SSPCON[6] Бит переполнения приемника модуля SSP SSPSR SSP Shift Register Сдвиговый регистр модуля SSP SSPSTAT SSP STATus Регистр состояния модуля SSP
Приложение А. Список сокращений, символических имен и аббревиатур 629 STATUS Status Register Регистр состояния микроконтроллера STKPTR STacK PoinTeR (PIC18XXXX) Указатель стека SYNC SYNChronous; TXSTA[4] Включение синхронного режима USART TOCKI Timer 0 ClocK Input Вход внешнего тактового сигнал^ Таймера 0 TOCS Timer 0 Clock Select; OPTION_REC[5] Бит выбора источника тактового сигнала Таймера 0 TOIE Timer 0 Interrupt Enable; INTCON[5] Бит разрешения прерывания от Таймера 0 TOIF Timer 0 Interrupt Flag; INTCON[2] Флаг прерывания от Таймера 0 TOSE TimerO Set Edge; OPTION_REC[4] Бит выбора активного фронта Таймера 0 T1CKI Timer 1 ClocK Input Вход внешнего тактового сигнала Таймера 1 T2CKSw Timer 2 ClocK Source; T2CON [1:0] Биты выбора коэффициента деления предделителя Таймера 2 TOCON Timer 0 CONtrol register (PIC 18XXXX) Регистр управления Таймера 0 T1CON Timer 1 CONtrol register Регистр управления Таймера 1 T2CON Timer 2 CONtrol register Регистр управления Таймера 2 TIG Timer 1 Gate Бит стробирования Таймера 1 T1GPOL Timer 1 Gate input POLarity; TICON[7] Полярность сигнала стробирования Таймера 1 T1SYNC Timer 1 SYNChronize; ТICON[2] Бит синхронизации внешнего тактового сигнала Таймера 1 T1OSCEN Timer 1 OSCillator ENable; T1CON[3] Бит включения тактового генератора Таймера 1 TABLAT TABleLATch (PIC18XXXX) Регистр защелки табличного доступа к памяти программ TMRO TiMeR 0 Счетный регистр Таймера 0 TMR1CS TiMeR 1 Clock Select; TICONf 1 ] Выбор источника тактового сигнала Таймера 1 TMR1H TiMeR 1 High Счетный регистр Таймера 1 (старший байт)
630 Приложения TMR1IE Timer 1 Interrupt Enable; PIE1 [0] Бит разрешения прерывания от Таймера 1 TMR1IF Timer 1 Interrupt Flag; PIR1 [0] Флаг прерывания от Таймера 1 TMR2IE Timer 2 Interrupt Enable; PIE1 [1] Бит разрешения прерывания от Таймера 2 TMR2IF Timer 2 Interrupt Flag; PIR1 [ 1] Флаг прерывания от Таймера 2 TMR1L TiMeR 1 Low Счетный регистр Таймера 1 (младший байт) TMR1ON TiMeR 1 ON; T1CON[0] Бит включения Таймера 1 TMR2ON TiMeR2 ON; T2CON[2] Бит включения Таймера 2 TMR3H TiMeR 3 High (PIC18XXXX) Счетный регистр Таймера 3 (старший байт) TMR3L TiMeR 3 Low (PIC 18ХХХХ) Счетный регистр Таймера 3 (младший байт) TO Watchdog Time Out; STATUS [4] Флаг тайм-аута сторожевого таймера TOUTPSw Timer 2 OUTput Post Scaler; T2CON[3:0] Биты выбора коэффициента деления постделителя Таймера 2 TRISA TRIStateX Регистр направления передачи данных порта X TTL Transistor Transistor Logic см. ТТЛ TTY TeleTYpewriter Телетайп TX Transmit Выход передатчика USART TX9 Transmit 9-bit; TXSTA[6] Бит разрешения 9-битной передачи USART TX9D Transmit 9-th bit Data; TXSTA[0] Значение 9-го бита передаваемого слова данных USART TXEN Transmit ENable; TXSTA[5] Разрешение передатчика USART TXIE Transmit Interrupt Enable; PIE1 [4] Бит разрешения прерывания от передатчика USART TXIF Transmit Interrupt Flag; PIR1 [4] Флаг прерывания от передатчика USART TXREG Transmit data REGister Буфер передатчика USART
Приложение А. Список сокращений, символических имен и аббревиатур 631 TXSTA Transmit STAtus Регистр состояния передатчика USART UA Update slave 10-bit Address; SSPSTATfl ] Флаг обновления адреса ведомого устройства (режим 10-битного z протокола 12С модуля MSSP) UART Universal Asynchronous Receiver Transmitter Универсальный асинхронный приемопередатчик USART Universal Synchronous-Asynchronous Receiver Transmitter Универсальный синхронно-асинхронный приемопередатчик VLSI Very Large-Scale Integration см. СБИС VR/7 Voltage Reference mode; VRCON[3:0] Биты выбора режима работы ИОН VREN Voltage Reference ENable; VRCON[7] Включение ИОН VRCON Voltage Reference CONtrol Регистр управления ИОН VROE Voltage Reference Output Enable; VRCON[6] Управление выходом ИОН VRR Voltage Reference Range; VRCON [5] Выбор диапазона ИОН W Working register Рабочий регистр WCOL Write COLlision; SSPCON[7] Бит конфликта записи модуля SSP WR WRite; EECON1[1] Бит запуска операции записи FLASH/ЕЕ PROM WREC Working REGister (PIC18XXXX) Рабочий регистр в памяти данных WREN WRite ENable; EECON1[2] Разрешение операции записи FLASH/EEPROM WRERR WRite ERRor; EECON1[3] Флаг ошибки записи EEPROM Z Zero flag; STATUS[2] Флаг нуля
Приложение Б РЕГИСТРЫ СПЕЦИАЛЬНОГО НАЗНАЧЕНИЯ МИКРОКОНТРОЛЛЕРОВ PIC16F87XA Адрес Имя Бит 7 Бит 6 Бит 5 Бит 4 БитЗ Бит 2 Бит 1 БитО Сброс no питанию Прочие сбросы БанкО 00 1NDF” Обращение к регистру, адрес которого записан в FSR (не физический регистр) — - 01 TMR0 8-битный таймер/счетчик реального времени xxxx xxxx uuuu uuuu 02 PCL1)3) Младшие 8 бит счетчика команд 0000 0000 0000 0000 03 STATUS1’ 1RP RP1 RPO TO FD _z DC c 0001 1XXX 000? ?uuu 04 FSR” Регистр адреса при косвенной адресации xxxx xxxx uuuu uuuu 05 PORTA — — RA5 RA4 RA3 RA2 RAI RAO --0X 0000 --0U 0000 06 PORTB RB7 RB6 RB5 RB4 RB3 RB2 RBI RB0 xxxx xxxx uuuu uuuu 07 PORTC RC7 RC6 RC5 RC4 RC3 RC2 RC1 RC0 xxxx xxxx uuuu uuuu 08 PORTD2’ RD7 RD6 RD5 RD4 RD3 RD2 RD1 RD0 xxxx xxxx uuuu uuuu 09 PORTE2’ — — — — — RE2 RE1 RE0 -XXX -uuu 0А PCLATH” - - - Буфер записи для 5 старших битов счетчика команд ---0 0000 0 0000 ОВ INTCON” G1E PEIE T01E 1NTE RBIE T01F INTF RBIF 0000 ooox 0000 ooou ОС PIR1 PSPIF2 ADIF RCIF TXIF SSPIF CCP11F TMR21F TMR1IF 0000 0000 0000 0000 0D PIR2 — CMIF — EEIF BCLIF — — CCP21F -R-0 0--0 -R-0 0--0 0Е TMR1L Таймер 1, младший байт xxxx xxxx uuuu uuuu 0F TMR1H Таймер 1, старший байт xxxx xxxx uuuu uuuu 10 T1CON - tickpsi | tickpso |tioscen|tisync |tmrics|tmrion --00 0000 --UU uuuu И TMR2 Таймер 2 0000 0000 0000 0000 12 T2CON TOUTPS3|TOUTPS2| TOUTPS1 |TOUTPSo|TMR20N|T2CKPSl|T2CKPSO -000 0000 -000 0000 13 SSPBUF Буфер приемника MSSP/регистр передатчика xxxx xxxx uuuu uuuu 14 SSPCON WCOL SSPOV SSPEN СКР SSPM3 SSPM2 SSPM1 SSPMO 0000 0000 0000 0000 15 CCPR1L Регистр захвата/сравнения/ШИМ модуля ССР1 (младший байт) xxxx xxxx uuuu uuuu 16 CCPR1H Регистр захвата/сравнения/ШИМ модуля ССР1 (старший байт) xxxx xxxx uuuu uuuu 17 CCP1CON — — CCP1X CCP1Y ССР1МЗ CCP1M2 CCP1M1 CCP1M0 --00 0000 --00 0000 18 RCSTA SPEN RX9 SREN CREN ADDEN FERR OERR RX9D 0000 ooox 0000 ooox 19 TXREG Регистр данных передатчика USART 0000 0000 0000 0000 1А RCREG Регистр данных приемника USART 0000 0000 0000 0000 1В CCPR2L Регистр захвата/сравнения/ШИМ модуля ССР2 (младший байт) xxxx xxxx uuuu uuuu 1С CCPR2H Регистр захвата/сравнения/ШИМ модуля ССР2 (старший байт) xxxx xxxx uuuu uuuu 1D CCP2CON — CCP2X CCP2Y CCP2M3 |CCP2M2|CCP2M1|CCP2MO --00 0000 --00 0000 1Е ADRESH Старший байт результата преобразования АЦП xxxx xxxx uuuu uuuu 1F ADCONO ADCS1 ADCS0 CHS2 CHS1 CHS0 GO - ADON 0000 00-0 0000 00-0
Приложение Б. Регистры специального назначения микроконтроллеров PIC 16F87XA 633 (продолжение) Адрес Имя Бит 7 Бит 6 Бит 5 Бит 4 БитЗ Бит 2 Бит 1 Бит 0 Сброс no питанию Прочие сбросы Банк 1 80 1NDF1’ Обращение к регистру, адрес которого записан в FSR (не физический регистр) - - 81 OPT1ON_ REG 1NTEDG T0CS T0SE PSA PS2 PSI PSO 1111 1111 1111 1111 RBPU 82 PCL1’-3’ Младшие 8 бит счетчика команд 0000 0000 0000 0000 83 STATUS1’ 1RP RP1 RP0 ТО PD DC c 0001 1XXX 000? ?uuu 84 FSR1’ Регистр адреса при косвенной адресации xxxx xxxx uuuu uuuu 85 TRISA Регистр направления передачи данных порта А --11 1111 --11 1111 86 TRISB Регистр направления передачи данных порта В 1111 1111 1111 1111 87 TRISC Регистр направления передачи данных порта С 1111 1111 1111 1111 88 TRISD2’ Регистр направления передачи данных порта D 1111 1111 1111 1111 89 TR1SE2’ 1BF OBF 1BOV PSPMODE TRISE2|TRISE1 TR1SE0 0000 -111 0000 -111 8А PCLATH1’ - - - Буфер записи для 5 старших битов счетчика команд ---0 0000 0 0000 8В INTCON1’ G1E РЕ1Е Т01Е INTE RB1E T01F 1NTF RB1F 0000 ooox 0000 ooou 8С P1E1 PSP1E2’ AD1E RC1E TXIE SSP1E CCP11E TMR21E TMR11E 0000 0000 0000 0000 8D PIE2 — СМ1Е — EEIE BCL1E — — CCP2IE -R-0 0--0 -R-0 0--0 8Е PCON — — — — — — POR BOR --?? --UU 91 SSPCON2 GCEN ACKSTAT ACKDT ACK.EN RCEN PEN RSEN SEN 0000 0000 0000 0000 92 PR2 Регистр периода Таймера 2 1111 1111 1111 1111 93 SSPADD Регистр адреса синхронного последовательного порта (режим 12С) 0000 0000 0000 0000 94 SSPSTAT SMP СКЕ D/A Р S R/W UA BF 0000 0000 0000 0000 98 TXSTA CSRC ТХ9 TXEN SYNC — BRGH TRMT TX9D 0000 -010 0000 -010 99 SPBRG Регистр контроллера скорости передачи USART 0000 0000 0000 0000 9С CMCON C2OUT сюит C2INV C11NV CIS СМ2 CM1 CM0 0000 0110 uuuu uuuu 9D CVREF CVREN CVROE CVRR — CVR3 CVR2 CVR1 CVR0 -000- 0000 uuu- uuuu 9Е ADRESL Результат преобразования АЦП (младший байт) xxxx xxxx uuuu uuuu 9F ADCON1 ADFM PCFG3 PCFG2 PCFG1 PCFG0 0--- 0000 0--- 0000 Банк 2 100 INDF1’ Обращение к регистру, адрес которого записан в FSR (не физический регистр) - - 101 TMR0 8-битный таймер/счетчик реального времени xxxx xxxx uuuu uuuu 102 PCL1’-3’ Младшие 8 бит счетчика команд 0000 0000 0000 0000 103 STATUS1’ IRP RP1 RP0 ТО PD Z DC C 0001 1ХХХ 000? ?uuu 104 FSR1’ Регистр адреса при косвенной адресации xxxx xxxx uuuu uuuu 106 PORTB RB7 RB6 RB5 RB4 RB3 RB2 RBI RB0 xxxx xxxx uuuu uuuu 10А PCLATH1’ - - - Буфер записи для 5 старших битов счетчика команд ---0 0000 ---0 0000 10В INTCON1’ G1E РЕ1Е Т01Е 1NTE RB1E T01F 1NTF RB1F 0000 ooox 0000 ooou ЮС EEDATA Регистр данных EEPROM (младший байт) xxxx xxxx uuuu uuuu 10D EEADR Регистр адреса EEPROM (младший байт) xxxx xxxx uuuu uuuu 10Е EEDATH — — Регистр данных EEPROM (старший байт) xxxx xxxx uuuu uuuu 10F EEADRH — — — Регистр адреса EEPROM (старший байт) xxxx xxxx uuuu uuuu
634 Приложения (продолжение) Адрес Имя Бит 7 Бит 6 Бит 5 Бит 4 БитЗ Бит 2 Бит 1 БитО Сброс по питанию Прочие сбросы Банк 3 180 INDF1’ Обращение к регистру, адрес которого записан в FSR (не физический регистр) - - 181 OPT1ON_ REG RBPU 1NTEDG T0CS TOSE PSA PS2 PS1 PSO 1111 1111 1111 1111 182 PCL1’-3’ Младшие 8 бит счетчика команд 0000 0000 0000 0000 183 STATUS1* 1RP RP1 RPO ТО PD Z DC с 0001 1ХХХ ооо? ?иии 184 FSR1’ Регистр адреса при косвенной адресации хххх хххх ииии ииии 186 TRISB Регистр направления передачи данных порта В 1111 1111 1111 1111 18А PCLATH1’ - - - Буфер записи для пяти старших битов счетчика команд ---0 0000 ---0 0000 18В INTCON1’ G1E PEIE Т01Е 1NTE RB1E T01F INTF RB1F 0000 ооох 0000 ооои 18С EECON1 EEPGD — — — WREER WREN WR RD х--- хооо х--- иооо 18D EECON2 Регистр 2 управления EEPROM (физически не реализован) - - *’ К этому регистру можно обращаться из любого банка. 2) Не реализован в 28-выводных моделях линейки. 3’ Адрес следующей команды, если PIC в «спящем» режиме. Условные обозначения: х Значение неизвестно U Значение не изменяется R Зарезервировано, всегда необходимо записывать О ? Значение зависит от условий сброса — Не реализовано, читается как О
Приложение В ЭЛЕМЕНТЫ ЯЗЫКА СИ Операции, приоритет и ассоциативность Операция Действие Пример Наивысший приоритет Направленность действия (ассоциативность) => 0 Вызов функции sqr() [] Элемент массива х[6] Элемент структуры PIA1.CRA -> Элемент структуры по указателю PIA1->CRA Унарные операторы Направленность действия (ассоциативность) <= t Логическое отрицание !х Инверсия (обратный код) ~х — Изменение знака у = -х + Унарный плюс у = X - +(y+z) ++ Инкрементирование х++ или ++х — Декрементирование X ИЛИ X & Определение адреса &х * Обращение по адресу *адрес (тип) Преобразование типа (long)x sizeof Определение размера в байтах sizeof х Арифметические операции Направленность действия (ассоциативность) => * Умножение z = х*у / Деление z = x/y % Остаток от деления (деление по модулю) z = x%y (только целые типы) + Сложение z = х+у - Вычитание z = х-у Операции сдвига Направленность действия (ассоциативность) => >> Сдвиг влево z = х»3 << Сдвиг вправо z = x<<3 Операции отношения Направленность действия (ассоциативность) => < Меньше, чем while(x < 3) <= Меньше или равно while(x <= 3)
636 Приложения (продолжение) Операция Действие Пример > Больше, чем while(x > 3) >= Больше или равно while(x >= 3) == Равно while(x =— 3) 1= Не равно while(x != 3) Побитовые операции Направленность действия (ассоциативность) => & Побитовое И x & OxFE (Сброс 0-го бита) А Побитовое Исключающее ИЛИ х л 0x01 (Инвертирование 0-го бита) 1 Побитовое ИЛИ х 10x01 (Установка 0-го бита) Логические операции Направленность действия (ассоциативность) => && Логическое И х && у истина, если истинны оба операнда II Логическое ИЛИ х || у истина, если истинен хотя бы один из операндов Условная операция х = (у > z)?5:10 х = 5, если у > z, иначе х = 10 Операции присваивания Направленность действия (ассоциативность) <= = Простое присваивание х = 3 += Присваивание со сложением х += 3 (х = х + 3) -= Присваивание с вычитанием х -= 3 (х = х - 3) *— Присваивание с умножением х *= 3 (х = х * 3) /= Присваивание с делением х/=3(х = х/3) %= Присваивание с делением по модулю х%=3(х = х%3) &= Присваивание с побитовым И х &= 3 (х = х & 3) л= Присваивание с побитовым Исключающее ИЛИ х Л= 3 (х = х Л 3) 1= Присваивание с побитовым ИЛИ х |= 3 (х = х | 3) <<= Присваивание со сдвигом влево х <<= 3 (х = х « 3) >>= Присваивание со сдвигом вправо х »= 3 (х = х » 3) Направленность действия (ассоциативность) => > Последовательное выполнение if(x = 0, у = 3; х < 10, х++) Низший приоритет
Приложение Г НАБОР КОМАНД МИКРОКОНТРОЛЛЕРОВ С 14-БИТНЫМ ЯДРОМ 14-битный код команды Команда Мнемоника Адресат Изм. флаги Операции W F Z D с 11 1110 LLLL LLLL Сложение константы c W addlw LL л/ л/ л/ л/ w <- w + #LL 00 0111 dfff ffff Сложение W и регистра addwf f,d V л/ V V V d <- w + f 11 1110 LLLL LLLL Побитное «И» константы и W andlw LL у! л/ • • w <- w • #LL 00 0101 dfff ffff Побитное «И» XV и регистра andwf f,d V л/ • • d <- w • f 01 OOnn nfff ffff Сброс л-го бита в регистре bcf f,n у1 • • • fn <- 0 01 Olnn nfff ffff Установка л-го бита в регистре bsf f,n у! • • • fn <- 1 01 lOnn nfff ffff Пропуск, если л-й бит регистра сброшен btfsc f,n • • • PC++, если fn = = 0 01 linn nfff ffff Пропуск, если л-й бит регистра установлен btfss f,n • • • РС++,- если fn == 1 10 Oaaa aaaa aaaa Вызов подпрограммы call aaa • • • TOS <- PC, PC <- aaa 00 0001 Ifff ffff Очистка регистра clrf f • • f <- 00 00 0001 0000 0011 Очистка XV clrw л/ • • w <- 00 00 0000 0000 0100 Сброс сторожевого таймера clrwdt • • • wdt <- 00 00 1001 dfff ffff Инвертирование регистра comf f,d л/ V • • d <- f 00 0011 dfff ffff Декрементирование регистра decf f,d л/ л/ у/ • • d <- f-- 00 1011 dfff ffff Декрементирование регистра и пропуск, если 0 decfsz f,d V V • • • d <- f-~, PC++, если f == 0 10 laaa aaaa aaaa Безусловный переход goto aaa • • • pc <- aaa 00 1010 dfff ffff Инкрементирование регистра incf f,d л/ л/ л/ • • d <- f++ 00 1111 dfff ffff Инкрементирование регистра и пропуск, если 0 incfsz f,d у! л/ • • • d <- f++, PC++, если f == 0 11 1000 LLLL LLLL Побитовое «ИЛИ» константы и W iorlw LL л/ • • w <- w v #LL 00 0100 dfff ffff Побитовое «ИЛИ» XV и регистра iorwf f,d л/ V • • d <- w v f
(продолжение) 14-битный код команды Команда Мнемоника Адресат Изм. флаги Операции XV F Z D с 00 1000 dfff ffff Пересылка регистра movf f,d л/ л/ л/ • • d <- f 11 0000 LLLL LLLL Пересылка константы в XV movlw LL л/ • • • w <- #LL 00 0000 Ifff ffff Пересылка W в регистр movwf f л/ • • • f <- w 00 0000 0000 0000 Нет операции nop • • • — 11 0100 LLLL LLLL Возврат из подпрограммы; константа в XV retlw у/ • • • w <- #LL, PC <- TOS 00 0000 0000 1000 Возврат из подпрограммы return • • • PC <- TOS 00 0000 0000 1001 Возврат из прерывания retfie • • • GIE <- 1, PC <- TOS 00 1101 dfff ffff Циклический сдвиг регистра влево rlf f,d J л/ • • ь7 —| C |—~j 7 Регистр 0 00 1100 dfff ffff Циклический сдвиг регистра вправо rrf f,d л/ • • Ьо 7 Регистр 0 |~*4 C |— 00 0000 0110 0011 Переход в спящий режим sleep • • wdt <- 0, такт, сигнал выкл. 11 1100 LLLL LLLL Вычесть XV из константы sublw LL у/ л/ w <- #LL - w 00 0010 dfff ffff Вычесть XV из регистра subwf f,d V у/ у/ d <- f - w 00 1110 dfff ffff Поменять местами полубайты регистра swapf f,d л/ • • d <- f [7:4] <-> f[3:0] 11 1010 LLLL LLLL Побитовое «Искл. ИЛИ» константы и XV xorlw LL 'J • • w <- w @ #LL 00 0110 dfff ffff Побитовое «Искл. ИЛИ» XV и регистра xorwf f,d л/ у/ • • d <- w @ f Приложения Условные обозначения: V Воздействует на флаг TOS Вершина стека d Адресат; 0 = w, 1 = f ++ Инкреме нтирование LL Значение константы # Константа wdt Сторожевой таймер/предделитель ааа Адрес PC++ Пропуск следующей команды fn n-й бит регистра GIE Бит глобального разрешения прерываний W Рабочий регистр • Не воздействует на флаг == Условие равенства f Регистр — Декрементирование PC Счетчик команд
предметный указатель 7-сегментный индикатор — 182, 361, 369, 598 N А AND см. Операция, И (AND) ASCII см. Код, ASCII NAND см. Операция, И-НЕ (NAND) NOR см. Операция, ИЛИ-HE (NOR) NOT см. Операция, НЕ (NOT) В Baud rate см. Скорость передачи BCD см. Код, двоично-десятичный (BCD) Bi-quinary см. Код, двоично-десятичный (BCD), сдвоенный пятизначный код Brownout см. Сброс, по снижению питания О OR см. Операция, ИЛИ (OR) Р PC см. Счетчик команд (PC) Power Down см. Спящий режим D D-защелка см. Защелка, D-типа D-триггер см. Триггер, D-типа R RS-232 см. Интерфейс, RS-232 RS-422 см. Интерфейс, RS-422 RS-423 см. Интерфейс, RS-423 Е EEPROM см. ЭСППЗУ EEPROM-память данных см. Модуль EEPROM EPROM см. СППЗУ RS-485 см. Интерфейс, RS-485 RS-триггер см. Триггер, RS-типа RTL см. Язык регистровых передач S F Fuse см. Бит конфигурации Space — 432 т н Handshake см. Квитирование Т-триггер см. Триггер, Т-типа и I 12С АСК — 395 NACK — 395 адрес — 397 состояние СТАРТ — 395 состояние СТОП — 395 UART см. Модуль UART USART см. Модуль USART W Wсм. Регистр, рабочий (W) X XNOR см. Операция, Исключающее ИЛИ-НЕ м Mark — 432 (XNOR) XORсм. Операция, Исключающее ИЛИ (XOR) MPLAB — 262, 286, 615
640 Предметный указатель Автоматические переменные см. Язык Си, переменные, автоматические Адресация абсолютная, памяти программ — 117 битовая — 128 кодом команды — 116 константы — 69, 94, 116 косвенная, памяти данных — 100, 123, 578 прямая, памяти данных — 68, 94, 100, 118 Аккумулятор — 48 Аналоговый компаратор см. Модуль компаратора Аналого-цифровое преобразование (АЦП) — 488 Арифметико-логическое устройство (АЛУ) — 39, 57, 93, 575 Архитектура — 58 гарвардская — 59, 80, 89, 572 фон-неймановская — 58 Ассемблер — 70, 238 абсолютный — 240 директива — 105, 240 «define — 244, 359, 401 ___config — 315, 452, 556, 607 bankisel — 272 banksei — 271 cblock — 243, 267 code — 255, 267 da — 569 de — 549, 607 dw — 550, 553 end — 244, 268 equ — 105, 240, 267 extern — 257, 268 global — 257, 268 high — 203, 569 if — 402 include — 241, 268 local — 251, 270 low — 203, 569 macro — 251, 268 org — 202, 216, 244, 267 pagesel — 273 radix — 553 res — 257, 268 udata — 255, 257, 268 udata_ovr — 255, 257, 268 комментарий — 70, 245 макрокоманда — 251, 401, 420 Addf— 268 Вес — 269 Bne — 252 Countdown — 270 Delay_lms — 251 Delay_600 — 401 Delay_cycles — 270 Exgwf — 269 Movlf— 270 метка — 105, 126, 154, 245 $ — 267 арифметические операции — 267 основание двоичное — 267 десятичное — 267 шестнадцатеричное — 267 перемещаемый — 253 язык — 240 АЦП см. Модуль АЦП Б Байт — 19 Банк — 98 Банк быстрого доступа — 577 Бит — 17 ADCS/? (ADCONO[7:6]) — 511, 519, 521 ADDEN (RCSTA[3]) — 427 ADFM (ADCON1[7]) — 514 ADIE (INTCON[6]) — 221, 518 ADIE (PIE[6J) — 583 ADIF (ADCONOfl]) — 221 ADIF (PIR1 [6]) — 505, 519, 539, 583 ADIP (IPR1[6]) — 583 ADON (ADCON0[0]) — 509 BF (SSPSTATfO]) — 383, 389, 391, 409 BORPCONfO]) — 321 BRGH (TXSTA[2J) — 428 BSRw (BSR[3:0]) — 576 C1INV(CMCON[4]) — 498 C1OUT (CMCON[6]) — 498 C21NV(CMCON[5]) — 498 C2OUT (CMCON[7]) — 498 CCP1IE (Р1ЕЦ2]) — 469, 471 CCP1 IF(PIR1[2]) — 469, 471 CCP2IE (P1E2[OJ) — 469 CCP2IF (PIR2[0]) — 469 CHS/? (ADCONO[5:3]) — 512, 517, 521 CIS (CMCON[3]) — 499, 534 CKE(SSPSTAT[6]) — 386, 411 CKP (SSPCON[4]) — 386, 411, 412 CMIE (PIE2[6]) — 498 CMIE (PIR2[6]) — 498 CMIF (PIR2[6]) — 502, 535 CM/? (CMCON[2:0]) — 496 CREN (RCSTA[4]) — 427 CVREN (CVRCON[7J) — 499 CVRw (CVRCON[3:0]) — 499 CVROE (CVRCON[6]) — 501
Предметный указатель 641 CVRR (CVRC0N[5]) — 500 D/A(SSPSTAT[5]) — 410 EEIE (INTC0N[6]) — 221 EEIE(PIE1[7]) — 546 EEIE (PIE2[4]) — 552 EEIF(EECON1[4]) — 107, 221 EEIF (PIR1[7]) — 546 EEIF (PIR2[4]) — 552 EEPGD (EECON1[7]) — 552 GBPU (0PTI0N_REG[7]) — 341 GCEN (SSPCON2[7J) — 410, 413 GIE (INTC0N[7]) — 211, 214, 216, 221, 294, 308, 412, 458, 466, 502, 518, 519, 539, 546, 583 GIEH (INTC0N[7]) — 584 GIEL (INTC0N[6]) — 583 GO/DONE (ADCONO[2]) — 516, 518, 521, 539 INTE (INTCON[4J) — 213, 225, 294, 460 INTEDG (OPTION_REG[6]) — 212 INTF (INTCON[1J) — 212, 216, 218, 227, 230 I PEN (RCON[7]) — 584 IRP (STATUS[7]) — 127, 272, 317 OERR (RCSTA[1]) — 427, 429 P (SSPSTAT[4]) — 410 PCFGn (ADCON1[3:0]) — 513, 521 PD (STATUS[3]) — 96, 308, 319, 454 PD (STATUS[4]) — 319 PEIE (INTCON[6]) — 222, 412, 427, 502, 539 POR(PCON[1]) — 317 PSA (OPTION_REG[3]) — 102, 452, 456 PS« (OPTION_REG[2:0]) — 102, 103, 452, 454 R/W (SSPSTAT[2]) — 410 RBIE (INTCON[3]) — 348 RBIF(INTCON[0]) — 348 RBPU (OPT1ON_REG[7]) — 341, 347 RCIE (PIE1[5]) — 427 RC1F (PIR1[5]) — 222, 427 RD (EECON1[0]) — 107, 546, 552 RPO (STATUS[5]) — 96, 98, 118, 217, 224, 271, 317, 327 RP1 (STATUS[6]) — 119, 224, 271, 317 RX9(RCSTA[6]) — 427 S (SSPSTAT[3]) — 410 SEN (SSPCON2[0]) — 411, 412 SMP (SSPSTAT[7]) — 386 SPEN (RCSTA[7]) — 426 SSPEN (SSPCON[5]) — 384, 409 SSPIE (PIE1[3]) — 389, 412 SSPIF (PIR1 [3]) — 389, 409 SSPMm (SSPCON[3:0]) — 384, 409 SSPOV(SSPCON[6]) — 386, 389, 410 SYNC (TXSTA[4]) — 426 TOCS (OPTION_REG[5]) — 103, 455 TOIE (INTCON[5]) — 456, 458 TOIF(INTCON[2]) — 102, 456 TOSE (OPTION_REG[4]) — 103, 455 TlCKPSw (T1CON[5:4J) — 464 T1GINV(T1CON[7]) — 466 T1OSCEN (T1CON[3J) — 464 T1SYNC (T1CON[2J) — 464 T2CKPS« (T2CON[1:0]) — 474 TMR1CS (T1CON[1]) — 464 TMR1GE (T1CON[6]) — 466 TMR1IE (PIE1[O]) — 464, 467 TMR1IF (PIR1[O]) — 462, 464 TMR1ON (T1CON[0]) — 463 TMR2IE (P1E1[1]) — 473 TMR2IF (PIR1[1]) — 473, 479 TMR2ON (T2CON[2]) — 474 TO (STATUS[4]) — 96, 102, 308, 317, 321, 453, 457, 563 TOUTPSm (T2CON[5:2]) — 474 TX9 (TXSTA[6J) — 426 TXEN (TXSTA[5J) — 426 TXIE(PIE1[4]) — 426 TXIF (PIR1[4]) — 426, 429 UA(SSPSTAT[1]) — 410 VREN (VRCON[7]) — 502 WCOL(SSPCON[7]) — 386, 390, 410, 413 WR(EECON1[1]) — 107 WREN (EECON1[2]) — 107 WRERR (EECON[3]) — 547 WRERR(EECON1[3]) — 107 знаковый — 23 управления страницами — 118 Бит конфигурации — 313, 556 BODEN — 314, 320 СР — 313, 557 CPD — 314, 557 СРи — 556 DEBUG — 314 FOSCm — 313 LVP — 314 MCLRE — 319 PWRTE — 313, 318, 321 WDTE — 313, 452 WRT — 314, 556 WRT« — 557 задание в Си — 316 программирование — 615 режим высоковольтного программирования — 312 режим низковольтного программирования — 312, 607 Блок выборки — 90, 572 Блок-схема — 82
642 Предметный указатель Булева алгебра — 26 Буфер с тремя состояниями — 33, 34, 333 Бюджет ресурсов — 78 Быстрый стек см. Стек, быстрый В Вектор — 254 высокоприоритетное прерывание — 584 низкоприоритетное прерывание — 583 прерывания — 211, 216, 219, 295, 458, 608 сброса - 92, 216, 225, 256, 317, 319, 321, 453, 575, 608 Вентиль — 27 Витая пара — 434 Время выполнения команды — 109, 155, 173 Входной порт см. Параллельный ввод/вывод Выборка — 503 Выборка и исполнение — 62, 70 Вывод AN» — 304, 503, 513 C1OUT — 498 C2OUT — 498 ССР1 — 468, 476 ССР2 — 468, 476 СК — 426 DT — 426 GP3 — 337, 339, 341 INT — 210, 228, 460, 486 INT» — 583 MCLR — 305, 312, 316, 326, 596 OSC1 — 93, 305, 309, 323, 326, 599 OSC2 — 93, 309, 326, 599 RA4 — 337, 339 RA» — 104, 152, 229 RB» — 98, 104, 210, 312 RC» — 384, 406 RX — 426 SCK — 384 SCL — 406 SDA — 406 SDI — 384 SDO — 384 SS — 384, 389 T0CKI — 101, 455, 457, 486 T1CKI — 462 T1G — 463 ТХ — 426 Выход двухтактный — 32 открытый коллектор — 32, 33, 339, 341, 396 открытый сток — 32, 339, 341 с тремя состояниями — 33, 332 Выходной порт см. Параллельный ввод/вывод Гарвардская архитектура см. Архитектура, гарвардская Генератор RC — 309, 322 кварцевый — 309 режимы — 309 Генератор см. Тактовый сигнал Глобальные данные см. Язык Си, данные, глобальные Глобальные объявления — 188 д ' Данные — 57 Двоично-десятичный код см. Код, двоично- десятичный (BCD) Дополнительный код двбичное число — 23 деление сдвигом — 25 знак — 23 переполнение — 24, 29, 39, 575 десятичное число — 22 Дуплексный обмен — 376 3 Загрузчик — 240, 249 Заем — 21, 95, 138 Защелка D-типа — 44 Защита кода см. Память программ, защита кода Знаковый бит см. Дополнительный код, двоичное число, знак И И см. Операция, И (AND) ИЛИ см. Операция, ИЛИ (OR) ИЛИ-HE см. Операция, ИЛИ-HE (NOR) Инвертор, программируемый см. Программируемый инвертор И-НЕ см. Операция, И-НЕ (NAND) Интерфейс RS-232 — 432 RS-422 — 434 RS-423 — 432 RS-485 — 434, 435 несимметричный — 432 симметричный — 434 Исключающее ИЛИ см. Операция, Исключающее ИЛИ (XOR) Исключающее ИЛИ-HEcjw. Операция, Исключающее ИЛИ-HE (XNOR) Исполнительный блок — 93, 575 Исполняемый код см. Машинный код Исходный код см. Файл, исходный код
Предметный указатель 643 К Квантование — 489 Квитирование — 330, 395 Ключ бистабильный — 43 Код 7-сегментный — 183 ASCII — 16, 18, 267 двоично-десятичный (BCD) — 20, 95, 233 сдвоенный пятизначный код (bi-quinary) — 157 упакованный — 111, 165, 231, 590 двоичный — 16 взвешенный — 18 невзвешенный — 18 десятичный — 16 унарный — 352, 610 шестнадцатеричный — 20 Код операции (КОП) — 40, 50, 68, 589 Команда — 57 addlw — 67, 69, 116, 132, 139 addwf — 68, 94, 116, 118, 132 addwfc — 589 andlw — 144 andwf— 143 be — 592 bcf — 96, 99, 128, 137, 289, 335 bne — 592 bnn — 593 bra — 592 bsf — 96, 99, 128, 138, 289 btfsc — 128, 134, 144, 154 btfss — 128, 137, 155, 289 btg — 99, 591 call — 117,170, 173, 272, 584, 592 clrf — 96, /22, 133} 135 clrw — 133, 584 clrwdt — 102, 309, 452, 456, 458 comf— 142 epfseq — 593 epfsgt — 593 cpfslt — 593 daw — 590 defsnz — 594 decf— 136, 590 decfsz — 155, 271, 591 goto — 72, 112, 117, 153, 173, 272, 592 incf — 134, 136, 590 inefsz — 156 infsnz — 594 iorlw — 145 iorwf— 145 Ibsr — 576, 589 Ifsr — 578, 589 movf — 67, 68, 110, 112, 130, 217, 577 movff — 580, 589 movlw — 99, 129 movwf— 67, 129 mullw — 575 mulwf— 575, 580 negf — 590 nop — 152, 156, 308, 331, 401, 421, 552, 589, 591 rcall — 592 retfie — 211, 218, 227, 584 retlw — 173, 183, 311 return — / 73, 584, 592 rlcf— 592 rlf — 150, 352 rlncf — 592 rref— 592 rrf — 147 rrnef — 592 setf— 592 sleep — 96, 214, 286, 308, 310, 453, 499, 518, 538 subfwb — 590 sublw — 139 subwf— 138, 139 subwfb — 590 swapf— 131, 217 tblrd — 185, 572 tblwt — 572 tris — 335 tstfsz — 594 xorlw — 147, 416 xorwf— 146 двухсловная — 589 условного перехода — 592 чтение-модификация-запись — 138, 335, 581 компилятор — 276 Компоновщик — 253 Компьютер — 57 Конвейер — 70, 92, 93, 380, 572 очистка — 109, 155 сброс — 92, 154 Л Логическая единица— 17 Логические схемы комбинационные — 43 последовательностные — 43 Логический нуль — 17 Ложная частота — 493 Локальные переменные — 180 м Макрокоманда см. Ассемблер, макрокоманда Машинный код — 67, 239, 315
644 Предметный указатель Машинный цикл — 93 Метод последовательного опроса — 208 Микроконтроллер — 10, 72 14500 — 80 6801 — 79 6805 — 80, 273 68НС11 — 79, 108 68НС12 — 79 68НС16 — 79 PIC10FXXX — 87, 325 PIC12С508/9 — 348 PIC12F629 — 305, 320 модуль EEPROM — 543 тактирование — 311 P1C12F675 — 305, 320, 466 модуль BOR — 320 модуль EEPROM — 543 модуль АЦП — 509 модуль компаратора — 496, 502 Таймер 1 — 466 тактирование — 311 PIC16C5XX — 454 Р1С16С71 — 221, 505 PIC16C74 — 218, 572 Р1С16С74В — 616 Р1С16С83/4 — 88 PIC16C84 — 615 PIC16C924 — 325 Р1С16СХХХ— 87, 454 PIC16F627 — 88, 119, 123, 127, 218, 222, 254, 271, 283, 292, 304, 596 модуль BOR — 321 память данных— 121 память программ — 91 P1C16F628 — 88, 98, 100, 103, 117, 119, 127, 218, 222, 271, 304 память данных— 121 PIC16F62X — 304 модуль EEPROM — 543 PIC16F648 — 304 модуль EEPROM — 543 P1C16F673 — 509 P1C16F73 — 88 PIC16F74 — 272 PIC16F83 — 88 PIC16F84 — 88, 212, 218, 300, 323, 596 память данных — 120 порт А — 337 слово конфигурации — 313 P1C16F84A— 240, 323, 332 PIC16F873 — 302 PIC16F874 — 88, 103, 210, 303, 596 модуль АЦП — 520 P1C16F876 — 272, 302 память программ — 91, 550 P1C16F877 — 88, 103, 104, 117, 173, 210, 303, 349, 596 память программ — 91, 550 P1C16F877A — 414 P1C16F87X — 185, 224, 302, 304, 321, 332, 429 модуль EEPROM — 543 модуль АЦП — 510 память программ — 550 порты ввода/вывода — 326 слово конфигурации — 312 тактирование — 311 P1C16F87XA — 411 компаратор — 496, 498 ламять программ — 557 слово конфигурации — 315, 557 P1C16LF87X — 304 Р1С17С42 — 57/ Р1С18СХХХ— 571 PIC18F242 — 572 PIC18F252 — 572 P1C18F442 — 572 P1C18F452 — 572 PIC18F8720 — 583 PIC18FXX2 — 572 Р1С18ХХХХ — 87, 252, 269, 272, 465, 468 Микропроцессор — 74 4004 — 19, 73 6502 — 75 6800 — 74 68008 — 76 6802 — 74 6809 — 74 8008 — 74 8080 — 74 8085 — 74 8086 — 76 8088 — 76 Микросхема 24ХХХ (12С EEPROM) — 439, 449, 542 27С64 (СППЗУ) — 41 6264 (ОЗУ) — 55 74НСТ164 (8-битный сдвиговый S1PO- регистр) — 370, 598 74НСТ595 (стробируемый SlPO-регистр) — 373, 374 74LS00 (4 элемента 2И-НЕ) — 31 74LS138 (Дешифратор) — 35 74LS139 (Сдвоенный дешифратор) — 35, 54 74LS148 (Приоритетный шифратор) — 36 74LS244 (Сдвоенный 4-битный буфер с тремя состояниями) — 34 74LS283 (4-битный сумматор) — 38
Предметный указатель 645 74LS373 (8-битный параллельный регистр- защелка) — 46 14\£ЗЛ1 (8-битный параллельный регистр) — 46 74LS382 (АЛУ) — 39 74LS670 (Регистровый файл) — 55 74LS688 (8-битный компаратор) — 36 74LS74 (2 D-триггера) — 46 DS18S20 (Цифровой термометр) — 444 МАХ233 (Сдвоенный приемопередатчик RS-232) — 435 МАХ485 (Приемопередатчик RS-485) — 435 МАХ505 (Счетверенный ЦАП) — 525 МАХ506 (Счетверенный ЦАП) — 525, 541 МАХ518 (Сдвоенный ЦАП) — 398 МАХ549А (Сдвоенный ЦАП) — 378 К531 ИД 14 (Сдвоенный дешифратор) — 35, 54 К537РУ17 (ОЗУ) — 55 К555АП5 (Сдвоенный 4-битный буфер с тремя состояниями) — 34 К555ИВ1 (Приоритетный шифратор) — 36 К555ИД7 (Дешифратор) — 35 К555ИМ6 (4-битный сумматор) — 38 К555ИР22 (8-битный параллельный регистр- защелка) — 46 К555ИР26 (Регистровый файл) — 55 К.555ИР27 (8-битный параллельный регистр) — 46 К555ЛАЗ (4 элемента 2И-НЕ) — 31 К555ТМ2 (2 D-триггера) — 46 К573РФ4/6 (СППЗУ) — 41 Многопроцессорные системы — 391 Модуль ССР — 462, 468, 583 расширенный — 479 режим ШИМ — 468, 476 режимы захвата — 468, 469, 484 режимы сравнения — 468, 471, 481 Модуль EEPROM — 543, 607 запись — 546 чтение — 546 Модуль MSSP см. Модуль ведущего синхронного последовательного порта (MSSP) Модуль SSP см. Модуль синхронного последовательного порта (SSP) Модуль UART — 423 Модуль USART — 424 Модуль АЦП — 503 тактовый сигнал — 511 Модуль ведущего синхронного последовательного порта (MSSP) — 382, 406 Модуль захвата/сравнения/ШИМ — 468 Модуль компаратора — 495, 534 Модуль опорного напряжения компаратора — . 499, 534 Модуль последовательного коммуникационного интерфейса (SCI) — 424 Модуль синхронного последовательного порта (SSP) — 382 Модульное программирование — 168, 169 Монтажное ИЛИ — 33 н Нагрузочная линия — 353 Наложение спектров — 495 защита — 493, 529, 534 Натуральный дешифратор — 35 НЕ см. Операция, НЕ (NOT) Нечетные двоичные числа — 167 О Обнаружение ошибок контроль по четности — 29, 167, 420, 427 контрольная сумма — 166, 250 сдвоенный пятизначный код — 158 Обработка прерываний см. Прерывание, обработка Обработчик прерывания — 171 Обратный код см. Операция, дополнение Объектный колем. Файл, объектный код Операционная система (ОС) — 278 Операция арифметическая — 131 битовая — 137 вычитание — 21, 132, 138 в дополнительном коде — 590 декрементирование — 136 в дополнительном коде — 590 деление — 25 дополнение — 142 W — 143, 147 И (AND) — 27, 143, 289, 377 ИЛИ (OR) — 27, 144, 289, 377 WIH-HE(NOR) — 28, 43 И-НЕ (NAND) — 27, 31, 44 инкрементирование — 136 в дополнительном коде — 590 Исключающее ИЛИ (XOR) — 28, 145, 269 Исключающее ИЛИ-HE (XNOR) — 29, 36 многобайтный сдвиг — 75/ НЕ (NOT) — 26 пересылка — 128 проверка на ноль — 67, 130, 141, 143, 594 пропуск — 64, 152 сдвиг — 147 сдвиг влево арифметический — 25 сдвиг влево логический — 25 сдвиг вправо арифметический — 26 сдвиг вправо логический — 26
646 Предметный указатель сложение — 21, 132 в дополнительном коде — 590 сравнение — 139, 593 умножение — 25, 580 реализация сдвигом и сложением — 186 Опрос — 209 Открытый коллектор см. Выход, открытый коллектор Открытый сток см. Выход, открытый сток Ошибка синтаксическая — 251 четности — 448 п Память EEPROM см. ЭСППЗУ EPROM см. СППЗУ данных — 58 ОЗУ — 55 ПЗУ — 40 ППЗУ — 41 программ — 59 СППЗУ — 41 энергонезависимая — 106 Память данных — 61, 66, 89, 97, 575 переключение банков — 68, 118, 217, 225, 327, 330, 554, 575 состояние после сброса — 317 Память программ — 61, 66, 90, 103, 112, 117, 250, 272, 550, 574 защита кода — 313, 556, 615 Параллельный ввод/вывод — 104, 311, 325, 581 выходной каскад — 340 порт В — 98, 104, 106 внутренняя подтяжка — 341, 596 порт С — 337 порт D — 337 порт Е — 326, 337, 512 порт GP — 337 GP3 — 337, 339 портА— 104, 106, 152, 512, 596 RA4 — 337, 339 Расширение портов — 349 Параллельный порт — 325 Переключатель интерфейс — 340 подавление дребезга — 44, 205, 603 Переключение банков см. Память данных, переключение банков Перенос — 21, 95 Периферийный интерфейс, параллельный ввод/вывод см. Параллельный ввод/вывод Периферийный интерфейсный контроллер (Р1С) — 80 Персональный компьютер — 75 Подавление дребезга см. Переключатель, подавление дребезга Подпрограмма— 168, 171 вложенная — 174, 211, 603 обработки прерывания — 171 рекурсивная — 174 Полубайт — 19 Полудуплексный обмен — 376 Порт X см. Параллельный ввод/вывод, порт X Последовательный ввод/вывод — 370 1 -Wire — 443, 448 12С — 393 SPI — 378, 606 асинхронный — 418, 486 Последовательный порт см. Последовательный ввод/вывод Прерывание — 209 внешнее — 210, 583 задержка — 211 запрос — 210 изменение порта В — 212 изменение порта GPIO — 348 маска — 211, 214 многобайтные значения — 220 обработка — 210 от EEPROM — 107 от модуля ССР — 469 от модуля USART — 426, 427 от модуля АЦП — 505, 583 от модуля компаратора — 498, 536 от Таймера 0 — 456, 458 переключение контекста — 218 приоритет — 583 процедура обработки прерывания — 210, 412, 599 на языке Си — 295, 487, 532 сохранение контекста — 217, 227, 414 флаг — 212 Приоритетный шифратор — 36 Программа — 57, 83 MSSP в режиме 12С для сбора данных — 415 USART — 430 Асинхронный последовательный ввод/вывод — 421, 431 Возведение в квадрат — 258, 553 Вывод по SP1 — 604 высокоприоритетная — 210 Вычисление квадратного корня — 198, 200, 259, 291 Вычисление среднего — 164 Вычисление среднеквадратичного значения — 255, 293 Генератор псевдослучайных чисел — 297
Предметный указатель 647 Декрементирование 2-байтного значения — 157 Деление — 161 Дефибриллятор — 536 Дешифратор 7-сегментного индикатора — 184, 202, 604 Задержка — 175, 195, 205, 251, 359 независимая от тактовой частоты — 359 Запись на шину SPI — 371, 372 Инкрементирование BCD-числа — 111, 233, 591 Интерфейс с клавиатурой — 344, 345 Клавиатура — 346, 359 Компаратор — 355, 522 Обнаружение ошибок в сдвоенном пятизначном коде — 158 Обработчик внешнего прерывания — 215 Определение пикового значения сигнала — 531 Определение позиции бита — 149 Очистка массива — 123, 125 Параллельный обмен с квитированием — 330 Передача по последовательному каналу — 293 Перемножение массивов — 580 Преобразование двоичного числа в двоично- десятичное — 159, 196, 604 программный стек — 201 Работа с ЦАП МАХ549А (SPI) — 381 Сложение 2-байтных чисел — 135 Сложение 3-байтных чисел — 590 Умножение — 162, 192 Управление шаговым двигателем — 358 фоновая — 210, 216, 412 Часы реального времени — 231 Чтение из модуля АЦП — 516 в спящем режиме — 520 Чтение по шине SPI — 377 Программатор — 312, 615 Программируемый инвертор — 29, 38 Программное обеспечение — 57, 61 Р Рабочий регистр см. Регистр, рабочий (W) Растягивание тактового сигнала — 395 Расширение знака — 25 Регистр — 46, 98 ADCONO — 221, 509, 517 ADCON1 — 509, 513, 515 ADRESH — 514 ADRESL — 514 ANSEL — 509 BSR — 576 CCPR1H — 469, 471 CCPR1L — 469, 471 CMCON — 285, 496, 535 CVRCON — 499 счет — 53 EEADR — 106, 107, 545, 552 EEADRH — 550 EECON1 — 106, 107, 214, 219, 221, 545, 552 EECON2 — 106, 108, 545, 552 EEDATA — 106, 107, 545, 552 EEDATH — 550 FSR — 101, 123, 189, 191, 233, 402, 558 FSRO — 578 FSR1 — 578 FSR2 — 578 INDF — 101, 123, 189 INDFi — 578 INTCON — 102, 108, 207, 583 LATX — 581 OPTION REG — 102, 103, 212, 341, 347 OSCAL — 311 PCL — 104, 202, 575 PCLATH — 104, 117, 173, 202, 272, 285, 575 состояние после сброса — 317 PCLATU — 575 PCON — 317 PI El — 222, 426 PIE2 — 224, 469, 552 PIR1 — 222, 426, 462 PIR2 — 224, 469, 552 PLUSWi — 578 PORTB — 212 PORTx — 106 POSTDECi — 578 POSTINCi — 578 PR2 — 473, 477 PREINCi - 578 PRODH — 428, 575, 580 PRODL — 575, 580 RCREG — 426 RCSTA — 426 SPBRG — 428 SSPADD — 407, 410 SSPBUF — 383, 389 SSPCON — 384, 408 SSPCON2 — 409 SSPSTAT — 383, 384, 408 STATUS — 50, 66, 95, 98, 118, 135, 138, 317, 575 STKPTR — 575 TOCON — 582 T1CON — 463 T2CON — 473 TMRO — 101, 102 TM RIH — 462
648 Предметный указатель TMR1L— 462 TRIS* — 106, 127, 225, 327, 384, 498 TXREG — 426 TXSTA — 426 VRCON — 502 WREG — 575, 591 индексный — 123 общего назначения (РОН) — 98, 120, 577 рабочий (W) — 48, 66, 93, 575, 585 сдвиговый — 51 специального назначения (РСН) — 98, 100, 122, 577 указателя стека — 173 указатель на данные — 123 Регистр состояния см. Регистр, STATUS Редактор связей см. Компоновщик Режимы адресации — 115 С Сброс — 316 BSR — 576 GIE — 211 IRP — 127 PCLATH —117 YX3 — 96 RBPU — 341 RPO — 98, 119 SEN — 411 SSPEN — 384, 409 ЛО — 96 TRISX — 336 бит маски прерывания — 214 внешний — 316, 319, 596 модуль ССР — 468, 472 модуль АЦП — 332, 498 модуль компаратора — 496 параллельные порты — 323, 328 по включению питания — 317, 453, 563 аналоговые модули — 496, 509 модуль АЦП — 512 по снижению питания — 320 бит конфигурации — 314 сторожевой таймер — 102, 452, 453, 455, 563 Таймер 0 — 455 Таймер 1 — 463 Таймер 2 — 473 Сдвиговый регистр см. Регистр, сдвиговый Сигнал аналоговый — 489 цифровой — 489 Симплексный обмен — 376 Симулятор — 265 Скорость передачи бод — 419 Скрытая область памяти — 313, 549, 556 Слабая подтяжка — 341 Слово — 19 двойное — 19 счетверенное — 19 Слово конфигурации — 313, 556 Смарт-карта — 369 Сопровождение программы — 245 СППЗУ — 41 Спящий режим — 214, 222, 308, 321, 389, 412, 453 изменение состояния порта В — 348 использование в языке Си — 538 модуль АЦП — 511, 518 модуль компаратора — 499 прерывания — 321 Таймер 1 — 464 Статические переменные см. Язык Си, переменные, статические Стек — 93, 172, 603 аппаратный — 172, 211 быстрый — 584 программный — 188 Сторожевой таймер — 96, 102, 321, 450, 458, 561 модуль EEPROM — 547 постделитель — 452 спящий режим — 308, 321 Счетчик — 53 со сквозным переносом — 54 Счетчик команд (РС) — 62, 63, 72, 91, 93, 103, 117, 152, 173, 184, 202, 211, 272, 322, 575 т Таблица истинности — 26 Таблица преобразования — 183, 202, 358, 549, 553, 569 Таймер Таймер 0 — 101, 454, 479, 482, 562, 582, 599, 600 Таймер 1 — 462, 481, 487, 498, 599 Таймер 2 — 462, 473 компаратор — 473 Таймер 3 — 582 Тактовый генератор запуск — 318 Тактовый сигнал — 93, 309 АЦП см. Модуль АЦП, тактовый сигнал Текстовый редактор — 245 Триггер D-типа — 45, 228 RS-типа — 44, 45 Т-типа — 52 Тристабильный см. Выход, стремя состояниями
Предметный указатель 649 У Унарный код см. Код, унарный Ф Файл — 96 абсолютный объектный код — 249, 262 формат Intel HEX — 250 включаемый — 240, 315, 330 заголовочный — 240, 453, 521 исходный код — 70, 239, 252 командный файл компоновщика — 254, 267 листинга — 246 макроопределений — 252 машинный код см. Файл, абсолютный объектный код объектный код — 70, 239 перемещаемый объектный — 253 сообщений об ошибках — 246, 251 Фильтр трехточечный — 166, 540 Флаг — 50, 95, 96, 154 С — 50, 66, 67, 95, 109, 136, 137, 138, 147, 322, 592 DC — 95, 111, 322, 592 N — 575, 590 OV— 575, 590 Z — 50, 66, 67, 96, 110, 112, 126, 130, 132, 139, 217, 322, 590 ошибки кадрирования — 427 ошибки переполнения — 427 прерывания см. Прерывание, флаг Фон-неймановская архитектура см. Архитектура, фон-неймановская ц Центральный процессор (ЦПУ) — 58, 60 Цикл - 124, 282, 293 бесконечный — 225, 453 записи — 65 чтения — 65 Цифро-аналоговое преобразование (ЦАП) — 378, 475, 488, 522 ШИМ — 475, 523 ч Чтение — модификация — запись — 138 ш Шаговый двигатель — 356 Шестнадцатеричный код см. Код, шестнадцатеричный Шина — 50 Шина данных — 33, 50, 58 Широтно-импульсная модуляция (ШИМ) — 475 э Энергонезависимая память см. ЭСППЗУ Энергопотребление — 305 ЭСППЗУ — 88, 106, 543 внешнее — 439 Я Язык ассемблера — 240 Язык регистровых передач — 116 Язык Си — 278 абсолютный адрес — 288 биты конфигурации — 316, 452 данные глобальные — 295 шестнадцатеричные — 254, 288 директива #bit — 289, 328, 539 #byte — 289, 328 «define — 288 #device ADC — 521 «include — 283 #int_ccpl — 487 #int_ext — 295 #int_rtcc — 532 #use de lay () — 431 #use fast_io() — 346 #use i2c() — 418 #use rs232() — 431 #use standard_io() — 347 комментарии — 288 массивы — 296 модификатор const — 296 оператор for() - 293 if-else — 293 return — 280, 282, 286 while — 282, 285, 289, 332 операции с битами — 290, 539 операция ! (HE) — 332 & (И) — 289, 377 —(декремент) — 282, 286 + (сложение) — 285 ++ (инкремент) — 286, 295 » (сдвиг вправо) — 293 | (ИЛИ) — 289, 377 приведение типа, явное — 293 переменные автоматические — 258, 533 глобальные — 532 статические — 258, 532 процедура обработки прерывания — 295
650 Предметный указатель тип main() — 283 int — 281 port_b_pullups() — 347 long int — 533 printf() — 431 unsigned int — 281 read_adc() — 521, 539 unsigned long — 281, 285 read_eeprom() — 559 void — 295 set_adc_channel() — 521 функция — 281 set_timerl() — 486 bclr() — 377 set_timer2() — 475 bset() — 377 set_tris_x() — 328 delay_cycles() — 295, 332 setup_adc() — 521, 539 delay_ms() — 295 setup_adc_ports() — 332, 521 delay_us() — 295 setup_ccpl() — 486 disable_interrupts() — 539 setup_counters() — 454 enable_interrupts() — 294, 539 setup_spi() — 392 get_timerl() — 486 setup_timerl() — 486 get_timer2() — 475 setup_timer2() — 475 getch() — 431 slccpO — 338 i2c_read() — 418 spi_data_is_in() — 393 i2c_start() — 418 spi_read() — 393 i2c_stop() — 418 spi_write() — 392 i2c_write() — 418 write_eeprom() — 559
СИДКАТЦЕН PIC-микроконтроллеры. Все, что вам необходимо знать Главный редактор В. М. Халикеев Ответственный редактор Т. Е. Брод Технический редактор Н. В. Тищенко Верстальщик И. С. Каинова Корректор Г. Б. Абу де ев а Подписано в печать 06.05.2008. Формат 70x100/16. Бумага офсетная. Гарнитура «NewtonC». Печать офсетная. Объем 41 п. л. Усл. п. л. 53,1 Тираж 3000 экз. Код QPIC. Заказ № 213 Издательский дом «Додэка-ХХ1» ОКП 95 3000 105318 Москва, а/я 70 Тел./факс: (495) 366-24-29, 366-09-22 E-mail: books@dodeca.ru; red@dodeca.ru Отпечатано с готовых диапозитивов в ОАО «Московская типография №6» 115088 Москва, ул. Южнопортовая, д.24