Форум по микроконтроллерам: Программирование c нуля в AVRStudio 5 - Форум по микроконтроллерам

Перейти к содержимому

  • 13 Страниц +
  • 1
  • 2
  • 3
  • Последняя »
  • Вы не можете создать новую тему
  • Вы не можете ответить в тему

Программирование c нуля в AVRStudio 5 основы и основные понятия

#1 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 18 Сентябрь 2011 - 13:49

Каждый человек, который только начинает осваивать программирование микроконтроллеров, да и вообще программирование, упирается сразу в несколько вопросов:
1.Какой микроконтроллер выбрать для максимально быстрого освоения?
2.Какой основной инструмент (программу) использовать для начала работы?
3.Какие аппаратные средства доступны для начала программирования?
4.Какую литературу использовать?
5.Где общаться и получать вразумительные советы?

Когда я начинал, то сам столкнулся с этими вопросами. Начал искать литературу и решил, что надо начинать с PIC-ов. Перевес в сторону PIC-ов определился из за небольшого количества команд микропроцессоров среднего семейства - всего 35 , и наличием IDE - интегрированной среды разработки MPLAB. Что касается AVR, сначало смутило количество команд - 136, и множество программ для AVR, в которых, начинающий просто запутается, многие пользовались AVRStudio 4, кто то писал на СИ в WINAVR, CodeVision, IAR, для отладки пользовались дополнительными программами, все зависило от личных приоритетов. Компания ATMEL наконец то сделала попытку "родить" универсальную среду разработки IDE - AVR Studio 5 для программирования микроконтроллеров AVR. Много критических замечаний было сказано в адрес этой программы, и вслед за ней были выпушены AVR Studio 5.1 и Atmel Studio 6.0-7.0. Конечно по объему - это монстры - инсталлируемый файл занимает от 600 до 900 МБайт. Все программы похожи как братья-близнецы. Программы ориентированы для работы на языке программирования Си, хотя ассемблер полностью поддерживается. AVR Studio и Atmel Studio поддерживают вторую версию ассемблера, которая максимально приближена к Си.

Итак, попробуем ответить на возникшие вопросы:
1.Выбор микроконтроллера определяется теми задачами, которые вы перед собой поставили. Микроконтроллеры AVR имеют “избыточный” набор команд, и поэтому большинство программистов используют в среднем около 40 инструкций, редко прибегая к остальным. С другой стороны, когда требуется нетипичное решение, дополнительные команды могут оказаться весьма кстати, позволяя значительно сократить объем программы. Технология производства микроконтроллеров сегодня одинакова как для PIC так и для AVR - (Reduced Instruction Set Computer)-микроконтроллеры с сокращенным набором команд. Большинство из них имеют флеш-память, которая позволяет многократно их перезаписывать. Кроме этого микроконтроллеры AVR работают в 4 раза быстрее микроконтроллеров PIC.
2. Для начала, чтобы начать писать программы, нужно скачать интегрированную среду разработки AVR Studio 5 (или Atmel Studio 6.0-7.0), а чтобы наглядно видеть результат своей работы, не используя паяльник или макетную плату достаточно установить программу Proteus v7.0-7.8 (или Proteus v8.1-v8.3)
3. AVR Studio 5 поддерживает программатор STK-500, инструкции по сборке которого, можно легко найти в просторах всемирной паутины. Для программ Atmel Studio готовые программаторы AVR Dragon или различные варианты AVR ISP и Atmel ICE
4. Рекомендую: Джон Мортон. Микроконтроллеры AVR. Вводный курс. И хороший цикл лекций по ссылке: http://radiokot.ru/start/mcu_fpga/avr/ Для освоения основ программирования на языке ассемблер и СИ рекомендую книгу - Белов А.В. Самоучитель разработчика устройств на микроконтроллерах AVR.
5.Советы вы можете получать на любом форуме, где так или иначе затронуты темы по микроконтроллерам. Главное на форумах правильно формулировать вопросы, чтобы четко получать ответы. Абстрактные вопросы не приветствуются, и скорее всего вместо ответа вы получите жесткую критику, или ваш вопрос останется без внимания!

Скачать AVR Studio 5 (Atmel Studio 6.0-7.0) можно с официального сайта после регистрации (регистрация абсолютно бесплатная и ни к чему не обязывает!)
http://www.atmel.com...studio_overview
http://www.atmel.com...TMELSTUDIO.aspx

Proteus вместе с патчем можно скачать здесь:
http://eldigi.ru/site/prog/1.php
а версию v8.3 здесь http://rutracker.org...c.php?t=5065809

AVR Studio 5
Создание проекта
Примечание: AVR Studio "не любит" русских названий, поэтому проекты должны быть с английской транскрипцией. Старайтесь размещать проекты по кратчайшему пути к основному диску, избегать ветвлений в путях доступа к файлам проекта.

Запускаем программу, после некоторого "молчания" появляется окно:
Прикрепленное изображение: Первый запуск.jpg
В левом верхнем углу кликаем New Project...
Прикрепленное изображение: Создание проекта.jpg
Выбираем наверху AVR Assembler
В строке Name: пишем имя пректа (я написал Pracsis, вы можете Praxis или что то наподобие...)
В строке Location: путь и место хранения файлов проекта ( в том числе файлы .asm и .hex)
Имя проекта введенное в строке Solution name: будет выводится в меню при старте (я эту строку не трогаю, чтобы не путаться)
Прикрепленное изображение: Редактирование имени проекта.jpg
кликаем OK
Появляется окно выбора микроконтроллера (Device Selection)
Прикрепленное изображение: Выбор микроконтроллера.jpg
Выбираем контроллер (я выбрал ATtiny2313A)
кликаем ОК
Появляется станица редактора
Прикрепленное изображение: Страница редактора.jpg

в Atmel Studio 7.0
Прикрепленное изображение: new project.png Прикрепленное изображение: assembler.png Прикрепленное изображение: name project.png Прикрепленное изображение: chip.png Прикрепленное изображение: editor.png

Для программирования в СИ выбираем
Прикрепленное изображение: project1C.png Прикрепленное изображение: pros1.png Прикрепленное изображение: pros2.png

получаем такой редактор
Прикрепленное изображение: progC.png

Все наши файлы можно посмотреть в Моих документах (по умолчанию, если при создании проекта путь к файлам был изменен, то ищем их там, где вы их указали в строке Location:)
Прикрепленное изображение: Просмотр файлов сохраненного проекта.jpg
В папке Debug находится скомпиллированный .hex файл, который нам будет нужен для прошивки микроконтроллера.

Для удобства работы с программой во время отладки очень удобно отображение нумерации строк. Для этого необходимо:
Tools>Options>Text Editor>All Languages>Line numbers установить галочку>OK

Чтобы не портить зрение можно увеличить размер шрифта:
Tools>Options>Fonts and Colors>поменять значение Size>OK

Для удобства работы с курсором:
VAssistX>Visual Assist Options>Display>установить галочку напротив Highlight current line>выделить Background>OK

Избавиться от навязчивого волнистого красного подчеркивания комментариев можно:
VAssistX>Visual Assist Options>Underlines> снять галочку с Underline spelling errors in comments and strings using>OK

Теперь мы готовы, чтобы начать писать программу. Но для того, чтобы правильно писать, нужно соблюдать определенные правила. Дальше рассмотрим как это сделать....

Сообщение отредактировал galrad: 07 Ноябрь 2015 - 18:17

1

#2 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 24 Сентябрь 2011 - 18:39

Пишем первую программу!
Большинство начинают с мигания светодиодов, и мы не исключение. Если микроконтроллер, даже самый быстрый, не “общается” с внешним миром, ценность его сводится к нулю. Нам нужно чтобы микроконтроллер принял информацию, обработал по заданному алгоритму и выдал результат в понятной для нас форме.
Алгоритм, (от имени персидского математика аль-Хорезми) — точный набор инструкций, описывающих порядок действий исполнителя для достижения результата решения задачи за определенное время.
Чтобы правильно писать программы нужно понять, как работает микроконтроллер, чем мы и будем заниматься, а пока составим первую программу на ассемблере.
Вообще написать программу можно в любом текстовом редакторе, так же как вы бы написали письмо другу, например. После этого, текст надо скомпилировать (иногда говорят - ассемблировать) т.е. перевести в форму, понятную процессору. Раньше, расширение текстового файла .txt переименовывали в .asm и обрабатывали специальной программой, называемой компилятором. Суть работы компилятора в переводе письменных символов понятных для человека в машинный код (в код нулей и единиц) и создание нового файла с расширением .hex
Что делать с этим .hex файлом? А он нужен для программатора, чтобы записать код программы в ПЗУ (постоянное запоминающее устройство) микроконтроллера. Мы вместо программатора будем использовать программу Proteus, которая модулирует реальную работающую схему.
В AVR Studio 5 редактор, компилятор, отладчик и программатор все в одном флаконе!
Директива – это предписание редактору, с которым мы будем работать. В большинстве своем название директивы и ее функция в разных редакторах совпадают. Например, если сравнить редактор MPLAB IDE для микроконтроллеров PIC, то разницы практически нет!
А вот команды отличаются. Список команд формируется производителем и в какой-то степени зависит от технологии производства. Даже у одного производителя в разных семействах может быть разный список команд по мере изменения их структуры.
Команда – это единичная функция, с помощью которой мы можем управлять микроконтроллером.
Программа - это последовательность команд, соответствующая определенному алгоритму (составленная нами).
Любая программа начинается с “шапки”, где прописывается основные функции программы, ее автор, название контроллера, который будет использоваться, тактовая частота и другие данные. “Шапка” - это “правило хорошего тона” в программировании.
Обычно в “шапке” прописывается файл инициализации .includeххххdef.inc. директивы .device, .list, .nolist, и другие.
В AVR Studio 5 эти директивы прописаны по умолчанию. В предыдущей версии AVR Studio 4 эти директивы нужно было прописывать, поэтому я их оставил в виде комментариев.
Комментарии пишутся произвольно, как вам нравиться и главное, чтобы они для вас оставались понятными. Нередко, из-за неполных комментариев, через некоторый промежуток времени сам автор не может разобраться в собственной программе. Перед комментариями ставиться знак ; .
Я ввел “шапку” предложенную Джоном Мортоном…
Прикрепленное изображение: Текст программы_1_1.jpg
Редактор AVR Studio 5 – понимает написанные, директивы и команды выделяя их в синий цвет, числовые данные остаются черными, непонятные выражения выделяет в красный, комментарии в зеленый.

Прежде чем начать, что, то писать рассмотрим директивы ассемблера микроконтроллеров AVR. Перед каждой директивой ставиться точка:
Директива Описание
.BYTE Резервировать байты в ОЗУ
.CSEG Сегмент программы
.DB Определить байт – константу во Flash-памяти или EEPROM
.DEF Назначить регистру символическое имя
.DEVICE Определяет устройство, для которого компилируется программа
.DSEG Сегмент данных
.DW Определяет слово во Flash-памяти или EEPROM
.ENDM, Конец макроса
.ENDMACRO
.EQU Установить постоянное выражение
.ESEG Сегмент EEPROM
.EXIT Выход из файла
.INCLUDE Вложить другой файл
.LIST Включить генерацию листинга
.LISTMAC Включить разворачивание макросов в листинге
.MACRO Начало макроса
.NOLIST Выключить генерацию листинга
.ORG Установить положение в сегменте
.SET Установить для переменной эквивалентное выражение

Полное описание команд и директив ассемблера можно посмотреть по ссылке: http://dfe.karelia.r.../avrasm-rus.htm
Мы еще не раз вернемся к директивам ассемблера, а сейчас попробуем написать небольшой текст программы, где будем включать светодиоды.
Для удобства выберем микроконтроллер ATtiny2313A. Если кто-то пойдет дальше и будет экспериментировать в “железе”, этот контроллер один из самых доступных, очень часто применяется в различных конструкциях, которые можно найти в бескрайних просторах сети.
Начинаем работать с программой Proteus. Чтобы поначалу не сидеть и составлять схему, я выложил готовый проект.
Прикрепленный файл  ATTINY2313_led.rar (27,18К)
Количество загрузок:: 2586
Нужно лишь распаковать и стартовать его (при условии, что Proteus уже установлен). Должна появиться вот такая картинка:
Прикрепленное изображение: начало.jpg
Запустим AVR Studio 5, и в редакторе напишем небольшую программу с подробными комментариями:
.def temp=r16 		; директива .def назначает регистру r16 имя temp

;====================================================
; Начало программы
.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
rjmp Cicle            ; Возвращаемся к метке Cicle, зацикливаемся



Скомпилируем ее, кликнув по F7
В окошке Output появятся информация о проведенной компиляции, в конце должна быть надпись Build succeeded, которая подтверждает удачную сборку .hex файла.
Прикрепленное изображение: Комиляция.jpg
В Atmel Studio 7.0
Прикрепленное изображение: компиляция.jpg Прикрепленное изображение: ошибка.jpg

Этот файл по умолчанию размещен в Моих документах, в проекте AVR Studio 5.
Переходим в Proteus, кликаем на рисунок контроллера и вводим путь, где находиться .hex файл:

Прикрепленное изображение: установка пути_1_1.jpg Прикрепленное изображение: установка пути_1_2.jpg Прикрепленное изображение: установка пути_2_1.jpg Прикрепленное изображение: установка пути_2_2.jpg Прикрепленное изображение: установка пути_2_3.jpg Прикрепленное изображение: результат_2.jpg
После нажатия кнопки Старт видим результат! Чтобы остановить эмуляцию – кликаем Стоп. Если прошить микроконтроллер программатором и собрать схему, то результат будет таким-же.

Попробуем разобраться, с программой:
Строка:
 .def temp=r16 		; директива .def назначает регистру r16 имя temp
Для удобства мы назначили регистру r16 имя temp, это значит что в любом месте программы, при написании слова temp программа будет обращаться к регистру r16.
Что это за регистр – r16?
Он относится к регистрам общего назначения, которые обозначаются как r0….r31, т.е их всего 32 по 8 бит. Вторая половина регистров общего назначения r16…r31 выполняют функцию временного хранения информации (как аккумуляторы, могут принимать и могут отдавать информацию, для сравнения - у PIC контроллеров всего один аккумулятор W). Последние 6 регистров общего назначения условно объединены в три 16-битных:

Прикрепленное изображение: РОН.gif

Обратите внимание, что старший байт пишется раньше, младший позже.

Прикрепленное изображение: XYZ.gif

r27:r26 называется – регистром X,
r29:r28 называется - регистром Y,
r31:r30 называется – регистром Z

Программа будет так же работать если слово temp будет присвоено любому другому регистру общего назначения от r16 до r31 например .def temp=r20. После компиляции (клавиша F7 в AVR Studio 5), разницы в работе программы никакой нет.
(Можете попробовать, теперь вы знаете, как это сделать!)

Строка:
.cseg 				; директива .cseg определяет начало сегмента, где будет расположен основной код программы. 
В AVR Studio 5 это директива прописана по умолчанию.

В микроконтроллерах AVR выделяют разные области памяти, для хранения программного кода, данных в постоянной памяти или EEPROM, для обозначения этой области существуют директива:
.cseg - Программный сегмент
.dseg – Сегмент данных
.eseg – сегмент EEPROM

Строка:
.org 0                ; начало первой строки программы
Программа начинается со стоки указанной дириктивой .org, и при завершении будет возвращаться к ней.

Строка:
rjmp Start            ; относительный переход к метке Start (в PIC соответствует команде goto)

Это команда относительного перехода к метке Start.
Программа выполняется последовательно от верхней стоки к нижней. Если требуется перейти в другую область программы то используется команда rjmp

Строки:
ser temp              ; устанавливает все биты регистра temp  в 1
clr temp              ; обнуляет регистр temp (устанавливает все биты регистра temp  в 0)

Команда ser выполняется с регистрами r16…r31, а команда clr может выполнятся ко всем регистрам, в том числе и регистрам ввода-вывода (регистрам специального назначения). Назначение этих регистров мы рассмотрим позже.

Строка:
ldi temp,0b11001100  
Команда ldi записывет в регистр temp двоичное число 11001100 (не будет ошибкой если вместо 0b11001100 написать 204 или CCh, в одном случае в десятичной форме в другом в шестнадцатеричной)

Строка:
out DDRB,temp
Команда out выводит значение регистра temp в регистр порта DDRB.

Что такое порт и как он работает?
Если упростить до минимума, то порт можно представить как вывод микроконтроллера на который в режиме вывода можно произвольно подать напряжение или отключить, а в режиме ввода определить, есть ли напряжение, поданное извне или нет.
Есть три режима работы, это вывод, ввод и отключенное состояние.
Управляют портами регистры PinX, PortX, DDRX.
Х – означает порт, которым управляют эти регистры.
Регистр DDRX управляет режимами ввода-вывода, как реле – включили (записали 1) – подключился на линию вывода, отключили (записали 0) – переключился на линию ввода (по умолчанию).

Прикрепленное изображение: коммутация портов.jpg

Чтобы вывести данные в порт Х, нужно регистр DDRX переключить в режим вывода (прописать 1) а данные переслать в регистр PortX.
Считать значение порта Х можно, если регистр DDRX переключить в режим ввода (прописать 0), из регистра PinX. Чтобы подключить подтягивающие резисторы в режиме ввода, надо отправить данные в PortX.

Еще раз просмотрим программу, все просто!
Но, есть небольшая ошибка, очень типичная для начинающего программиста! Дело в том, что если значение порта не меняется в течение всего цикла, то лучше установить его один раз в инициализации, и если нужно, то менять уже в основном цикле программы main.
Более правильный вариант будет выглядеть следующим образом:
.def temp=r16 		; директива .def назначает регистру r16 имя temp

;====================================================
; Начало программы
.cseg 				; директива .cseg определяет начало сегмента, где будет расположен
                  	; основной код программы. В AVR Studio 5 это директива не
                  	; обязательна
.org 0            	; начало первой строки программы
rjmp Reset        	; относительный переход к метке Start (в PIC соответствует 
                  	; команде goto)
; ==================================================== 
; Инициализация
Reset:
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 
ldi temp,0b11001100	; включает светодиоды
out PortB, temp   	; порта B

; ==================================================== 
; Программа
main: 
rjmp main        	; Возвращаемся к метке main, бесконечный цикл


Сейчас рассмотрим основы программирования в СИ
После шапки также как в ассемблере прописаны директивы. Напомню, что
директива – это предписание компилятору языка (ассемблер, СИ или другому языку программирования) выполнить то или иное действие в момент компиляции. В свою очередь – компиляция - это перевод написанной программы в машинный код и запись в виде специального файла. Если файл не формируется, а происходит простой перевод, то такой процесс называется трансляцией. Запомните, что директивы не транслируются в код, они указывают каким образом этот код формировать.
Директивы в СИ начинаются с решетки, лишь затем идет ее название. Мы их рассмотрим в процессе изучения программирования.
Наиболее часто в наших программах на СИ встречаются директивы:
#Define – директива замены. Любое выражение, встречающееся в программе, будет заменено на указанное в #define
#Include – директива вставки файла или библиотеки, устанавливает библиотеку ввода-вывода io.h (расширение h - хедер файл), который содержит описание адресов регистров, векторов прерываний, имен битов регистров микроконтроллера
Любая программа, написанная на языке Си, должна иметь следующий минимальный фрагмент текста, который называют функцией main:

int main(void)
{
while(1) { }
}


У функции main есть заголовок – int main(void) и тело – оно ограниченно фигурными скобками { }. В тело функции прописывается основная программа.
В Си часто говорят о возвращаемом значении. Дело в том, что в любой функции происходит дейстсвия связанные с изменением переменных, например сложение, вычитание, сопоставление определенных значений, математические вычисления. Результатом подобных действий и будет возвращаемое значение функции. Если заведомо не нужно ничего возвращать, то прописывается слово void (пустой).
Перед именем функции main указывается тип возвращаемого (вычисляемого) значения int, которому соответствует целое 2-х байтное число с диапазоном значений от – 32768 до 32767.
После имени функции в скобках ( ) указываются параметры, которые определяют условия работы функции при ее вызове. Если параметров нет – используется ключевое слово void
Для программирования микроконтроллеров, внутри тела функции main, обязательно прописывается бесконечный цикл while(1) { }, у которого так же есть заголовок и тело. Единица в параметрах означает, что значения цикла всегда истина и цикл будет выполнятся бесконечно т.е.как только основная программа дойдет до конца, она не зависнет, а начнется с начала. В теле цикла и будет наш код.
В Си нередко для завершения выполнения функции используется ключевое слово return.
#include <avr/io.h>    	// стандартная библиотека ввода-вывода

int main(void)
{
	DDRB = 255;    	// Порт PB на вывод
	PORTB = 0b11001100;    	// Включить биты порта

//---------------------------------------- Бесконечный цикл
	while(1)
	{
	}
	return 0;    	// конец программы
}


Прикрепленное изображение: 1С.png

В Си программа кажется несколько короткой и более лаконичной. В принципе так оно и есть. Но, нередко более короткая на вид программа на СИ может оказаться значительно объемной в сравнении с идентичной программой на ассемблере.

Теперь займитесь практикой, попробуйте самостоятельно поменять картину свечения светодиодов.

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

Сообщение отредактировал galrad: 03 Ноябрь 2015 - 21:35

1

#3 Пользователь офлайн   MAZ 

  • Консерватор - до мозга костей и суставов
  • PipPipPipPip
  • Группа: SuperMod
  • Сообщений: 1 385
  • Регистрация: 15 Февраль 11
  • ГородРоссия

Отправлено 24 Сентябрь 2011 - 20:55

Отличная статья! Спасибо!
Если бы Бог не был консерватором, вместо десяти заповедей мы имели бы десять предложений
0

#4 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 27 Сентябрь 2011 - 22:47

Мы научились включать и выключать светодиоды, а как сделать, чтобы они сами заморгали? Понятно, что после того, как их включили, нужно сделать паузу, затем выключить, снова выждать паузу и снова включить. А как сделать паузу?
Есть конечно, команда NOP, которая сделает паузу равную одному такту, но мы ее никак не увидим.
Такт – это один импульс тактового генератора. Современные контроллеры работают с тактовыми частотами от 1 до 60 мегагерц (от 1 до 60 миллионов импульсов в секунду). Большинство команд AVR выполняется за один такт, где то около 10 команд выполняется за 2 такта.
Поэтому паузу сделанную одной командой Nop, увидеть невозможно, а если нам нужна пауза в полсекунды - тогда нужно минимум написать 500 тысяч команд Nop. Но, это не реально!.
Может остановить программу?
А остановить мы ее не можем, иначе будет сбой программы. Есть другое решение – можно программу зациклить, чтобы отсчитать какое-то количество тактов, а затем продолжить.
Самый простой вариант зацикливания:
main:   	rjmp main

Строка начинается метки main:
Метка обязательно заканчивается двоеточием – в этом ее отличие. Метка может иметь любое сочетание букв и цифр написанное в английской транскрипции (как в предыдущем примере я написал main: и ничего, программа без проблем распознала эту метку).
Кто изучал программы для PIC, видимо заметил, что там после метки, двоеточие не ставится.
Далее стоит команда rjmp main – относительный переход к метке main т.е. на самого себя. Все “бесконечный цикл”.
Из этой программы можно выйти, только если были заданы условия прерывания, или включен сторожевой таймер 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 Reset        	; относительный переход к метке Start (в PIC соответствует 
                      ; команде goto)
; ====================================================
; Инициализация
Reset:
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 

; ===================================================
; Программа
main: 
ldi temp,0b11001100   ; включает светодиоды
out PortB, temp   	; порта B
rcall delay   		; вызов подпрограммы задержки
clr temp              ; выключает светодиоды
out PortB, temp   	; порта B
rcall delay   		; вызов подпрограммы задержки
rjmp main        	; Возвращаемся к метке main, зацикливаемся
; ====================================================
; Подпрограмма задержки
; ====================================================
delay:    
ldi Temp1,0          ; записать в регистр temp1 знчение 0
ldi Temp2,0          ; записать в регистр temp2 знчение 0
ldi Temp3,2          ; записать в регистр temp3 знчение 2

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

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

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


Здесь файлы проекта для Proteus:
Прикрепленный файл  ATTINY2313_led_1.rar (27,14К)
Количество загрузок:: 1067
компилируем, загружаем проект, распаковываем и запускаем:
Прикрепленное изображение: кнопки.jpg
Смотрим как работает программав Proteus (как загрузить .hex файл подробно описано во 2-ой части). Что видим? Заморгали светодиоды?
Прикрепленный файл  моргание.wmv (491,56К)
Количество загрузок:: 1034
Разберем, что мы тут понаписали:
Строка:
clr temp – обнуляет регистр temp

Строка:
rcall delay   		; вызов подпрограммы задержки

команда rcall вызывает подпрограмму с меткой delay, которая реализует нужную нам задержку. Выход из подпрограммы происходит по команде - ret
По команде ret происходит возврат в прежнюю точку, откуда была вызвана подпрограмма. Подпрограммы удобно создавать, когда в программе требуется многократно выполнять одно и тоже действие. Достаточно написать одну подпрограмму и обращаться к ней в любой момент, по мере необходимости.
Как работает наша подпрограмма delay?
Нам нужно правильно рассчитать время задержки. Возьмем во внимание, что внутренняя (без внешнего кварцевого генератора) тактовая частота нашего микроконтроллера равна 8 МГц. По умолчанию, происходит деление этой частоты на 8 т.е. реальная тактовая частота равна 1 МГц, и задержка одного такта составит 1 миллисекунду. Для того, чтобы организовать задержку в 0,5 секунды, нужно пропустить 500 тысяч тактов. Как это сделать?
А для этого мы организуем 3-х байтный счетчик из регистров Temp1, Temp2, Temp3
Почему трехбайтный? Давайте посмотрим, как выглядит число 500000 в двочной и шестнадцатиричной системах.
[b0] 00000111 10100001 00100000;
07 A1 20 [h]
Наглядно видно, что число состоит из трех байт.
Вроде все просто, но если мы запишем эти числа в регистры, то на самом деле задержка окажется значительно больше, чем мы ожидали. Почему?
А ведь кроме регистров есть в подпрограмме команды, которые выполняются за определенное количество тактов, они то и забирают дополнительное время. Чем больше команд, тем большее время будет выполняться задержка. Если грубо прикинуть, то время задержки будет примерно в четыре раза больше ожидаемого! Поэтому я записал в младшие регистры нули, а в старший 2.

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


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

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

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

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

