Глава 4 Микроконтроллер 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.
• Поймете, как можно манипулировать содержимым счетчика команд и какую роль в этом играет регистр специального назначения 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). Основное отличие заключается в том, что к внутренней шине данных памяти данных, помимо ЦПУ, подключены периферийные модули. Чтобы лучше понять материал этой главы, рекомендую еще раз просмотреть материал, касающийся указанного компьютера. Вообще говоря, в микроконтроллерах PIC реализована гарвардская архитектура с ее раздельными модулями памяти программ и данных, в которой периферийные устройства отображены на адресное пространство памяти данных. То есть с точки зрения программы все эти устройства расположены в памяти данных. То же касается и различных служебных регистров, таких как регистры статуса и управления, а также счетчика команд.
Рис. 4.1. Архитектура микроконтроллера PIC16F84
Блок выборки
Блок выборки, показанный отдельно на Рис. 4.2, предназначен для выборки команд из памяти программ и загрузки их в конвейер. Адрес каждой извлекаемой команды задается счетчиком команд. В свою очередь код операции, выделенный из кода команды, подается на вход дешифратора команд, который активирует в требуемой последовательности соответствующие узлы исполнительного блока.
Рис. 4.2. Блок выборки микроконтроллера PIC16F84
Память программ
Основным элементом блока выборки является память программ. Программное обеспечение во встраиваемых системах фиксировано, поскольку после включения питания микроконтроллер должен сразу же приступить к выполнению своих задач, не тратя время на загрузку программы. Поэтому память программ, как правило, реализуется в виде ПЗУ какого-либо типа. В большинстве микроконтроллеров PIC используется многократно-программируемое ПЗУ; если в обозначении модели присутствует буква «F», то это FLASH-память. В памяти программ микроконтроллера PIC16F84 может храниться до 1024 команд, размер каждой из которых составляет 14 бит (см. Рис. 3.5 на стр. 68). В новых микроконтроллерах семейства среднего уровня размер памяти программ колеблется от 1024 (например, PIC16F627) до 8192 (PIC16F876/7) команд.
Счетчик команд
Счетчик команд (Program Counter — PC) используется для адресации, или указания, считываемой из памяти программ команды. Этот 13-битный регистр обычно инкрементируется после каждой выборки, функционируя как двоичный счетчик. Однако, как мы с вами увидим в следующей главе, существует ряд команд, таких как команда goto, выполнение которых вызывает переход к другому месту в памяти программ. Соответственно, нормальное функционирование счетчика команд нарушается. Кроме того, программист может напрямую обращаться к счетчику команд через память данных (см. Рис. 4.8).
Несмотря на то что показанный на Рис. 4.3 счетчик команд является 13-битным регистром, способным адресовать 213 = 8192 команды, в микроконтроллере PIC16F84 задействованы только младшие 10 бит (210 = 1024). Если программа попытается перейти по адресу, находящемуся вне этого диапазона, то адрес «свернется» по границе памяти программ. В любом случае счетчик команд очищается при сбросе микроконтроллера, т. е. первая выполняемая команда всегда располагается по адресу h’000’. Этот адрес называется вектором сброса.
Рис. 4.3. Память программ микроконтроллера PIC16F84
Конвейер
Конвейер реализован в виде двух 14-битных регистров. В первом регистре хранится команда, только что считанная из памяти программ по адресу, определяемому значением счетчика команд. Второй регистр подключен к дешифратору команд и соответственно содержит исполняемую в данный момент команду. Такое решение позволяет осуществлять выборку команды одновременно с исполнением команды, выбранной в предыдущем цикле. Естественно, при этом предполагается, что последовательность выполнения команд имеет линейный характер. Для команд, выполняющих переход к другому месту памяти программ, команда, находящаяся на вершине конвейера, должна быть заменена на другую. Этот процесс называется сбросом конвейера и увеличивает время исполнения команды на один цикл. Как видно из Рис. 4.4, обычно команда выполняется за один машинный цикл.
Рис. 4.4. Фазы машинного кода
Дешифратор команд
Дешифратор команд представляет собой логическую схему, которая декодирует каждое поле 14-битного слова команды и управляет передачей соответствующих адресов и данных на требуемые входы исполнительного блока, а также конфигурирует АЛУ.
Все модели микроконтроллеров PIC имеют встроенный генератор, задающий последовательность выполнения внутренних микроопераций в соответствии с сигналами, поступающими от дешифратора команд. В качестве времязадающего элемента обычно используется внешний кварцевый резонатор, подключаемый к выводам OSC1 и OSC2 микроконтроллера (Рис. 4.2). Именно этот резонатор определяет тактовую частоту микроконтроллера fOSC. Более подробно о генераторе мы поговорим в главе 10. Максимальная тактовая частота большинства моделей среднего уровня составляет 20 МГц, однако в некоторых ранних моделях частота не могла превышать 10, а то и 4 МГц. Минимальная тактовая частота ничем не ограничена.
Частота тактового генератора делится на четыре с помощью схемы, показанной на Рис. 2.25 (стр. 54), чтобы получить четыре не перекрывающихся тактовых сигнала. Каждый из этих сигналов используется схемой дешифратора для управления внутренними узлами процессора в требуемой последовательности. Соответственно длительность одного машинного цикла равна четырем периодам внешнего сигнала fOSC (см. Рис. 4.4). Таким образом, при использовании резонатора с частотой 4 МГц частота машинных циклов составляет fOSC/4, или один миллион циклов в секунду, что соответствует периоду машинного цикла, равному 1 мкс.
В зависимости от текущей фазы машинного цикла блок выборки выполняет следующие операции:
Q1: Инкрементирование счетчика команд и выставление его содержимого на шину адреса памяти программ.
Q4: Считывание кода команды с шины данных памяти программ в регистр команд IR1 и одновременно с этим пересылка предыдущей команды по конвейеру в регистр команд IR2, подключенный к дешифратору команд.
Стек
Стек представляет собой отдельный блок из восьми 13-битных регистров, который подключен к счетчику команд. В 6-й главе мы увидим, что стек используется для хранения предыдущего значения счетчика команд, т. е. «запоминает» точку возврата при вызове подпрограммы.
Исполнительный блок
Исполнительный блок (Рис. 4.5) осуществляет извлечение данных из памяти данных или непосредственно из кода команды и выполняет обработку этого значения, используя АЛУ. Режим работы АЛУ определяется сигналами, формируемыми дешифратором команд. Результат помещается либо в рабочий регистр W, либо обратно в память данных, перезаписывая исходное значение.
Арифметико-логическое устройство (АЛУ)
Основным элементом исполнительного блока является АЛУ (см. Рис. 2.10 на стр. 39), которое осуществляет обработку данных, поступающих из двух источников. Одним из этих источников является 8-битный рабочий регистр W. В качестве другого источника может выступать:
• Регистр данных, указанный в команде. Например, команда addwf h’20’,f прибавляет содержимое рабочего регистра к содержимому регистра h’20’.
• Константа, являющаяся частью слова команды (см. Рис. 3.6 на стр. 69). К примеру, команда addlw 5 прибавляет число 5 к содержимому рабочего регистра.
В первом случае результат может быть помешен либо обратно в память данных, если бит адресата равен 0 (см. Рис. 3.5 на стр. 68), либо в рабочий регистр, если этот бит равен 1, например в случае команд типа addwf h'20',w
Рис. 4.5. Исполнительный блок микроконтроллера PIC16F84
Регистр STATUS
Этот регистр, тесно связанный с АЛУ, содержит три бита-флага, которые используются для информирования программы о результате выполнения команды. В частности, не было ли переноса при выполнении операции сложения.
Флаг переноса
Бит 0 регистра STATUS используется в качестве флага переноса С (от англ. carry — перенос). Основное его назначение — хранение бита переноса предыдущей операции сложения. При операциях вычитания значение этого бита соответствует инвертированному биту заема (см. Пример 4.2). Например, 24–12 = 12В¯1, а 12–24 = 88B¯0. Флаг С также используется в операциях сдвига, как показано на Рис. 5.13 (стр. 148). Пометка «R/W?» на Рис. 4.6 отражает тот факт, что этот бит доступен как для чтения (R), так и для записи (W), а после сброса по питанию его состояние не определено (при любом другом сбросе состояние этого бита не изменяется).
Рис. 4.6. Формат регистра STATUS микроконтроллера PIC16F84
Флаг десятичного переноса
Бит 1 регистра STATUS используется в качестве флага десятичного переноса DC (от англ. digit carry). Этот флаг функционирует точно так же, как и обычный флаг С, только содержит бит переноса из младшего полубайта в старший, т. е. из 3-го бита в 4-й. Аналогично флагу С, флаг DC содержит дополнение бита заема из 3-го бита в 4-й.
Информация о наличии или отсутствии переноса между полубайтами полезна при работе с данными, представленными в двоично-десятичном коде (посмотрите хотя бы на Пример 4.5). При использовании этого кода в каждом полубайте хранится 4-битное представление десятичного числа от 0 до 9 (см. стр. 20), и флаг десятичного переноса указывает на возникновение переноса между десятичными разрядами.
Флаг нуля
Бит 2 регистра STATUS используется в качестве флага нуля Z (от англ. zero — ноль). Этот бит устанавливается, если результат выполнения команды равен нулю, и сбрасывается в противном случае.
В отличие от многих других микроконтроллеров, в микроконтроллерах PIC отсутствуют команды, специально предназначенные для сброса или установки флагов, подобные команде sec, имеющейся в микроконтроллерах семейств 6800/5/11 компании Motorola.
Тем не менее, как видно из Рис. 4.7, к регистру STATUS можно обращаться как к регистру данных с адресом h’03’. Поэтому любая команда, которая изменяет содержимое регистра данных, может в принципе использоваться для изменения состояния флагов. Однако существует определенная проблема, заключающаяся в том, что многие из этих команд сами по себе влияют на состояние одного или нескольких флагов (см., к примеру, Табл. 5.1 на стр. 129) и таким образом переопределяют значение, являющееся результатом выполнения команды. Если мы, к примеру, попробуем сбросить все флаги с помощью команды очистки регистра сlrf 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-м битах, будет описано в последующих главах. В общих чертах флаг (Power Down) сбрасывается при выполнении команды sleep, которая используется для выключения тактового генератора и перевода микроконтроллера в режим ожидания с малым потреблением (< 1 мкА). Флаг
(Time Out) сбрасывается при наступлении тайм-аута сторожевого таймера[70]. Оба этих флага доступны только для чтения — их состояние не может быть изменено программно. После сброса указанные флаги устанавливаются в 1.
Все эти биты называются флагами или, иногда, семафорами, поскольку они сигнализируют о том или ином результате выполнения команды. Бит 5 этого регистра несколько отличается от остальных, так как на его состояние не влияют происходящие события. Наоборот, флаг RP0 используется программистом для изменения состояния процессора. Чтобы проиллюстрировать назначение этого бита-переключателя, нам потребуется более подробно изучить структуру памяти данных микроконтроллера PIC16F84.
На Рис. 4.7 приведена упрощенная модель памяти данных микроконтроллера PIC16F84. Эту память данных можно представить в виде картотечного шкафа, который в данном случае имеет два отделения (банка). Внутри каждого отделения имеется некоторое количество папок, или файлов, каждый из которых содержит восемь битов данных. Наравне с этим термином также используется термин регистр[71].
Рис. 4.7. Упрощенная структура памяти данных микроконтроллера PICI6F84
Вообще говоря, в нашей картотеке присутствуют файлы двух типов. Некоторые из этих файлов имеют названия и выполняют специальные, заранее заданные функции. Такие файлы называются регистрами специального назначения (РСН). РСН используются для управления и отслеживания состояния микроконтроллера и его различных периферийных устройств. В частности, как мы уже видели на Рис. 4.6, регистр с адресом h’03’ является регистром STATUS, а регистр с адресом h’06’ представляет собой регистр данных параллельного порта В, связанного с выводами RB7…RB0 (обычно обозначаемыми как RB[7:0]) микроконтроллера, как показано на Рис. 4.1.
Остальные файлы, выделенные на рисунке серым цветом, программист может называть по своему усмотрению и использовать для хранения пользовательских данных общего назначения. В микроконтроллере PIC16F84 имеется 68 таких регистров общего назначения (РОН), адреса которых лежат в диапазоне h’0C’…h’4F’. Во всех микроконтроллерах PIC среднего уровня регистры с младшими адресами используются в качестве РСН, а регистры со старшими адресами — в качестве РОН. Однако в более новых представителях семейства, таких как PIC16F628, требуется больше РСН в связи с большим числом периферийных устройств. При этом под РСН резервируется память с адресами до h’1F’. С учетом сказанного, во всех программах, приведенных в настоящей книге, предполагается, что для хранения переменных доступна область памяти, начиная с адреса h’20’.
Во многих моделях объем памяти для хранения пользовательских данных больше, чем в микроконтроллере PIC16F84. И тем не менее даже в самой развитой модели среднего уровня объем памяти не превышает 368 байт. Это не так уж и много, поэтому программы должны использовать эту ограниченную емкость очень эффективно[72].
Возвращаясь к Рис. 3.5 (стр. 68), можно заметить, что из 14 бит, составляющих код команды, семь зарезервировано под адрес операнда в памяти данных. Семь битов дают нам всего 27 = 128 адресов, т. е. страницу или банк, вмещающий в себя 128 регистров. Для преодоления этого ограничения необходимо где-то взять дополнительные биты, чтобы расширить диапазон адресов. В микроконтроллере PIC16F84 для этой цели используется 5-й бит регистра STATUS, который называется RP0 (Register Page 0[73]). Этот бит выполняет роль 8-го бита адреса, позволяя использовать память данных, содержащую до 256 регистров. При RP0 = 0 (после сброса по питанию) обращения производятся к 0-му банку памяти данных (регистры h’00’…h’7F’). При RP0 = 1 разрешается доступ к 1-му банку, т. е. к регистрам h’80’…h’FF’.
Большинство моделей среднего уровня имеют 4 банка ОЗУ и используют уже два бита регистра STATUS — RP1:RP0 (6-й и 5-й биты соответственно); см. Рис. 5.5 на стр. 122. В результате формируется 9-битный адрес памяти данных. Более подробно этот вопрос будет рассмотрен в следующей главе.
Несмотря на то что использование банков памяти является довольно эффективным вариантом преодоления 7-битного ограничения на размер адреса, их использование может вызвать трудности у неопытных программистов. В качестве примера рассмотрим фрагмент кода, в котором осуществляется запись числа b’00001111’ в регистр h’86’. Для выполнения этой операции мы воспользуемся командой movlw, загружающей 8-битную константу в рабочий регистр:
movlw b’000011111; Загружаем константу h’0F’ в W
movwf h’86’; и копируем его в регистр с адресом h’86’
В коде ошибочно указан адрес h’86’, или b’10000110’, значение которого слишком велико для имеющегося размера поля. Большинство ассемблеров просто обрежут биты, выходящие за границы поля, в результате чего мы получим адрес 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 для этих целей используются следующие команды[74].
∙ bcf
Команда сброса бита регистра данных (Bit Clear File) позволяет программисту сбросить любой бит в любом регистре. К примеру, команда bcf h’20’,7 сбрасывает 7-й бит регистра h’20’. Состояние остальных битов регистра при этом не изменяется.
∙ bsf
Команда установки бита регистра (Bit Set File) позволяет программисту установить любой бит в любом регистре. К примеру, команда bsf h131’,3 устанавливает 3-й бит регистра h’31’. Состояние остальных битов не изменяется.
Возвращаясь к нашему примеру, теперь мы можем написать:
bsf 3,5; Установка 5-го бита (RP0) регистра STATUS позволяет обращаться к 1-му банку памяти
movlw b100001111’; Загружаем константу h’0F’ в W
movwf h’86’; и копируем его в регистр с адресом h’86’
bcf 3,5; Сбрасываем RP0 для возврата к 0-му банку
На самом деле использование 1-го банка в микроконтроллере PIC16F84 сведено к минимуму. Все 68 РОН отображены на оба банка памяти, т. е. по адресу h’n’ и по адресу h’n+80’ расположен один и тот же регистр. Например, если программисту необходимо прочитать содержимое регистра h’20’, не имеет значения, какой из банков при этом используется процессором, поскольку в регистре h’A0’ находятся те же самые данные, а не просто их копия! Такое встречается достаточно редко, поскольку микроконтроллеры 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). В сфере встраиваемых устройств, когда коды команд хранятся в ПЗУ какого-либо типа, такие адреса являются фиксированными и, соответственно, не могут модифицироваться.
Альтернативным способом, используемым в том или ином виде всеми вычислительными устройствами, является хранение адреса операнда в каком-либо регистре. В случае 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 (h’01’), доступный для чтения и записи, может тактироваться внешним сигналом, подаваемым на вход микроконтроллера TOCKI (Timer0 ClocK In), который совмещен с линией RA4 порта А. Кроме того, инкрементирование счетчика может происходить по внутреннему тактовому сигналу Q4 (Рис. 4.4), частота которого составляет 1/4 частоты кварцевого резонатора. Частота обоих сигналов (и внешнего, и внутреннего) может быть снижена при помощи внутреннего 8-битного предделителя. Коэффициент деления предделителя задается тремя младшими битами регистра OPTION_REG, расположенного по адресу h’81’ (см. Рис. 13.2 на стр. 452), которые называются PS2:PS1:PS0. Соответственно, коэффициент деления будет равен 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 связаны следующие регистры:
∙ TMR0 (h’01’)
Этот 8-битный суммирующий счетчик, иногда называемый таймером/счетчиком реального времени, осуществляет счет импульсов, поступающих на вход таймера. Данный регистр в любой момент времени доступен как для чтения, так и для записи. При переполнении счетчика (при смене значения с h’FF’ на h’00’) он устанавливает бит T0IF (Timer 0 Interrupt Flag) регистра управления прерываниями INTCON (см. Рис. 7.3 на стр. 213). Этот флаг может использоваться для генерации прерывания.
∙ OPTION_REG (h’81’)
Для управления Таймером 0 используются шесть битов этого регистра, расположенного по адресу h’81’ (см. Рис. 13.2 на стр. 452):
• PS2, PS1, PS0 (биты 2, 1 и 0 соответственно) определяют коэффициент деления предделителя (2PS+1) Таймера 0 или постделителя (2PS) сторожевого таймера.
• T0SE (бит 4) позволяет программисту задать фронт импульсов на входе T0CKI, по которому будет осуществляться инкрементирование счетчика:
0 — нарастающий фронт, 1 — спадающий фронт.
T0CS (бит 5) используется для выбора источника тактового сигнала таймера: 0 — системный тактовый сигнал, 1 — импульсы со входа T0CKI.
• Остальные два бита регистра используются для выбора активного фронта внешнего прерывания и конфигурирования входов порта В.
Счетчик команд
Мы уже говорили (см. Рис. 4.2), что микроконтроллеры PIC среднего уровня имеют 13-битный счетчик команд (Program Counter — PC), выполняющий функцию указателя на команды в пределах 8 Кбайт. Сколько именно битов счетчика используется в каждой конкретной модели, зависит от имеющегося объема памяти программ. Так, в микроконтроллере PIC16F84 используется 10 бит (210 = 1 Кбайт), в PIC16F628 — 11 бит (211 = 2 Кбайт), в PIC16F874 — 12 бит (212 = 4 Кбайт), а в PIC16F877 задействованы все 13 бит.
Иногда может потребоваться изменить состояние счетчика команд из программы. Для этого младший байт PC напрямую доступен через регистр специального назначения PCL (Program Counter Low). А для изменения всех 13 бит требуется дополнительный регистр. Регистр-защелка старшего байта PCLATH не является в действительности старшим байтом PC, а служит в качестве буфера. Изменение содержимого регистра PCLATH не влияет на старший байт счетчика команд, однако одновременно с записью в регистр PCL новое значение PCLATH загружается в старший байт 13-битного счетчика команд. Таким образом, как показано на Рис. 4.8, все 13 бит счетчика команд обновляются одновременно. Запомните эту особенность, она потребуется вам при ответе на Вопрос для самопроверки 4.2.
Рис. 4.8. Изменение всех 13 битов счетчика команд при записи в регистр PCL
Для манипулирования счетчиком команд используются следующие регистры:
∙ PCL (h’02’)
Регистр PCL физически является младшим байтом счетчика команд. Этот регистр доступен как для чтения, так и для записи.
∙ PCLATH (h’0A’)
Регистр PCLATH является регистром-защелкой для хранения данных, которые должны быть загружены в старший байт счетчика команд. Загрузка старшего байта происходит при записи в регистр PCL, что обеспечивает одновременное обновление всех 13 бит счетчика.
Не забывайте, что регистр PCLATH используется только в качестве буфера, поэтому значение, считанное из него, не будет соответствовать текущему состоянию старшего байта счетчика команд[75].
Параллельные порты ввода/вывода
Способность одновременно изменять или контролировать состояние нескольких цифровых линий представляет собой воистину универсальную возможность систем на базе микроконтроллеров. В зависимости от типа корпуса микроконтроллеры среднего уровня имеют от 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 образовано от слова TRIState[76] (см. Рис. 11.3 на стр. 333). Эти регистры находятся в 1-м банке, поскольку они обычно конфигурируются в начале программы и впоследствии не изменяются.
В качестве примера рассмотрим следующую ситуацию. Предположим, что нам необходимо сделать выводы 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»[77]. Директивой называется псевдокоманда, которая, как правило, не генерирует реальный машинный код, а используется для передачи информации транслятору. Начиная с этого момента, мы будем для ясности присваивать нашим регистрам и битам имена.
В качестве примера напишем код, который формирует на выводе RB7 положительный импульс (предполагается, что ЦПУ работает с 0-м банком):
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[n] в 1 делает вывод RA[n] входом, а сброс в 0 — выходом[78]. При любом сбросе все биты регистра 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, здесь же мы просто приведем последовательность действий для выполнения операций чтения/записи.
Чтение
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.
• Бит признака преждевременного завершения цикла записи.
• Бит признака нормального завершения цикла записи.
Более полную информацию можно получить из Рис. 15.2, приведенного на стр. 545.
∙ EECON2 (h’89’)
Этот управляющий регистр физически не существует, и при его чтении всегда возвращается нулевое значение. Указанный адрес используется для загрузки последовательности, разрешающей цикл записи. Последовательность состоит из двух чисел h’55’ и h’AA’, записываемых друг за другом.
Прерывания
Регистр управления прерываниями INTCON, расположенный по адресу h’0B’[79], содержит биты маски и статуса, управляющие реакцией микроконтроллера на прерывания. Использование этого регистра описывается в главе 7. Большинство периферийных устройств имеют собственные биты, относящиеся к прерываниям и расположенные в других управляющих регистрах (см., например, Рис. 7.5 на стр. 223).
Примеры
Пример 4.1
Объясните, каким образом внедрение в схему блока выборки команд конвейера увеличивает производительность микроконтроллеров PIC. Предвидите ли вы какие-либо проблемы, связанные с поддержкой команд перехода (таких как goto), относительно структуры конвейера?
Решение
Наличие конвейера является обязательным условием для организации параллельной работы блока выборки и исполнительного блока. То есть для того, чтобы иметь возможность исполнять команду и одновременно с выборкой из памяти программ команды n + 1, требуется внутренний элемент с памятью, который передавал бы код команды в дешифратор команд. Поскольку все команды имеют одинаковый размер (14 бит), структуру регистров конвейера и управление ими можно значительно упростить. Большинство традиционных CISC-процессоров имеют команды, длина которых может быть различной. К примеру, размер команд микроконтроллера 68НС11 колеблется от 1 до 4 байт, т. е. длительность фазы выборки составляет от 1 до 4 транзакций на шине. Некоторые более развитые процессоры имеют многоступенчатый конвейер, каждый этап которого связан с определенной частью исполнительного блока. За счет этого можно реализовать несколько одновременных потоков исполняемых команд.
Проблема, связанная с конвейером, вытекает из предположения, что команды программы будут исполняться последовательно, т. е. так, как они расположены в памяти программ. При этом команды, не удовлетворяющие этому условию и изменяющие содержимое счетчика команд, требуют очистки конвейера, с тем чтобы код адресованной команды оказался на вершине конвейера. Например, если командой к является команда goto n, то к тому моменту, когда процессор узнает, что в действительности на следующем шаге необходимо будет выполнить команду n, команда k + 1 будет уже загружена в конвейер. Поэтому необходимо выполнить холостой цикл, во время которого код команды n будет напрямую загружен в конвейер (разумеется, команда k + 1, код которой находится на вершине конвейера, не выполняется). Иногда эту операцию называют очисткой конвейера. Соответственно, такие команды, как goto, выполняются за два машинных цикла. Команды условного пропуска (см. главу 5) выполняются за два цикла в случае пропуска и за один цикл в противном случае. Все остальные команды выполняются за один машинный цикл.
Пример 4.2
Можете ли вы определить, почему после операции вычитания или сложения с отрицательным числом значение флага С является дополнением к биту заема?
Подсказка: вспомните правила двоичной арифметики в дополнительных кодах (стр. 22).
Решение
Операция вычитания во всех микроконтроллерах PIC реализована одинаково: байт данных переводится в дополнительный код, а затем выполняется сложение, как показано на Рис. 2.9 на стр. 39. В этой ситуации итоговый бит переноса равен 0, если результат сложения получается отрицательным, и 1, если положительным. Например:
1. 06 — 0А —> 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. Почему?
Решение
Из текста на стр. 67 мы узнали, что команда movf устанавливает флаг Z, если содержимое регистра-адресата равно нулю, в противном случае флаг Z сбрасывается. Так что следующий фрагмент программы
movf STATUS,w; Скопировать содержимое регистра h’03’(STATUS) в W,
movwf h’40’; а потом в регистр h’40’
действительно скопирует содержимое регистра h’03’ в регистр h’40’. Но до тех пор, пока все биты регистра STATUS не будут равны нулю, флаг Z будет постоянно сбрасываться. При нормальной работе флаги и
равны 1, поэтому итоговым значением флага Z всегда будет 0, независимо от его исходного состояния.
Разумеется, это ограничение можно обойти. Одно из таких решений показано на стр. 217.
Пример 4.4
Как бы вы задали следующую конфигурацию некоторых РСН из 1-го банка:
• OPTION_REG b’10101111’
• TRISA b’00011110’
• TRISB b’11111111’
Решение
Поскольку все три регистра находятся в 1-м банке, нам необходимо будет переключить банки перед записью данных и переключиться на 0-й банк после конфигурирования регистров.
STATUS equ 3; Регистр STATUS расположен по адресу h’03’
RP0 equ 5; Бит RP0 — 5-й
OPTION_REG equ h’81’; Регистр OPTION_REG расположен по адресу h’81’
TRISA equ h’85’; Регистр направления порта А
TRISB equ h’86’; Регистр направления порта В
bsf STATUS,RP0 ; Переходим к 1-му банку
movlw b’10101111’ ; Первую константу
movwf OPTION_REG; в регистр OPTION_REG
movlw b’00011110’; Вторую константу
movwf TRISA; в TRISA
movlw b’11111111’; Третью константу
movwf TRISB; в TRISB
bcf STATUS,RPO; Возвращаемся к 0-му банку
Пример 4.5
Напишите программу для инкрементирования упакованного BCD-числа, находящегося в памяти данных по адресу h’20’.
Решение
Два двоично-десятичных (BCD) разряда можно упаковать в один байт, т. е. он может использоваться для хранения чисел от 0 до 99. Например, значение 01001001h’20’ соответствует числу 49. Инкрементирование числа, хранящегося в таком хитром виде, с использованием обычных правил двоичного сложения может привести к некорректному результату. Например, Ь’01001001 + 1’(49 + 1) даст нам b’01001010’ (h’4A’), тогда как нам необходимо получить число Ь’01010000’ (h’50’). Аналогично, Ь’ 10011001 + 1’ (99 + 1) даст нам Ь’10011010’ (h’9A’) вместо Ь’00000000’ + Ь’1’ (h’1 00’).
Из приведенных примеров можно увидеть, что если после инкрементирования какого-либо разряда BCD-числа он равен 10, то необходимо его обнулить, а к старшему разряду прибавить единицу. Воспользовавшись этим рассуждением, сформулируем перечень задач, которые должна выполнять наша программа:
1. Инкрементировать BCD-число по правилам обычной двоичной арифметики.
2. Если младший полубайт результата равен 10, прибавить к результату число 6.
3. Если старший полубайт результата равен 10, прибавить к нему число 6.
В Программе 4.1 приведена эффективная реализация описанного алгоритма. После инкрементирования по правилам обычной двоичной арифметики к результату прибавляется число 6 и проверяется состояние флага DC. Этот флаг установится только в том случае, если исходное значение полубайта было равно десяти (h’0A’ + 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) *
; ****************************
STATUS equ 3; Регистр STATUS
C equ 0; Флаг переноса — бит 0
DC equ 1; Флаг десятичного переноса — бит 1
BCD equ h’20’; Исходное BCD-число — в регистре h’20’
; -----------------------------
BCD_INC
incf BCD, w; Инкрементируем число и помещаем в W
addlw 6; Прибавляем шесть
btfss STATUS,DC; Это было нужно, ЕСЛИ был десятичный перенос,
addlw -6; ИНАЧЕ не нужно
;Теперь проверим старший разряд, прибавляя к нему 6 и проверяя флаг переноса
addlw h’60’; Прибавим h’60’ (т. е. шесть к старшему разряду)
btfss STATUS,С; Это было нужно, ЕСЛИ был перенос,
addlw — h’60’; ИНАЧЕ отменяем коррекцию
; Инкрементированное и скорректированное BCD-число теперь в W
movwf BCD; Помещаем его в память
В качестве альтернативного варианта можно было бы вычитать перед инкрементированием число девять и, если в результате инкрементирования флаг Z окажется установленным, инкрементировать старший разряд, в противном случае прибавить 10. Ту же операцию следует повторить для старшего разряда.
Вопросы для самопроверки
4.1. Когда микропроцессор используется в вычислительном устройстве общего назначения, программа обычно загружается в ОЗУ, доступное как для чтения, так и для записи, и выполняется уже оттуда. Это означает, что в один момент времени в системе может выполняться текстовый редактор, а в другой — программа работы с электронными таблицами. Разумеется, это неприменимо к встраиваемым приложениям, в которых программа хранится в энергонезависимом ПЗУ какого-либо типа. Объясните, для чего так сделано, и укажите преимущества различных вариантов исполнения энергонезависимой памяти — ПЗУ (ROM), СППЗУ (EPROM) и ЭСППЗУ (EEPROM).
4.2. Микроконтроллер среднего уровня PIC16F877 имеет память программ объемом 8 Кбайт, в которой может храниться до 8192 14-битных команд, расположенных в диапазоне адресов h’0000’…h’1FFF’. Как, не прибегая к помощи команды 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.5. Используя конфигурационные значения из Примера 4.4, напишите программу, формирующую положительный импульс на выводе RA0 длительностью 4 мкс. Предполагается, что тактовая частота равна 4 МГц.
4.6. Можете ли вы написать последовательность команд, которая выдаст на вывод RA1 ВЫСОКИЙ уровень, затем сформирует на выводе RA0 четыре импульса и в завершение выставит на вывод RA1 НИЗКИЙ уровень? Не забудьте сконфигурировать регистр TRISA.
4.7. В большинстве электронных часов используется кварцевый резонатор частотой 32.768 кГц, часто называемый «часовым». Из-за больших объемов выпуска эти резонаторы имеют низкую стоимость. Хотя использование такого резонатора и снизит скорость выполнения программы, из Рис. 10.3 на стр. 306 можно увидеть, что мощность, рассеиваемая микроконтроллером, прямо пропорциональна тактовой частоте. Поэтому «часовой» резонатор является достаточно привлекательным выбором для многих экономичных приложений.
Можете ли вы вычислить длительность машинного цикла при использовании такого резонатора? Какой смысл имеет значение 32 768 для времязадающих узлов?
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК