Программирование c нуля в AVRStudio 5 (ч.4)

Автор: Радик Просмотров: 3233

 

 

 

Большинство команд AVR выполняется за один такт, где то около 10 команд выполняется за 2 такта.
Поэтому паузу сделанную одной командой Nop, увидеть невозможно, а если нам нужна пауза в полсекунды - тогда нужно минимум написать 500 тысяч команд Nop. Но, это не реально!.

 

Может остановить программу?
А остановить мы ее не можем, иначе будет сбой программы. Есть другое решение – можно программу зациклить, чтобы отсчитать какое-то количество тактов, а затем продолжить.
Самый простой вариант зацикливания:

Cycle:       rjmp Cycle

Строка начинается метки Cycle:
Метка обязательно заканчивается двоеточием – в этом ее отличие. Метка может иметь любое сочетание букв и цифр написанное в английской транскрипции (как в пред идущем примере я написал Cicle: и ничего, программа без проблем распознала эту метку).

Кто изучал программы для PIC, видимо заметил, что там после метки, двоеточие не ставится.
Далее стоит команда rjmp Cycle – относительный переход к метке Cycle т.е. на самого себя. Все “бесконечный цикл”.
Из этой программы можно выйти, только если были заданы условия прерывания, или включен сторожевой таймер WDR, иначе никак. Есть программы, основное тело которой состоит только из такой строки, вся остальная часть построена на подпрограммах, вызываемых прерываниями. Но эти случаи мы рассмотрим чуть позже.
Вообще – любая программа - это цикл, имеющий начало и конец
Самый короткий цикл, это тот который мы только, что рассмотрели, состоящий всего из одной команды. Нередко применяется тогда, когда от контроллера не требуется никаких действий, только ожидание.
Программа может представлять из себя огромный цикл, включающий в свою структуру множество мелких циклов – подпрограмм. Пишутся они с помощью команд ветвления и команд вызова подпрограмм.
Рассмотрим некоторые из них:

breq – Перейти (на определенную строку) если результат пред идущей строки =0


Если значение регистров указанных в пред идущей строке не равно 0, то команда игнорируется (как будто ее нет)

brne - Перейти (на определенную строку) если результат пред идущей строки не 0


Если значение регистров указанных в пред идущей строке равно 0, то команда игнорируется (как будто ее нет)

rcall - Относительный вызов подпрограммы


ret – Выход из подпрограммы


Здесь функции команд понятны без комментприев.
Еще рассмотрим пару команд, которые очень часто испоьзуются:

inc – Инкремент, прибавить к регистру единицу


dec – Декремент, вычесть из регистра  единицу


Попробуем дописать предыдущую программу:

.def temp=r16         ; директива .def назначает регистру r16 имя temp
.def temp1=r17        ; директива .def назначает регистру r17 имя temp1
.def temp2=r18        ; директива .def назначает регистру r18 имя temp2
.def temp3=r19        ; директива .def назначает регистру r19 имя temp3
;====================================================
; Начало программы
.cseg                 ; директива .cseg определяет начало сегмента, где будет расположен
; основной код программы. В AVR Studio 5 это директива не
; обязательна
.org 0                ; начало первой строки программы
rjmp Start            ; относительный переход к метке Start (в PIC соответствует
; команде goto)
; ====================================================
Start:
ser temp              ; устанавливает все биты регистра temp  в 1
out DDRB,temp         ; переводит все биты
out DDRD,temp         ; порта B  и D на вывод
clr temp              ; обнуляет регистр temp (устанавливает все биты регистра temp  в 0)
out PortB,temp        ; отключает подтягивающие резисторы
out PortD,temp        ; портов B и D
Cicle:
ldi temp,0b11001100   ; включает светодиоды
out PortB, temp       ; порта B
rcall Pause           ; вызов подпрограммы задержки
clr temp              ; выключает светодиоды
out PortB, temp       ; порта B
rcall Pause           ; вызов подпрограммы задержки
rjmp Cicle            ; Возвращаемся к метке Cicle, зацикливаемся
; ====================================================
; Подпрограмма задержки
; ====================================================
Pause:    
ldi Temp1,0          ; записать в регистр temp1 знчение 0
ldi Temp2,0          ; записать в регистр temp2 знчение 0
ldi Temp3,2          ; записать в регистр temp3 знчение 2

Pause1:    
dec Temp1            ; вычесть из значения  регистра temp1 единицу
brne Pause1          ; если значение temp1 не равно 0 перейти к метке Pause1

dec Temp2            ; вычесть из значения  регистра temp2 единицу
brne Pause1          ; если значение temp2 не равно 0 перейти к метке Pause1