После того, как ввели текст, компилируем программу (F7).
Кликаем (View)
Прикрепленное изображение: view.jpg
В меню (View) кликаем (Toolbars)
Прикрепленное изображение: view1.jpg
Ставим галочку в подменю (AVR Drbugger)
Прикрепленное изображение: view2.jpg
То же самое в Atmel Studio 7.0
Прикрепленное изображение: отладка.png
Выходим из подменю на страницу редактора, устанавливаем курсор на строке в начале подпрограммы задержки и кликаем (F9)
Возникает красный кружек с выделенной строкой (Breakpoint) точка останова.
Прикрепленное изображение: view3.jpg
Точно также выделяем вторую строку (Breakpoint)
Прикрепленное изображение: view4.jpg
1.Кликаем на зеленый треугольник (Start), тем самым запускаем симуляцию.
2.Кликаем на символ микросхемы, если справа не появилось окно (Processor).
3.Устанавливаем желтую стрелку на верхнем Breakpoint-е, кликая по зеленому треугольнику (Start).
4.Выделяем строку (Cycle Counter) и прописываем 0.
5.Устанавливаем тактовую частоту процессора
Прикрепленное изображение: view5.jpg
в Atmel Studio 7.0
Прикрепленное изображение: счетчик.png
Кликаем на зеленый треугольник (Start) и ждем появления желтого треугольника в нижнем Breakpoint-е.
Смотрим строку Stop Watch (секундомер).
Это время от начала подпрограммы задержки, до его завершения.
Прикрепленное изображение: view6.jpg
в Atmel Studio 7.0
Прикрепленное изображение: счетчик_1.png
Чтобы убрать Breakpoint-ы, нужно подвести курсор на красный кружек и кликнуть (F9).
Таким образом можно определить длительность выполнения любого отрезка программы, и очень точно выставить временные интервалы.

В нашей программе время отчитывается с момента включения светодиода до момента его отключения. Частота процессора соответствует 1 МГц. Установим Breakpoint на команде включения светодиода, а второй на команде выключения.
Прикрепленное изображение: включение.jpg
Запустим Debug-ер, кликнув зеленый треугольник. Смотрим показания секундомера Stop Watch.
Прикрепленное изображение: выключение.jpg
Это реальное время включения светодиодов (394 255,00 миллисекунд). Снова обнуляем, и запускем Debug-ер,
Прикрепленное изображение: пауза.jpg
теперь в показаниях секундомера смотрим время паузы (394 257,00 миллисекунд). Получили разницу в 2 миллисекунды. Попробуйте самостоятельно, с помощью команд Nop, сделать одинаковыми время включения и паузу светодиодов.

Программа на СИ
В СИ используются встроенные библиотеки, значительно упрощающие создание программы.
В нашем случае, за счет использования встроенных библиотек задержки #include <util/delay.h>, текст программы значительно сократился.
Время задержки может выставляться в микро _delay_ms() и милли _delay_us() секундах. Алгоритм работы программы такой же как и в ассемблере.
#define F_CPU 8000000UL					// установка частоты микроконтроллера	
#include <avr/io.h>						// стандартная библиотека ввода-вывода
#include <util/delay.h>					// библиотека задержки

int main(void)
{
	DDRB = 255;							// Порт PB на вывод

//---------------------------------------- Бесконечный цикл
	while(1)
	{
		PORTB = 0b11001100;				// Включить биты порта	
		_delay_ms(500);					// устанавливаем задержку на 0,5 секунд 
		PORTB = 0b00000000;				// Обнуляет биты порта
		_delay_ms(500);					// устанавливаем задержку на 0,5 секунд 			
	}
	return 0;							//конец программы
}
Текст написанной на СИ более лаконичный, хотя по объему он не меньше, чем в ассемблере.

Теперь изменим программу таким образом, чтобы получить возможность управлять включением светодиодов с помощью кнопок.
;====================================================
; Установки
.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 Reset        	; относительный переход к метке Reset (в PIC соответствует 
                      ; команде goto)
; ====================================================
; Инициализация
Reset:
ser temp              ; устанавливает все биты регистра temp  в 1
out DDRB,temp 		; переводит все биты порта B на вывод
clr temp1 			; обнуляет регистр temp1
out DDRD,temp1        ; переводит все биты порта D на ввод 		
out PortB,temp1   	; отключает подтягивающие резисторы портов B
out PortD,temp        ; включает подтягивающие резисторы портов D 
; ====================================================
; Программа
main: 
in temp,PinD          ; считывает значение порта D и 
out PortB, temp   	; переводит результат в порт B
rcall delay   		; вызов подпрограммы задержки
clr temp              ; выключает светодиоды
out PortB, temp   	; порта B
rcall delay   		; вызов подпрограммы задержки
rjmp main        	; Возвращаемся к метке main, зацикливаемся
; ====================================================
; Подпрограмма задержки
delay:    
clr temp1            ; обнулить регистр temp1
clr temp2            ; обнулить регистр temp2
ldi temp3,2          ; записать в регистр temp3 число 2

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

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

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


Строка:
in temp,PinD 

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

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

Программа в СИ
#define F_CPU 8000000UL					// установка частоты микроконтроллера	
#include <avr/io.h>						// стандартная библиотека ввода-вывода
#include <util/delay.h>					// библиотека задержки

int main(void)
{
	DDRB = 255;							// Порт PB на вывод
	DDRD = 0;                            	// Порт PD на ввод
	PORTD = 255;                     	// Подтягивающие резисторы на порт PD

//---------------------------------------- Бесконечный цикл
	while(1)
	{
		PORTB = PIND;					// Вывести в порт PB значение порта PD
		_delay_ms(500);					// устанавливаем задержку на 0,5 секунд			
		PORTB = 0;						// Обнуляет биты порта
		_delay_ms(500);					// устанавливаем задержку на 0,5 секунд
	}
	return 0;							//конец программы
}



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

Вот небольшой видеоролик, того, что должно получиться.
Прикрепленный файл  кнопки.wmv (1,73МБ)
Количество загрузок:: 1106
Далее мы напишем программу бегущих огней, выясним, что такое стек.

Сообщение отредактировал galrad: 03 Ноябрь 2015 - 23:53

0

#5 Пользователь офлайн   Юрий С. 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 7
  • Регистрация: 24 Март 11
  • ГородДонецк

Отправлено 06 Ноябрь 2011 - 23:52

Отличная тема! Отличная статья! Огромное спасибо Вам, и ждём продолжения.

С уважением.
0

#6 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 16 Ноябрь 2011 - 07:47

Сегодня рассмотрим программу “бегущих огней” и “бегущих теней”. Примеры “бегущих огней” можно найти почти в любой обучающей литературе. Чтобы получить “бегущие огни” на выходах порта, нужно последовательно переключать один бит
Прикрепленное изображение: 1.jpg
“Бегущая тень” получается при инверсии “бегущих огней”. В принципе сложностей никаких нет – и вам под силу сделать это самостоятельно. Мы попробуем усложнить задачу и объединить в одной программе “бегущие огни” и “бегущие тени”, сделать их непрерывными т.е. рисунок светящихся огоньков будет переходить из конца в начало, а еще включим функцию реверса огней т.е нажав на кнопку сможем “двигать” огоньки в обратном направлении.
Проект в Proteus будет выглядеть так:
Прикрепленное изображение: 2.jpg
Программа:
; ****************************************
; Автор:                 				*
; Дата:                                  *
; Версия:                                *
; Название файла:                        *
; Для AVR:  ATtiny2313   				*
; Тактовая частота:  8мГц                *
; ****************************************

; Выполняемые функции:  реверсивные бегущие огни

; ================================================== Установки
; .device 			;
; .nolist 			; 
; .include            ; 
; .list   			; 
; ===================================================
.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 определяет начало сегмента кодов
.org 0                ; начало первой строки программы
Reset:
; ==================================================== Инициализация стека
; ldi temp,Ramend 	; определение
; out SPL, temp   	; верхушки стека 
; ==================================================== Инициализация портов
ser temp              ; устанавливает все биты регистра temp  в 1
clr temp1 			; обнуляет регистр temp1
ldi temp2,0b00000001  ; записываем в temp2 число 1
out DDRB,temp 		; переводит все биты порта B на вывод
out DDRD,temp1        ; переводит все биты порта D на ввод   	
out PortB,temp1   	; отключает подтягивающие резисторы портов B
out PortD,temp2   	; отключает подтягивающие резисторы портов D кроме 0 порта
 ; ==================================================== Программа
ldi temp,0b00000001   ; определяет рисунок бегущего огонька

Loop:
SBIC PinD,0   		; опрашивает  0 бит регистра ввода
rjmp right            ; и переходит на строку right, если 0 бит равен 1

left: 				; блок перемещения огонька влево
rol temp              ; циклический сдвиг влево через флаг C
brcc led              ; если перенос очищен, то перейти на метку led
ldi temp1,0b00000001  ; если C=1, то записываем 
add temp, temp1   	; в 0 бит единицу
rjmp led              ; и переходим на метку led

right:                ; блок перемещения огонька вправо
ror temp              ; циклический сдвиг вправо через флаг C
brcc Led              ; если перенос очищен, то перейти на метку led
ldi temp1,0b10000000  ; если C=1, то записываем    
add temp, temp1   	; в 7 бит единицу

led:                  ; блок вывода огонька в порт
out PortB,temp        ; вывод в порт D значения регистра temp
rcall delay   		; вызов подпрограммы задержки
rjmp Loop            ; Возвращаемся к метке Cicle, зацикливаемся
; ==================================================== Подпрограмма задержки
delay:    
clr temp1 			; обнулить регистр temp1
clr temp2 			; обнулить регистр temp2
ldi temp3,2   		; записать в регистр temp3 число 2

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

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

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


Работает программа очень просто, огонек начинает двигаться в обратную сторону, если нажать кнопку. Очень легко сформировать бегущую тень, если в строке формирования рисунка прописать все единицы и оставить один нулик. Поэкспериментируйте с разными рисунками огней.
Должно выглядеть примерно так:
“бегущий огонек”:Прикрепленный файл  beg_og1.wmv (1,01МБ)
Количество загрузок:: 789
“бегущая тень”:Прикрепленный файл  beg_ten1.wmv (878,09К)
Количество загрузок:: 549
“бегущая полоска”:Прикрепленный файл  beg_polos1.wmv (831,7К)
Количество загрузок:: 555
Программа “бегущих огней”но написанная несколько иначе:
; ****************************************
; Автор:                 				*
; Дата:                                  *
; Версия:                                *
; Название файла:                        *
; Для AVR:  ATtiny2313   				*
; Тактовая частота:  8мГц                *
; ****************************************

; Выполняемые функции: реверсивные бегущие огни - 2

; ================================================== Установки
; .device
; .nolist
; .include
; .list
; ===================================================
.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 определяет начало сегмента, где будет расположен основной код программы. 
.org 0                ; начало первой строки программы
; ==================================================== Инициализация стека
ldi temp,Ramend
out SPL, temp
; ==================================================== Инициализация портов
ser temp              ; устанавливает все биты регистра temp  в 1
clr temp1 			; обнуляет регистр temp1
ldi temp2,0b00000001  ; записываем в temp2 число 1
out DDRB,temp 		; переводит все биты порта B на вывод
out DDRD,temp1        ; переводит все биты порта D на ввод        
out PortB,temp1   	; отключает подтягивающие резисторы портов B
out PortD,temp2   	; отключает подтягивающие резисторы портов D кроме 0
 ; ==================================================== Программа
ldi temp,0b11111110   ; определяет рисунок бегущего огонька

Loop:
out PortB,temp
 
SBIC PinD,0   		; опрашивеет  0 бит регистра ввода
rjmp left 			

right:
clc
SBIC PinB,0   		; опрашивеет  0 бит регистра ввода 	
sec
ror temp
rjmp led

left:
clc
SBIC PinB,7   		; опрашивеет  7 бит регистра ввода      
sec
rol temp

led:
rcall delay   		; вызов подпрограммы задержки
rjmp Loop 			; Возвращаемся к метке Loop, зацикливаемся
; ==================================================== Подпрограмма задержки
delay:    
clr temp1 			; обнулить регистр temp1
clr temp2 			; обнулить регистр temp2
ldi temp3,2   		; записать в регистр temp3 число 2

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

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

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


В программе появились новые команды и строчки. Мы изучим их в процессе отладки.
А сейчас вернемся в IDE AVR Studio 5 и рассмотрим более подробно некоторые функции. Эти функции абсолютно идентичны Atmel Studio, поэтому я не буду в дальнейшем делать оговорки.
Сначала копируем или пишем программу в редактор. Компилируем программу нажимая F7 или кликаем по среднему значку панели сборки проекта:
Прикрепленное изображение: 3.jpg
Назначения клавиш слева - направо:
Сборка проекта (Build)
Сборка решения (Build Solution) F7
Отмена (Cancel)
По результатам сборки в окне Output, будет предоставлен отчет о процессе компиляции, а в Solution Explorer будут показаны файлы полученные в результате сборки. Программа создает несколько файлов .hex, .lss, .map, .obj. Файл .hex для программирования микроконтроллера, .lss содержит подробный листинг программы и отчет о компиляции, который можно изучить, если дважды кликнуть по этому файлу, .map файл показывает карту оперативной памяти и задействованные в программе регистры.
Прикрепленное изображение: 4.jpg

