- •Запись и выполнение простых программ
- •Задания для домашней подготовки
- •Задания к лабораторной работе
- •Организация условных переходов, подпрограмма и стек
- •Задания для домашней подготовки
- •Задания к лабораторной работе
- •Выполнение арифметических операций. Обмен данными с eeprom.
- •Задания для домашней подготовки
- •Задания к лабораторной работе
- •Организация работы с внешними устройствами. Порты ввода/вывода.
- •Регистр данных порта b - portb
- •Регистр направления данных порта b - ddrb
- •Выводы порта b - pinb
- •Задания для домашней подготовки
- •Задания к лабораторной работе
Выполнение арифметических операций. Обмен данными с eeprom.
Цель работы:изучение способов организации и исследование программ выполнения арифметических операций; изучение организации обмена данными микропроцессора с EEPROM.
Краткие сведения из теории
Из двух способов определения чисел с фиксированной и плавающей точкой, первый получил наибольшее распространение при программировании восьмиразрядных микропроцессоров. Это связано с отсутствием специальных команд, позволяющих оперировать микропроцессору числами с плавающей точкой.
Число с фиксированной точкой можно представить двоичными числами без знака, имеющими значения от 0 до 255. В свою очередь, двоичное восьмиразрядное число с фиксированной точкой можно представить как число со знаком, имеющее значение от –128 до +127. При этом отрицательные числа представляются в дополнительном коде, а старший, седьмой, разряд числа используется как знаковый. Такое представление чисел не позволяет выполнять арифметические операции с использованием переноса при сложении и заема при вычитании.
Проведение арифметических операций сложения, вычитания, умножения, деления, вычисления специальных функций рассмотрим на примерах соответствующих подпрограмм.
Программа сложения массива однобайтных чисел с получением двухбайтного результата – П-3.1. Слагаемые должны быть расположены в последовательных адресах памяти. Входными параметрами подпрограммы являются адрес первого слагаемого, записанный в регистровую пару Z, и число слагаемых, записанное в регистре R16. Выходным параметром подпрограммы является сумма, записанная в паре регистров R1:R0. Алгоритм программы сложения состоит в том, что после прибавления каждого элемента массива определяется переполнение R0 (флаг C=1), и если это имеет место, то содержимое регистра R1 увеличивается на 1. Таким образом, за счет суммирования единиц переноса получается старший байт суммы.
П-3.1
Адрес |
Маш. код |
Метка |
Мнемокод |
Комментарий | |
0000 |
ED0F |
MAIN: |
LDI R16,0xDF |
Главная программа |
Устанавливаем указатель стека |
0001 |
BF0D |
|
OUT 0x3D,R16 | ||
0002 |
E6E0 |
|
LDI R30,0x60 |
Устанавливаем указатель Z на начало массива данных | |
0003 |
27FF |
|
CLR R31 | ||
0004 |
E005 |
|
LDI R16,0x05 |
Указываем размер массива (5 элементов) | |
0005 |
D001 |
|
RCALL ADDB |
Вызов подпрограммы сложения | |
0006 |
9508 |
|
RET |
Завершение программы | |
|
|
|
|
| |
0007 |
2400 |
ADDB: |
CLR R0 |
Очищаем регистры выходного параметра R0 | |
0008 |
2C10 |
|
MOV R1,R0 |
и R1. | |
0009 |
9021 |
NEXT: |
LD R2,Z+ |
Загружаем из памяти по адресу Z(R31:R30) элемент массива в регистр R2; при этом Z увеличивается на 1. | |
000A |
0C02 |
|
ADD R0,R2 |
Прибавляем R2 к R0 (без учета переноса). | |
000B |
F408 |
|
BRCC NO_C |
Если нет переполнения – переход на NO_C, | |
000C |
9413 |
|
INC R1 |
иначе – увеличиваем R1 на 1. | |
000D |
950A |
NO_C: |
DEC R16 |
Уменьшаем кол-во элементов, которые осталось просуммировать. | |
000E |
F7D1 |
|
BRNE NEXT |
Если элементы еще есть, то переход на NEXT, | |
000F |
9508 |
|
RET |
иначе – возврат из подпрограммы ADDB. |
Микропроцессор может производить арифметические операции с числами двойной или большей длиной машинного слова. Так как МП имеет 8-разрядное АЛУ, то операции с такими числами должны проводиться по байтам начиная с младших байтов. Например, операция сложения чисел 17F5+3411 будет проводиться в следующем виде:
Старший байт |
Флаг С |
Младший байт |
Числа |
00010111 + |
|
11110101 + |
17F5 + |
00110100 |
|
00010001 |
3411 |
+1 |
1 (перенос) |
|
|
01001100 |
|
00000110 |
4C06 |
Операция вычитания чисел 6F5C-13C5 будет осуществляться в таком виде виде:
Старший байт |
Флаг С |
Младший байт |
Числа |
01101111 - |
|
01011100 - |
6F5C - |
00010011 |
|
11000101 |
13C5 |
-1 |
1 (заем) |
|
|
01011011 |
|
10010111 |
5B97 |
Из приведенных примеров видно, что при суммировании(вычитании) младших байтов чисел необходимо применять команду ADD (SUB), а для сложения (вычитания) остальных – команду ADC (SBC), которая будет учитывать состояние флага C регистра статуса SREG.
Программа нахождения разности чисел, имеющих одинаковую длину П-3.2. Входные параметры R16 – длина чисел в байтах, X – адрес младшего байта вычитаемого, Y – адрес младшего байта уменьшаемого. Каждое из чисел записывается последовательно в ячейки памяти, начиная с младших байтов. Результат заносится в область памяти, отведенную под вычитаемое. В случае, если уменьшаемое меньше вычитаемого, на выходе будет установлен флаг переноса.
П-3.2
Адрес |
Маш. код |
Метка |
Мнемокод |
Комментарий | |
0000 |
ED0F |
MAIN: |
LDI R16,0xDF |
Главная программа |
Устанавливаем указатель стека |
0001 |
BF0D |
|
OUT 0x3D,R16 | ||
0002 |
E6A0 |
|
LDI R26,0x60 |
Устанавливаем указатель X на младший байт вычитаемого | |
0003 |
27BB |
|
CLR R27 | ||
0004 |
E8C0 |
|
LDI R28,0x80 |
Устанавливаем указатель Y на младший байт уменьшаемого | |
0005 |
27DD |
|
CLR R29 | ||
0006 |
E004 |
|
LDI R16,0x04 |
Указываем размер числа (4 байта) | |
0007 |
D001 |
|
RCALL SUBB |
Вызов подпрограммы вычитания | |
0008 |
9508 |
|
RET |
Завершение программы | |
|
|
|
|
| |
0009 |
9488 |
SUBB: |
CLC |
Сбрасываем флаг переноса С для вычитания младших байтов чисел без переноса.. | |
000A |
900C |
NEXT: |
LD R0,X |
Загружаем байт вычитаемого числа из памяти. | |
000B |
9019 |
|
LD R1,Y+ |
Загружаем байт уменьшаемого числа из памяти с постинкрементом адреса. | |
000C |
0810 |
|
SBC R1,R0 |
Вычитаем с учетом переноса: R1=R1-R0-C | |
000D |
921D |
|
ST X+,R1 |
Результат записываем по адресу байта вычитаемого числа с постинкрементом адреса. | |
000E |
950A |
|
DEC R16 |
Уменьшаем количество байт, которые осталось обработать. | |
000F |
F7D1 |
|
BRNE NEXT |
Если байты еще есть, то переход на NEXT, | |
0010 |
9508 |
|
RET |
иначе – выход из подпрограммы. |
Выполнение команд ST, DEC, BRNE не воздействует на состояние флага переноса, поэтому на выходе из подпрограммы состояние флага будет определено последней выполненной командой SBC. Таким образом, если уменьшаемое было меньше вычитаемого, флаг будет установлен, в противном случае – сброшен.
Умножение чисел. Существует несколько алгоритмов умножения чисел. В самом простом случае умножение можно заменить многократным сложением, например 14*3= 14+14+14. Существенный недостаток этого способа– значительная длительность процесса вычисления. В более сложном случае умножение осуществляется в столбец. Этот алгоритм применим и для умножения двоичных чисел, например:
0110 * 0011 |
= 6 = 3 |
0110 |
|
0110 |
|
0000 |
|
0000 |
|
00010010 |
=18 |
При вычислении результата по данному алгоритму необходимо осуществить многократное суммирование со сдвигом влево множимого, при одновременной проверке содержимого разрядов множителя со стороны его младшего разряда. При этом, если в очередном разряде множителя записана «1», то множимое прибавляется к сумме и сдвигается влево на один разряд, а если в разряде записан «0», то произойдет только сдвиг множимого. Сдвиг множимого влево можно заменить сдвигом суммы вправо. По этому алгоритму (рис.3.1) работает подпрограмма умножения двух однобайтных чисел с получением двухбайтного результата – П-3.3. Входные параметры: регистр R16 – множимое, регистр R17 – множитель. Результат перемножения записывается в регистры R1:R0.
П-3.3
Адрес |
Маш. код |
Метка |
Мнемокод |
Комментарий | |
0000 |
ED0F |
MAIN: |
LDI R16,0xDF |
Главная программа |
Устанавливаем указатель стека |
0001 |
BF0D |
|
OUT 0x3D,R16 | ||
0002 |
E90C |
|
LDI R16,0x9C |
Задаем множимое | |
0003 |
E31F |
|
LDI R17,0x3F |
Задаем множитель | |
0004 |
D001 |
|
RCALL MULT |
Вызов подпрограммы умножения | |
0005 |
9508 |
|
RET |
Завершение программы | |
|
|
|
|
| |
0006 |
2411 |
MULT: |
CLR R1 |
Очищаем содержимое регистров результата | |
0007 |
2400 |
|
CLR R0 | ||
0008 |
2422 |
|
CLR R2 |
Устанавливаем указатель разряда в 00000001 | |
0009 |
9423 |
|
INC R2 | ||
000A |
9488 |
|
CLC |
Очищаем флаг C | |
000B |
922F |
M1: |
PUSH R2 |
Сохраняем указатель разряда в стеке | |
000C |
2221 |
|
AND R2,R17 |
Проверяем содержимое очередного разряда множителя | |
000D |
2C21 |
|
MOV R2,R1 |
Загружаем старший байт суммы в R2 (команда MOV не влияет на регистр статуса) | |
000E |
F009 |
|
BREQ M2 |
Если в разряде множителя 0, то переход к M2 | |
000F |
0E20 |
|
ADD R2,R16 |
Прибавляем к R2 множимое | |
0010 |
9427 |
M2: |
ROR R2 |
Сдвигаем сумму вправо с переносом (младший бит переходит в C) | |
0011 |
2C12 |
|
MOV R1,R2 |
Сохраняем результат в R1 | |
0012 |
9407 |
|
ROR R0 |
Сдвигаем младший байт суммы вправо с переносом (C переходит в старший разряд) | |
0013 |
902F |
|
POP R2 |
Получаем из стека указатель разряда | |
0014 |
1C22 |
|
ROL R2 |
Сдвигаем указатель на следующий разряд | |
0015 |
F7A8 |
|
BRCC M1 |
Если разряд не последний, продолжаем с M1, | |
0016 |
9508 |
|
RET |
иначе – выход из подпрограммы. |
Деление чисел. Деление двоичных чисел, как и чисел, представленных в любой другой системе счисления, основывается на последовательном вычитании делителя из делимого и остатков от деления. Однако двоичное деление реализуется проще, так как использование только двух цифр (0 и 1) исключает в каждом цикле деления необходимость определения числа делителей, содержащихся в текущем значении делимого или остатка (достаточно только сравнить их).
Схема алгоритма деления двоичных чисел приведена на рис.3.2. Подпрограмма деления в программе П-3.4 построена по этому алгоритму. Входными параметрами для нее являются: регистрR16 – делимое и регистр R17 – делитель; выходными параметрами: частное - R1 и остаток R0.
Вычисление специальных функций. Для вычисления специальных функций (sin x, cos x, tg x, ln x, x2 и др.) применяются специальные алгоритмы. Функции sin x, cos x, ln x можно вычислить, воспользовавшись их разложением в ряд:
для любого x (рад)
для любого x (рад)
для 0 < x 1
Число членов ряда определяется из условия получения требуемой точности.
Для вычисления функции с точностью до целых чисел, можно применить алгоритм, основанный на том, что квадрат числа можно определить сложением последовательности нечетных чисел:
Число |
Сумма нечетных чисел |
|
Результат |
1 |
1 |
= |
1 |
2 |
1+3 |
= |
22 |
3 |
1+3+5 |
= |
32 |
4 |
1+3+5+7 |
= |
42 |
5 |
1+3+5+7+9 |
= |
52 |
Исходя из приведенного примера видно, что какое число необходимо возвести в квадрат, такое же количество последовательных нечетных чисел начиная с 1 необходимо сложить.
Вычисление специальных функций по приведенным выражениям занимает длительное время и обеспечивает низкую точность. Поэтому в тех случаях, когда ставятся жесткие требования по быстродействию и точности, применяется вычисление функций с помощью таблиц. Пример программы вычисления квадрата числа x – П-3.5.
Входной параметр R16 – число x, выходной параметр R0 – значение x2.
П-3.4
Адрес |
Маш. код |
Метка |
Мнемокод |
Комментарий | |
0000 |
ED0F |
MAIN: |
LDI R16,0xDF |
Главная программа |
Устанавливаем указатель стека |
0001 |
BF0D |
|
OUT 0x3D,R16 | ||
0002 |
E90C |
|
LDI R16,0x9C |
Задаем делимое | |
0003 |
E31F |
|
LDI R17,0x3F |
Задаем делитель | |
0004 |
D001 |
|
RCALL DIVB |
Вызов подпрограммы деления | |
0005 |
9508 |
|
RET |
Завершение программы | |
|
|
|
|
| |
0006 |
922F |
DIVB: |
PUSH R2 |
Запоминаем в стеке состояние вспомогательных регистров | |
0007 |
923F |
|
PUSH R3 | ||
0008 |
92FF |
|
PUSH R31 | ||
0009 |
2411 |
|
CLR R1 |
Обнуляем частное | |
000A |
2400 |
|
CLR R0 |
Обнуляем остаток | |
000B |
E0F8 |
|
LDI R31,0x08 |
Устанавливаем счетчик битов | |
000C |
1F00 |
M1: |
ROL R16 |
Сдвигаем старший бит делимого в C | |
000D |
2C20 |
|
MOV R2,R0 |
Копируем промежуточное делимое в R2 | |
000E |
1C22 |
|
ROL R2 |
Сдвигаем C в младший разряд R2 | |
000F |
1A21 |
|
SUB R2,R17 |
Вычитаем делитель из R2 | |
0010 |
F408 |
|
BRCC M2 |
Если С=1 – восстановить содержимое R2 | |
0011 |
0E21 |
|
ADD R2,R17 |
путем прибавления делителя | |
0012 |
2C02 |
M2: |
MOV R0,R2 |
Возвращаем в R0 промежуточное делимое | |
0013 |
B63F |
|
IN R3,0x3F |
Инверсия флага переноса C | |
0014 |
9408 |
|
SEC | ||
0015 |
FC30 |
|
SBRC R3,0 | ||
0016 |
9488 |
|
CLC | ||
0017 |
1C11 |
|
ROL R1 |
Сдвигаем С в младший разряд частного | |
0018 |
95FA |
|
DEC R31 |
Уменьшаем счетчик битов | |
0019 |
F791 |
|
BRNE M1 |
Пока все биты не проверены переходим к M1 | |
001A |
91FF |
|
POP R31 |
Восстанавливаем из стека состояние вспомогательных регистров | |
001B |
903F |
|
POP R3 | ||
001C |
902F |
|
POP R2 | ||
001D |
9508 |
|
RET |
Возврат из подпрограммы |
Для работы программы П-3.5 необходимо заполнить таблицу квадратов чисел в оперативной памяти с адреса 0x60 следующими числами: 0x00, 0x01, 0x04, 0x09, 0x10, 0x19, 0x24, 0x31, 0x40, 0x51, 0x64. Подпрограмма SQR вычисляет квадрат числа от 0 до 10.
П-3.5
Адрес |
Маш. код |
Метка |
Мнемокод |
Комментарий | |
0000 |
ED0F |
MAIN: |
LDI R16,0xDF |
Главная программа |
Устанавливаем указатель стека |
0001 |
BF0D |
|
OUT 0x3D,R16 | ||
0002 |
E005 |
|
LDI R16,0x05 |
Задаем число | |
0003 |
D001 |
|
RCALL SQR |
Вызов подпрограммы SQR | |
0004 |
9508 |
|
RET |
Завершение программы | |
|
|
|
|
| |
0005 |
93FF |
SQR: |
PUSH R31 |
Запоминаем в стеке состояние вспомогательных регистров | |
0006 |
93EF |
|
PUSH R30 | ||
0007 |
27FF |
|
CLR R31 |
Очищаем старший байт регистра Z | |
0008 |
E6E0 |
|
LDI R30,0x60 |
Задаем смещение младшему байту регистра Z, равное адресу начала таблицы квадратов | |
0009 |
0FE0 |
|
ADD R30,R16 |
Вычисляем абсолютный адрес значения | |
000A |
8000 |
|
LD R0,Z |
Загружаем в R0 значение | |
000B |
91EF |
|
POP R30 |
Восстанавливаем из стека состояние вспомогательных регистров | |
000C |
91FF |
|
POP R31 | ||
000D |
9508 |
|
RET |
Выход из подпрограммы |
Поскольку таблица квадратов имеет постоянные данные, то нет смысла хранить их в оперативной памяти. При отключения питания микроконтроллера все данные, находящиеся в оперативной памяти теряются. Таким образом, чтобы снова получить таблицу квадратов в SRAM МК, все значения необходимо программно вычислить и записать начиная с адреса 0x60. Для этого потребуется дополнительно реализовать подпрограмму возведения в степень, что приведет к излишнему расходу памяти. Выходом в данной ситуации является использование энергонезависимой памяти EEPROM микроконтроллера. Записав один раз таблицу квадратов в EEPROM (на стадии прошивки микроконтроллера) мы можем не беспокоиться о потере данных.
Программирование EEPROM. EEPROM можно рассматривать как отдельное устройство, доступ к которому осуществляется через порты ввода/вывода.
Порт 0x1E называется регистром адреса (EEAR), 7 разрядов которого адресуют 128 ячеек памяти. Старший разряд зарезервирован и всегда читается как 0.
Порт 0x1D называется регистром данных (EEDR). В режиме записи EEPROM этот 8-ми битный регистр должен содержать данные, которые будут записаны в энергонезависимую память по адресу в EEAR. В режиме чтения в этот регистр попадают данные, считанные из EEPROM по адресу, заданному в EEAR.
Порт 0x1C называется регистром управления (EECR), который устанавливает режимы обращения к EEPROM (чтение, запись, разрешение записи).
Управление EEPROM осуществляется установкой битов порта EECR:
Бит 0 – EERE – разрешение чтения из EEPROM. Сигнал EERE является стробом чтения из EEPROM. После установки нужного адреса в регистре EEAR, необходимо установить бит EERE. После того, как бит EERE будет аппаратно очищен, в регистр EEDR будет выставлен считанный байт данных. Чтение EEPROM занимает одну команду и не требует отслеживания бита EERE. При установке бита EERE, МП останавливается на 2 цикла перед тем, как будет выполнена следующая команда. Перед чтением необходимо убедиться что EEPROM не занят.
Бит 1 – EEWE – разрешение записи в EEPROM. Сигнал EEWE является стробом записи в EEPROM. После установки правильных адреса и данных для записи в EEPROM необходимо установить бит EEWE. При записи «1» в бит EEWE должен быть установлен бит разрешения записи (EEMWE), тогда происходит запись в EEPROM.
После того, как время записи истечет (от 2.5 мс до 4 мс в зависимости от напряжения), бит EEWE очищается аппаратно. Вы можете отслеживать этот бит и ожидать его установки в 0, перед тем как записать (или считать) следующий байт. При установке бита EEWE, МП останавливается на 2 цикла перед исполнением следующей команды.
Бит 2 – EEMWE – управление разрешением записи. Этот бит определяет, будут ли записаны данные при установке EEWE. Если бит EEMWE установлен, то при установке EEWE данные записываются по выбранному адресу в EEPROM. Если этот бит сброшен, то установка EEWE не имеет эффекта. После программной установки этот бит сбрасывается аппаратно через 4 такта микропроцессора.
Биты 3-7 – зарезервированы.
Для записи в EEPROM должна соблюдаться следующая последовательность действий:
Ждем готовности EEPROM (сброса EEWE)
Устанавливаем адрес (записываем адрес в EEAR)
Устанавливаем данные (записываем данные в EEDR)
Устанавливаем в 1 бит EEMWE (разрешение записи)
Не позже 4-х тактов после установки EEMWE устанавливаем EEWE.
Для чтения EEPROM должна соблюдаться следующая последовательность действий:
Ждем готовности EEPROM (сброса EEWE)
Устанавливаем адрес (записываем адрес в EEAR)
Устанавливаем в 1 бит EERE (строб чтения)
Читаем данные из порта EEDR.
Если регистры данных или адреса изменяются во время операции записи, то запись в ячейку прерывается и результат операции записи становится неопределенным. В связи с этим, перед началом цикла записи в EEPROM, необходимо запретить прерывания, если в подпрограммах прерывания осуществляется доступ к EEPROM.
Подпрограмма П-3.6 осуществляет запись в EEPROM. Адрес задается в регистре R16, данные – в регистре R17.
П-3.6
Метка |
Мнемокод |
Комментарий |
EEPROM_WR: |
SBIC 0x1C,1 |
Проверяем бит 1 (EEWE) |
|
RJMP EEPROM_WR |
Если не сброшен, то снова проверяем |
|
OUT 0x1E,R16 |
Устанавливаем адрес в EEAR |
|
OUT 0x1D,R17 |
Устанавливаем данные в EEDR |
|
SBI 0x1C,2 |
Разрешаем запись EEMWE = 1 |
|
SBI 0x1C,1 |
Подаем строб на запись EEWE =1 |
|
RET |
Выход из подпрограммы |
Подпрограмма П-3.7 осуществляет чтение из EEPROM. Адрес задается в регистре R16, данные возвращает - R17.
П-3.7
Метка |
Мнемокод |
Комментарий |
EEPROM_RD: |
SBIC 0x1C,1 |
Проверяем бит 1 (EEWE) |
|
RJMP EEPROM_RD |
Если не сброшен, то снова проверяем |
|
OUT 0x1E,R16 |
Устанавливаем адрес в EEAR |
|
SBI 0x1C,0 |
Подаем строб на чтение EERE = 1 |
|
IN R17,0x1D |
Получаем из порта данные в выходной регистр |
|
RET |
Выход из подпрограммы |