dec Temp3            ; вычесть из значения  регистра temp3 единицу
brne Pause1          ; если значение temp1 не равно 0 перейти к метке Pause1
ret                  ; выйти из подпрограммы


Здесь файлы проекта для Proteus:

ATTINY2313_led_1.zip

компилируем, загружаем проект, распаковываем и запускаем:

 

alt

 

Смотрим как работает программав Proteus (как загрузить .hex файл подробно описано во предыдущих частях). Что видим? Заморгали светодиоды?

Разберем, что мы тут понаписали:
Строка:

clr temp – обнуляет регистр temp


Строка:

rcall Pause           ; вызов подпрограммы задержки


Команда rcall вызывает подпрограмму с меткой Pause, которая реализует нужную нам задержку. Выход из подпрограммы происходит по команде - ret
По команде ret происходит возврат в прежнюю точку, откуда была вызвана подпрограмма. Подпрограммы удобно создавать, когда в программе требуется многократно выполнять одно и тоже действие. Достаточно написать одну подпрограмму и обращаться к ней в любой момент, по мере необходимости.
Как работает наша подпрограмма Pause?
Нам нужно правильно рассчитать время задержки. Возьмем во внимание, что внутренняя (без внешнего кварцевого генератора) тактовая частота нашего микроконтроллера равна 8 МГц. По умолчанию, происходит деление этой частоты на 8 т.е. реальная тактовая частота равна 1 МГц, и задержка одного такта составит 1 миллисекунду. Для того, чтобы организовать задержку в 0,5 секунды, нужно пропустить 500 тысяч тактов. Как это сделать?
А для этого мы организуем 3-х байтный счетчик из регистров Temp1, Temp2, Temp3
Почему трехбайтный? Давайте посмотрим, как выглядит число 500000 в двочной и шестнадцатиричной системах.

[b0] 00000111 10100001 00100000;
07 A1 20 [h]

Наглядно видно, что число состоит из трех байт.
Вроде все просто, но если мы запишем эти числа в регистры, то на самом деле задержка окажется значительно больше, чем мы ожидали. Почему?
А ведь кроме регистров есть в подпрограмме команды, которые выполняются за определенное количество тактов, они то и забирают дополнительное время. Чем больше команд, тем большее время будет выполняться задержка. Если грубо прикинуть, то время задержки будет примерно в четыре раза больше ожидаемого! Поэтому я записал в младшие регистры нули, а в старший 2.

Pause:    
ldi Temp1,0          ; записать в регистр temp1 знчение 0
ldi Temp2,0          ; записать в регистр temp2 знчение 0
Не будет ошибкой, если мы пропишем clr temp1 и  clr temp2
ldi Temp3,2          ; записать в регистр temp3 знчение 2

После того, как прописали значения регистров переходим к самой задержке:
Строки:

Pause1:    
dec Temp1            
brne Pause1

Команда dec Temp1 – вычитает из регистра temp1, (в который сейчас прописан нуль) единицу, результатом будет число FFh или 255 (чтобы проверить - сделайте обратное действие в восьми битном регистре, произойдет переполнение и обнуление регистра)

команда brne Pause1 – сравнивает значение пред идущего регистра (регистра temp1) с нулем. Если результат не равен нулю, то пересылает к метке Pause1. Цикл повторяется до тех пор, пока значение temp1 не станет равным нулю.
Если значение регистра temp =0, то команда brne игнорируется и выполняется пустой такт (как Nop), а прогамма переходит к строке ниже.
Аналогично происходит и с регистрами temp2 и temp3, пока не будет выполнена команда – ret.

А что делать, если нам надо вычислить задержку с точностью до нескольких микросекунд?
Для этого используем AVR Studio 5
В редакторе пишем текст (можно скопировать и перенести отсюда).

После того, как ввели текст, компилируем программу (F7).
Кликаем (View):

 

alt

В меню (View) кликаем (Toolbars)

 

alt

 

Ставим галочку в подменю (AVR Drbugger)

 

alt

 

Выходим из подменю на страницу редактора, устанавливаем курсор на строке в начале подпрограммы задержки и кликаем (F9)
Возникает красный кружек с выделенной строкой (Breakpoint) точка останова.

 

alt

 

Точно также выделяем вторую строку (Breakpoint)

 

alt

1.Кликаем на зеленый треугольник (Start), тем самым запускаем симуляцию.
2.Кликаем на символ микросхемы, если справа не появилось окно (Processor).
3.Устанавливаем желтую стрелку на верхнем Breakpoint-е, кликая по зеленому треугольнику (Start).
4.Выделяем строку (Cycle Counter) и прописываем 0.
5.Устанавливаем тактовую частоту процессора

 

alt