Как правило, написание программы занимает меньшее время. Чем сам процесс ее отладки. Поэтому сегодня рассмотрим общие принципы отладки программ.
Отладка Debug, начинается с компиляции программы, и если обнаружены явные ошибки, в нижнем окне появляется предупреждения, которые указывают на ее место расположения.
Прикрепленное изображение: 5.jpg
Отладка программы может выполняться в режимах симуляции и эмуляции.
Симуляция не требует специальных отладочных средств и проводится непосредственно в самой программе. Эмуляция предполагает подключение отладочных плат или программаторов, и позволяет наблюдать процесс отладки наглядно сразу в “железе”. Повторюсь, что для AVR Studio 5 можно самому собрать аналог STK500, весьма мощный и функциональный программатор-отладчик.
Мы будем выполнять отладку в режиме симуляции. Для управления процессом симуляции существует панель отладки (Debug Toolbar)
Прикрепленное изображение: 6.jpg
Назначение кнопок панели отладки указаны в таблице
Прикрепленное изображение: 7.jpg
В процессе отладки можно наблюдать за изменениями значений переменных в окне Locals
Прикрепленное изображение: 8.jpg
Контролируя значения переменных, можно понять логику программы.
Для трассировки программы (пошаговой отладки) предназначена (AVR Debug Toolbar) – панель отладки AVR.
Прикрепленное изображение: 9.jpg
Назначение кнопок панели указаны в таблице
Прикрепленное изображение: 10.jpg
Мы уже пользовались опцией просмотра состояния процессора, еще раз посмотрим функции этого окна.
Прикрепленное изображение: 11.jpg
Параметры состояния процессора перечислены ниже
Прикрепленное изображение: 12.jpg
В этом окне, а так же отдельно можно посмотреть текущее состояние регистров общего назначения и 16-битные регистры X,Y,Z.
Прикрепленное изображение: 13.jpg
Окно просмотра памяти позволяет наблюдать, а при необходимости и модифицировать все виды памяти.
Прикрепленное изображение: 14.jpg
Окно дезассемблирования показывает программу на языке ассемблера. Это окно актуально для языков высокого уровня, когда нужно посмотреть, как составлена та или иная команда в кодах ассемблера.
Прикрепленное изображение: 15.jpg
Для контроля и управления регистрами ввода-вывода существует окно просмотра периферии IO View
Прикрепленное изображение: 16.jpg
В этом окне можно включать или выключать конкретные биты регистров ввода-вывода, имитируя нажатие на кнопку или активируя встроенное устройство микроконтроллера. Единичка записанная в бит обозначена темным квадратиком, светлый (пустой) квадрат означает ноль. Мы можем произвольно включать и отключать биты регистров.
Теперь вернемся к нашим бегущим огонькам.
Начало в принципе знакомое и повторяется с предыдущих программ, появилась новая строчка:
Инициализация стека.
Что такое стек? Зачем он нужен?
Стек-это участок оперативной памяти, состоящий из нескольких ячеек, для временного хранения данных, связанных с вызовом подпрограмм.
Стек ассоциируют с разными предметами, например блинами положенными друг на друга или патронами в магазине автомата Калашникова. Суть стека такова – зашел последним - вышел первым. Чтобы съесть самый нижний блин, который был первым нужно сначала съесть верхние, которые легли позже, чтобы выстрелить из патрона, который мы вставили раньше всех, нужно использовать те, которые вставили позже. Самый последний, будет использован первым.
Стек в AVR используется для хранения адресов при выходе из подпрограмм и для кратковременного хранения данных.
Для обращения к стеку (стековой памяти) существует специальный регистр – указатель стека SPH:SPL, для микроконтроллеров с большим объемом ОЗУ, и SPL для небольших объемов ОЗУ.
В AVR стековая память работает по принципу уменьшения на единицу, где верхушкой является самый последний адрес ОЗУ, у ATTINY2313 это 0х00DF. Адрес ОЗУ 0х00DF называется RAMEND (конец ОЗУ). Таким образом, стековая память заполняет ОЗУ – сверху вниз, от большего адреса к меньшему.
При включении микроконтроллера, регистр SPL обнуляется, и если не инициализировать верхушку стека, заполнения стековой памяти не происходит, возникает сбой. В AVR Studio 5 процесс инициализации стековой памяти происходит по умолчанию, поэтому строки инициализации не обязательны, но и не будет ошибкой, если мы пропишем эти стоки. Давайте проверим.
Компилируем нашу программу, где строки инициализации прописаны как комментарии. Кликнем по значку (Step into) F11.
Справа появляется окно Processor (окно состояния процессора), если окно не появилось нужно сделать, как указано во второй части.
Смотрим вторую строчку Stack Pointer (Указатель стека), где указан адрес 0X00DF, который выделен красным цветом, говорящий, что изменения в ячейке сделаны в предыдущем такте, самой программой. Попробуем изменить адрес верхушки стековой памяти. Выйдем из режима отладки, кликнув (Stop Debugging) Ctrl+Shift+F5. Отредактируем строки инициализации и вместо RAMEND запишем произвольное число большее, чем адреса регистров ввода-вывода, т.е. начиная с 0х0060, например 0х0074.
Получаем такие строки:
; ====================================================
; Инициализация стека 
ldi temp, 0Х0074 	; определение
out SPL, temp   	; верхушки стека 
; ====================================================

Входим в режим трассировки F11, смотрим в параметрах состояния процессора строчку Stack Pointer, красной строчкой выделен адрес 0х00DF, кликнем по F11, два раза. Адрес верхушки стека стал равен 0х0074, т.е. мы инициализировали стековую память по адресу 0х0074. В режиме отладки, мы можем инициализировать любой участок ОЗУ, просто 2 раза кликнув по строчке Stack Pointer и вручную набрав нужный нам адрес.
Прикрепленное изображение: 17.jpg
У стековой памяти есть еще оно полезное свойство, мы можем произвольно записать значение регистра общего назначения в стек c помощью команды push. И наоборот записать значение из стека в регистр общего назначения командой pop.
команда push – записывает значение РОН в стековую память по адресу, установленному в указателе стека, и вычитает из этого адреса единицу.
команда pop – записывает в РОН значение стековой памяти по адресу, установленному в указателе стека, и добавляет к этому адресу единицу.
Обратите внимание, что первая строка (Program Counter) стала равна 2 – это адрес программной строки, которая сейчас будет выполнена. (Program Counter) сокращенно PC – счетчик команд, это регистр в котором значение в процессе выполнения линейной программы инкрементируется, но при выполнении команд перехода значение PC может резко меняется. Для того, чтобы выполнение программы продолжить с нужной нам стоки, можно вместо метки записать выражение PC+/-N.
Например программу задержки можно записать без меток, таким образом:
dec temp1
brne PC-1

dec temp2
brne PC-3

dec temp3
brne PC-5
ret

Попробуйте переписать и убедиться, что программа работает. Можно легко перейти на любую строку программы, набрав ее в строчке Program Counter.
Итак, мы рассмотрели основные функциональные окна AVR Studio 5 и написали несколько программ. Как вы убедились программа достаточно проста по структуре и интуитивно понятна.
Для программирования микросхем рекомендую собрать аналог STK5000 по ссылке: http://ra4nal.qrz.ru/stk500.shtml
Прикрепленный файл  stk500.rar (523,61К)
Количество загрузок:: 540
отлично работает и незаменим в случаях, когда неправильно прошиты фьюзы (биты конфигурации).

Программа написанная на СИ
#define F_CPU 1000000UL					// установка частоты микроконтроллера	
#include <avr/io.h>						// стандартная библиотека ввода-вывода
#include <util/delay.h>					// библиотека задержки

int main(void)
{
	DDRB = 0xFF;							// Порт PB на вывод
	DDRD = 0;								// Порт PD на вход
	PORTD |=(1<<0);							// Подтягивающий резистор на PD0

//---------------------------------------- Бесконечный цикл

	while(1)						// Бесконечный цикл
	{
		if(PIND & (1<<PD0))			// Если нулевой бит порта PD установлен
		{
		_delay_ms(300);				// Определяем задержку в 0,3 секунд
		PORTB=(PORTB<<1);			// сдвиг влево
		if(PINB==0)					// если порт PB обнуляется то
		PORTB=1;					// пропсываем в порт PB значение 1
		}
									// Если нулевой бит порта PD обнулен
		else
		{
		_delay_ms(300);				// Определяем задержку в 0,3 секунд
		PORTB=(PORTB>>1);			// сдвиг вправо
		if(PINB==0)					// если порт PB обнуляется то
		PORTB=128;					// пропсываем в порт PB значение 128
		}
	}
	return 0;							//конец программы
}

Рассмотрим здесь отдельные команды:
PORTD |=(1<<0); записывает в 0 бит порта PD значение 1 (устанавливает 0 бит порта PD).
команда ветвления if() {...} else {...} звучит примерно так: Если условие (в обычных скобках соответствует логической 1) то выполняются строки прописанные в верхних фигурных скобках, иначе выполняются строки прописанные в нижних фигурных скобках.
PIND & (1<<PD0) определяет установлен 0 бит порта PD или нет.
Обратите внимание на то, что все команды в СИ заканчиваются точкой с запятой.

В последующих статьях рассмотрим внутреннюю структуру контроллеров AVR, и особенности программного управления. Акцент будет перенесен на особенности написания програм в ассемблере, уже выходя за рамки контроллера ATTINY2313, что даст вам болше возможностей для самостоятельного творчества.

Сообщение отредактировал galrad: 06 Ноябрь 2015 - 22:47

0

#7 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 27 Ноябрь 2011 - 12:03

А вот скажите лучше, если снести Майкрософт Вижен Студио, будет ли пятёрка работать?
Насколько понял, в четвёрке была возможность работы совместно с Протеусом. Отсюда вопрос: в чём принципиальное отличие пятёрки от четвёрки (кроме интерфейса)?
Кстати, ни кто не поделится моделькой ATTiny45 для Протеуса 7.2? :rolleyes:
0

#8 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 03 Декабрь 2011 - 11:37

Просмотр сообщенияDmitry Dubrovenko (27 Ноябрь 2011 - 12:03) писал:

А вот скажите лучше, если снести Майкрософт Вижен Студио, будет ли пятёрка работать?

Нет. Пятая студия без Вижен работать не будет...

Цитата

Насколько понял, в четвёрке была возможность работы совместно с Протеусом. Отсюда вопрос: в чём принципиальное отличие пятёрки от четвёрки (кроме интерфейса)?

Пятерку "накачали" кучей различных библиотек типа протокола I2C, вывод информации на ЖКИ, различные протоколы обмена по последовательному интерфейсу и т.п. Все библиотеки ориентированы под Си. Если работать в асемблере, то разница небольшая, единственное в пятерке можно поначалу не "заморачиваться" по поводу начальной инициализации, там все прописано в прикрепляемых файлах, которые вызываются автоматически (после выбора девайса). А вообще для ассемблера четверка предпочтительнее - размер небольшой и работает быстрее.

Сообщение отредактировал galrad: 03 Декабрь 2011 - 11:45

0

#9 Пользователь офлайн   pmikle 

  • Прибывший
  • Группа: Пользователи
  • Сообщений: 1
  • Регистрация: 03 Декабрь 11

Отправлено 03 Декабрь 2011 - 18:45

Установил AVR Studio 5. Возникла проблема с HELP:
1. При выборе View Help открывается IE, где выдается следующий текст:

The topic you requested could not be found in local help.
Have you installed the local help content?
How to install and configure local help using Help Library Manager.
Check online for your topic at:
http://msdn.microsof...OUSERGUIDE.aspx
Other options to try
Do you have the right content installed locally?
How to add new content to your local help store with Help Library Manager.
How to enable online only help.

После некоторых усилий (устанавливал на 2-х ПК с XP и одном с Windows 7) понял, что нет HlpViewer.exe
(На одном из ПК с XP View Help заработал нормально). В чем причина и как решить проблему на остальных 2-х не понятно.

2. Не понятно откуда можно установить Help по AVR Studio 5.
В Help Library Manager из Online можно установить только документацию по продуктам MS.
При попытке устновить с диска (Program Files\Atmel\AVR Studio 5.0\help) получаю ошибку со ссылкой на Event log для детального просмотра.
В Event log следующее:

An error occurred while updating local content: Microsoft.Help.CacheLib.CacheLibCabSignatureException: The digital signature on file D:\ProgramData\Microsoft\HelpLibrary\content\Atmel Corporation\incoming\132a7778-1ad4-4c53-bf4c-eca3bbedec45\AT32UC3A3_help.cab could not be verified. ...

Если кто сталкивался с такой проблемой и знает как ее решить, подскажите.
Спасибо.
0

#10 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 04 Декабрь 2011 - 05:09

Просмотр сообщенияgalrad (03 Декабрь 2011 - 11:37) писал:

А вообще для ассемблера четверка предпочтительнее
А с Протеусом она, всё-таки, дружит?
А взять где? :rolleyes:
0

#11 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 05 Декабрь 2011 - 08:58

Просмотр сообщенияDmitry Dubrovenko (04 Декабрь 2011 - 05:09) писал:

А с Протеусом она, всё-таки, дружит?

Пятерка с протеусом не интегрируется, по крайней мере, мои попытки были безрезультатными...

Цитата

А взять где? :rolleyes:

вот ссылка как можно скачать с оф.сайта четверку, или пятерку, без разницы - главное работает:

