Глава 16 Дальнейшее развитие
В 1994 году компания Microchip представила на суд общественности первого представителя старшей линейки микроконтроллеров PIC17C42, работающего на частоте 25 МГц. В основу этого микроконтроллера легла та же гарвардская RISC-архитектура, которая использовалась в предыдущих моделях, однако количество команд было увеличено до 55 (при этом система команд была совместима снизу вверх). Новая архитектура обеспечивала поддержку памяти бóльших объемов, а также имела расширенные возможности в части косвенной адресации, управления прерываниями и стеком.
В 1999 году было представлено семейство микроконтроллеров PIC18CXXX с максимальной тактовой частотой 40 МГц и 16-битным набором команд. Количество команд в этих микроконтроллерах было увеличено до 75, причем большинство из них были введены для поддержки языков высокого уровня. Также в этих микроконтроллерах была реализована более развитая система прерываний, косвенной адресации и управления стеком.
Чтобы несколько сбалансировать наше обсуждение, посвященное большей частью микроконтроллерам среднего уровня, в этой главе приводится обзор расширенного семейства на примере микроконтроллеров линейки PIC18FXX2. Эти модели специально предназначены для замены более ранних моделей среднего уровня линейки PIC16F87X, на примере которых мы и изучали микроконтроллеры PIC.
После прочтения этой главы вы:
• Сможете критически сравнить архитектуры семейств среднего и расширенного уровня.
• Познакомитесь с побайтно адресуемой структурой 16-битной памяти программ.
• Разберетесь в организации памяти данных, позволяющей при помощи регистра выбора банка использовать до 16 банков памяти по 256 регистров каждый.
• Узнаете, как можно использовать три 12-битных регистра FSR/i для косвенной адресации с пред/пост инкрементом/декрементом, а также для относительной адресации со смещением, хранящимся в рабочем регистре.
• Поймете, каким образом реализована поддержка приоритетов прерываний.
• Познакомитесь с основными изменениями в стандартных периферийных модулях.
• Узнаете, каким образом был расширен набор команд.
Линейка микроконтроллеров 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. Архитектура 40-выводных микроконтроллеров PIC18F442/52
Блок выборки
Блок выборки команд, находящийся в верхней левой части Рис. 16.1, имеет гарвардскую архитектуру с двухуровневым конвейером, в котором могут находиться две 16-битные команды, за счет чего циклы выборки и исполнения команд осуществляются параллельно. Команды хранятся в FLASH-памяти программ в виде 16-битных слов. Как уже говорилось в главе 15, микроконтроллер может самостоятельно читать и записывать данные в свою память программ. В семействе PIC18XXXX содержимое памяти программ рассматривается как совокупность 8-битных данных, передаваемых в обоих направлениях посредством регистра специального назначения TABLAT. Адрес байта в памяти программ, к которому производится обращение, содержится в трех регистрах указателя TBLPTR. Для копирования адресованного байта в регистр TBLAT используется команда tblrd. Соответственно команда tblwt используется для записи данных в память программ, однако реальная запись осуществляется 8-байтными блоками при помощи регистра EECON1, подобно тому, как это было показано на Рис. 15.7 (стр. 558).
Большинство команд в микроконтроллерах с расширенным ядром — 16-битные (см. Рис. 16.4), и только несколько команд занимают два слова памяти программ. Однако для облегчения доступа к таблицам однобайтных данных и строкам память программ организована побайтно. Как можно увидеть из Рис. 16.2, все команды занимают по два байта памяти программ и размещаются только по нечетным адресам. Например, 6-я команда будет располагаться по адресу h’000A’, а 7-я команда — по адресу h’000C’. Такое размещение команд вызвано отсутствием 0-го бита в 21-битном счетчике команд. Соответственно при линейном выполнении программы содержимое счетчика команд изменяется с шагом 2. Таким образом, данная архитектура поддерживает память программ объемом до 220 слов или 221 байт. Между тем в моделях, которые мы взяли в качестве образца, реализован 17-битный счетчик команд.
Рис. 16.2. Упрощенное представление памяти программ моделей PIC18FX42
Как и в микроконтроллерах среднего уровня, счетчик команд при сбросе обнуляется. Младший байт счетчика команд РС[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, в каждой ячейке памяти содержится один байт, однако схема адресации регистров разительно отличается от той, которая использовалась в семействе среднего уровня (см. Рис. 4.7 на стр. 97).
Рис. 16.3. Структура памяти данных старшего семейства
Из Рис. 16.3 видно, что память данных разбита на 16 банков по 256 регистров каждый, что дает максимальный объем памяти, равный 4 Кбайт. Переключение этих банков осуществляется посредством регистра выбора банка BSR. Текущий банк задается битами BSR[3:0] и выбирается с помощью дешифратора 4x16. Поскольку сам регистр BSR расположен в 15-м банке, а указывает после сброса по питанию на нулевой банк, то для изменения регистра BSR, независимо от используемого в данный момент банка памяти, была введена специальная команда lbsr. Так, если нам необходимо переключиться на 5-й банк, достаточно выполнить команду lbsr 5.
Младшие 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’026’,w,0, а для копирования регистра h’096’ — команду movf h’096’,w,1. Схема доступа определяется последним числом (0 или 1). На практике такое явное указание способа обращения применяется достаточно редко, поскольку ассемблер автоматически использует банк быстрого доступа (т. е. команду формата, 0) для адресации регистров из диапазона h’000’…h’07F’ и h’F80’…h’FFF’.
Рис. 16.4. Формат слова типичной команды, обращающейся к памяти данных
В моделях PIC18FX52 для хранения регистров общего назначения предназначена область памяти вплоть до верхней границы 5-го банка, т. е. по адрес h’5FF’ включительно. В микроконтроллере PIC18FX42, оделенном памятью не так щедро, для этой цели используются банки 0…2, т. е. ячейки с адресами до h’2FF’ включительно.
Любые микропроцессоры и микроконтроллеры могут использовать косвенную или индексную адресацию для эффективной работы с массивами данных и таблицами, размещенными в ОЗУ. В младшем и среднем семействах косвенная адресация осуществляется с использованием регистра FSR, выступающего в качестве указателя на память данных, и специального механизма, включающего обращение к виртуальному регистру INDF. Точно такой же подход используется и в старшем семействе, но на более глубоком уровне. В этом семействе имеется три отдельных регистра косвенной адресации — FSR0, FSR1 и FSR2. Каждый из этих регистров-указателей реализован в виде двух РСН, как показано на Рис. 16.5, а. То есть в регистре косвенной адресации может храниться 12-битное число, что позволяет ему указывать на любой регистр в адресном пространстве памяти данных, не обращая внимания на ее сегментированную структуру. Команда загрузки регистра косвенной адресации lfsr позволяет за одно действие скопировать 12-битную константу в любой из трех регистров FSR. Так, чтобы регистр FSR2 указывал на регистр h’500’, достаточно выполнить одну команду lfsr 2, h’500’.
Каждый из регистров косвенной адресации имеет несколько различных режимов работы, показанных на Рис. 16.5, а. Конкретный режим определяется по тому, к какому из пяти виртуальных регистров производится обращение. Вот эти регистры (i = 0, 1 или 2):
∙ INDFi (простая косвенная адресация)
Команда clrf INDF2 обнулит регистр, адрес которого находится в регистре FSR2.
∙ POSTDECi (косвенная адресация с постдекрементом)
Команда clrf POSTDEC2 обнулит регистр, адресованный 12-битным регистром FSR2, а затем декрементирует содержимое регистра косвенной адресации.
∙ POSTINCi (косвенная адресация с постинкрементом)
Команда clrf POSTINC2 обнулит регистр, адресованный 12-битным регистром FSR2, а затем инкрементирует содержимое регистра косвенной адресации.
∙ PREINCi (косвенная адресация с прединкрементом)
Команда clrf PREINC2 сначала инкрементирует содержимое регистра FSR2, а затем обнулит регистр, адресованный регистром FSR.
∙ PLUSWi (относительная косвенная адресация)
Если в рабочем регистре находится число h’06’, то команда clrf PLUSW2 обнулит регистр, адрес которого получается сложением содержимого FSR2 и константы h’06’. Ни содержимое регистра FSR2, ни содержимое рабочего регистра при этом не изменяется. Содержимое рабочего регистра интерпретируется как число со знаком, представленное в дополнительном коде (—128…+127).
Рассмотрим в качестве примера два массива по восемь байтов, обозначенных метками NUM1 и NUM2 (см. Рис. 16.5, б). Предположим, что нам необходимо перемножить i-е элементы этих массивов для получения массива из восьми 16-битных чисел NUM3. Старший байт i-го элемента массива NUM3 будет располагаться по смещению —8 относительно младшего байта. В Программе 16.1 для обращения к массиву NUM1 используется регистр FSR0, а для обращения к массиву NUM2 — регистр FSR1. Поскольку массив NUM3 представляет собой по существу два байтовых массива, сдвинутых друг относительно друга на восемь байтов, регистр FSR2 используется для указания на массив младших байтов произведений. При инициализации в эти указатели с помощью команды Ifsr заносятся адреса последних элементов каждого массива, выделенные на Рис. 16.5, б серым цветом.
Рис. 16.5. Косвенная адресация посредством регистра FSR0
Программа 16.1. Перемножение двух массивов однобайтных значений
; **********************
; ФУНКЦИЯ: Перемножает NUM1[8] х NUM2[8] = NUM3[16]
; ВХОД: Глобальные массивы NUM1[8], NUM2[8]
; ВЫХОД: Глобальный массив NUM3[16]
; **********************
ARRAY_MUL lfsr 0,NUM1+7; Указываем на последний элемент NUM1
Ifsr 1,NUM2+7; Указываем на последний элемент NUM2
lfsr 2,NUM3+d’15’; Указываем на младший байт последнего элемента NUM3
movlw 8; Инициализируем счетчик цикла
movwf COUNT
M_LOOP movf POSTDECO,w; Берем NUM1[n]
mulwf POSTDEC1; Умножаем на NUM2[n]
movlw -8; Задаем смещение для обращения к старшим байтам NUM3
movf f PRODH,PLUSW2; Сохраняем старший байт произведения
movf f PRODL,POSTDEC2; Сохраняем младший байт произведения
decfsz COUNT,f; Возвращаемся к началу цикла
goto MLOOP
return
Основной в программе является команда mulwf. Эта команда формирует в регистрах специального назначения PRODH: PRODL 16-битное произведение, получаемое перемножением содержимого рабочего регистра и заданного регистра данных. Режим косвенной адресации с постдекрементом используется как для пересылки первого сомножителя в рабочий регистр, так и для указания второго сомножителя.
Для копирования содержимого регистра PRODL в младший байт текущего элемента массива NUM3[], а регистра PRODH — в старший байт, в программе используется команда movff, осуществляющая пересылку между двумя регистрами данных. Эта команда, занимающая два слова памяти программ (см. стр. 589), использует для идентификации регистра-источника и регистра-приемника 12-битные адреса, что позволяет ей обращаться к любой ячейке памяти данных, не используя механизм банков. Сначала содержимое PRODH копируется в старший байт элемента массива с использованием режима относительной косвенной адресации. Поскольку в рабочий регистр было предварительно записано число -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, если величина втекающего или вытекающего тока превышает паспортные значения или если нагрузка имеет большую емкость, а период переключения достаточно мал, то результат выполнения команды типа «чтение-модификация-запись», примененной к регистру PORTX, будет непредсказуем. Манипулирование содержимым регистра защелки вместо содержимого соответствующего регистра порта даст нам определенную независимость от условий работы схемы. Например, команда btfsc LATB,7 пропустит следующую команду, если 7-й бит порта В сброшен, даже если вывод RB7 окажется подтянутым к ВЫСОКОМУ уровню из-за слишком большого вытекающего тока. Очевидно, что в этом случае надежность программы будет гораздо выше, нежели при использовании эквивалентной команды btfsc PORTB,7.
При записи в регистр LATX или соответствующий ему регистр PORTX изменяется состояние триггера данных. Таким образом, команда movwf LATB по своему действию идентична команде movwf PORTB.
Рис. 16.6. Упрощенная схема одной линии порта ввода/вывода микроконтроллеров старшего семейства
Таймер 0
Таймер 0, который практически в неизменном виде перешел из семейства младшего уровня в семейство среднего уровня, теперь стал 16-битным и обзавелся собственным предделителем, не связанным с постделителем сторожевого таймера. При необходимости этот таймер можно переключить в 8-битный режим с помощью нового регистра управления T0CON.
Таймер 3
В микроконтроллерах старшего семейства реализован дополнительный 16-битный таймер, который похож по своей структуре на Таймер 1. Таймер 3 может тактироваться от внешнего низкочастотного генератора Таймера 1, который также может использоваться в качестве системного при необходимости уменьшения потребляемого тока. Каждый из модулей ССР может работать как с Таймером 1, так и с Таймером 3, что дает нам возможность использования двух независимых временных шкал.
Некоторые представители старшего семейства имеют более крупные корпуса и больший ассортимент периферийных модулей, чем рассматриваемые модели. Так, 80-выводной микроконтроллер PIC18F8720 имеет память программ объемом 128 Кбайт, память данных объемом 3840 байт, EEPROM объемом 1024 байта и предоставляет пользователю до 68 линий ввода/вывода. На входе модуля 10-битного АЦП в этой модели имеется 16-канальный мультиплексор. Кроме того, в данном микроконтроллере реализовано два модуля USART и пять модулей CCP/PWM, а также дополнительный 8-битный таймер.
Обработка прерываний
Как и в моделях среднего семейства, каждое периферийное устройство может генерировать запрос на прерывание. Вдобавок к внешнему прерыванию INT, которое теперь называется INTO, появилось два новых внешних прерывания — INT1 (вывод RB1) и INT2 (вывод RB2). Для поддержки этих внешних прерываний были введены три соответствующих регистра INTCON.
Наиболее заметным отличием в системе прерываний стало появление двух уровней приоритета. В микроконтроллерах среднего уровня при обработке запроса какого-либо разрешенного прерывания все прерывания от других источников автоматически запрещались из-за сброса бита GIE регистра INTCON. При выходе из обработчика прерывания по команде retfie бит GIE устанавливался снова, чтобы можно было обработать отложенные или последующие запросы прерываний. Несмотря на то что такой механизм необходим для предотвращения конфликтов между запросами прерываний, он может вызвать определенные проблемы. Возьмем, к примеру, биомедицинский монитор, в котором обработчик прерывания используется для управления достаточно медленной линией связи, предназначенной для передачи телеметрических данных в центральный процессор с периодом в один час. Представьте, что у пациента произошла остановка сердечной деятельности. Приоритеты здесь очевидны, однако датчик, формирующий последнее прерывание, окажется заблокированным!
В микроконтроллерах расширенного семейства каждому источнику прерывания сопоставлено три бита[190]. Так, у модуля АЦП имеется флаг прерывания ADIF (PIR1 [6]) для индикации запроса прерывания, бит маски ADIE (Р1Е[6]) для разрешения прерывания от этого источника, а также бит приоритета ADIP (IPR1 [6]), определяющий приоритет прерывания от данного источника: ADIP = 1 — высокий приоритет (состояние после сброса по питанию), ADIP = 0 — низкий приоритет.
При возникновении запроса прерывания от источника с низким приоритетом, происходит переход по вектору низкоприоритетных прерываний, расположенному по адресу h’018’, и сбрасывается бит GIEL (глобальное разрешение прерываний с низким приоритетом) в INTCON[6]. В результате обработка любого другого прерывания с низким приоритетом будет невозможна до завершения обработки текущего прерывания. Однако если во время обработки низкоприоритетного прерывания будет получен запрос прерывания от источника с высоким приоритетом, то выполнение текущего обработчика приостановится и процессор перейдет по вектору высокоприоритетного прерывания, расположенному по адресу h’008’ (что соответствует адресу слова h’004’ вектора прерывания предыдущих семейств). Одновременно с этим сбрасывается бит GIEH (глобальное разрешение прерываний с высоким приоритетом) в INTCON[7], запрещая все прерывания независимо от их приоритетов.
При переходе к соответствующему вектору прерывания содержимое счетчика команд помещается на вершину стека, как и в семействе среднего уровня. Помимо этого, в трех скрытых регистрах, называемых иногда быстрым стеком (fast stack), сохраняется содержимое рабочего регистра, регистра STATUS и регистра BSL. Для восстановления сохраненного контекста одновременно со счетчиком команд необходимо, чтобы определенный бит слова команды retfie был установлен в 1. Для этого команда retfie записывается с параметром: retfie 1 или, более удобочитаемо, retfie FAST. Если же команда будет записана без параметров, то этот бит окажется сброшен и при возврате из обработчика прерывания будет восстановлен только счетчик команд.
Необходимо очень аккуратно использовать этот механизм быстрого сохранения/восстановления контекста, поскольку в быстром стеке может храниться только одна копия этих системных регистров. Если запрос прерывания с высоким приоритетом прервет выполнение обработчика прерывания с низким приоритетом, то содержимое быстрого стека будет перезаписано новыми значениями! Поэтому при наличии в программе прерываний с разными приоритетами команда retfie FAST должна использоваться только в обработчиках прерываний с высоким приоритетом. Если же прерывания в программе вообще не используются, то быстрый стек можно использовать для сохранения контекста при вызове обычных подпрограмм. Так, по команде call FAST происходит вызов подпрограммы с одновременным сохранением контекста, а по команде return FAST — возврат из подпрограммы с восстановлением контекста.
В трех регистрах INTCON хранятся флаги прерываний, биты маски и приоритета для прерывания от Таймера 0, прерывания по изменению состояния порта В и трех внешних прерываний. Управление остальными прерываниями осуществляется с помощью регистров PIR1:2, РIЕ1:2 и IRP1:2. При включении питания схема обработки прерываний работает так же, как и неприоритетная схема среднего семейства, т. е. состояние битов приоритета игнорируется. Кроме того, для разрешения работы приоритетной системы прерываний должен быть установлен в 1 бит IPEN регистра управления сбросом RCON (RCON[7]).
Система команд
В Табл. 16.1 перечислены все 75 команд, поддерживаемые ядром PIC18. Сравнивая эту таблицу с приведенной в Приложении Г, можно заметить, что сохранились все предыдущие команды, за исключением команды clrw. Эта команда стала избыточной, поскольку рабочий регистр теперь может обрабатываться как обыкновенный регистр данных и, соответственно, его сброс может быть осуществлен командой clrf WREG. Также была расширена функциональность ряда старых команд, например, в части воздействия на флаги отрицательного значения (N) и переполнения (OV). У нескольких команд появились расширенные варианты; например, команда addwfс прибавляет содержимое рабочего регистра к указанному регистру данных с учетом переноса (см. Программу 16.2).
Условные обозначения:
√ - Воздействует на флаг; LLL - 12-битная константа; fn - n-й бит регистра; w - Рабочий регистр; TOS - Вершина стека; != - Условие неравенства; SP - Указатель стека; offset - Смещение ±128 слов; • - Не воздействует на флаг; d - Адресат: 0 = w, 1 = f; PC - Счетчик команд; f - Регистр данных; (TOS) - Содержимое вершины стека; ++ - Инкрементирование; # - Константа; Offset - Смещение ± 1024 слова; LL - 8-битная константа; b - Обращение к ОЗУ посредством BSR; РС++ - Пропуск следующей команды; WDT - Сторожевой таймер; == - Условие равенства; -- - Декрементирование; ааа - Адрес; GIE - Бит глобального разрешения прерываний
Чтобы завершить главу, перечислим основные отличия нового набора команд, используя то же деление по выполняемым функциям, что и в главе 5.
Команды пересылки данных
Наиболее важным нововведением в этой группе команд является команда movff, которую мы уже использовали в Программе 16.1. Эта команда позволяет копировать содержимое любого регистра-источника в любой регистр-приемник, невзирая на сегментированную структуру памяти данных и не используя рабочий регистр. Поскольку для указания каждого из регистров требуется полный 12-битный адрес, команда movff занимает два слова в памяти программ и выполняется за два машинных цикла. Двоичный код этой команды выглядит следующим образом:
Четыре старших бита второго слова b’1111’ имитируют код операции команды nор[191]. Это отличительная особенность всех двухсловных команд, позволяющая избежать проблем при переходе на середину такой команды (в качестве примера см. Программу 16.4).
Команда lfsr, также занимающая два слова в памяти программ, загружает 12-битную константу (значение адреса) в один из трех регистров косвенной адресации FSR, как показано в Программе 16.1. Команда lbsr является однословной.
Команды арифметических операций
Одним из назначений микроконтроллеров PIC18XXX является реализация приложений начального уровня[192] для цифровой обработки сигналов (DSP, ЦОС) в реальном масштабе времени. Задачи ЦОС требуют значительной вычислительной мощности, поэтому эта категория команд претерпела, наверное, самые большие изменения.
Одним из наиболее вопиющих недостатков набора команд младшего и среднего семейств является отсутствие в нем команд сложения с учетом переноса и вычитания с учетом заема. По этой причине операции сложения и вычитания многобайтных чисел получаются достаточно громоздкими и медленными. Команда addwfс прибавляет содержимое рабочего регистра W к содержимому указанного регистра данных плюс значение бита переноса. Результат, как и прежде, помещается либо в рабочий регистр, либо обратно в регистр данных. При этом значение бита переноса изменяется соответствующим образом. Для примера в Программе 16.2 приведен код подпрограммы, выполняющей сложение двух 3-байтных чисел и получающей 4-байтный результат. За исключением операции сложения младших байтов, при переходе к старшим байтам учет бита переноса осуществляется естественным образом, независимо от требуемой точности. Без такой команды каждое сложение пришлось бы сопровождать операцией условного инкрементирования.
Программа 16.2. Сложение трехбайтных чисел
TP_ADD clrf NUM3_V; Обнуляем старший байт результата
movf NUM1_L,w; Берем младший байт 1-го числа
addwf NUM2_L, w; Прибавляем младший байт 2-го числа
movwf NUM3_L; Сохраняем младший байт результата
movf NUM1_H,w; Берем средний байт 1-го числа
addwfс NUM2_H,w; Прибавляем средний байт 2-го числа
movwf NUM3_H; Сохраняем средний байт результата
movf NUM1_U,w; Берем старший байт 1-го числа
addwfс NUM2_U,w; Прибавляем старший байт 2-го числа
movwf NUM3_U; Сохраняем старший байт результата
btfsc STATUS,С; Пропускаем, ЕСЛИ нет переноса
incf NUM3_V,f; ИНАЧЕ прибавляем 1
Аналогичным образом команда subwfb вычитает из заданного регистра содержимое W плюс бит заема, сформированный предыдущими арифметическими операциями. Также появилась команда subfwb, которая выполняет вычитание в обратном порядке, т. е. рабочий регистр вычитается из регистра данных.
Все команды сложения, вычитания, а также команды инкрементирования и декрементирования обеспечивают полную поддержку чисел со знаком, представленных в дополнительном коде (в сочетании с флагами N и OV). Команды incf и decf теперь воздействуют на все флаги арифметических операций, упрощая, таким образом, выполнение многобайтных вычислений (напоминаю, раньше эти команды воздействовали только на флаг Z). Новая команда negf вычисляет дополнительный код числа, находящегося в заданном регистре. Соответственно если в регистре находилось число со знаком, то значение знака меняется на противоположное.
Одной из наиболее важных операций при цифровой обработке сигналов является операция умножения. Поэтому в систему команд были добавлены две команды умножения, вычисляющие за один машинный цикл 16-битное произведение двух беззнаковых 8-битных чисел, как показано в Программе 16.1. На базе этих команд можно создавать относительно короткие подпрограммы для умножения многобайтных чисел, представленных в дополнительном коде.
Команда десятичной коррекции daw упрощает реализацию сложения чисел, представленных в BCD-коде. Как мы уже говорили на стр. 111, при выполнении операций над такими числами с использованием обычной двоичной арифметики результат необходимо корректировать, чтобы исключить шесть избыточных значений Ь’1011’…Ь’1111’. Команда 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 b’0110’; Конфигурируем порт А как цифровой
movwf ADCON1
bcf LATA,0; Начнем с НИЗКОГО уровня на выводе RA0
bcf TRISA,0; Делаем RA0 выходом
; Где-то в программе ----------
movlw d’40’; Загружаем в W число 40
LOOP btg LATA,0; Изменяем состояние вывода RA0 1~
decfsz WREG,f; Декрементируем до нуля 1|3~
goto LOOP; ИНАЧЕ выходим из цикла 2~
... ...; Далее
В ядре PIC18 команда goto занимает два слова памяти программ. Поэтому при выполнении команды decfsz произойдет переход на второе слово команды goto. Именно по этой причине машинный код второго слова всех четырех двухсловных команд таков, что при непосредственном переходе на это слово оно интерпретируется как команда nор. Из-за этого время выполнения команды увеличивается на один машинный цикл.
И наконец, команда setf предоставляет программисту возможность непосредственно устанавливать все биты заданного регистра в 1, дополняя, таким образом, команду clrf, имевшуюся в предыдущих семействах.
Команды логических операций и операций сдвига
В данной категории изменения коснулись только группы команд циклического сдвига. Как мы видели из Рис. 5.13 (стр. 148), команды rlf и rrf сдвигают содержимое заданного регистра через бит переноса. Новые же команды осуществляют сдвиг в обход этого бита. Эти команды имеют мнемоники rIncf (сдвиг влево, не через флаг переноса) и rrncf (сдвиг вправо, не через флаг переноса). Команды сдвига, доставшиеся в наследство от семейства среднего уровня, для единообразия были переименованы в rlcf и rrcf. Еще раз напоминаю, что, поскольку к рабочему регистру можно обращаться как к регистру данных, его содержимое можно сдвигать точно так же, как и содержимое любого другого регистра.
Команды передачи управления
Все команды, имевшиеся в предыдущих семействах, т. е. команды goto, call и три команды возврата, были сохранены. Однако первые две из них теперь занимают два слова в памяти программ и способны осуществлять переход в пределах 20 Мелов, позволяя, таким образом, забыть о страничной организации памяти программ, которая имела место в микроконтроллерах с ядром PIC16. Кроме того, команда call может теперь сохранять контекст программы в теневом стеке (если прерывания не используются) при ее вызове с параметром FAST. Возврат из подпрограммы, вызванной таким образом, осуществляется командой return FAST.
Большинство переходов, осуществляемых с помощью команд goto, относительно короткие. К примеру, команда goto LOOP в Программе 16.4 возвращается назад всего на две команды. Новая однословная команда bra позволяет осуществлять переход в пределах —1023…+1024 слов. Таким образом, используя вместо goto LOOP команду bra LOOP, мы экономим одно слово памяти программ, хотя время выполнения команды остается равным двум машинным циклам. Аналогичным образом команда относительного вызова подпрограмм rcall позволяет осуществлять вызов близлежащих подпрограмм, правда, она не имеет возможности сохранения контекста.
К командам безусловного перехода добавились десять команд условного перехода, в которых переход осуществляется по определенному значению того или иного флага регистра STATUS, за исключением флага DC. В принципе проверить состояние этих флагов можно с помощью команд btfssn и btfsc, однако все, что мы получим в результате их выполнения, — это пропуск единственного слова памяти программ. А команды условного перехода позволяют осуществлять переход в пределах +128…—127 слов при установке определенного флага регистра STATUS, как, например, команда Ьс (переход, если флаг С установлен), или его сбросе, например, Ьnс (переход, если флаг С сброшен). Предположим, что на порт С микроконтроллера подается значение от датчика температуры, представленное в дополнительном коде. В следующем фрагменте кода при положительной температуре выполняется сброс регистра, названного SIGN, а при отрицательной температуре все биты этого регистра устанавливаются в 1. Модуль значения температуры помещается в регистр TEMPERATURE.
clrf SIGN; Обнуляем регистр признака знака и копируем
movf f PORTC,TEMPERATURE; принятое значение температуры
movf TEMPERATURE,f; Проверим на ноль или отрицательное число
bnn NEXT; Если > 0, ТО обходим инвертирование
negf TEMPERATURE,f; ИНАЧЕ меняем знак числа
setf SIGN; и устанавливаем биты регистра признака знака
NEXT…..; Продолжаем
Команда bnn NEXT выполняет переход к метке NEXT при положительном значении. В противном случае меняется знак числа и с помощью команды setf в регистр SIGN заносится число h’FF’.
На стр. 139 мы с вами видели, что для сравнения двух беззнаковых чисел, скажем W и содержимого регистра данных, необходимо вычесть одно число из другого с последующей проверкой состояния флагов С и Z. Три новые команды сравнения/пропуска выполняют эту проверку автоматически. Команда cpfseq пропускает следующую команду, если беззнаковое число в заданном регистре равно числу в рабочем регистре, cpfsgt — если оно больше, чем число в W, a cpfsit — если это число меньше находящегося в W.
Возьмем для примера код системы контроля уровня топлива, приведенный на стр. 140. Если в баке остается менее 20 л, то включается сигнальная лампочка, а если менее 5 л — звуковой сигнал. Теперь этот же код будет выглядеть следующим образом:
ALARM bcf DISPLAY,BUZZER; Выключаем пищалку
bcf DISPLAY,LAMP; Выключаем лампочку
movlw 4; Проверим уровень 5 литров
cpfsgt FUEL; Пропускаем, ЕСЛИ > 4 литров
bsf DISPLAY,BUZZER; ИНАЧЕ включаем звуковой сигнал
movlw d’19’; Проверим уровень 20 литров
cpfsgt FUEL; Пропускаем, ЕСЛИ >19 литров
bsf DISPLAY,LAMP; ИНАЧЕ включаем лампочку
NEXT…..; Продолжаем
Не забывайте, что данные команды сравнения корректно работают только с беззнаковыми числами. Для сравнения чисел, представленных в дополнительном коде, необходимо по-прежнему выполнять операцию вычитания и проверки флагов N, OV и Z.
И последняя новая команда в этой группе, tstfsz, проверяет содержимое заданного регистра (не изменяя его) и пропускает следующую команду, если оно равно нулю. К командам incfsz и decfsz прибавились команды infsnz и dcfsnz, выполняющие пропуск команды, если результат инкрементирования/декрементирования не равен нулю.