Кликаем на зеленый треугольник (Start) и ждем появления желтого треугольника в нижнем Breakpoint-е.
Смотрим строку Stop Watch (секундомер).
Это время от начала подпрограммы задержки, до его завершения.

 

alt

Чтобы убрать Breakpoint-ы, нужно подвести курсор на красный кружек и кликнуть (F9).
Таким образом можно определить длительность выполнения любого отрезка программы, и очень точно выставить временные интервалы.

В нашей программе время отчитывается с момента включения светодиода до момента его отключения. Частота процессора соответствует 1 МГц. Установим Breakpoint на команде включения светодиода, а второй на команде выключения.

 

alt

Запустим Debug-ер, кликнув зеленый треугольник. Смотрим показания секундомера Stop Watch.

 

alt

Это реальное время включения светодиодов (394 255,00 миллисекунд). Снова обнуляем, и запускем Debug-ер:

 

alt

теперь в показаниях секундомера смотрим время паузы (394 257,00 миллисекунд). Получили разницу в 2 миллисекунды. Попробуйте самостоятельно, с помощью команд Nop, сделать одинаковыми время включения и паузу светодиодов.
Изменим программу таким образом, чтобы получить возможность управлять включением светодиодов с помощью кнопок.

.def temp=r16         ;директива.defназначаетрегистру r16 имя temp
.def temp1=r17        ;директива.defназначаетрегистру r17 имя temp1
.def temp2=r18        ;директива.defназначаетрегистру r18 имя temp2
.def temp3=r19        ;директива.defназначаетрегистру r19 имя temp3
;====================================================
;Началопрограммы
.cseg                 ;директива.cseg определяетначалосегмента,гдебудетрасположен
                      ;основнойкодпрограммы.В AVR Studio5этодирективане
                      ;обязательна
.org 0                ;началопервойстрокипрограммы
rjmp Start            ;относительныйпереходкметкеStart PIC соответствует 
                      ;командеgoto)
;====================================================
Start:
ser temp              ;устанавливаетвсебитырегистра temp  в1
out DDRB,temp         ;переводитвсебитыпорта B навывод
clr temp1             ;обнуляетрегистр temp1
out DDRD,temp1        ;переводитвсебитыпорта D наввод         
outPortB,temp1       ;отключаетподтягивающиерезисторыпортов B
outPortD,temp        ;включаетподтягивающиерезисторыпортов D 
Cicle: 
in temp,PinD          ;считываетзначениепорта D и 
outPortB, temp       ;переводитрезультатвпорт B
rcall Pause           ;вызовподпрограммызадержки
clr temp              ;выключаетсветодиоды
outPortB, temp       ;порта B
rcall Pause           ;вызовподпрограммызадержки
rjmp Cicle            ;ВозвращаемсякметкеCicle,зацикливаемся
;Подпрограммазадержки
;====================================================
Pause:    
clr temp1             ;обнулитьрегистр temp1
clr temp2             ;обнулитьрегистр temp2
ldi temp3,2           ;записатьврегистр temp3 число2

Pause1:    
dec temp1             ;вычестьиззначения  регистра temp1 единицу
brne Pause1           ;еслизначение temp1 неравно0перейтикметкеPause1

dec temp2             ;вычестьиззначения  регистра temp2 единицу
brne Pause1           ;еслизначение temp2 неравно0перейтикметкеPause1

dec temp3             ;вычестьиззначения  регистра temp3 единицу
brne Pause1           ;еслизначение temp1 неравно0перейтикметкеPause1
ret                   ;выйтиизподпрограммы

Строка:

in temp,PinD


Команда in считывает значение регистра порта D, и записывает в регистр temp.

Небольшое отступление: По поводу правильного введения цифровых значений в программу. Программа AVR Studio 5, как и предыдущие версии не понимает интеловской формы введения шестнадцатеричных чисел, где после значения ставиться маленькая буква h, например A5h. В AVR Studio 5 правильно писать 0хА5. В обычном тексте удобно писать и пояснять именно в интеловской форме, но в тексте программ такая форма недопустима.

Вернемся в Proteus.
Теперь у нас появилась возможность подключить кнопки и понаблюдать, как они влияют на светодиоды.
Если подвести курсор на кнопку и кликнуть клавишей break, то нажатие кнопки будет зафиксировано, таким же образом можно отжать кнопку. Поиграйте с кнопками, понаблюдайте за процессом, попробуйте что-нибудь изменить в программе.

Далее мы напишем программу бегущих огней, выясним, что такое стек.

(Продолжение следует)


Все права принадлежат ChipMK.ru. При копировании материала ссылка обязательна. 2011-2017 © ChipMK.ru

ChipMk.ru Яндекс.Метрика
PRCY.ru