0

#12 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 05 Декабрь 2011 - 11:41

Просмотр сообщенияgalrad (05 Декабрь 2011 - 08:58) писал:

Пятерка с протеусом не интегрируется
Это я в курсе. :yes:
На Казусе разработчик сказал, что и не собираются.
Я про четвёрку спрашивал. :rolleyes:
0

#13 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 06 Декабрь 2011 - 05:43

Просмотр сообщенияDmitry Dubrovenko (05 Декабрь 2011 - 11:41) писал:

Я про четвёрку спрашивал.

С четверкой, протеус работает без проблем (сначала надо установить протеус, а затем студио 4, интеграция происходит автоматически...)

Сообщение отредактировал galrad: 06 Декабрь 2011 - 05:43

0

#14 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 07 Декабрь 2011 - 11:05

Просмотр сообщенияgalrad (06 Декабрь 2011 - 05:43) писал:

сначала надо установить протеус, а затем студио 4, интеграция происходит автоматически...
Что-то у меня не произошла... :unknw:
Может не там (в Tools) смотрю?
0

#15 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 07 Декабрь 2011 - 19:19

Просмотр сообщенияDmitry Dubrovenko (07 Декабрь 2011 - 11:05) писал:

Что-то у меня не произошла...

На Казусе эту тему уже рассматривали:
http://kazus.ru/foru...ead.php?t=13966
может ответ кроется здесь?
Я скачивал по этой ссылке: http://eldigi.ru/site/prog/1.php
проблем не было, спокойненько встраивается в MPLAB и Studio 4.18

Сообщение отредактировал galrad: 07 Декабрь 2011 - 19:29

0

#16 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 09 Декабрь 2011 - 20:43

Просмотр сообщенияgalrad (07 Декабрь 2011 - 19:19) писал:

На Казусе
Читал, читал..
Только у меня Студия 4.19, а Протеус 7.2
Да и проблема там малость другая. У меня окно "Протеус ВСМ" вообще нигде не фигурирует.
0

#17 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 20 Декабрь 2011 - 22:23

Для того чтобы писать более сложные программы, нужно хорошо представлять структуру и взаимодействие разных узлов микроконтроллера между собой, но основные принципы сохраняются независимо от модели контроллера. В принципе человек, который изучил микроконтроллеры PIC, достаточно быстро осваивает AVR или ARM.
Управление микроконтроллером осуществляется изменением логического уровня ячеек (битов) в регистрах, расположенных в оперативной памяти. Конкретному устройству соответствует конкретный регистр, как правило 8-битный (1 байтный), состоящий из 8 ячеек памяти. В любую ячейку можно записать 0 или 1.
Создадим произвольную таблицу, где каждая строка состоит из 8 ячеек и имеет свой порядковый номер, соответствующий одному регистру. Пусть наша таблица состоит из 128 ячеек или из 16 восьмибитных регистров.
Прикрепленное изображение: таблица.jpg
В микроконтроллере есть разные функциональные устройства – это порты, таймеры, АЦП, устройства ШИМ, устройства последовательного приема и передачи данных, и много еще чего. Кроме этого функции конкретного устройства могут меняться, например, сначала принять информацию, а затем переключиться на передачу. Как же они работают? Все проще, чем кажется! Функции определенного устройства жестко привязывается к конкретным ячейкам памяти. Например, если нам нужно на какую-то ножку подать напряжение, мы устанавливаем конкретную ячейку в 1. Пусть, к примеру, за вывод положительного напряжения на 4 ножку отвечает ячейка 9h:3h. Значит, если мы пропишем в ячейку 9h:3h единицу, то на 4- ой ножке получим положительное напряжение. А как мы узнаем, что эта ячейка отвечает за эту функцию? Ячеек много и запомнить номер каждой ячейки очень сложно.
Чтобы было легко работать, производители присвоили каждой строке с определенным порядковым номером, называемым адресом, свое имя, соответствующее устройству. Пусть у нас строка 9h, будет называться регистром PortB, строка 8h регистром PinB, а строка 7h регистром DdrB. Таким образом 7h, 8h, 9h – это адреса регистров с названиями PortB, PinB, DdrB.
Соответствующие регистры указываются в специальном файле с расширением .inc, в самом начале программы. Этот файл становится доступным с помощью директивы .include
Например для attiny2313:
.include "at2313def.inc"
Напомню, что в настоящем контроллере регистр DdrB отвечает за направление ввод-вывод, если в ячейку регистра пропишем 1, то соответствующая ножка будет работать на вывод, подключаясь к регистру PortA , если пропишем 0, то перейдет в режим чтения из регистра PinB. Если в режиме ввода прописать 1 в ячейку регистра PortB, то к соответствующей ножке подключиться подтягивающий резистор.
Таким образом, создается таблица, где указываются все регистры, управляющие внутренними устройствами микроконтроллера, и называются регистрами ввода-вывода оперативной памяти.
Реальная таблица регистров ввода-вывода для attiny2313 выглядит следующим образом:

Прикрепленное изображение: карта регистов ввода_вывода.jpg

Обратите внимание, что таблица для attiny2313 начинается с адреса 0х20 и заканчивается 0х5F (соответствуют регистрам SFR в PIC-ах). Это значит, что регистры, начиная с адреса 0х60 и до 0хDF (0хDF- последний адрес оперативной памяти для tiny2313) могут использоваться для временного хранения данных, создания промежуточных регистров определяемых программистом (как регистры GRP в PIC-ах). В более современных микроконтроллерах адресное пространство регистров специального назначения может быть больше, и это надо учитывать при создании программ. Для упрощения жизни программиста существует определение SRAM_START прописанное в файле ***.inc, которое определяет начало свободной оперативной памяти.
Напомню, что первые 32 регистра называются регистрами общего назначения (РОН), “полноценные” из них регистры старшей группы от 16 до 32. (каждый регистр r16-r32 соответствует W в PIC-ах)
Младшие РОН, от 0 до 15, не дают записать в себя число напрямую. Только через регистры старшей группы. (Почему? Ответ знают только производители!)
Адресация в микроконтроллерах AVR линейная (в PIC оперативная память разделена на несколько страниц – так называемые банки), начинается с 0x0000 и может заканчиваться 0хFFFF
Общая структура оперативной памяти представлена на рисунке:

Прикрепленное изображение: структура ОЗУ.jpg

Теперь посмотрим, как устроен сам микроконтроллер.
Любой контроллер начинается с тактового генератора (oscillator), который может работать как с внутренней RC- времязадающей цепочкой (internal oscillator), так и с внешним кварцевым резонатором, или же может работать от внешних тактовых импульсов. С тактового генератора импульсы через предделитель и таймер(timing and control),(timer/ counter) приходят на счетчик программ – PC (Programm counter), который последовательно начинает обращаться к 16 разрядным инструкциям во флешь - памяти (программе написанную и прошитую программистом), начиная с адреса указанного директивой .org. Инструкция (команда) определяет следующее действие, создавая приращение счетчику команд (если программа без относительных переходов, то после каждой инструкции значение PC увеличивается на единицу, а если есть относительные переходы, то автоматически выполняется расчет приращения).

Прикрепленное изображение: блок-диагрвмма AVR.jpg

Если возникает событие, связанное с прерыванием, то существующее значение PC записывается в стековую память (Stack pointer), организованную из последних адресных регистров ОЗУ, и которую можно вызвать в виде определения Ramend. По окончании прерывания, значение PC извлекается из стековой памяти, и дальше, с этого адреса продолжает выполнять инструкции. Инструкции в процессе выполнения могут обращаться непосредственно по указанному адресу, или косвенно через сдвоенные регистры общего назначения X, Y,Z. (r27:r26; r29:r28; r31:r30). Кроме этого происходит постоянное обращение к регистру статуса (Status register), где прописываются и считываются флаги. Регистры ввода-вывода (General purpose register)позволяют управлять встроенными устройствами, принимать и выдавать данные. Область памяти данных (Sram) отведена для временного хранения промежуточных результатов, констант, которые необходимы в процессе выполнения программы, но при отключении питания эти данные не сохраняются. Встроенная флешь предназначена для сохранения текущей информации в случаях отключения питания, для хранения кодов доступа, различных настроек и т.п.
Вся эта канитель крутиться вокруг арифметико-логического устройства (ALU), по сути – сердца процессора, где и происходят все логические операции.
На следующем рисунке показана вся память микроконтроллера attiny2313:

Прикрепленное изображение: Обшая карта памяти.jpg

Ну, вроде как с общей схемой работы микроконтроллера мы разобрались в том понимании, которое нам необходимо для успешного создания рабочей программы. Дальнейший акцент будет смещен в сторону устройств ввода-вывода, т.к. именно их мы включаем и выключаем программно для создания какого либо функционального устройства. Самым сложным для начинающего программиста считается именно детальное изучение регистров ввода-вывода. У каждого свой путь, кто-то читает даташиты (курит даташит) – инструкции от производителя, кто-то разбирает готовые исходники, а некоторые пытаются писать свои коды, доводя “мозг до кипения”. Скажу сразу – нужно делать все вместе – разбирать исходники, заглядывая в даташиты, и дописывать под себя. Если возникает дефицит информации, то прямиком на форум, задавать вопросы. Но, не просите написать вам готовые коды, и не задавайте абстрактных вопросов на отвлеченные темы, потому, что это не поможет!
Итак! С чего начинается любая программа? Если вы ответите – с инициализации, то я соглашусь с вами, но скажу, что есть некоторое но…
Любая программа начинается с инструкций компилятора, а именно с директив. Дело в том, что сами директивы позволяют создавать структурные звенья программы, формировать универсальные блоки, задавать условия. Очень часто бывает так, что начинающий программист выпрашивает исходник у более опытного товарища, и потирая руки открывает текст, и … не видит знакомых ему команд, а только, что то в виде непонятных меток. А где же программа? В сомнениях компилирует программу и… как ни странно, получает нормальный файл прошивки. В чем же дело? А хитрость в том, что вся программа организована в виде готовых библиотек.
А что это за библиотеки? А вы представьте – стеллажи на которых множество книг, у каждой книги свое название и, конечно же разное содержание. Так вот содержание – это и есть исходный код небольшой программы, имеющий свое имя – как название книги, и все они расположены в одном файле – как на стеллаже. Чтобы создать программу нужно всего лишь составить последовательность имен. Самое интересное в том, что имена этим программам присваиваются совершенно произвольно (все зависит от фантазии программиста). Вот и приходиться иногда “гадать” на каком же языке написана программа (а принцип по сути тот же, что на языках высокого уровня).
Запутал окончательно? Тогда все по порядку!
Любая программа-компилятор подчиняется собственным инструкциям, которые называются директивами. Как правило, директивы разных программных компиляторов имеют одинаковые или схожие названия выполняющие похожие функции. В предыдущих статьях функции некоторых директив уже рассматривались и многие уже нам знакомы. И все таки давайте рассмотрим их детально. А если немного подзабыли, то повторим!

Полный список директив контроллеров AVR расположен в виде таблицы:

Прикрепленное изображение: direct.jpg

Если вам приходилось сталкиваться с языком Cи, то наверно “броситься в глаза” сходство с директивами препроцессора этого языка. Тем самым производители решили частично “унифицировать” ассемблер. Хитрость заключается в том, что можно взять готовые библиотеки программ Си и легко адаптировать в ассемблере, и так же создавая программу в Си легко адаптировать ассемблерные библиотеки. Имея такую универсальность, можно без значительных затрат написать свою RTOS (Real-Time Operating System) – операционную систему реального времени. Но об этом несколько позже. Последняя верся ассемблера (версия 2.1.9) стала более функиональной, и состоит из 29 директив. Расмотрим новые директивы, которые не вошли в описания ассемблера AVRASM: http://dfe.karelia.r.../avrasm-rus.htm


Директивы ассемблера AVRASM2 версии 2.1.9

В Ассемблере до версии 2.1.9 директивы AVRASM2 начинаются с #:
#define
#elif
#else
#endif
#error #if
#ifdef
#ifndef
#include
#message #pragma
#undef
#warning
# (пустая директива)

В Ассемблере версии 2.1.9 директивы начинаются с точки, также как и директивы AVRASM.

Для более подробного знакомства предлагаю учебное пособие Зуборева А.А.

Ассемблер для AVR

Дальше определимся с инициализацией программ.

Сообщение отредактировал galrad: 07 Ноябрь 2015 - 07:38

0

#18 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 22 Декабрь 2011 - 01:04

Продолжим разбор теоретических основ, без которых невозможно полноценное создание программ.

