Примеры программирования

Написание программ – целое искусство, поэтому здесь можно на нескольких примерах попытаться дать только общее представление о том, как это делается, – заостряя внимание на специфических особенностях программирования для МК, а не программирования вообще. Полного обзора команд и даже комментариев по каждой встречающейся команде вы также здесь не увидите – это потребовало бы отдельной книги (см. [19, 20], а также книгу автора [21]).

 

Самая простая программа

Давайте напишем сначала самую простую программу, которая будет включать светодиод сразу при включении питания и больше ничего не делать. Для примера возьмем контроллер ATtiny2313, схема подключения которого вместе с программирующим разъемом показана на рис. 19.2.

 

 

Рис. 19.2. Схема подключения ATtiny2313

* * *

 

Подробности

Обратим внимание на RC‑цепочку (R1 и С1), которая подсоединена к выводу Reset . Она служит для более надежного сброса контроллера при не слишком качественном источнике питания, если установление напряжения затягивается. В техническом описании указано, что по выводу Reset уже имеется встроенный фильтр дребезга с «подтягивающим» резистором, так что эта цепочка оказывается вроде бы и ненужной (ее установка рекомендовалась для семейства Classic, где фильтра не было). Тем не менее, для более надежной работы МК рекомендуется во всех случаях устанавливать резистор R1 с номиналом 3–5 кОм (встроенный резистор имеет слишком большой номинал). Что касается конденсатора С1, то если для управления сбросом применяется внешний монитор питания, как описано в главе 18 , то, разумеется, он будет только мешать, в остальных случаях он необязателен, но делает работу схемы более стабильной. Резисторы R2‑R4 при наличии программирующего разъема устанавливать обязательно, в противном случае надежность схемы снижается.

Заметим, что в схеме на рис. 19.2 показана только небольшая часть функциональности входных/выходных линий, которые все, включая даже Reset и выводы подсоединения кварца (кроме, конечно, выводов питания), имеют как минимум две, а большинство даже три функции. Подробно обо всех этих функциях можно узнать из технического описания, частично вы с ними познакомитесь из дальнейшего изложения.

 

* * *

Светодиод мы сразу взяли двухцветный – в целях дальнейшего усовершенствования схемы. Светодиод L56 (можно также взять L57 или малогабаритный L36) светится зеленым, если плюс питания находится со стороны ключа (скоса на корпусе), и красным – если наоборот. Здесь мы его подключили к выводам PD5 и PD6 порта D. Таким образом, если на этих выводах будет одинаковый уровень (неважно, единица или ноль), то светодиод погашен, если разный – то в зависимости от того, на каком выводе единица, светодиод будет гореть красным или зеленым. Токоограничивающий резистор R5 при логическом уровне на выходе порта примерно 4,5 В обеспечит ток через светодиод около 5 мА.

Пусть мы для начала хотим просто зажечь светодиод при включении питания, демонстрируя, что контроллер работает. Для определенности выберем зеленое свечение. Тогда нам необходимо создать программу, которая делает следующее:

1. Конфигурирует нужные выводы порта D (PD5 и PD6) на выход.

2. Устанавливает на выводе PD6 логическую единицу (логический ноль на выводе PD5 устанавливается по умолчанию).

И что, программа будет состоять всего из двух команд? Увы, не все так просто.

В регистры портов записывать непосредственное значение нельзя, можно только командой out переносить информацию из какого‑нибудь рабочего регистра. Поэтому добавится третья команда – сначала мы установим нужные биты в некоем рабочем регистре, специально выбранном для этих целей из числа регистров общего назначения (РОН), затем загрузим весь полученный байт в регистр порта. Причем установить непосредственное значение можно также не в любом из РОН, а только в регистрах с номерами с 16 по 31, поэтому выберем себе регистр r16 в качестве рабочего. Тогда последовательность команд будет выглядеть так:

 

ldi r16, 0Ь0Н00000 ; устанавливаем биты номер 5 и 6 в регистре r16

out DDRD,r16 ; выводим это значение в регистр направления порта D

sbi PortD,6 ; устанавливаем в единицу бит 6 регистра данных порта D

 

Здесьldi – команда загрузки непосредственного значения (load immediate ), out – команда вывода в какой‑либо регистр ввода/вывода (РВВ), sbi (set bit input/output ) – команда установки бита с выбранным номером в РВВ. А что означает запись 0Ь0Н00000 ? Это хорошо знакомое нам двоичное представление числа – 0b впереди как раз и означает, что это именно двоичная запись. Таким образом в данном случае удобнее отсчитывать биты, но если мы запишем это же число в hex‑форме 0х60 (или $60), или даже просто десятичное 96, ничего не изменится.

Заметим, что способ установки значений РВВ через команду out – не единственный, можно было бы установить биты 5 и 6 регистра DDRD двумя командами sbi (есть и другие способы).

* * *

 

Подробности

