Система команд микроконтроллера AVR MEGA128
Базовый набор команд языка ASSEMBLER для микроконтроллеров AVR содержит 120 инструкций, которые можно разделить на 4 группы: команды пересылки данных; арифметические и логические команды; инструкции для работы с битами; команды управления ходом исполнения программы. Команды пересылки данных. Группа команд пересылки данных включает в себя инструкции по загрузке значений констант, пересылки данных типа регистр – регистр, регистр – память, регистр – порт ввода/вывода. Команды данной группы являются двухоперандными, причем первым операндом является приемник данных, а вторым – источник данных. Команда загрузки констант ldi R, K применяется для записи непосредственного значения К в регистр – приемник R. В качестве регистра – приемника могут использоваться регистры общего назначения R16 – R31. Если константа представлена в двоичной или шестнадцатеричной системах счисления, то перед значением константы К необходимо указать спецификатор системы счисления 0b – для двоичной, 0х – для шестнадцатеричной соответственно. Примеры: ldi R16, 125 загрузка в R16 десятичного числа 125; ldi R20, 0xFF загрузка в R20 шестнадцатеричной константы FFh; ldi R23, 0b11011001 загрузка в R23 двоичной константы 11011001. Команда пересылки данных между регистрами mov Rd, Rs используется для пересылки значения из регистра-источника Rsв регистр-приемник Rd. Операнды в команде являются исключительно регистрами общего назначения R0 – R31. Примеры: mov R16, R0 загрузка в R16 значения из регистра R0; mov R17, R20 загрузка в R17 значения из регистра R20. В командах пересылки данных между регистром и ячейкой памяти используется механизм косвенной адресации, при котором адрес ячейки памяти заносится в один из 16-разрядных регистров Х,Y,Z (cм. рисунок 1.3). Форматы команд: ld R8, (R16) – загрузка данных из ячейки памяти, адрес которой находится в 16-разрядном регистре R16, в регистр общего назначения R8; st (R16), R8 – загрузка данных из регистра общего назначения R8 в ячейку памяти, адрес которой находится в 16-разрядном регистре R16; ldd R8, (R16+Q) – загрузка данных в регистр общего назначения R8 из ячейки памяти, адрес которой находится как сумма значения, находящегося в 16-разрядном регистре R16 , и смещения Q; std (R16+Q), R8 – загрузка данных из регистра общего назначения R8 в ячейку памяти, адрес которой находится как сумма значения, находящегося в 16-разрядном регистре R16 , и смещения Q; ld R8, (R16) – загрузка в регистр общего назначения R8 данных из ячейки памяти, адрес которой находится в 16-разрядном регистре R16;
Примеры: ld R2, X загрузка в R2 значения из памяти по адресу, указанному в Х; st Y, R5 загрузка значения из регистра R5 в память по адресу, указан- ldd R5, Z+1 загрузка в R5 байта из памяти по адресу Z+1; std Y+4, R7 загрузка байта из регистра R7 в память по адресу Y+4. Для обращения к портам ввода/вывода в микропроцессоре предусмотрены специальные команды in и out: in R, P ввод данных из порта с адресом Р в регистр общего назначения R; out P, R вывод данных из регистра общего назначения R в порт с Примеры: in R10, 0x15 ввод данных из порта с адресом 15h в регистр общего назначения R10; out 0x2F, R8 вывод данных из регистра общего назначения R8 в порт с адресом 2Fh; Арифметические и логические команды. Для работы с целыми двоичными числами целочисленное АЛУ микроконтроллера AVR MEGA128 поддерживает более десятка арифметических и логических команд. Основными арифметическими командами являются инструкции сложения, вычитания и умножения. Операндами в командах данной группы могут быть только регистры общего назначения. Результат операции (кроме умножения) записывается по адресу первого операнда. Основные команды для выполнения операций сложения, вычитания и умножения (для чисел без знака): add Rd, Rs команда сложения (addition), действие: Rd=Rd+Rs; sub Rd, Rs команда вычитания (subtraction), действие: Rd=Rd–Rs; mul Rd, Rs команда умножения (multipl.), действие: R1, R0=Rd*Rs. Команды изменяют флаги переноса С, переполнения V, знака N, S, и нуля Z. При выполнении операции умножения n -значных чисел местонахождение результата разрядностью 2n фиксировано и не указывается в команде: при умножении двух байтов результат размером в слово заносится в регистровую пару (R1, R0), в R0 – младшее слово, в R1 – старшее слово. Примеры: add R10, R15 R10 = R10 + R15; sub R2, R7 R2 = R2 – R7; mul R5, R16 R1,R0 = R5 * R16. Команды положительного и отрицательного приращения (инкремента и декремента): inc R инкремент (increment), действие R=R + 1; dec R декремент (decrement), действие R=R – 1. В качестве операнда в этих командах допускается использовать только регистр общего назначения. Примеры: inc R20 действие R20 = R20 + 1; dec R16 действие R16 = R16 – 1. Основными логическими командами микроконтроллера AVR MEGA128 являются: or Rd, Rs логическое “или”; действие: Rd = Rd or Rs; and Rd, Rs логическое “и”; действие: Rd = Rd and Rs; eor Rd, Rs “исключающее или”; действие: Rd = Rd eor Rs. Данные команды выполняют операции поразрядного логического “или”, логического “и”, “исключающего или” (см. таблицу 1.2) над операндами, находящимися в регистрах общего назначения, причем результат записывается по адресу первого операнда. Примеры: or R7, R11 действие: R7 = R7 or R11; and R10, R11 действие: R10 = R10 and R11; eor R25, R30 действие: R25 = R25 eor R30.
Таблица 1.2 – Таблицы истинности логических операций or, end, eor
Указанные команды используются для выполнения операций поразрядного маскирования: or – для установки единиц в заданных разрядах, and – для установки нулей, еor – для выяснения совпадений значений битов первого операнда с маской. Команды изменяют флаги нуля, Z, знака N и переполнения V. Примеры поразрядных логических операций, иллюстрирующие применение механизма маскирования битов, приводятся в таблице 1.3. Таблица 1.3 – Примеры поразрядных логических операций
Команда поразрядного инвертирования: com R логическое отрицание; действие: R = 0b11111111 – R, выполняет изменение значений двоичных разрядов операнда (регистр общего назначения) на противоположные. Пример: com R3 действие: R3 = 0b11111111 – R3. Полный перечень арифметических и логических команд микроконтроллера AVR MEGA128 приводится в Приложении Б.
Команды для работы с битами. Дополняют совокупность логических операций команды сброса, установки и проверки значений отдельных битов. Команды сброса cbi P, n и установки sbi P, n битов предназначены для присваивания значений 0 и 1 отдельным битам портов ввода/вывода соответственно. Первым операндом в этих командах является адрес порта ввода/вывода, вторым – номер бита (от 0 до 7). Примеры: cbi 0x17, 5 действие: 0x175 = 0; sbi 0x40, 1 действие: 0x401 = 1. Команда логического сдвига lsl R осуществляет сдвиг влево на одну позицию всех битов операнда, а в младший разряд добавляется нуль. Старший бит операнда поступает в флаг переноса С. В качестве операнда могут использоваться только регистры общего назначения. Команда lsr R выполняет сдвиг вправо на одну позицию всех битов операнда, а в старший разряд добавляется нуль. Младший бит операнда поступает в флаг переноса С. Механизм работы и синтаксис аналогичен команде lsl. Примеры использования команд логического сдвига: lsl R17 выполнить логический сдвиг влево всех разрядов в R17; lsr R9 выполнить логический сдвиг вправо всех разрядов в R9. Поменять местами младшую и старшую тетрады байта, загруженного в регистр общего назначения, можно с помощью команды swap R. Следующий фрагмент иллюстрирует действие команды swap: ldi R19, 0b01001101 загрузить константу 0b01001101 в регистр R19; swap R19 в результате исполнения команды swap в регистре R19 будет сохранено значение 0b11010100. Дополняют перечень команд для работы с битами инструкции для сброса/установки значений флаговых разрядов в регистре статуса SREG, описание которых приводится в Приложении Б. Команды сравнения, условного и безусловного перехода. Команда сравнения cp Rd, Rs – осуществляет действие Rd–Rs и устанавливает флаги нуля Z, отрицательного результата N, переполнения V, переноса C и дополнительного переноса H. Результат не сохраняется по адресу первого операнда, а только формируются флаги. Операндами могут быть только регистры общего назначения. Команды условного перехода вызываются сразу после команд сравнения (или других инструкций, вызывающих изменения битов регистра состояния SREG) и на основе анализа флагов осуществляют переход по указанному адресу (метке) в памяти команд. Наиболее распространенными среди команд этой группы являются: breq M переход на М, если равно; brne M переход на М, если неравно; brlo M переход на М, если меньше; brsh M переход на М, если больше или равно.
Пример совместного использования команд сравнения и условного перехода: cp R1, R5 сравнить значения в регистрах R1 и R5; breq lbl1 выполнить переход на метку lbl1, если значения в регистрах R1 и R5 равны (R1–R5=0). Команда rjmp М осуществляет безусловный переход по указанному rjmp lbl2 безусловный переход на метку lbl2. Команда jmp М осуществляет безусловный переход по указанному rjmp lbl3 безусловный переход на метку lbl3. Полный перечень команд сравнения и перехода приводится в Приложении Б.
1.1.4 Синтаксис и основные операторы языка С Сложные программные проекты можно более компактно описывать (по сравнению с языком Assembler) с помощью языка программирования С, который обладает возможностями языков низкого и высокого уровня, а также большой библиотекой функций. Алфавит языка С состоит из строчных и заглавных букв латинского алфавита, цифр (0 – 9) и специальных символов. Причем, при записи идентификаторов и ключевых слов необходимо учитывать регистр символов. Так, идентификаторы sysreg и Sysreg не являются одинаковыми. Все ключевые слова должны быть набраны строчными буквами. Разделителем между операторами является символ “;” Закомментированные строки начинаются с идущих подряд двух символов “//”. Константы в языке С декларируются с помощью директивы #define в соответствие с синтаксисом: #define имя константы значение. При работе с аппаратными средствами удобно записывать константы в двоичной и шестнадцатеричной формах. Перед значением констант ставятся символы 0b и 0x для двоичного и шестнадцатеричного представлений соответственно. Регистр символов при записи шестнадцатеричных констант не имеет значения. Примеры объявления констант: #define k 25; // объявлена десятичная константа k=25; #define KX 0xF5; // объявлена шестнадцатеричная константа KX=F5h; #define k2 0x6e; // объявлена шестнадцатеричная константа k2=6Eh; #define KB 0b1010; // объявлена двоичная константа KB=0b1001. Базовыми целыми типами данных в языке С являются: сhar (размер 1 байт, диапазон значений – 128 ¸ 127) и int (размер 2 байта, диапазон значений – 32768 ¸ 32767). Модификатор unsigned, записывыемый перед именем базового типа, позволяет интерпретировать значения приведенных выше типов данных как числа без знака: unsigned сhar (размер 1 байт, диапазон значений 0 ¸ 255), unsigned int (размер 2 байта, диапазон значений 0 ¸ 65535). При этом старший разряд является битом данных, а не знаковым битом числа. Все переменные должны быть декларированы до их использования в программе. При записи имен переменных необходимо учитывать регистр символов. Формат объявления переменной: тип данных имя переменной [=начальное значение]. Если несколько переменных имеют одинаковый тип данных, то при объявлении их идентификаторы можно перечислить через запятую. Начальное значение переменной можно не указывать. Примеры: char A=10; int B, C, D; unsigned int E, F. Рассмотрим основные операторы языка С. Оператор присваивания имеет следующий синтаксис: идентификатор = выражение. В выражениях над операндами могут использоваться следующие арифметические и логические операции языка С: Арифметические операции: + сложение; – вычитание; * умножение, / деление. Логические операции: || логическое ИЛИ; && логическое И; ! логическое НЕ; | побитовая операция ИЛИ; & побитовая операция И; ^ побитовая операция исключающее ИЛИ; ~ побитовая операция НЕ; << логический сдвиг влево; >> логический сдвиг вправо. Язык С относится к строго типизированным языкам программирования: переменным одного типа нельзя непосредственно присваивать значения другого типа данных. Для однозначного определения приоритета операций в выражениях необходимо использовать круглые скобки (). Пример оператора присваивания: A = (B+C)*D. Оператор условия if/else позволяет выполнять одно из двух действий в зависимости от условия. Синтаксис оператора: if (условие) выражение1; [ else выражение2]. Условие представляет собой выражение, заданное с помощью операций отношения: == равно; != не равно; < меньше; <= меньше или равно; > больше; >= больше или равно. Если условие истинно, то выполняется выражение1, если ложно – выражение2. Часть else может отсутствовать. Если, в зависимости от условия необходимо выполнить фрагмент программы, состоящий из нескольких операторов, то их необходимо поместить в фигурные скобки { }. Пример использования оператора условия: if (A==B) {C=D+E; I=N+5}; else {C=D–E; I=N–5;}. Оператор выбора switch/case позволяет избирательно выполнить фрагмент программного кода, в зависимости от значения выражения. Формат оператора: switch (целочисленное выражение) { case константа1: выражение1; break; case константа2: выражение2; break; ... case константаN: выражениеN; break; [ default: действия по умолчанию;] } Оператор break должен находиться во всех ветвях, в противном случае нарушится выборочное выполнение команд в ветвях после case. Ветвь default можно не указывать. Пример использования оператора выбора: switch (num) { case 0: A=B+C; C=D+2; break; case 1: A=B–C; C=D+10; break; case 5: A=B*C; C=D+15; break; } Оператор цикла с параметром for используется в тех случаях, когда заранее известно количество итераций цикла. Синтаксис оператора цикла for приведен ниже: for (инициализирующее выражение; условное выражение; модифицирующее выражение) { операторы тела цикла; } Рассмотрим работу цикла for: инициализирующее выражение при первом запуске цикла присваивает начальное значение счетчику цикла; затем анализируется условное выражение (цикл выполняется пока условие истинно). Каждый раз после всех строк тела цикла выполняется модифицирующее выражение, в котором происходит изменение счетчика цикла. Выход из цикла произойдет, как только условное выражение получит значение false. Пример оператора цикла: s=0; m = 1; for (i=1; i<=10; i++) { s=s+i; m=m*i; } Оператор цикла с предусловием while применяется, когда число повторений неизвестно, но необходимо выполнить некоторое условие. Формат оператора приведен ниже: while (условное выражение) { операторы тела цикла; } Оператор начинается с ключевого слова while, за которым следует логическое выражение, возвращающее значения false или true. Операторы, заключенные в фигурных скобках, образуют тело цикла. Пример использования оператора цикла while: a=0; while (a<10) { b=(c+d)*a–f; a=a+2; } Подпрограммы на языке С оформляются в виде функций. Описание функции имеет следующий синтаксис: [Тип возвращаемого значения] имя функции ([список параметров]) { [декларации локальных переменных] операторы тела функции; [ return выражение;] } Если функция не возвращает значения, то тип возвращаемого значения не указывается и секция return не используется. При отсутствии параметров после имени функции обязательно указываются пустые круглые скобки (). Тело функции заключается в фигурные скобки { }. Переменные, объявленные в теле функции, являются локальными (видимыми только в пределах тела функции). Функции, возвращающие значения, можно использовать в правой части операторов присваивания. Рассмотрим пример декларации функции, возвращающей, в зависимости от значения аргумента С, сумму или разность двух целых чисел: int sum (int A, int B, char C) { if (C>=0) return A+B; else return A–B; } Пример функции, не имеющей аргументов и не возвращающей значения: init_data () { A=10; B=100+А; }
При программировании микроконтроллера часто возникает необходимость использовать ассемблерный код в программе, написанной на языке С. В этом случае фрагмент программы, составленный из ассемблерных операторов, помещается в операторные скобки: #asm операторы языка Assembler #endasm; Пример фрагмента программы на языке Assembler: #asm mov r1,r5 ldi r17,0xf5 #endasm; Одиночный ассемблерный оператор в теле С – программы может быть записан в виде директивы #asm("Оператор языка Assembler”);. Пример: #asm ("out 0x12,r16"); // выполнить команду out 0x12,r16. Функции, написанные на ассемблере, возвращают значения через регистр R30 для типов char и unsigned char, и регистровую пару (R31, R30) для типов int и unsigned int (в R31 – старший, а в R30 – младший байты). Параметры функции передаются через стек, на вершину которого указывает регистр Y, причем старший байт слова записывается по старшему адресу. Механизмы вызова и возврата из функции осуществляются средствами компилятора С. Директива #pragma warn- запрещает компилятору генерировать предупреждения о том, что функция не возвращает результат стандартным способом (с помощью оператора return). В качестве примера рассмотрим ассемблерную функцию, выполняющую суммирование двух целых чисел и возвращающую значение типа int (пояснения к ассемблерному коду прилагаются): int summa (int A, int B) { #asm ldd R27, Y+3 загрузить старший байт параметра А; ldd R26, Y+2 загрузить младший байт параметра А; ldd R25, Y+1 загрузить старший байт параметра В; ld R24, Y; загрузить младший байт параметра В; add R24, R26 выполнить суммирование младших байтов А и В; adc R25, R27 выполнить суммирование старших байтов А и В c учетом флага переноса С; mov R30, R24 записать младший байт суммы в регистр R30; mov R31, R25 занести старший байт суммы в регистр R31; #endasm } Вызов данной функции может осуществляться следующим образом: ... int С; C=summa (10, 15); ... Часто в ассемблерных подпрограммах возникает необходимость получить доступ к значениям переменных, объявленных в С–программе. Размещение переменных (в регистрах процессора или памяти данных) можно определить из файла с расширением *.map, имя которого совпадает с именем файла исходного кода программы. Данный файл генерируется компилятором и находится в одном каталоге с исходным модулем программы. Рассмотрим структуру программы на С. Текст исходного модуля программы начинается с директив препроцессора #include <имя файла.h>, с помощью которых в программный код проекта подгружаются файлы заголовков, содержащие объявления различных констант, идентификаторов и прототипы функций. Пример использования данной директивы, подгружающей заголовочный файл mega128.h: #include <mega128.h> Далее следуют объявления констант (с помощью директив #define) и глобальных переменных. Затем записываются декларации функций, используемых в тексте главной программы, расположенной в теле функции main(), с операторов которой начинается выполнение программы. Тело функции main() расположено внутри фигурных скобок {}.
1.1.5 Принципы программного управления светодиодами, подключенными к внешним выводам портов ввода/вывода микроконтроллера AVR ATMEGA128 Согласно технической документации на выходных линиях микроконтроллеров AVR при уровне напряжения, соответствующем “логическому нулю”, ток нагрузки составляет порядка 20 мА. Стандартные светодиоды потребляют ток в пределах 3¸20 мА при рабочем напряжении порядка 1,5¸4 В. Это позволяет непосредственно подключать светодиод к выходной линии порта последовательно с ограничивающим ток резистором (см. рисунок 1.5). Второй вывод цепи необходимо подсоединить к положительному полюсу источника питания (+5 В). Для управления светодиодом необходимо подавать на соответствующий вывод микроконтроллера уровни “логического нуля” или “логической единицы”. При появлении на выводе микроконтроллера, к которому подключен светодиод, уровня «логического нуля», падение напряжения на светодиоде будет достаточным для свечения. При формировании на соответствующем выводе микроконтроллера напряжения “логической единицы” падения напряжения на светодиоде не будет, и он будет погашен.
Рисунок 1.5 – Схема подключения светодиода к выходу порта ввода/вывода микроконтроллера AVR
Для управления уровнями напряжения на выходных линиях микроконтроллера можно применять алгоритмы маскирования или, непосредственно, команды для работы с битами (cbi, sbi), описанные в пункте 1.1.3 Прямое обращение к регистрам портов ввода/вывода на языке Assembler обеспечивается с помощью команд in и out (см. пункт 1.1.3). Компилятор языка С допускает использование идентификаторов регистров ввода/вывода: DDRX – регистр управления направлением передачи данных, PORTX регистр вывода данных, PINX – регистр ввода данных, где Х – обозначение порта ввода/вывода. Например, программно доступные регистры порта А обозначаются как: DDRA, PORTA, PINA (см. таблицу 1.1). Если линия порта ввода/вывода Х программируется на вывод, то в соответствующем бите регистра управления направлением передачи данных DDRX должна быть установлена 1, если на ввод – то 0. Рассмотрим примеры: unsigned char a, b;декларация переменных а и b размером в байт; DDRB=0b0101110;линии 0, 5 и 7 порта В запрограммированы на вывод данных, остальные (1,2,3,6) – на ввод. DDRD=0xFF;установка всех линий порта D в режим вывода данных; а=0х12;присвоить переменной а значение 12h; PORTD = a; вывести данные (значение переменной а) в порт вывода D. DDRС=0;установка всех линий порта С в режим ввода данных; b=PINС; записать данные из порта ввода С в переменную b. Для работы с отдельными разрядами регистров портов ввода/вывода на языке С можно использовать конструкции: POTTX.N и PINX.N, где N – номер бита. Например, PORTB.2=0; второй бит порта D сбросить в 0; PORTB.4=1; четвертый бит порта D установить в 1; PORTD.5=PINF.3;пятому биту порта D присвоить значение третьего бита порта F; Наиболее универсальным способом работы с отдельными разрядами регистров портов ввода/вывода является использование масок и поразрядных логических операций.
1.2 Описание лабораторной установки
Лабораторная работа выполняется в индивидуальном порядке. На каждом рабочем месте должны быть установлены: многофункциональный лабораторный макет на базе микроконтроллера AVR ATMEGA 128, ПЭВМ типа IBM PC/AT c инсталлированным программным обеспечением: операционной системой MS–WINDOWS v. 9x, 2000, XP и программатором на основе кросс-компилятора языка программирования C CodeVision AVR. 1.2.1 Описание лабораторного макета Лабораторный макет представляет собой универсальное устройство на базе 8-разрядного микроконтроллера AVR ATMEGA 128 (fт=11,0592 MГц), в состав которого входят (рисунок 1.6): микроконтроллер AVR ATMEGA128, графический ЖК-дисплей Toshiba T6963C, блок светодиодов, клавиатуры: 3´4 и 3´1 (количество столбцов ´ количество строк), АЦП, внешний аналоговый термодатчик, пьезоизлучатель, последовательный интерфейс RS-232C. Сопряжение лабораторного макета и программатора (ПЭВМ) обеспечивается с помощью последовательного интерфейса SPI, конструктивно использующего стандартный разъем Centronics DB-25. Общий вид передней панели лабораторного макета приведен на рисунке 1.7. Управление встроенным в макет графичес- Рисунок 1.6 – Структурная схема лабораторного макета на базе
|