Основной синтаксис AVRASM совместим с AVRASM2 с исключениями, отмеченными ниже:
- Ключевые слова.
- Директивы препроцессора.
- Комментарии.
- Продолжения строк.
- Строки и символьные константы.
- Составные инструкции в строке.
Ключевые слова. В отличие от AVRASM встроенные идентификаторы (ключевые слова) зарезервированы и не могут быть переопределены. Ключевые слова включают все инструкции (команды), регистры R0-R31 и X, Y, Z и все функции. Ключевые слова Ассемблер распознаёт независимо от регистра, в котором они набраны, за исключением чувствительных к регистру опций, в которых ключевые слова набираются в нижнем регистре (т.е. "add" зарезервирован, а "ADD" – нет).
Директивы препроцессора. AVRASM2 считает директивами препроцессора все строки, начинающиеся с '#' (или первый непустой символ в строке, так как пробелы и символы табуляции игнорируются).
Комментарии. Дополнительно к классическим комментариям Ассемблера, начинающимся с ';', AVRASM2 признает комментарии в Cи-стиле:
. . . . . . . . . . ; Остальная часть строки является комментарием.
// Подобно ';' остальная часть строки является комментарием.
/* Блок комментариев может располагаться в нескольких строках.
Этот стиль комментариев не может быть вложенным */
Ассемблер распознает разделители комментария (';') в стиле AVRASM, а также комментарии Cи-стиля. Однако ';' используется в синтаксисе языка Cи, что может привести к конфликту при использовании ';' в качестве разделителя комментария, поэтому не рекомендуется использовать комментарии в стиле Ассемблера вместе с директивами препроцессора AVRASM2.
Продолжение строки. Подобно Cи, строки исходных кодов могут быть продолжены посредством '\ '– обратной косой черты в конце строки. Это особенно полезно для длинных макроопределений препроцессора и для длинных директив .db.
Пример:
.db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 11,12, 21, 214,235,634, \n', 0, 2, \
12, 3,"’это продолжение верхней строки", '\n', 0, 3, 0
Строки и символьные константы. AVRASM2 понимает строки и символы так же, как AVRASM, кроме того, служебные символы, которые не поддерживаются AVRASM. Строка, заключенная в двойные кавычки ("), может быть использована только вместе с директивой .db. Строка передается буквально, никакие служебные символы и NUL-окончания не распознаются. Символьные константы, заключенные в одиночные кавычки ('), могут использоваться везде, где допустимо целое выражение. Служебные символы в Cи-стиле распознаются с тем же значением, как в Cи

Прикрепленное изображение: служебные символы.jpg

Ассемблером также распознаются \ooo (ooo = восьмеричное число) и \xhh (hh = шестнадцатеричное число).
Примеры:
.db "Hello\n"
// - эквивалент:
.db 'H', 'e', 'l', 'l', 'o', '\\', 'n,
Для того чтобы создать эквивалент Cи-строки "Привет, мир \n", делают следующим образом:
.db " Hello, world", '\n', 0
Составные инструкции в строке. AVRASM2 допускает составные инструкции (команды) и директивы в строке, но их использование не рекомендовано. Это нужно для того, чтобы поддерживать распаковку (расширение) многострочных макроопределений препроцессора.
Операнды. Дополнительно к операндам AVRASM AVRASM2 поддерживает выражения с плавающей точкой.

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

Операторы
Прикрепленное изображение: таблица1.jpg
Прикрепленное изображение: таблица2.jpg

Функции

В Ассемблере определены следующие функции:
LOW(выражение) – возвращает младший байт выражения;
HIGH(выражение) – возвращает второй байт выражения;
BYTE2(выражение) – то же, что и функция HIGH;
BYTE3(выражение) – возвращает третий байт выражения;
BYTE4(выражение) – возвращает четвёртый байт выражения;
LWRD(выражение) – возвращает биты 0-15 выражения;
HWRD(выражение) – возвращает биты 16-31 выражения;
PAGE(выражение) – возвращает биты 16-21 выражения;
EXP2(выражение) – возвращает 2 в степени (выражение);
LOG2(выражение) – возвращает целую часть log2(выражение).
Следующие функции определены только в AVRASM2:
INT (выражение) – преобразовывает выражение с плавающей точкой в целое (т.е. отбрасывает дробную часть);
FRAC(выражение) – выделяет дробную часть выражения с плавающей точкой (т.е. отбрасывает целую часть);
Q7(выражение) – преобразовывает выражение с плавающей точкой в форму пригодную для инструкций FMUL/ FMULS/FMULSU (знак + 7-битовая дробная часть);
Q15(выражение) – преобразовывает выражение с плавающей точкой в форму пригодную для инструкций FMUL/ FMULS/FMULSU (знак + 15-битовая дробная часть);
ABS( ) – возвращает абсолютную величину постоянного выражения;
DEFINED(символ) – возвращает «истина», если символ прежде определен директивами .equ, .set или .def. Обычно используется вместе с директивами if (.if defined(foo)), но может быть использовано в любом контексте. В отличие от других функций DEFINED(символ) требует наличия круглых скобок вокруг своего аргумента.
Ассемблер AVRASM2 различает регистр символов (AVRASM не различает).

Ну, вот выложил минимум теории необходимой для программирования современных контроллеров. (из учебного пособия по ассемблеру, автор Зубарев Александр Александрович).
Если вглядываться в последние тенденции, то видно, что ассемблер унифицируется и разные производители вынуждены “приходить к общему знаменателю”, а именно аналогии языка Си.

Чтобы быстро писать программы, программист создает свои библиотеки – небольшие микропрограммы в виде сборок - макроопределений, например микропрограммы задержки, или модули инициализации ЖКИ. Удержать в голове множество таких микропрограмм физически невозможно, тем более, что многие программисты даже не помнят все команды, от силы 40-60, а остальные берут из справочника по мере необходимости. Проще один раз написать микропрограмму и использовать в виде макроопределения при создании других программ. Макроопределению можно дать любое произвольное название так, как вам нравиться. Чтобы пользоваться готовыми макроопределениями достаточно нескольких директив. Вспомним их.

.INCLUDE - вложить другой файл
Встретив директиву INCLUDE, компилятор открывает указанный в ней файл, компилирует, его пока файл не закончится или не встретится директива EXIT, после этого продолжает компиляцию начального файла со строки, следующей за директивой INCLUDE. Вложенный файл может также содержать директивы INCLUDE.

Синтаксис:
.INCLUDE "имя_файла"
Пример:
; файл iodefs.asm:
.EQU sreg = 0x3f ; Регистр статуса.
.EQU sphigh = 0x3e ; Старший байт указателя стека.
.EQU splow = 0x3d ; Младший байт указателя стека.
; файл incdemo.asm
.INCLUDE iodefs.asm ; Вложить файл.
in r0,sreg ; Прочитать регистр статуса.


.MACRO - начало макроса
С директивы MACRO начинается определение макроса. Макрос (макроопределение) – это микропрограмма, состоящая из последовательности операторов, которые несколько раз встречаются в тексте программы. Эта последовательность операторов включается в тело макроса. Кроме этого, макрос имеет заголовок, состоящий из имени и списка параметров, и окончание. После того как макрос определен, повторяющиеся участки кода в тексте программы заменяются на его имя. Это сокращает текст программы. При встрече имени макроса позднее в тексте программы компилятор заменит его имя на операторы из тела макроса. Таким образом, исполнение макроса заключается в его «расширении».
Макрос в AVRASM может иметь до 10 параметров, к которым в его теле обращаются через @0 – @9. Макрос в AVRASM2 может иметь неограниченное число параметров.
При вызове параметры перечисляются через запятые. Определение макроса заканчивается директивой ENDMACRO.
По умолчанию в листинг включается только вызов макроса, для разворачивания макроса необходимо использовать директиву LISTMAC. Макрос в листинге показывается знаком +.
Синтаксис:
.MACRO имя_макроса
Пример:
.MACRO SUBI16 ; Начало макроопределения.
subi @1,low(@0) ; Вычесть младший байт параметра 0 из параметра 1.
sbci @2,high(@0) ; Вычесть старший байт параметра 0 из параметра 2.
.ENDMACRO ; Конец макроопределения.
.CSEG ; Начало программного сегмента.
SUBI16 0x1234,r16,r17 ; Вычесть 0x1234 из r17:r16.

Пример:
Создадим макрос под именем write

.macro write
ldi @0,@5
ldi @2,@3
ldi @4,@1
.endm

Вызываем макрос и прописываем параметры
;
write r16,0x1,r17,0x2,r18,0x3
;
После выполнения макроса в регистры записываются числа
R16=3
R17=2
R18-1


.ENDM
.ENDMACRO - конец макроса
Директива определяет конец макроопределения и не принимает никаких параметров. Для информации по определению макросов смотрите директиву MACRO.
Синтаксис:
.ENDMACRO
.ENDM
Пример:
.MACRO SUBI16 ; Начало определения макроса.
subi r16,low(@0) ; Вычесть младший байт первого параметра.
sbci r17,high(@0) ; Вычесть старший байт первого параметра.
.ENDMACRO


.LISTMAC - включить разворачивание макросов в листинге
После директивы LISTMAC компилятор будет показывать в листинге содержимое макроса. По умолчанию в листинге показываются только вызов макроса и передаваемые параметры.
Синтаксис:
.LISTMAC
Пример:
.MACRO MACX ; Определение макроса.
add r0,@0 ; Тело макроса.
eor r1,@1
.ENDMACRO ; Конец макроопределения.
.LISTMAC ; Включить разворачивание макросов.
MACX r2,r1 ; Вызов макроса (в листинге будет показано тело макроса).


Рассмотрим практический пример в нашей последней программе (бегущие огни).

Если мы зададим программе задержки макроопределение PAUSE в виде

MACRO PAUSE
dec @0
brne PC-1

dec @1
brne PC-3

dec @2
brne PC-5
.ENDMACRO


То сама программа задержки примет вид

delay:
PAUSE temp1, temp2, temp3
ret


Но не следует забывать, что фактический объем программы не уменьшается. Написанная таким образом программа приобретает более короткий и понятный для нас вид, но во время компиляции все строки указанные в макроопределении полностью прописываются.

Ну вот, вроде как пора и к программированию возвращаться!
Директивы, операторы и функции посмотрели и запомнили.
Теперь нужно инициализировать микроконтроллер. Под инициализацией понимается активация встроенных устройств, установка векторов прерываний, подключение портов, полная очистка оперативной памяти от возможного “мусора”. Конечно - же при корректно составленной программе вроде–бы и никакой очистки не нужно, но почему-то в силу неясных причин, нередко без начальной очистки ОЗУ, контроллер виснет или начинает вести себя непредсказуемо...
Чтобы избежать этого, нам нужно очистить область стека(RAMEND), область данных ОЗУ (SRAM_START-RAMEND), область регистров общего назначения(r0–r31).

Универсальные процедуры очистки (для всех микрконтроллеров AVR) выглядят следующим образом:

Очистка Стековой памяти

 ldi r16,low(RAMEND);
 out SPL,r16;
 .if (RAMEND)>=0x0100;
 ldi r16,high(RAMEND);
 out SPH,r16;
 .endif;
 .ENDM;


Очистка области данных ОЗУ

LDI	ZL,Low(SRAM_START)	; Адрес начала ОЗУ в индекс
LDI	ZH,High(SRAM_START)
CLR	R16				; Очищаем R16
Flush:		
ST 	Z+,R16			; Сохраняем 0 в ячейку памяти
CPI	ZH,High(RAMEND+1)	; Достигли конца оперативки?
BRNE	Flush				; Нет? Крутимся дальше!
 
CPI	ZL,Low(RAMEND+1)	; А младший байт достиг конца?
BRNE	Flush
 
CLR	ZL				; Очищаем индекс
CLR	ZH


Очистка регистров общего назначения

LDI	ZL, 30		; Адрес самого старшего регистра	
CLR	ZH		; Обнуляем
DEC	ZL		; Уменьшаем адрес на единицу
ST	Z, ZH		; Записываем в регистр 0
BRNE	PC-2		; Зацикливаемся до полного обнуления


Все три микропрограммы можно записать как макроопределения, каждый под своим названием и поместить в один отдельный файл. Сам файл можно вызвать с помощью директивы .INCLUDE в начале программы.

В процессе инициализации, можно задать режим полноценного использования регистров r0-r15, так же как r16-r31. Макроопределение будет выглядеть следующим образом:

.MACRO LDIL
PUSH	R17	; Сохраним значение одного из старших регистров в стек.
LDI	R17,@1	; Загрузим в него наше непосредственное значение
MOV	@0,R17	; Перебросим значение в регистр младшей группы. 
POP 	R17		; Восстановим из стека значение старшего регистра.
.ENDM


Теперь с помощью одной команды LDIL rX,Y
можно легко записать в регистр rX, любое 1байтное число Y,
где rX может быть любым из r0-r15
(Не забываем, что созданная нами команда LDIL rX,Y в пять раз медленнее настоящей команды LDI и занимает объем в пять строк…)


Таким образом. С помощью макросов (макроопределений) можно разрешить множество неоднозначных ситуаций, связанных с адресацией, управлением устройств ввода вывода, и.т.п. создавая свою библиотеку…

Далее рассмотрим прерывания, работу встроенных таймеров, использование ШИМ.

Сообщение отредактировал galrad: 22 Декабрь 2011 - 11:07

0

#19 Пользователь офлайн   galrad 

  • Завсегдатай
  • PipPipPip
  • Группа: Пользователи
  • Сообщений: 240
  • Регистрация: 26 Февраль 11

Отправлено 29 Январь 2012 - 12:21

Поговорим о прерываниях. Слово прерывание говорит само за себя, происходит остановка какого - то процесса на какое - то время, для того чтобы выполнить дополнительные действия. Прерывания могут быть внешними или внутренними. Приведу простой пример, услышанный из уст моего друга… Собрался он помыть посуду на кухне, взялся с азартом, засучив рукава…но посуда оказалась жирной и он был вынужден прерваться, чтобы найти на одной из полок кухонного гарнитура средство для мытья жирной посуды, после чего снова продолжил свое занятие. Но в какой-то момент зазвонил телефон, и он опять прервался от своей работы, поднял трубку, звонила теща и сказала, что придет в гости, значит надо сходить в магазин купить продукты к ее приходу. Сходил в магазин и только после этого домыл посуду.
На этом примере видно два вида прерываний, первое – связано с выполнением основной работы - поиск средства для жирной посуды -внутреннее прерывание, второе – телефонный звонок – внешнее прерывание.
В микроконтроллере внешние прерывания возникают за счет сигналов поступающих от других источников, внутренние – за счет устройств встроенных в сам микроконтроллер.
Чем же так привлекательны прерывания?
Первое - это то, что мы можем остановить основной процесс для выполнения каких либо других функции, с последующим продолжением этого процесса.
Вторым, и наверное во многих случаях основным считается ускорение процесса выполнения всех функций, за счет внутренних дополнительных устройств. Вернемся к нашему примеру. Допустим, мой друг взялся мыть посуду, когда его жена уже пришла домой. Увидев жирную посуду, он просит ее найти средство для мытья посуды, и пока он моет, она уже принесет ему это средство. Но, вот зазвонил телефон, трубку поднимет жена, поговорит с мамой и сходит в магазин. Совместно все дела сделаны очень быстро!
А еще проще зациклится – т.е. основной программы нет.
Мой друг сидит на диване и ничего не делает, домоработница увидев грязную посуду, говорит ему об этом, и получив разрешение, начинает мыть сама. Когда звонит телефон, он говорит жене, чтобы она подняла трубку, жена разговаривает по телефону, и поле разговора идет в магазин за продуктами… Красота! В таком случае в микроконтроллере одновременно работают несколько устройств ввода-вывода (в современных микроконтроллерах их может быть достаточно много) и общая производительность процессора возрастает во много раз, но прерывания от устройств обрабатываются последовательно одно за другим (не одновременно), в зависимости от приоритета (в нашем примере жена имеет больший приоритет, нежели домоработница).
В микроконтроллерах AVR большим приоритетом пользуется то прерывание, которое прописано ближе к нулевой строке программы в таблице векторов прерываний (см. дальше)

За управление прерываниями отвечают несколько регистров
SREG –регистр статуса (состояния). Смотрим таблицу устройств ввода-вывода. Седьмой бит регистра SREG –флаг I (interrupt), который называется флагом глобального разрешения прерываний. Если флаг опущен (седьмой бит равен нулю), то все прерывания запрещены. Если флаг поднять (установить I в 1), мы разрешим прерывания.

Устанавливается и сбрасывается флаг I командами:
SEI - разрешить прерывания
CLI - запретить прерывания

Какие из прерываний будут работать, задается с помощью регистров называемых – масками прерываний.
Обозначаются маски прерываний следующим образом:
TIMSK,..,..,.. – управление прерываниями от таймеров и других встроенных устройств.
GIMSK (GIKR в семействе Mega) - управление всеми внешними прерываниями.
Маски прерываний в свою очередь зависят от флагов прерываний:
TIFR и GIFR соответственно (не путайте с флагом глобального разрешения прерываний).

Последовательность выполнения прерываний:
При включении микроконтроллера все флаги прерываний сброшены в 0. Для включения прерываний программа должна установить флаг I регистра SREG в 1. После этого прописать регистры маски с установленными локальными прерываниями (прерывания, которые нам нужны).
Когда приходит (сигнал) запрос на прерывание, то он поднимает флаг прерывания (даже в том случае если прерывание запрещено, для организации вложенных прерываний и приоритета между разными прерываниями). Если нет запрета прерываний, то контроллер обратится к соответствующему (Interrupt Vectors) - вектору прерываний, приостанавливая текущую программу.
Вектор прерывания – это фиксированная строка программной области, куда переходит программа в случае возникновения прерывания.
Весь список векторов прерывания – называется таблицей векторов прерывания, который располагается в начале программного кода.
Итак, в момент обращения к вектору прерывания, флаг I регистра SREG и флаг вызвавший прерывание сбрасывается в 0, запрещая другие прерывания. Если в процессе выполнения прерывания, возникли другие запросы прерываний, флаги этих прерываний остаются поднятыми. По окончании выполнения текущего прерывания флаг I регистра SREG поднимается, разрешая выполнение следующего. Если пришли несколько запросов, и их флаги окажутся поднятыми то первым будет выполнено прерывание, чей вектор меньше по адресу в таблице, ближе к началу памяти. За ним второй, и так далее. Кроме этого программист может организовать так называемое вложенное прерывание, когда в процессе выполнения программы прерывания возникает еще одно прерывание. Тогда прекращается выполнение текущего прерывания и выполняется новое, после завершения которого, возобновляется выполнение остановленного прерывания.

В качестве примера приведена таблица векторов прерывания для ATtiny2313
Прикрепленное изображение: таблица перываний 2313_1.jpg

Таблица векторов прерывания для Атмега16 выглядит следующим образом:
Прикрепленное изображение: таблица перываний ATMega16.jpg

При сравнении, таблицы совершенно не совпадают.
В семействе ATtiny строка вектора прерывания занимает 16 бит, а в семействе Mega занимают 32 бита (обратите внимание на адреса векторов прерывания, напомню, что адресная строка в программной области представлена 16 битным словом).

Программный код для ATtiny2313 может выглядеть следующим образом:
.cseg
.org 0

rjmp Reset
rjmp INT_0
rjmp INT_1
rjmp Timer1_capt1
rjmp Timer1_comp1
rjmp Timer1_OVF1
rjmp Timer0_OVF0
rjmp UART_RX
rjmp UART_UDRE
rjmp UART_TX
rjmp ANA_COMP
rjmp PCINT
rjmp Timer1_compB
rjmp Timer0_compA
rjmp Timer0_compB
rjmp USI_START
rjmp USI_OVERFLOW
rjmp EE_READY
rjmp WDT_ OVERFLOW


Как видно, вектор прерывания создает относительный переход на метки программ прерываний. Ниже в таблице показаны варианты; 1. Когда нет прерываний; 2, 3. с внешним прерыванием по входу INT_1.
Если метки “пустые” (под меткой нет программы), то ничего не происходит, и программа последовательно “пробежавшись” по оставшимся меткам благополучно доходит до команды RETI- Interrupt return - выход из обработчика прерывания как показано в первом столбце таблицы.
Прикрепленное изображение: векторы прерываний.jpg

Чтобы выполнить программу прерывания, например по входу INT_1, нужно метку INT_1: вынести из списка. Это схематично показано во втором столбце таблицы.
Но, программисту неудобно каждый раз прописывать все прерывания и отдельно метки к ним, особенно в последних моделях, где таблица достаточно большая, проще в строке вектора прерывания сразу написать команду RETI, если прерывание не используется. Тогда программа будет выглядеть, как показано в третьем столбце таблицы.

В AVR-контроллерах в зависимости от модели может быть от 1 до 8 входов внешних прерываний.
Рассмотрим систему управления внешними прерываниями. Для этого предусмотрены следующие комбинации I/O-регистров в зависимости от модели (см. соответствующий DataSheet):
- GIMSK, EIFR, PCMSK, MCUCR;
- GIKR, GIFR, MCUCR;
- EIMSK, EICR, EIFR;
GIMSK, GIKR, EIMSK - маски прерываний,
EIFR, PCMSK, GIFR, EIFR – флаги прерываний
Для разрешения или запрещения внешних прерываний предназначены управляющие регистры: GIMSK-(General Interrupt Mask Register)(Tiny), GICR- (General Interrupt Control Register)(Mega), MCUCR – (MCU Control Register)

Tiny
Прикрепленное изображение: GIMSK.jpg
и соответствующий ему регистр флагов
Прикрепленное изображение: EIFR.jpg
EIFR- External Interrupt Flag Register: 1- разрешено, 0 – запрещено.. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.

Биты управления регистра GIMSK:
Бит 7 – INT1: External Interrupt Request 1 Enable – бит разрешения прерывания INT1: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT1 настроен как выход. Бит INT1 настраиваются на прерывание в регистре флагов EIFR. Вывод INT1 синхронизирован с тактовым генератором.

Бит 6 – INT0: External Interrupt Request 0 Enable - бит разрешения прерывания INT0: 1 – разрешено, 0 – запрещено.. Прерывание будет формироваться, даже если вывод INT0 настроен как выход. Бит INT0 настраиваются на прерывание в регистре флагов EIFR. Вывод INT10 синхронизирован с тактовым генератором.



Бит 5 – PCIE: Pin Change Interrupt Enable – бит разрешения прерывания на выводах PCINT0…7: 1- разрешено, 0 – запрещено.. Любое изменение на любом из выводов PCINT0…7 будет формировать прерывание. Выводы PCINT0…7 настраиваются на прерывание индивидуально, битами в регистре флагов PCMSK.
Прикрепленное изображение: PCMSK.jpg
PCMSK - Pin Change Mask Regiser - регистр флагов PCMSK: 1- разрешено, 0 – запрещено.. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний. Выводы PCINT0…7 не синхронизированы с тактовым генератором, т.е. прерывание наступает по факту изменения на любом из выводов.


Mega8
Прикрепленное изображение: GICR.jpg
и соответствующий ему регистр флагов
Прикрепленное изображение: GIFR Mega8.jpg
GIFR– General Interrupt Flag Register: 1- разрешено, 0 – запрещено.. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.

Биты управления регистра GICR:
Бит 7 - INT1: External Interrupt Request 1 Enable – бит разрешения прерывания INT1: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT1 настроен как выход. Бит INT1 настраиваются на прерывание в регистре флагов GIFR

Бит 6 – INT0: External Interrupt Request 0 Enable - бит разрешения прерывания INT0: 1 – разрешено, 0 – запрещено.. Прерывание будет формироваться, даже если вывод INT0 настроен как выход. Бит INT0 настраиваются на прерывание в регистре флагов GIFR

Mega16

Прикрепленное изображение: GICR_M16.jpg
Прикрепленное изображение: GIFR M16.jpg
GIFR– General Interrupt Flag Register: 1- разрешено, 0 – запрещено.. Каждый бит (флаг) разрешает соответствующему выводу работать в качестве источника прерываний.

Биты управления регистра GICR:
Бит 7 – INT1: External Interrupt Request 1 Enable – бит разрешения прерывания INT1: 1 – разрешено, 0 – запрещено. Прерывание будет формироваться, даже если вывод INT1 настроен как выход. Бит INT1 настраиваются на прерывание в регистре флагов GIFR

Бит 6 – INT0: External Interrupt Request 0 Enable - бит разрешения прерывания INT0: 1 – разрешено, 0 – запрещено.. Прерывание будет формироваться, даже если вывод INT0 настроен как выход. Бит INT0 настраиваются на прерывание в регистре флагов GIFR

Бит 5 – INT2: External Interrupt Request 2 Enable - бит разрешения прерывания INT2: 1 – разрешено, 0 – запрещено.. Прерывание будет формироваться, даже если вывод INT2 настроен как выход. Бит INT2 настраиваются на прерывание в регистре флагов GIFR


Функциями входов INT0 и INT1во всех контроллерах управляют младшие биты регистра MCUCR
Прикрепленное изображение: MCUCR.jpg
MCUCR– MCU Control Register
Биты управления:
Биты 1, 0 – ISC01, ISC00 (Interrupt Sense Control 0 Bit 1 and Bit 0) – состояние данных битов определяет событие на выводе INT0, при котором формируется прерывание INT0:
ISC01=0, ISC00=0 – уровень логического нуля;
ISC01=0, ISC00=1 – любая смена логического состояния;
ISC01=1, ISC00=0 – по спадающему фронту;
ISC01=1, ISC00=1 – по нарастающему фронту.

Биты 3, 2 – ISC11, ISC10 (Interrupt Sense Control 1 Bit 1 and Bit 0) – состояние данных битов определяет уровень сигнала на выводе INT1, по которому формируется прерывание INT1:
ISC11=0, ISC10=0 – уровень логического нуля;
ISC11=0, ISC10=1 – любая смена логического состояния;
ISC11=1, ISC10=0 – по спадающему фронту;
ISC11=1, ISC10=1 – по нарастающему фронту.

Прикрепленное изображение: фронт сигнала.jpg

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

ldi r16,0x80	; запишем в r16 число 0b10000000
ldi r17,0x0C	; запишем в r17 число 0b00001100
out MCUCR,r17	; прерывание сформируется по нарастающему фронту ISC11=1, ISC10=1
out GMSK,r16	; выставим маску INT0
sei


Кстати на tiny2313 можно сформировать прерывание на любых выводах PCINT0…7, на Mega до 48 серии эти возможности отсутствуют…
Есть такие операции, при выполнении которых, возникшие прерывания могут вызвать сбой программы. В таких случаях перед началом выполнения операции пишем CLI, а после SEI. Называются такие операции – атомарными.
Желательно, чтобы программы прерываний были компактными и выполнялись с максимальной скоростью, потому, что целью любых прерываний является фиксация события. Если по разным причинам программа выполняется медленно, то достаточно зафиксировать событие и обработать его чуть позже.
Если векторы используемых в программе прерываний следуют в таблице не подряд, то для расположения нужного вектора в нужном месте памяти программ следует использовать директивы .org, например программа с прерываниями по INT0 и INT1 для ATtyny2313 будет следующей:
; =================================================== Установки
.def temp=r16 		; директива .def назначает регистру r16 имя temp

;==================================================== Начало программы
.cseg         		; директива .cseg определяет начало сегмента кодов
.org 0            	; начало первой строки программы

; =================================================== Векторы прерывания
rjmp Reset          	; Reset - Переход на начало программы
.org INT0addr
rjmp INT_0
.org INT1addr
rjmp INT_1

; =================================================== Инициализация портов
Reset:
ser temp          	; устанавливаем все биты регистра temp  в 1
out DDRB,temp 		; переводим все биты порта B на вывод

clr temp     		; обнуляем регистр temp
out DDRD,temp    	; переводим все биты порта D на ввод
out PortB,temp   	; отключаем подтягивающие резисторы портов B

ldi temp,0b00001100  ; записываем в temp число 0b00001100
out PortD,temp   	; отключаем подтягивающие резисторы портов D кроме 2 и 3 бита

; =================================================== Инициализация прерываний
ldi temp,(1<<INT1)|(1<<INT0) ; запись единиц в биты соответствующие INT1 и INT0, регистра temp (0b11000000)
out GIMSK,temp      	; инициализация внешних входов INT1 и INT0
ldi temp,(1<<ISC11)|(1<<ISC01) ; запись единиц в биты соответствующие ISC11 и ISC01, регистра temp (0b00001010)
out MCUCR,temp      	; инициализация прерываний по спадающему фронту входного сигнала
SEI                  	; разрешение прерываний

; =================================================== Программа
main:
rjmp main        	; зацикливаемся

; ==================================================== Подпрограммы прерываний

INT_0:                 		; Условия прерывания по INT0
clr temp                  	; Обнуляем все выходы
out PORTB,temp  	; порота PORTB
ldi temp,1           		; Устанавливаем 0 бит
out PORTB,temp  	; порота PORTB и выходим из прерывания
reti

INT_1:                 		; Условия прерывания по INT1
clr temp                  	; Обнуляем все выходы
out PORTB,temp  	; порота PORTB
ldi temp,2           		; Устанавливаем 1 бит
out PORTB,temp  	; порота PORTB и выходим из прерывания
reti


Прикрепленное изображение: прерывания2313.png

Названия векторов прерываний можно посмотреть в файле tn2313def.inc под заголовком: INTERRUPT VECTORS. Этот файл можно найти в папках установленной программы AVR Studio 5.0 или Atmel Studio
для ATtiny2313 будет выглядеть так:

; ***** INTERRUPT VECTORS ************************************************
.equ INT0addr = 0x0001 ; External Interrupt Request 0
.equ INT1addr = 0x0002 ; External Interrupt Request 1
.equ ICP1addr = 0x0003 ; Timer/Counter1 Capture Event
.equ OC1Aaddr = 0x0004 ; Timer/Counter1 Compare Match A
.equ OC1addr = 0x0004 ; For compatibility
.equ OVF1addr = 0x0005 ; Timer/Counter1 Overflow
.equ OVF0addr = 0x0006 ; Timer/Counter0 Overflow
.equ URXCaddr = 0x0007 ; USART, Rx Complete
.equ URXC0addr = 0x0007 ; For compatibility
.equ UDREaddr = 0x0008 ; USART Data Register Empty
.equ UDRE0addr = 0x0008 ; For compatibility
.equ UTXCaddr = 0x0009 ; USART, Tx Complete
.equ UTXC0addr = 0x0009 ; For compatibility
.equ ACIaddr = 0x000a ; Analog Comparator
.equ PCIaddr = 0x000b ;
.equ OC1Baddr = 0x000c ;
.equ OC0Aaddr = 0x000d ;
.equ OC0Baddr = 0x000e ;
.equ USI_STARTaddr = 0x000f ; USI Start Condition
.equ USI_OVFaddr = 0x0010 ; USI Overflow
.equ ERDYaddr = 0x0011 ;
.equ WDTaddr = 0x0012 ; Watchdog Timer Overflow

.equ INT_VECTORS_SIZE = 19 ; size in words

а эта программа для микроконтроллера ATmega64 где задействованы 7 внешних прерываний и прерывание по совпадению таймера для формирования аппаратной задержки:
/*
 ATMega64
 1000000Hz
 programm led contol interrupt
*/ 
.include "m64def.inc"
 ; =================================================== Установки

.def 	temp = r16								; Определение основного рабочего регистра temp
.def 	temp1 = r17							; Определение дополнительного рабочего регистра 
.def 	temp2 = r18							; Определение дополнительного рабочего регистра 
.def 	temp3 = r19							; Определение дополнительного рабочего регистра
.def 	temp4 = r20							; Определение дополнительного рабочего регистра
.equ 	time = 3024							; Присвоение перменой time значения 3024


; =================================================== Начало программного кода

.cseg											; Выбор сегмента программного кода
.org	0										; Установка текущего адреса

; =================================================== Векторы прерываний
rjmp RESET										;Обработка сброса
.org INT0addr									; Установка текущего адреса
rjmp EXT_INT0									;Обработка внешнего прерывания INT0
.org INT1addr									; Установка текущего адреса
rjmp EXT_INT1									;Обработка внешнего прерывания INT1
.org INT2addr									; Установка текущего адреса
rjmp EXT_INT2									;Обработка внешнего прерывания INT2
.org INT3addr									; Установка текущего адреса
rjmp EXT_INT3									;Обработка внешнего прерывания INT3
.org INT4addr									; Установка текущего адреса
rjmp EXT_INT4									;Обработка внешнего прерывания INT4
.org INT5addr									; Установка текущего адреса
rjmp EXT_INT5									;Обработка внешнего прерывания INT5
.org INT6addr									; Установка текущего адреса
rjmp EXT_INT6									;Обработка внешнего прерывания INT6
.org INT7addr									; Установка текущего адреса
rjmp EXT_INT7									;Обработка внешнего прерывания INT7
.org OC1Aaddr									; Установка текущего адреса
rjmp OC1_A										;Обработка совпадения A таймера 1

; =================================================== Начало программы
    	
RESET:

; =================================================== Инициализация стека

ldi temp,low(RAMEND)							; Устанавливаем младший бит из значения RAMEND прописанного по
out SPL,temp									; умолчанию в файле m64def.inc
.if (RAMEND)>=0x0100							; Если значение опреративной памяти превышает число 256 то,
ldi temp,high(RAMEND)							; Устанавливаем старший бит из значения RAMEND прописанного по
out SPH,temp									; умолчанию в файле m64def.inc
.endif											; Конец условия

; =================================================== Инициализация портов
   	
ser temp										; устанавливает все биты регистра temp  в 1
clr temp1										; обнуляет регистр temp1 - все биты регистра temp1 в 0 
out DDRB, temp									; переводит все биты порта B на вывод
out DDRC,temp
out DDRD, temp1									; перевод битов PD,PE(0-7) на ввод
out DDRE, temp1
out PortB, temp1								; отключает подтягивающие резисторы портов B
out PortC,temp1
ldi temp2,(1<<PD0)|(1<<PD1)|(1<<PD2)|(1<<PD3)	; запись битов PD0, PD1, PD2, PD3
out PortD, temp2								; подключение подтягивающих резисторов на PD0, PD1, PD2, PD3
ldi temp2, (1<<PE4)|(1<<PE5)|(1<<PE6)|(1<<PE7)	; запись битов
out PortE, temp2								; подключение подтягивающих резисторов

; =================================================== Инициализация прерываний

ser temp										; устанавливает все биты регистра temp  в 1
;ldi temp,(1<<INT0)|(1<<INT1)|(1<<INT2)			; запись битов INT0, INT1 и  INT2
out EIMSK,temp									; инициализация внешних входов INT0, INT1 и  INT2
ldi temp,(1<<ISC01)|(1<<ISC11)|(1<<ISC21)|(1<<ISC31)	; запись битов ISC01, ISC11, ISC21, ISC31
sts EICRA,temp									; инициализация прерываний по спадающему фронту входного сигнала

; =================================================== Инициализация таймера T1

CLI												; Запрет прерываний

clr temp
out TCCR1A, temp
sts TCCR1C, temp
ldi temp,(1<<WGM12)|(1<<CS12)|(1<<CS10)			; Выбор режима таймера (WGM1(0,1,3)-0, WGM12-1)-CTC, (CS11-0, CS1(0,2)-1)-1024
out TCCR1B, temp
ldi temp, high(time)							; Старший полубайт кода совпадения
out OCR1AH, temp								; Запись в регистр совпадения старш.
ldi temp, low(time)								; Младший полубайт кода совпадения
out OCR1AL, temp								; Запись в регистр совпадения младш.

; =================================================== Запись в регистр маски прерываний
            	
ldi temp,(1<<OCIE1A)							; Разрешаем прерывание от таймера
out TIMSK, temp

SEI												; разрешение прерываний

; =================================================== программа 
ldi temp4, 0b10101010							; Начальная установка значения регистра temp4
Loop:
rjmp Loop

; =================================================== Подпрограммы


; =================================================== запись состояния кнопок по прерыванию
EXT_INT0:
ldi temp3,1										; Запись состояния кнопки PD0
out PortB,temp3
reti

EXT_INT1:
ldi temp3,2										; Запись состояния конпки PD1
out PortB,temp3
reti

EXT_INT2:
ldi temp3,4										; Запись состояния конпки PD2
out PortB,temp3
reti

EXT_INT3:
ldi temp3,8										; Запись состояния кнопки PD3
out PortB,temp3
reti

EXT_INT4:
ldi temp3,16									; Запись состояния конпки PD4
out PortB,temp3
reti

EXT_INT5:
ldi temp3,32									; Запись состояния конпки PE5
out PortB,temp3
reti

EXT_INT6:
ldi temp3,64									; Запись состояния конпки PE6
out PortB,temp3
reti

EXT_INT7:
ldi temp3,128									; Запись состояния конпки PE7
out PortB,temp3
reti

OC1_A:
com temp4										; Инверсия регистра temp4
out PortC,temp4
reti
; =================================================== Конец


Прикрепленное изображение: ATmega64.png

Как видно, сразу после внешних прерываний следует прерывание по совпадению таймера Т1 (режим СТС). В микроконтроллерах старших серий, таких как ATmega или других, нужно обращаться к строке прерывания через директиву .org, т.к. регистры прерываний уже 32 битные, а иногда могут чередоваться как 16 так и 32 разрядные, и точно угадать номер строки с нужным прерыванием бывает очень затруднительно!
Строки типа: EXT_INT0 или OC1Aaddr, прописаны в файле m64def.inc, который можно найти в папках программы-компилятора. Обычно название файла инициализации микроконтроллера прописывается в самом начале программы как .include "m64def.inc" или подобной ему.

Теперь посмотрим аналогичную программу с прерываниями для ATtiny2313 написанную на СИ
#define F_CPU 1000000UL					// установка частоты микроконтроллера	
#include <avr/io.h>						// стандартная библиотека ввода-вывода
#include <avr/interrupt.h>				// библиотека прерываний

ISR (INT0_vect)							// определение вектора прерываний INT0
{
	PORTB &=~(1<<PB1);						// обнулить PB1 
	PORTB |=(1<<PB0);						// установить PB0
}

ISR (INT1_vect)								// определение вектора прерываний INT1
{
	PORTB &=~(1<<PB0);						// обнулить PB0
	PORTB |=(1<<PB1);						// установить PB1
}

int main(void)
{
	DDRB = 0xFF;							// Порт PB на вывод
	DDRD = 0;								// Порт PD на вход
	PORTD=(1<<PD2)|(1<<PD3);				// Подтягивающий резистор на PD2 и PD3
	GIMSK=(1<<INT0)|(1<<INT1);				// Установка внешних прерываний INT0 и INT1	
	MCUSR=(1<<ISC11)|(1<<ISC01);			// Установка прерываний по спадающему фронту входного сигнала
	asm ("sei");

//---------------------------------------- Бесконечный цикл
	while(1)
	{
	}
	return 0;								//конец программы
}


Если в программе на Си есть функции прерываний, то их необходимо определить до функции main. Любое прерывание определяется функцией ISR (вектор прерывания) { программа прерывания }
названия веторов прерывания можно посмотреть в файле iotn2313.h под заголовком /* Interrupt vectors: */
В СИ для выполнения прерываний необходимо подключить библиотеку прерываний #include <avr/interrupt.h> и разрешить выполнение глобальных прерываний командой asm ("sei"); т.к.
непосредственное обращение к регистру SREG в СИ ограничено.

Прикрепленное изображение: прерывания2313СИ.png

Следует так же запомнить, что прерывания работают как с портами установленными на ввод, так и с портами установленными на вывод!

Дальше, подробно рассмотрим внутренние прерывания на примере встроенных таймеров.

Сообщение отредактировал galrad: 07 Ноябрь 2015 - 15:24

0

#20 Пользователь офлайн   Dmitry Dubrovenko 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 41
  • Регистрация: 24 Март 11
  • ГородСанкт-Ленинград

Отправлено 26 Февраль 2012 - 12:33

Цитата

В микроконтроллерах AVR большим приоритетом пользуется то прерывание, которое прописано ближе к нулевой строке программы в таблице векторов прерываний
А разве они не равноценны?
0

Поделиться темой:


  • 13 Страниц +
  • 1
  • 2
  • 3
  • Последняя »
  • Вы не можете создать новую тему
  • Вы не можете ответить в тему

3 человек читают эту тему
0 пользователей, 3 гостей, 0 скрытых пользователей