Обратите внимание на синтаксис команд – сначала пишется, куда писать, а потом, через запятую, откуда или что. Это справедливо для всех команд и для всех ассемблеров. Каждая команда пишется в отдельной строке. Весь текст после точки с запятой ассемблером игнорируется и представляет собой комментарии, которые нужно писать обязательно, иначе вы через пару месяцев и сами в своей программе не разберетесь. Если комментарий переходит на другую строку, то точку с запятой нужно ставить заново, в начале строки. В отличие от знака перевода строки, все пробелы и табуляции игнорируются (а там, где есть другой разделительный знак, в данном случае запятая, как видите, пробелов вообще может не быть), так что украшать текст отступами можно, а для удобства чтения и нужно. Строчные и прописные буквы не различаются: записи LDI ,Ldi иldi означают одно и то же – и это отличие AVR‑ассемблера от программных сред, основанных на C/C++. Вопреки распространяемым в Интернете сведениям (заимствованным из [22]), это правило соблюдается и для обновленных версий компилятора Avrasm2.

 

* * *

Ну, а теперь все? Можно скопировать это в Блокнот, сохранить с расширением asm и компилировать в hex‑файл? Ишь разбежались – нет, не все. Во‑первых, никаких таких DDRD и PortD AVR‑ассемблер не понимает. Если соответствия кодов команд и их мнемонических обозначений (ldi), а также обозначений рабочих регистров (r16) и их адресов зашиты в ассемблере, то адреса РВВ могут меняться от модели к модели, а их названия и вообще могут быть выбраны совершенно произвольным образом. Сам ассемблер понимает только конкретные числа, представляющие собой адреса этих регистров. Но писать программу без мнемонических обозначений было бы крайне неудобно, т. к. она оказалась бы совершенно нечитаемой (вторая команда, к примеру, тогда выглядела бы так: out $11, r16 ). Поэтому к нашей программе надо «пристегнуть» файл с мнемоническими обозначениями, который поставляется Atmel и в данном случае называется tn2313def.inc (при компиляции он должен находиться в одном каталоге с файлом программы). Это делается почти в точности, как в языке С , строкой:

 

.include "tn2313def.inc"; точка впереди обязательна!

 

* * *

 

Замечание

Файлы макроопределений (с расширением inc ), к сожалению, нельзя скачать с сайта Atmel в отдельности. Как и файл компилятора Avrasm2, эти файлы для каждого контроллера в отдельности проще всего добыть из пакета AVR Studio, где они находятся в папке avrassembler/include. Лучше, если это будет одна из последних версий AVR Studio, пятая или более поздняя, иначе при компилировании совместно с Avrasm2 возможны ошибки.

 

* * *

Если вы заглянете внутрь файла tn2313def.inc, то увидите, что он состоит из строк, начинающихся с директивы .equ . Мы могли бы не включать его в программу (хотя в память процессора он все равно не записывается, ибо не содержит команд, а только определения переменных), а лишь дописать к программе в начале текста строки:

 

.equ DDRD = $11

.equ PortD = $12

 

Мы часто будем применять директиву .equ , которая устанавливает соответствие между числами и их обозначениями, наряду с другой полезной директивой – .def .

Если провести аналогии с языком Turbo Pascal или Turbo С , то директива .equ (от англ. equal – равно) полностью аналогична определению констант, а директива .def (от англ. define – определить) аналогична определению переменных, с единственным отличием: тип переменной здесь не указывается, ибо он один‑единственный – число размером один байт. А вот в директиве .equ может быть указано число любого размера, а также и отрицательное, но, естественно, только целое.

Неудобно каждый раз писать r16 и помнить, что это у нас рабочая переменная для всяких текущих надобностей, потому лучше дописать еще такую строку:

 

.def temp = r16 ;рабочая переменная, от слова temporary (временный)

 

Окончательно исходный текст программы будет выглядеть так:

 

;программа зажигания светодиода

;процессор Tiny2313, частота 4 МГц

.include "tn2313def.inc"

.def temp = r16 ;рабочая переменная

ldi temp,0b01100000 ;устанавливаем биты номер 5 и 6 в temp

out DDRD,temp ;выводим это значение в регистр направления порта D

sbi PortD,6 ;устанавливаем в единицу бит 6 регистра данных порта D

Sleep

Exit

 

Программа займет в памяти программ контроллера ровно 8 байтов. Последняя команда sleep означает остановку процессора и выход в режим экономии – ведь должен процессор что‑то делать по окончании программы? Директива .exit предназначена для ассемблера и означает конец программы, указывать ее необязательно.

А зачем мы в заголовочном комментарии указали тактовую частоту процессора?

В данном случае она не имеет значения (лишь бы контроллер работал), но при использовании любых процедур, связанных со временем, это критично. И поскольку можно забыть, на какую частоту вы рассчитывали при написании программы, следует ее на всякий случай указывать в комментариях.

 








Дата добавления: 2016-05-11; просмотров: 1963;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.012 сек.