Самопрограммирование avr. Советы начинающим программистам микроконтроллеров

Теперь, когда мы уже ознакомлены с некоторыми возможностями и функциями микроконтроллеров, естественно, возникает логичный вопрос: что нужно для программирования микроконтроллеров? Какие необходимы программы и устройства, где их взять?


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

Структура и порядок написания программы

Первым делом, прежде чем приступить к написанию любой программы, а точнее кода программы, следует четко представлять, какие функции будет выполнять микроконтроллер. Поэтому сначала нужно определить конечную цель программы. Когда она определена и полностью понятна, тогда составляется алгоритм работы программы. Алгоритм – это последовательность выполнения команд. Применение алгоритмов позволяет более четко структурировать процесс написания кода, а при написании сложных программ часто позволяет сократить время, затрачиваемое на их разработку и отладку.

Следующим этапом после составления алгоритма является непосредственное написание кода программы. Программы для микроконтроллеров пишутся на языке Си или Ассемблере . Только Ассемблер больше относится к набору инструкций, нежели к языку программирования и является языком низкого уровня.


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

Здесь мы не будем сравнивать преимущества и недостатки написания программ на Ассемблере и Си. Со временем, приобретя некоторый опыт в программировании МК, вы сами для себя сделаете полезные выводы.

Сам код программы можно писать в любом стандартном текстовом редакторе, например в Блокноте. Однако на практике пользуются более удобными редакторами, о которых будет сказано далее.

Компиляция программы

Написанный нами код на Си еще вовсе не понятен микроконтроллеру, поскольку МК понимает команды только в двоичной (или шестнадцатеричной) системе, которая представляет собой набор нулей и единиц. Поэтому Си-шный код нужно преобразовать в нули и единицы. Для этого применяется специальная программа, называемая компилятор , а сам процесс преобразования кода называется компиляция .

Для прошивки МК применяется устройство, называемое программатор . В зависимости от типа программатора вход его подключается к COM или USB порту, а выход к определенным выводам микроконтроллера.


Существует широкий выбор программаторов и отладочных плат, однако нас вполне устроит самый простой программатор , который в Китае стоит не более 3 $.


После того, как микроконтроллер прошит, выполняется отладка и тестирование программы на реальном устройстве или, как еще говорят, на «железе».

Теперь давайте подытожим этапы программирования микроконтроллеров.


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

Следует заметить, что отладку и тестирование программы также выполняют до прошивки МК.

Необходимый набор программ

Существует множество полезных и удобных программ для программирования МК. Они бывают как платные, так и бесплатные. Среди них можно выделить три основных:

1) Atmel Studio

2) CodeVisionAVR

3) WinAVR

Все эти программы относятся к IDE I ntegrated D evelopment E nvironment – интегрированная среда разработки . В них можно писать код, компилировать и отлаживать его.

Следует обратить внимание на Code Vision AVR. Эта IDE позволяет упростить и ускорить написание кода. Однако программа платная.

На начальном этапе программирования все программы лучше прописывать вручную, без каких-либо упрощений. Это поможет быстро приобрести необходимые навыки, а в дальнейшем хорошо понимать и редактировать под свои нужды коды, написанные кем-то другим. Поэтому я рекомендую использовать программу Atmel Studio. Во-первых, она абсолютно бесплатна и постоянно обновляется, а во-вторых она разработана компанией, изготавливающей микроконтроллеры на которых мы будем учиться программировать.

Прошивка и отладка программы

Прошивать микроконтроллеры мы будем с помощью дополнительной программы .

Если микроконтроллера в наличии нет, то его работу можно эмитировать с помощью программы . Она значительно упрощает процесс отладки программы даже при наличии МК, чтобы его часто не перепрошивать, ведь любой МК имеет конечное число перезаписей, хотя это число и достаточно большое.

При прошивке и отладке МК его удобно располагать на макетной плате, но это вовсе не обязательно. Поэтому для большего удобства пригодится и макетная плата. Существует большой выбор макетных плат, однако я вам рекомендую брать ту, которая имеет по возможности большее число отверстий. Когда мы начнем подключать семисегментные индикаторы, вы оцените преимущества «больших» макетных плат.

Еще один важный элемент, который нам пригодится – это техническая документация на МК, называемая datasheet . В общем, нужно скачать datasheet на микроконтроллер ATmega8 .

В этой главе будет рассмотрена такая интересная особенность AVR, как самопрограммирование (имеется у многих моделей ATtiny и во всех моделях ATmega). Суть ее заключается в возможности изменять содержимое FLASH-памяти непосредственно из пользовательской программы. Микроконтроллеры Atmel были одними из первых в мире, в которых появилось это новшество. В разделе приведен пример загрузчика, использующего аппаратный интерфейс USART, и управляющей программы высокого уровня на языке Delphi.

Область применения

Какие же выгоды предоставляет способность самопрограммирования? Сначала может показаться, что такая возможность дает AVR такие же преимущества, какими, например, обладает любой процессор с Принстонской архитектурой. Это, во-первых, способность изменять собственные алгоритмы работы и, во-вторых, возможность вести отладку приложения, не прибегая к дополнительным аппаратным средствам. Но на практике ресурсов 8-разрядного микроконтроллера совершенно недостаточно для построения “самообучающихся” интеллектуальных систем. А что касается отладки, то сама структура FLASH-памяти здесь сводит на нет все достоинства самопрограммирования (возможность размещать коды программ в быстрой памяти SRAM у AVR отсутствует). Кроме того, переписывать на ходу секцию рабочего кода, что раньше делалось только с помощью программаторов, – довольно рискованное дело. Любая неточность здесь сразу приведет к сбою. И даже наличие самого аппаратного узла, отвечающего за смену “прошивки”, потенциально снижает надежность устройства. Если прикладная программа может изменять FLASH-память, то это может случиться и непроизвольно в результате какой-либо ошибки.

Однако, со временем оказалось, что все не так уж и плохо. Самопрограммирование позволяет успешно использовать FLASH-память, как альтернативу EEPROM. Рабочий ресурс FLASH, производимой по современной технологии, составляет ≈10000 циклов стирания/записи. Это показатель хоть и на порядок меньше, чем у EEPROM, но все равно очень высокий. Благодаря этому во FLASH-памяти можно хранить таблицы, списки, поправочные коэффициенты, а также любую другую информацию, требующую периодического не слишком частого обновления.

Но, наверное, самым главным достоинством самопрограммирования, является возможность создания практически любого интерфейса, для обновления программного обеспечения. Наибольшее практическое значение имеют компьютерные COM и USB. Последний из них в упрощенном виде может быть реализован программным способом. Процессом программирования со стороны AVR должна при этом управлять специальная программа-загрузчик boot-loader. Многие современные микроконтроллеры (а также новые модели AVR) поставляются с изначально “зашитым” на заводе загрузчиком, что дает возможность использовать очень простые программаторы, лишь немного потеряв в скорости программирования.

Перейти к следующей части:

Так, с работой ядра на предмет переходов и адресации разобрались. Пора обратить свой взор в другую область — память.

Ее тут два вида (EEPROM не в счет т.к. она вообщет переферия, а о ней потом):

  • RAM — оперативка
  • ROM — ПЗУ, она же flash, она же память программ

Так как архитектура у нас Гарвардская, то у оперативы своя адресация, а у флеша своя. В даташите можно увидеть структуру адресации ОЗУ.

Сразу обратите внимание на адреса! РОН и регистры периферии, а также ОЗУ находятся в одном адресном пространстве. Т.е. адреса с 0000 по 001F занимают наши регистры, дальше вплоть до адреса 005F идут ячейки ввода-вывода — порты. Через порты происходит конфигурирование всего, что есть на борту контроллера. И только потом, с адреса 0060 идет наше ОЗУ, которое мы можем использовать по назначению.

Причем обратите внимание, что у регистров I/O есть еще своя адресация — адресное пространство регистров ввода-вывода (от 00 до 3F), она указана на левой части рисунка. Блок IO/Register Эта адресация работает ТОЛЬКО в командах OUT и IN Из этого вытекает интересная особенность.

К регистрам периферии можно обратиться двумя разными способами:

  • Через команды IN/OUT по короткому адресу в пространстве адресов ввода-вывода
  • Через группу команд LOAD/STORE по полному адресу в пространстве адресов RAM

Пример. Возьмем входной регистр асинхронного приемопередатчика UDR он имеет адрес 0x0C(0х2С) в скобках указан адрес в общем адресном пространстве.

LDI R18,10 ; Загрузили в регистр R18 число 10. Просто так OUT UDR,R18 ; Вывели первым способом, компилятор сам; Подставит вместо UDR значение 0х0С STS 0x2C,R18 ; Вывели вторым способом. Через команду Store ; Указав адрес напрямую.

Оба метода дают идентичные результаты. НО! Те что работают адресацией в пространстве ввода-вывода (OUT/IN) на два байта короче. Это и понятно — им не нужно хранить двубайтный адрес произвольной ячейки памяти, а короткий адрес пространства ввода—вывода влезает и в двухбайтный код команды.

Правда тут возникает еще один прикол. Дело в том, что с каждым годом появляются все новые и новые камни от AVR и мяса в них все больше и больше. А каждой шкварке нужно свои периферийные регистры ввода-вывода. И вот, дожили, в ATMega88 (что пришла на замену Mega8) периферии уже столько, что ее регистры ввода-вывода уже не умещаются в лимит адресного пространства 3F.

Опаньки, приплыли. И вот тут у тех кто пересаживается с старых камней на новые начинаются недоуменные выражения — с чего это команды OUT/IN на одних периферийных регистрах работают, а на других нет?

А все просто — разрядности не хватило.

А ядро то единое, его уже не переделать. И вот тут ATMELовцы поступили хитро — они ввели так называемые memory mapped регистры. Т.е. все те регистры, что не влезли в лимит 3F доступны теперь только одним способом — через Load/Store.

Вот такой прикол. Если открыть какой нибудь m88def.inc то там можно увидеть какие из регистров ввода-вывода «правильные» а какие memory mapped.

Будет там бодяга вот такого вида:

; ***** I/O REGISTER DEFINITIONS ***************************************** ; NOTE: ; Definitions marked "MEMORY MAPPED"are extended I/O ports ; and cannot be used with IN/OUT instructions .equ UDR0 = 0xc6 ; MEMORY MAPPED .equ UBRR0L = 0xc4 ; MEMORY MAPPED .equ UBRR0H = 0xc5 ; MEMORY MAPPED .equ UCSR0C = 0xc2 ; MEMORY MAPPED .equ UCSR0B = 0xc1 ; MEMORY MAPPED .equ UCSR0A = 0xc0 ; MEMORY MAPPED бла бла бла, и еще много такого.equ OSCCAL = 0x66 ; MEMORY MAPPED .equ PRR = 0x64 ; MEMORY MAPPED .equ CLKPR = 0x61 ; MEMORY MAPPED .equ WDTCSR = 0x60 ; MEMORY MAPPED .equ SREG = 0x3f ;<------ А тут пошли обычные.equ SPL = 0x3d .equ SPH = 0x3e .equ SPMCSR = 0x37 .equ MCUCR = 0x35 .equ MCUSR = 0x34 .equ SMCR = 0x33 .equ ACSR = 0x30

Вот такие пироги.

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

Впрочем есть решение. Макроязык! Не нравится система команд? Придумай свою с блекджеком и шлюхами!
Сварганим свою собственную команду UOUT типо универсальный OUT

Аналогично и для команды IN Вообще, такими вот макросами можно ОЧЕНЬ сильно разнообразить ассемблер, превратив его в мощнейший язык программирования, рвущий как тузик тряпку всякие там Си с Паскалями.

Ну так о чем я… а о ОЗУ.

Итак, с адресацией разобрались. Адреса памяти, откуда начинаются пользовательские ячейки ОЗУ теперь ты знаешь где смотреть — в даташите, раздел Memory Map. Но там для справки, чтобы знать.

А в нашем коде оперативка начинается с директивы.DSEG Помните наш шаблончик?

Include "m16def.inc" ; Используем ATMega16 ;= Start macro.inc =============================== ; Макросы тут;= End macro.inc ================================= ; RAM ============================================= .DSEG ; Сегмент ОЗУ; FLASH =========================================== .CSEG ; Кодовый сегмент; EEPROM ========================================== .ESEG ; Сегмент EEPROM

Вот после.DSEG можно задавать наши переменные. Причем мы тут имеем просто прорву ячеек — занимай любую. Указал адрес и радуйся. Но зачем же вручную считать адреса? Пусть компилятор тут думает.

Поэтому мы возьмем и зададим меточку

0x0060 ## ;Variables 0x0061 ## 0x0062 ## 0x0063 ## ;Variables2 0x0064 ## 0x0065 ## ;Тут могла бы начинаться Variables4

В качестве ## любой байт. По дефолту FF. Разумеется ни о какой типизации переменных, начальной инициализации, контроля за переполнениями и прочих буржуазных радостей говорить не приходится. Это Спарта! В смысле, ассемблер. Все ручками.
Если провести аналогию с Си, то это как работа с памятью через одни лишь void указатели. Сишники поймут. Поймут и ужаснутся. Т.к. мир этот жесток и коварен. Чуть просчитался с индексом — затер другие данные. И хрен ты эту ошибку поймаешь если она сразу не всплывет.

Так что внимание, внимание и еще раз внимание. Все операции с памятью прогоняем через трассировку и ничего у нас не вылезет и не переполнится.

В сегменте данных работает также директива.ORG Работает точно также — переносит адреса, в данном случае меток, от сих и до конца памяти. Одна лишь тонкость — ORG 0000 даст нам самое начало ОЗУ, а это R0 и прочие регистры. А нулевой километр ОЗУ на примере Мега16 даст ORG 0x0060. А в других контроллерах еще какое-нибудь значение. Каждый раз в даташит лазать лениво, поэтому есть такое макроопределение как SRAM_START указывающее на начало ОЗУ для конкретного МК.

Так что если хотим начало ОЗУ, скажем 100 байт оставить под какой нибудь мусорный буффер, то делаем такой прикол.

1 2 3 4 .DSEG .ORG SRAM_START+100 Variables: .byte 3

DSEG .ORG SRAM_START+100 Variables: .byte 3

Готово, расчистили себе буфферную зону от начала до 100.

Ладно, с адресацией разобрались. Как работать с ячейками памяти? А для этих целей существует две группы команд. LOAD и STORE самая многочисленная группа команд.

Дело в том, что с ячейкой ОЗУ ничего нельзя сделать кроме как загрузить в нее байт из РОН, или выгрузить из нее байт в РОН.

Записывают в ОЗУ команды Store (ST**), а считываю команды Load (LD**).

Чтение идет в регистр R16…R31, а адрес ячейки задается либо непосредственно в команде. Вот простой пример. Есть трехбайтная переменная Variables, ее надо увеличить на 1. Т.е. сделать операцию Variables++

DSEG Variables: .byte 3 Variavles2: .byte 1 .CSEG ; Переменная лежит в памяти, сначала надо ее достать. LDS R16, Variables ; Считать первый байт Variables в R16 LDS R17, Variables+1 ; Считать второй байт Variables в R17 LDS R18, Variables+2 ; Ну и третий байт в R18 ; Теперь прибавим к ней 1, т.к. AVR не умеет складывать с константой, только; вычитать, приходиться извращаться. Впрочем, особых проблем не доставляет. SUBI R16,(-1) ; вообще то SUBI это вычитание, но -(- дает + SBCI R17,(-1) ; А тут перенос учитывается. Но об этом потом. SBCI R18,(-1) ; Математика в ассемблере это отдельная история STS Variables,R16 ; Сохраняем все как было. STS Variables+1,R17 STS Variables+2,R18

А можно применить и другой метод. Косвенную запись через индексный регистр.

DSEG Variables: .byte 3 Variavles2: .byte 1 .CSEG ; Берем адрес нашей переменной LDI YL,low(Variables) LDI YH,High(Variables) ; Переменная лежит в памяти, сначала надо ее достать. LD R16, Y+ ; Считать первый байт Variables в R16 LD R17, Y+ ; Считать второй байт Variables в R17 LD R18, Y+ ; Ну и третий байт в R18 ; Теперь прибавим к ней 1, т.к. AVR не умеет складывать с константой, только; вычитать, приходиться извращаться. Впрочем, особых проблем не доставляет. SUBI R16,(-1) ; вообще то SUBI это вычитание, но -(- дает + SBCI R17,(-1) ; А тут перенос учитывается. Но об этом потом. SBCI R18,(-1) ; Математика в ассемблере это отдельная история ST -Y,R18 ; Сохраняем все как было. ST -Y,R17 ; Но в обратном порядке ST -Y,R16

Тут уже заюзаны операции с постинкрементом и преддекрементом. В первой сначала читаем, потом прибавляем к адресу 1. Во второй вначале вычитаем из адреса 1, а потом сохраняем.

Подобными инкрементальными командами удобно перебирать массивы в памяти или таблицы какие.
А там есть еще и косвенная относительная запись/чтение LDD/STD и еще варианты на все три вида индексов (X,Y,Z). В общем, кури даташит и систему команд.

Стек
О, стек это великая вещь. За что я его люблю, так это за то, что срыв стека превращает работоспособную программу в полную кашу. За то что стековые операции требуют повышенного внимания, за то что если где то стек сорвет и сразу не отследишь, то фиг это потом отловишь… В общем, прелесть, а не штуковина.

Почему люблю? Ну дык, если Си это тупое ремесло, быстро и результативно, то Ассемблер это филигранное искусство. Как маньяки вроде Jim’a из бумаги и только из бумаги клепают шедевры, хотя, казалось бы, купи готовую сборную модель и клей себе в удовольствие. Так и тут — от самого процесса прет нипадецки. В том числе и от затраха с отладкой:))))

Так вот, о стеке. Что это такое? А это область памяти. Работает по принципу стопки. Т.е. какую последнюю положил, ту первой взял.

У стека есть указатель, он показывает на вершину стека. За указатель стека отвечает специальный регистр SP, а точнее это регистровая пара SPL и SPH. Но в микроконтроллерах с малым обьемом ОЗУ, например в Тини2313, есть только SPL

При старте контроллера, обычно, первым делом инициализируют стек, записывая в SP адрес его дна, откуда он будет рости. Обычно это конец ОЗУ, а растет он к началу.

Делается это таким вот образом, в самом начале программы:

1 2 3 4 5 LDI R16,Low(RAMEND) OUT SPL,R16 LDI R16,High(RAMEND) OUT SPH,R16

LDI R16,Low(RAMEND) OUT SPL,R16 LDI R16,High(RAMEND) OUT SPH,R16

Где RAMEND это макроопределение указывающий на конец ОЗУ в текущем МК.

Все, стек готов к работе. Данные кладутся в стек командой PUSH Rn, а достаются через POP Rn.
Rn — это любой из РОН.

Еще со стеком работают команды CALL, RCALL, ICALL, RET, RETI и вызов прерывания, но об этом чуть позже.

Давай-ка поиграемся со стеком, чтобы почувствовать его работу, понять как и куда он движется.

Вбей в студию такой код:

CSEG ; Кодовый сегмент LDI R16,Low(RAMEND) ; Инициализация стека OUT SPL,R16 LDI R16,High(RAMEND) OUT SPH,R16 LDI R17,0 ; Загрузка значений LDI R18,1 LDI R19,2 LDI R20,3 LDI R21,4 LDI R22,5 LDI R23,6 LDI R24,7 LDI R25,8 LDI R26,9 PUSH R17 ; Укладываем значения в стек PUSH R18 PUSH R19 PUSH R20 PUSH R21 PUSH R22 PUSH R23 PUSH R24 PUSH R25 PUSH R26 POP R0 ; Достаем значения из стека POP R1 POP R2 POP R3 POP R4 POP R5 POP R6 POP R7 POP R8 POP R9

А теперь запускай студию в пошаговое выполнение и следи за тем как будет меняться SP. Stack Pointer можно поглядеть в студии там же, где и Program Counter.

Вначале мы инициализируем стек и загрузим регистры данными. В результате получится следующая картина:

Дальше, командой POP, мы достаем данные из стека. Обрати внимание на то, что нам совершенно не важно откуда мы положили данные в стек и куда мы их будем сгружать. Главное порядок укладки! Ложили мы из старших регистров, а достанем в младшие. При этом указатель стека будет увеличиваться.

PUSH R16 PUSH R17 POP R16 POP R17

Например, я уже говорил про ограничение младших РОН — они не дают записать в себя число напрямую. Только через регистры старшей группы. Но это же неудобно!

Проблема решается с помощью макроса. Я назвал его LDIL — LDI low

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

Теперь можно легко применять нашу самодельную команду.

1 LDIL R0,18

Со временем, файл с макросами обрастает такими самодельными командами и работать становится легко и приятно.

Стековые ошибки
Стек растет навстречу данным, а теперь представьте что у нас в памяти есть переменная State и расположена она по адресу, например, 0х0450. В опасной близости от вершины стека. В переменной хранится, например, состояние конечного автомата от которого зависит дальнейшая логика работы программы. Скажем если там 3, то мы идем делать одно, если 4 то другое, если 5 то еще что-то и так до 255 состояний. И по логике работы после 3 должна идти 4ре, но никак не 10

И вот было там 3. И тут, в один ужасный момент, условия так совпали, что стек разросся и его вершина дошла до этой переменной, вписав туда значение, скажем 20, а потом борзо свалила обратно. Оставив гадость — классический пример переполнения стека. И логика программы вся нахрен порушилась из-за этого.

Либо обратный пример — стек продавился до переменных, но в этот момент переменные обновились и перезаписали стековые данные. В результате, со стека снялось что-то не то (обычно кривые адреса возврата) и программе сорвало крышу. Вот такой вариант, кстати, куда более безобидный, т.к. в этом случае косяк видно сразу и он не всплывает ВНЕЗАПНО спустя черт знает сколько времени.

Причем эта ошибка может то возникать, то исчезать. В зависимости от того как работает программа и насколько глубоко она прогружает стек. Впрочем, такое западло чаще встречается когда пишешь на Си, где не видно насколько активно идет работа со стеком. На асме все гораздо прозрачней. И тут такое может возникнуть из-за откровенно кривого алгоритма.

На долю ассемблерщиков часто выпадают другие стековые ошибки. В первую очередь забычивость. Что то положил, а достать забыл. Если дело было в подпрограмме или в прерывании, то искажается адрес возврата (о нем чуть позже), стек срывает и прога мгновенно рушится. Либо невнимательность — сохранял данные в одном порядке, а достал в другом. Опа и содержимое регистров обменялось.

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

У некоторых возникнет мысль, что можно же взять и стек разместить не на самом конце ОЗУ, а где нибудь поближе, оставив за ним карман для критичных данных. На самом деле не слишком удачная мысль. Дело в том, что стек можно продавить как вниз, командой PUSH так и вверх — командами POP. Второе хоть и случается намного реже, т.к. это больше грех кривых рук, чем громоздкого алгоритма, но тоже бывает.
Но главное это то, что стек сам по себе сверхважная структура. На ней держится весь механизм подпрограмм и функций. Так что срыв стека это ЧП в любом случае.

Стековые извраты
Моя любимая тема. =)))) Несмотря на то, что стековый указатель сам вычисляется при командах PUSH и POP, никто не мешает нам выковырять его из SP, да использовать его значения для ручного вычисления адреса данных лежащих в стеке. Либо подправить стековые данные как нам угодно.
Зачем? Ну применений можно много найти, если напрячь мозг и начать думать нестандартно:))))

Плюс через стек, в классическом Си и Паскале на архитектуре х86 передаются параметры и работают локальные переменные. Т.е. перед вызовом функции вначале все переменные пихаются в стек, а потом, после вызова функции, в стек пихаются байты будущих локальных переменных.

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

В AVR все несколько не так (видимо связано с малым обьемом памяти, где в стек особо не насуешься, зато есть прорва РОН, но механизм этот тоже можно попробовать использовать.

Правда это уже напоминает нейрохирургию. Чуть ошибся и пациент труп.

Благодаря стеку и ОЗУ можно обходиться всего двумя-тремя регистрами, не особо испытывая напряг по поводу их нехватки.

Флеш память

Память EEPROM маленькая, всего считанные байты, а иногда нужно сохранить кучу данных, например, послание инопланетянам или таблицу синусов, чтобы не тратить время на ее расчет. Да мало ли что нужно заранее заныкать в памяти. Поэтому данные можно забивать в память программ, в те самые килобайты флеша, что имеет контроллер на борту.

Записать то мы запишем, а как достать? Для этого сначала надо туда что-либо положить.
Поэтому добавляй в конце программы, в пределах сегмента.CSEG метку, например, data и после нее, используя оператор.db, вписывай свои данные.

Оператор DB означает что мы на каждую константу используем по байту. Есть еще операторы задающий двубайтные константы DW (а также DD и DQ).

1 data: .db 12,34,45,23

data: .db 12,34,45,23

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

Одна тонкость — дело в том, что адрес метки подставляет компилятор, а он считает его адресом перехода для программного счетчика. А он, если ты помнишь, адресует двубайтные слова — ведь длина команды у нас может быть либо 2 либо 4ре байта.

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

Для загрузки данных из памяти программ используется команда из группы Load Program Memory

Например, LPM Rn,Z

Она заносит в регистр Rn число из ячейки на которую указывает регистровая пара Z. Напомню, что Z это два регистра, R30 (ZL) и R31 (ZH). В R30 заносится младший байт адреса, а в R31 старший.

В коде выглядит это так:

LDI ZL,low(data*2) ; заносим младший байт адреса, в регистровую пару Z LDI ZH,high(data*2) ; заносим старший байт адреса, в регистровую пару Z ; умножение на два тут из-за того, что адрес указан в; в двубайтных словах, а нам надо в байтах. ; Поэтому и умножаем на два; После загрузки адреса можно загружать число из памяти LPM R16, Z ; в регистре R16 после этой команды будет число 12, ; взятое из памяти программ. ; где то в конце программы, но в сегменте.CSEG data: .db 12,34,45,23

программ и данных разделены (в отличие от классической архитектуры
фон Неймана в обычных компьютерах, где память общая). Раздельные шины для
этих областей памяти значительно ускоряют выполнение программы: данные и
команды могут выбираться одновременно.

32 регистра общего назначения (РОН). Atmel была первой компанией, далеко
отошедшей от классической модели вычислительного ядра, в которой выполне-
ние команд предусматривает обмен данными между АЛУ и запоминающими
ячейками в общей памяти. Введение РОН в таком количестве (напомним, что в
архитектуре х 86 всего четыре таких регистра, а в x 51 понятие РОН, как таковое,
отсутствует) в ряде случаев позволяет вообще отказаться от расположения гло-
бальных и локальных переменных в ОЗУ и от использования стека, операции с
которым усложняют и загромождают программу. В результате структура ас-
семблерной программы приближается к программам на языках высокого уровня.
Правда, это привело к некоторому усложнению системы команд, номенклатура
которых для AVR больше, чем в других RISC-семействах (хотя значительная
часть инструкций - псевдонимы).

Flash-память программ (10 000 циклов стирание/запись) с возможностью
внутрисистемного перепрограммирования и загрузки через последовательный
канал прямо в готовой схеме. О преимуществах такого подхода, ныне ставшего
общепринятым, подробно рассказано во введении .

Отдельная область энергонезависимой памяти (EEPROM, 100 000 циклов
стирание/запись) для хранения данных, с возможностью записи программным
путем, или внешней загрузки через SPI-интерфейс.

Встроенные устройства для обработки аналоговых сигналов : аналоговый
компаратор и многоканальный 10-разрядный АЦП.

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

Последовательные интерфейсы SPI , TWI (I

C ) и UART (USART ), позволяю-

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

Таймеры-счетчики с предустановкой и возможностью выбора источника счет-
ных импульсов: как правило, один-два 8-разрядных и как минимум один
16-разрядный, в том числе могущие работать в режиме многоканальной 8-, 9-,
10-, 16-битовой широтно-импульсной модуляции (PWM).

Возможность работы при тактовой частоте от 0 Гц до 16–20 МГц.

Диапазон напряжений питания от 2,7 до 5,5 В (в некоторых случаях от 1,8 или
до 6,0 В).

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

Встроенный монитор питания - детектор падения напряжения (Brown-out
Detection).

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

Семейства AVR

В 2002 г. фирма Atmel начала выпуск новых подсемейств 8-разрядных МК на базе
AVR-ядра. С тех пор все МК этого семейства делятся на три группы (подсемейст-
ва): Classic, Tiny и Mega. МК семейства Classic (AT90Sxxxx ) уже не выпускаются;
дольше всего в производстве "задержалась" очень удачная (простая, компактная и
быстродействующая модель) AT90S2313, но и она была в 2005 г. заменена на
ATtiny2313. Все "классические" AVR с первыми цифрами 2 и 8 в наименовании
модели (что означает объем памяти программ в килобайтах) имеют аналоги в се-
мействах Tiny и Mega. Для Mega при программировании возможна установка спе-
циального бита совместимости, который позволяет без каких-либо изменений ис-
пользовать программы, созданные для семейства Classic. Поэтому ряд примеров в
данной книге в целях упрощения изложения приводится в версии для семейства
Classic.
Примеры различных типов корпусов, в которых выпускаются микросхемы AVR,
приведены на рис. 1.1. Более подробную информацию на эту тему можно найти в
приложении 1 (табл. П1.2), а также в технической документации на устройства.
Отметим, что для радиолюбительских нужд и макетирования удобнее всего микро-
схемы в PDIP-корпусах, но не все модели МК в таких корпусах производятся.
Все семейства могут иметь две модификации: буква "L" в обозначении говорит
о расширенном диапазоне питания 2,7–5,5 В, отсутствие такой буквы означает диа-
пазон питания 4,5–5,5 В. При выборе конкретного типа микросхемы нужно быть
внимательным, т. к. L-версии одновременно также и менее быстродействующие, у
большинства из них максимальная тактовая частота ограничена значением 8 МГц.
Для "обычных" версий максимальная частота составляет 16 или 20 МГц. Хотя, как
правило, при запуске L-микросхем с напряжением питания 5 В на частотах до 10–
12 МГц неприятностей ожидать не следует (аналогично версии без буквы L вполне
могут работать при напряжении питания около 3 В, разумеется, не на экстремаль-
ных значениях частот), тем не менее при проектировании высоконадежных уст-
ройств следует учитывать это требование.
Микросхемы Tiny имеют Flash-ПЗУ программ объемом 1–8 кбайт и размещаются в
основном в корпусах с 8–20 выводами (кроме ATtiny28), т. е. они в целом предна-
значены для более простых и дешевых устройств. Это не значит, что их возможно-
сти во всех случаях более ограниченны, чем у семейства Mega. Так, например,
ATtiny26 при цене менее 2 долларов содержит таймер с высокоскоростным ШИМ-
режимом (в других моделях такого нет), а также 11-канальный АЦП с возмож-
ностью работы в дифференциальном режиме, с регулируемым входным усилителем

Часть I. Общие принципы устройства и функционирования Atmel AVR

и встроенным источником опорного напряжения, что характерно для старших мо-
делей. Микросхема ATtiny2313, как уже говорилось, представляет собой улучшен-
ную версию одного из наиболее универсальных и удобных "классических" AVR
AT90S2313.

Рис. 1.1. Примеры различных типов корпусов для МК AVR

Подсемейство Mega оснащено Flash-ПЗУ программ объемом 8–256 кбайт и корпу-
сами с 28–100 выводами. В целом МК этой группы более "навороченные", чем
Tiny, имеют более разветвленную систему встроенных устройств с более развитой
функциональностью.
Таблицы с основными характеристиками некоторых моделей Tiny и Mega из числа
самых ходовых приведены в приложении 1 . Там же даны некоторые общие техни-
ческие характеристики семейства AVR. Более подробные сведения можно почерп-
нуть из и фирменной технической документации, которая доступна на сайте
Atmel для каждой модели.
Кроме этих трех семейств, на базе AVR-ядра выпускаются специализированные
микросхемы для работы с USB-интерфейсом (AT90USBxxxx ), промышленным ин-
терфейсом CAN (AT90CANххх ), для управления ЖК-дисплеями (ATmega329 и др.),
с беспроводным интерфейсом IEEE 802.15.4 (ZigBee) для предприятий торговли и
некоторые другие. В последнее время некоторые микроконтроллеры серий Tiny и
Mega стали выпускаться в версиях со сверхмалым потреблением (технология
picoPower с напряжением питания от 1,8 В, в конце наименования МК этой серии
добавлена буква "P") и высокотемпературных для использования в автомобильной
промышленности (версии Automotive). Появилось семейство XMega с напряжением
питания 1,8–3,6 В, повышенным быстродействием (тактовая частота до 32 МГц),

Глава 1. Обзор микроконтроллеров Atmel AVR

12-разрядным 16-канальным АЦП и 2–4 каналами ЦАП (до сих пор в структуре
AVR они отсутствовали), несколькими каналами UART и других последовательных
портов (причем с возможностью работы в автономном режиме, при остановленном
ядре), встроенной поддержкой криптографии, усовершенствованным режимом
picoPower и другими "наворотами". Существует также отдельное семейство 32-раз-
рядных МК AVR32, предназначенное для высокоскоростных приложений, таких
как обработка видеопотока или распознавание образов в реальном времени.

Особенности
практического использования МК AVR

При использовании AVR возникает ряд вопросов практического характера, игно-
рирование которых может иногда привести к неработоспособности или сбоям уст-
ройства (а в некоторых случаях - даже к невозможности его запрограммировать).
Например, одна из таких проблем - возможность потери содержимого EEPROM
при выключении питания. Эту и подобные проблемы мы подробно рассмотрим в
соответствующих главах. Здесь же остановимся на некоторых общих вопросах
включения МК AVR.

О потреблении

МК AVR потребляют в среднем 5–15 мА (без учета потребления внешних уст-
ройств через выводы МК). Потребляемый ток зависит не только от степени "наво-
роченности" модели, но и от тактовой частоты и напряжения питания. На рис. 1.2
приведена типовая диаграмма зависимости тока потребления от напряжения пита-
ния и тактовой частоты для младших моделей семейства Mega.
Из рис. 1.2, в частности, следует, что значительно уменьшить потребление можно,
снижая тактовую частоту в тех случаях, когда время выполнения программы не-
критично. Это позволяет упростить программу, отказавшись от режимов энерго-
сбережения: например, при установке "часового" кварца 32 768 Гц в качестве так-
тирующего потребление МК может составить порядка 200–300 мкА.

З

АМЕТКИ НА ПОЛЯХ

Величину тока потребления 1–2 мА и менее можно условно считать приемлемой для
батарейных устройств, которые рассчитаны на долговременный режим непрерывной
работы. Элементы типоразмера АА (типа alcaline, т. е. щелочные) имеют емкость по-
рядка 2000 мА ч, т. е. устройство с указанным потреблением от этих элементов про-
работает не менее 1000 ч (реально даже несколько больше) или более 40 суток. Вре-
мя работы от батарей типоразмера D с энергоемкостью порядка 15–18 000 мА ч со-
ставит около года, чего для большинства практических применений достаточно.
Выбирать для питания подобных устройств (особенно, включающихся периодически
на короткое время) следует именно щелочные элементы, т. к. они обладают большой
емкостью, не текут при переразряде и, главное, имеют значительно больший срок
хранения (порядка 7 лет) по сравнению с другими типами элементов.

Но внимательное рассмотрение вопроса показывает, что именно этим - упроще-
нием программы - в подавляющем большинстве случаев преимущества более

Часть I. Общие принципы устройства и функционирования Atmel AVR

низкой тактовой частоты и ограничиваются. Графики на рис. 1.2 линейны, отсюда
следует, что пропорционально снижению тактовой частоты растет время выполне-
ния команд. Таким образом, процедура, выполнение которой при тактовой частоте
4 МГц займет 100 мкс, при тактовой частоте 32 768 Гц будет длиться более 12 мс.
Легко подсчитать, что в том и другом случае количество энергии, потребленной на
выполнение этой процедуры, будет одинаковым.

Рис. 1.2. Диаграмма зависимости тока потребления от напряжения питания

и тактовой частоты для младших моделей семейства Mega

Поэтому можно сделать следующий общий вывод: если вы не желаете вникать в
тонкости режимов энергосбережения и не реализуете их в программе, то для обще-
го снижения потребления нужно выбирать тактовую частоту как можно ниже (на
практике обычно достаточно ограничиться величиной 1 МГц, т. к. дальнейшее
снижение, скорее всего, не даст эффекта из-за дополнительного потребления внеш-
ними цепями, неизбежно присутствующими во всех схемах). Если же у вас преду-
смотрен один из режимов "глубокого" энергосбережения (см. главу 4 ), то тактовая
частота с точки зрения суммарного потребления практически не имеет значения.
Другое дело - выбор напряжения питания, которое желательно сделать как можно
меньше, если это позволяют внешние устройства. Зависимость тока потребления от
напряжения питания, как легко уяснить из графиков на рис. 1.2, нелинейная: с уве-
личением напряжения ток потребления быстро возрастает. Поэтому снижать на-
пряжение питания даже с учетом ограничения на тактовую частоту для большинст-
ва моделей AVR (не более 8 МГц при питании 2,7 В) все равно выгодно. Например,
устройство с питанием 3 В при тактовой частоте 8 МГц, согласно рис. 1.2, будет
потреблять около 3 мА или, в пересчете на единицы мощности, 9 мВт; на процеду-

Глава 1. Обзор микроконтроллеров Atmel AVR

ру длительностью 100 мкс уйдет энергия 0,9 мкДж. При частоте 16 МГц та же про-
цедура займет 50 мкс, но потребление при необходимом напряжении питания 5 В
составит около 14 мА, т. е. 70 мВт; итого на выполнение процедуры уйдет энергия
3,5 мкДж, почти в 4 раза больше.
Для всех внешних цифровых устройств, за редчайшим исключением, можно по-
добрать современный аналог, предназначенный для работы при напряжениях 2,7–
3,0 В (и даже ниже, если модель контроллера это позволяет), так что с этой стороны
ограничений нет; то, что большинство примеров в этой книге ориентировано на
напряжение питания 5 В, есть лишь дань традиции. К тому же примеры эти, как
правило, подразумевают питание от сети, где потребление не имеет большого зна-
чения. Лимитировать снижение напряжения питания могут светодиодные индика-
торы (из-за того, что прямое падение напряжения на светодиодах само по себе со-
ставляет порядка 2 В, а для больших индикаторов даже 5 В для управления недос-
таточно), но в таких устройствах потребление контроллера уже не играет большой
роли: четыре семисегментные цифры сами по себе будут потреблять ток порядка
100 мА и более. Другой случай представляют аналоговые схемы, где повышение
напряжения питания выгодно с точки зрения увеличения отношения "сигнал-шум".
Заметим, что выводы AVR могут в долговременном режиме отдавать значительный
ток (до 20-40 мА), однако не следует забывать об общем суммарном ограничении
на потребление по выводу питания (см. табл. П1.3). Следует также отметить, что
при подаче аналоговых напряжений на входы АЦП входной цифровой КМОП-
элемент (вход соответствующего порта) не отключается, и при значении данного
напряжения вблизи порога срабатывания элемента это может приводить к возрас-
танию потребления за счет протекания сквозного тока через выходные каскады
КМОП (в том числе иногда и при нахождении микросхемы в "спящем" режиме,
см. главу 14 ). Этого недостатка лишены микросхемы с технологией picoPower.

Некоторые особенности применения AVR в схемах

У большинства выводов МК имеется встроенный подключаемый "подтягивающий"
(т. е. подсоединенный к шине питания) резистор, что, казалось бы, решает одну из
обычных схемотехнических проблем, когда наличие такого резистора требуется
для подключения двухвыводных кнопок или выходов с "открытым коллектором".
Однако в критичных случаях необходим внешний резистор сопротивлением 2–
5 кОм (в критичных для потребления случаях до 10–30 кОм).
"Подтягивающий" резистор следует устанавливать не только на выводе /RESET
(о чем пойдет речь в главе 2 ), но и в том случае, когда выводы SCK, MOSI и MISO
соответствующих портов используются для программирования и подключены к
программирующему разъему ISP (см. главу 5 ), а также по выводам внешних преры-
ваний, если они задействованы. Если эти выводы не "подтягивать" к напряжению
питания дополнительными резисторами (хотя это и не оговорено в технической
документации), то не исключены ложные срабатывания внешних прерываний, пе-
резапуск системы, а при очень мощных помехах - даже порча программы в памя-
ти программ. С другой стороны, когда выводы программирования служат и в каче-

Часть I. Общие принципы устройства и функционирования Atmel AVR

стве обычных портов, сконфигурированных на выход, а в устройстве применяются
режимы энергосбережения, наличие "подтягивающих" резисторов может привести
к лишнему потреблению тока (при установке вывода в логический ноль через рези-
стор потечет ток от источника питания на вход МК). Если реализован один из ре-
жимов энергосбережения, то нужно тщательно проанализировать схему, чтобы ис-
ключить ситуации, при которых через эти резисторы протекает ток.
Также всегда следует устанавливать внешние резисторы при работе выводов МК на
общую шину, как в интерфейсе I

C (или просто при подсоединении входа МК к

выходу другого устройства с открытым коллектором, например, мониторов пита-
ния, описанных в главе 3 ), при подключении к двухвыводным кнопкам (особенно
при наличии внешнего прерывания, см. главы 4 и 5 ). Сопротивление встроенного
резистора (на самом деле представляющего собой, разумеется, полевой транзистор)
в таких случаях слишком велико для того, чтобы электромагнитные помехи ("на-
водки") на нем эффективно "садились".
Микросхемы AVR, как и всякая КМОП-логика, благодаря высокому порогу сраба-
тывания эффективно защищены от помех по шине "земли". Однако они ведут себя
гораздо хуже при помехах по шине питания. Поэтому не забывайте о развязываю-
щих конденсаторах, которые нужно устанавливать непосредственно у выводов пи-
тания (керамические 0,1–0,5 мкФ), а также про качество сетевых выпрямителей и
стабилизаторов.

Г Л АВ А

Общее устройство,
организация памяти,
тактирование, сброс

Общая структура внутреннего устройства МК AVR приведена на рис. 2.1. На этой
схеме показаны все основные компоненты AVR (за исключением модуля JTAG);
в отдельных моделях некоторые составляющие могут отсутствовать или различать-
ся по характеристикам, неизменным остается только общее 8-разрядное процессор-
ное ядро (GPU, General Processing Unit). Кратко опишем наиболее важные компо-
ненты, большинство из которых мы подробно будем рассматривать в дальнейшем.
Начнем с памяти. В структуре AVR имеются три разновидности памяти: flash-
память программ, ОЗУ (SRAM) для временных данных и энергонезависимая па-
мять (EEPROM) для долговременного хранения констант и данных. Рассмотрим их
по отдельности.

Память программ

Объем встроенной flash-памяти программ в AVR-контроллерах составляет от
1 кбайта у ATtiny11 до 256 кбайт у ATmega2560. Первое число в наименовании мо-
дели соответствует величине этой памяти из ряда: 1, 2, 4, 8, 16, 32, 64, 128 и
256 кбайт. Память программ, как и любая другая flash-память, имеет страничную
организацию (размер страницы, в зависимости от модели, составляет от 64 до
256 байт). Страница может программироваться только целиком. Число циклов пе-
репрограммирования достигает 10 тыс.
С точки зрения программиста память программ можно считать построенной из от-
дельных ячеек - слов по два байта каждое. Устройство памяти программ (и только
этой памяти) по двухбайтовым словам - очень важный момент, который нужно
твердо усвоить. Такая организация обусловлена тем, что любая команда в AVR
имеет длину ровно два байта. Исключение составляют команды

и некото-

рые другие (например,

), которые оперируют с 16-разрядными и более длинны-

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

Часть I. Общие принципы устройства и функционирования Atmel AVR

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

Рис. 2.1. Общая структурная схема микроконтроллеров AVR

З

АМЕТКИ НА ПОЛЯХ

Приведем пример интересного случая адресации, который представляет
команда для чтения констант из памяти LPM (а также ELPM в МК с памятью программ
128

кбайт и более). Эта команда подразумевает чтение по байтовому адресу, указан-

ному в двух старших РОН (образующих т. н. регистр Z, см. далее). Однако чтобы не
нарушать "чистоту" концепции организации памяти программ по словам, разработчики
запутали этот простой вопрос, указав в описании, что при вызове команды LPM стар-
шие 15 разрядов регистра Z адресуют слово в памяти, а младший разряд выбирает
младший или старший байт (при равенстве разряда 0 или 1 соответственно) этого

слова. Легко, однако, заметить, что байтовая и пословная организации памяти при та-
ком подходе эквивалентны.

Последний адрес существующего объема памяти программ для конкретной модели
обозначается константой

По умолчанию все контроллеры AVR всегда

начинают выполнение программы с адреса $0000. Если в программе нет прерыва-
ний, то с этого адреса может начинаться прикладная программа. В противном слу-

чае по данному адресу располагается т. н. таблица векторов прерываний , подроб-
нее о которой мы будем говорить в главах 4 и 5 . Здесь укажем лишь, что первым в
этой таблице (по тому же адресу $0000) всегда размещается вектор сброса

который указывает на процедуру, выполняющуюся при сбросе МК (в том числе и
при включении питания).

П

РИМЕЧАНИЕ

В ассемблере AVR можно обозначать шестнадцатеричные числа в "паскалевском"
стиле, предваряя их знаком $, при этом стиль языка С (0x00) тоже действителен, а вот
"интеловский" способ (00h) не работает. Подробнее об обозначениях чисел различных
систем счисления в AVR-ассемблере см. главу 5 .

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

Память данных (ОЗУ, SRAM)

В отличие от памяти программ, адресное пространство памяти данных адресуется
побайтно (а не пословно). Адресация полностью линейная, без какого-то деления
на страницы, сегменты или банки, как это принято в некоторых других системах.
Младшие МК семейства Tiny (включая Tiny1х и Tiny28) памяти данных, как тако-
вой, не имеют, ограничиваясь лишь регистровым файлом (РОН) и регистрами вво-
да-вывода (РВВ). В других моделях объем встроенной SRAM колеблется от
128 байт в представителях семейства Tiny (например, у ATtiny2313) до 4–8 кбайт
у старших моделей Mega.
Адресное пространство статической памяти данных (SRAM) условно делится на
несколько областей, показанных на рис. 2.2. Темной заливкой выделена часть, от-
носящаяся к собственно встроенной SRAM, до нее по порядку адресов расположе-
но адресное пространство регистров (первые 32 байта занимает РОН, еще 64 -
РВВ). Для старших моделей Mega со сложной структурой (например, ATmega128)
64-х регистров ввода-вывода может оказаться недостаточно, поэтому в них для до-
полнительных РВВ выделяется отдельное адресное пространство (от $60 до макси-
мально возможного в байтовой адресации значения $FF, итого таких регистров мо-
жет быть всего 160).

З

АМЕТКИ НА ПОЛЯХ

В архитектуре МК AVR понятие "ввода-вывода" употребляется в двух смыслах: во-

первых, имеются "порты ввода-вывода" (I/O ports), которые мы рассмотрим в главе 3 .
Во-вторых, "регистрами ввода-вывода" (РВВ) в структуре AVR называются регистры,
которые обеспечивают доступ к дополнительным компонентам, внешним по отноше-

нию к GPU, за исключением ОЗУ (в том числе и к портам ввода-вывода). Такое под-
разделение приближает структуру МК AVR к привычной конфигурации персонального
компьютера, где доступ к любым внешним по отношению к центральному процессору
компонентам, кроме памяти, осуществляется через порты ввода-вывода.

Часть I. Общие принципы устройства и функционирования Atmel AVR

Для некоторых моделей Mega (ATmega8515, ATmega162, ATmega128, ATmega2560
и др.) предусмотрена возможность подключения внешней памяти объемом до
64 кбайт, которая может быть любой статической
разновидностью (SRAM, Flash или EEPROM) с па-
раллельным интерфейсом.
Отметим, что адреса РОН и РВВ не отнимают про-
странство у ОЗУ данных (за исключением подклю-
чаемой внешней памяти у старших моделей Mega,
максимальный адрес которой ограничен значением
$FFFF): так, если в конкретной модели МК имеется
512 байт SRAM, а пространство регистров занимает
первые 96 байт (до адреса $60), то адреса SRAM
займут адресное пространство от $0060 до $025F
(т. е. от 96-й до 607-й ячейки включительно). Конец
встроенной памяти данных обозначается константой

Рис. 2.2. Адресное пространство

статической памяти данных (SRAM)

микроконтроллеров AVR

Операции чтения/записи в память одинаково работают с любыми адресами из дос-
тупного пространства, и при работе с SRAM нужно быть внимательным: вместо
записи в память вы легко можете "попасть" в какой-нибудь регистр. Например,
команда загрузки значения регистра

в регистр

) равносильна

записи в SRAM по нулевому адресу (

). Адрес в памяти для РОН сов-

падает с его номером. В то же время для непосредственной записи в РВВ по его
адресу в памяти к номеру регистра следует прибавить $20: так, регистр флагов

который для большинства моделей располагается в конце таблицы РВВ по адресу
$3F, в памяти имеет адрес $5F. Устанавливать РОН и РВВ прямой адресацией па-
мяти неудобно: такая запись всегда отнимает два такта вместо одного, характерно-
го для большинства других команд, хотя иногда это позволяет обойти ограничения
на манипуляции с некоторыми РВВ. Но если имеется готовая программа, работаю-
щая с SRAM, то при замене моделей процессоров на более старшие нужно быть
внимательным из-за того, что в них младшие адреса SRAM могут перекрываться
дополнительными РВВ.

Глава 2. Общее устройство, организация памяти, тактирование, сброс

Энергонезависимая память данных (EEPROM)

Все модели МК AVR (кроме снятого с производства ATtiny11) имеют встроенную
EEPROM для хранения констант и данных при отключении питания.
В разных моделях объем ее варьируется от 64 байт (ATtiny1х) до 4 кбайт (старшие
модели Mega). Конец EEPROM обозначается константой

(это обозначе-

ние введено только для более поздних моделей AVR, потому при использовании
этой константы иногда ее придется определять самому). Число циклов перепро-
граммирования EEPROM может достигать 100 тыс.
Напомним, что EEPROM отличается от Flash возможностью выборочного про-
граммирования побайтно (в принципе даже побитно, но этот способ недоступен
пользователю). Однако в старших моделях семейства EEPROM, как и flash-память
программ, имеет страничную организацию, правда, страницы эти невелики - до
4 байт каждая. На практике, как при программировании EEPROM по последова-
тельному каналу (т. е. через SPI-интерфейс программирования), так и при записи и
чтении EEPROM из программы, эта особенность не имеет значения, и доступ осу-
ществляется побайтно.
Чтение из EEPROM осуществляется в течение одного машинного цикла (правда, на
практике оно растягивается на четыре цикла, но программисту следить за этим спе-
циально не требуется). А вот запись в EEPROM протекает значительно медленнее,
и к тому же с точно не определенной скоростью: цикл записи одного байта может
занимать от 2 до ~ 4 мс и более. Процесс записи регулируется встроенным RC -
генератором, частота которого нестабильна (при более низком напряжении питания
можно ожидать, что время записи будет больше). За такое время при обычных так-
товых частотах МК успевает выполнить несколько тысяч команд, потому програм-
мирование процедуры записи требует аккуратности: например, нужно следить,
чтобы в момент записи не "вклинилось" прерывание (подробнее об этом см. гла-
вы 4
и 9 ).
Главная же сложность при работе с EEPROM - возможность повреждения ее со-
держимого при недостаточно быстром снижении напряжения питания в момент
выключения. Обусловлено это тем, что при уменьшении напряжения питания до
некоторого порога (ниже порога стабильной работы, но недостаточного для полно-
го выключения) из-за колебаний напряжения МК начинает выполнять произволь-
ные команды, в том числе может осуществить процедуру записи в EEPROM. Если
учесть, что типовая команда МК AVR выполняется за десятые доли микросекунды,
то ясно, что никакой реальный источник питания не может обеспечить снижение
напряжения до нуля за нужное время. По опыту автора при питании от обычного
стабилизатора типа LM7805 с рекомендованными значениями емкости конденсато-
ров на входе и на выходе содержимое EEPROM будет неизбежно испорчено при-
мерно в половине случаев.
Этой проблемы не должно существовать, если константы записывают в EEPROM
при программировании МК, а процедура записи в программе отсутствует (о том,
как сформировать файл с данными для EEPROM, см. раздел "Директивы и функ-
ции" главы 5
). Большая сохранность данных в таких случаях подтверждается и эм-

Часть I. Общие принципы устройства и функционирования Atmel AVR

пирическими наблюдениями, и тем, что разрешение записи в EEPROM - процеду-
ра двухступенчатая (см. главу 9 ). Во всех же остальных случаях (а их, очевидно,
абсолютное большинство - в EEPROM чаще всего хранят пользовательские уста-
новки и текущую конфигурацию при выключении питания) приходится принимать
специальные меры. Наиболее кардинальной и универсальной из них является уста-
новка внешнего монитора питания, удерживающего МК в состоянии сброса при
уменьшении напряжения питания ниже пороговой величины. Той же цели служит
встроенный детектор падения напряжения (Brown-out Detection, BOD), имеющийся
практически во всех моделях Tiny и Mega, но техническая документация не исклю-
чает при этом для надежности дублирования его и внешним монитором питания.
Подробнее о схеме BOD и режимах сброса МК см. далее в этой главе , а о програм-
мировании EEPROM и мерах предосторожности при ее использовании см. главу 9 .

Способы тактирования

Канонический способ тактирования МК - подключение кварцевого резонатора к
соответствующим выводам (рис. 2.3, а ). Емкость конденсаторов С1 и С2 в типовом
случае должна составлять 15–22 пФ (может быть увеличена до 33–47 пФ с одно-
временным повышением потребления). В большинстве моделей Tiny и Mega име-
ется специальный конфигурационный бит

Который позволяет регулировать

потребление. При установке этого бита в 1 (незапрограммированное состояние)
размах колебаний генератора уменьшается, однако при этом сужается возможный
диапазон частот и общая помехоустойчивость, поэтому задействовать этот режим
не рекомендуется. Может быть также выбран низкочастотный кварцевый резонатор
(например, "часовой" 32 768 Гц), при этом конденсаторы С1 и С2 могут отсутство-
вать, т. к. при установке

в значение 0 подключаются имеющиеся в составе

МК внутренние конденсаторы емкостью 36 пФ.
Кварцевый резонатор можно заменить керамическим. Автору этих строк удавалось
запускать МК на нестандартных частотах, используя вместо кварца в том же под-
ключении миниатюрную индуктивность (при ее значении 4,7 мкГн и емкостях кон-
денсаторов 91 пФ частота получается около 10 МГц), что заодно позволяет немного
уменьшить габариты схемы.
Естественно, тактировать МК можно и от внешнего генератора (рис. 2.3, б ). Осо-
бенно это удобно, когда требуется либо синхронизировать МК с внешними компо-
нентами, либо получить очень точную частоту тактирования, выбрав соответст-
вующий генератор (например, серии SG-8002 фирмы Epson).
Наоборот, когда точность не требуется, можно подключить внешнюю RC -цепочку
(рис. 2.3, в ). В этой схеме емкость С1 должна быть не менее 22 пФ, а резистор R1
выбирается из диапазона 3,3–100 кОм. Частота при этом определяется по формуле
F = 2/3 RC . С1 можно не устанавливать вообще, если записать лог. 0 в конфигура-
ционную ячейку

Подключив тем самым внутренний конденсатор 36 пФ.

Наконец, можно вообще отказаться от внешних компонентов и обойтись встроен-
ным RC -генератором, который способен работать на четырех приблизительных

Глава 2. Общее устройство, организация памяти, тактирование, сброс

значениях частот (1, 2, 4 и 8 МГц). В ряде моделей предусмотрена возможность
подстройки частоты этого генератора (подробнее см. или техническое описание
конкретных моделей). Эту возможность наиболее целесообразно использовать в
младших моделях Tiny, выпускающихся в 8-контактном корпусе - тогда выводы,
предназначенные для подключения резонатора или внешнего генератора, можно
задействовать для других целей, как обычные порты ввода-вывода.

Рис. 2.3. Способы тактирования МК AVR с использованием: а - кварцевого резонатора;

б - внешнего генератора; в - RC -цепочки

Семейство Classic встроенного RC -генератора не имеет, а специальных конфигура-

ционных ячеек у этих МК значительно меньше, и в общем случае на них можно не

обращать внимания. Для других семейств это не так. По умолчанию МК семейств

Tiny и Mega установлены в состояние для работы со встроенным генератором на
частоте 1 МГц (

0001), поэтому для других режимов нужно соответствую-

щим образом установить конфигурационные ячейки

(см. табл. 2.1). При этом

следует учитывать, что состояние ячеек

0000 (зеркальное по отношению к

наиболее часто употребляемому значению для кварцевого резонатора 1111) пере-

водит МК в режим тактирования от внешнего генератора, и при этом его нельзя

даже запрограммировать без подачи внешней частоты. О рекомендуемых установ-

ках конфигурационных ячеек и об особенностях их программирования см. также

главу 5 .

Таблица 2.1. Установка конфигурационных ячеек CKSEL

в зависимости от режимов тактирования

CKSEL3...0

Источник тактирования

Частота

Внешняя частота

Встроенный RC -генератор

Встроенный RC -генератор

Встроенный RC -генератор

Встроенный RC -генератор

Внешняя RC -цепочка

Часть I. Общие принципы устройства и функционирования Atmel AVR

Таблица 2.1 (окончание)

CKSEL3...0

Источник тактирования

Частота

Внешняя RC -цепочка

0,9... 3,0 МГц

Внешняя RC -цепочка

3,0... 8,0 МГц

Внешняя RC -цепочка

8,0... 12 МГц

Низкочастотный резонатор

Кварцевый резонатор

0,4... 0,9 МГц

Кварцевый резонатор

0,9... 3,0 МГц

Кварцевый резонатор

3,0... 8,0 МГц

1ххх (CKPOT=0)

Кварцевый резонатор

Сброс

Сбросом (RESET) называется установка начального режима работы МК. При этом
все РВВ устанавливаются в состояние по умолчанию - как правило, это нули во
всех разрядах, за небольшим исключением (а вот РОН могут принимать произ-
вольные значения, поэтому при необходимости начинать с какой-то определенной
величины переменные следует устанавливать в начале программы принудительно).
Программа после сброса начинает выполняться с начального адреса (по умолчанию
это адрес $0000).
Сброс всегда происходит при включении питания. Кроме этого, источниками сбро-
са могут быть следующие события: аппаратный сброс, т. е. подача низкого уровня
напряжения на вход RESET (правильнее его обозначать с инверсией: /RESET, т. к.
активный уровень тут низкий, и мы будем придерживаться этого правила); оконча-
ние отсчета установленного интервала сторожевого таймера; срабатывание схемы
BOD. Значение четырех младших битов регистра состояния

должно сигна-

лизировать о том, от какого источника производился сброс предыдущий раз (уста-
новка в 1 бита 0 - сброс при включении, бита 1 - аппаратный сброс, бита 2 - от
схемы BOD, бита 3 - от сторожевого таймера). На практике, по опыту автора, по
состояниям этого регистра надежно различаются от всех остальных лишь состоя-
ния сброса по таймеру (прочие флаги могут оказаться установленными все одно-
временно). Тем не менее эта информация может быть полезной, например, при ана-
лизе причин перерывов в работе круглосуточно работающих устройств (см. главу 12 ).
В младших МК семейства Tiny (кроме ATtiny28) нет встроенного "подтягивающе-
го" резистора на выводе /RESET, поэтому для надежной работы следует преду-
смотреть подключение внешнего резистора величиной 2–5 кОм от этого вывода к
напряжению питания. Автор также настоятельно рекомендует устанавливать по-
добный резистор для любых моделей AVR, т. к. встроенный резистор имеет боль-
шой номинал (100–500 кОм) и на нем могут наводиться помехи, способные привес-
ти к непредсказуемому сбросу. Также (хотя в технических описаниях такой реко-

Глава 2. Общее устройство, организация памяти, тактирование, сброс

мендации и не содержится) не помешает установка конденсатора 0,1–0,5 мкФ от
вывода /RESET на "землю" - это сглаживает неизбежный дребезг напряжения и
немного затягивает фронт нарастания напряжения на выводе /RESET по сравнению
с увеличением напряжения питания: когда наступит порог срабатывания схемы
сброса, напряжение питания всего МК уже установится.
В моделях Tiny, выпускающихся в 8-контактном корпусе (ATtiny11–ATtiny15), ес-
ли не требуется внешний сброс, вывод /RESET может выполнять функции обычно-
го порта ввода-вывода. С одним только нюансом: при конфигурировании этого
контакта на выход он работает, как вывод с открытым коллектором, а не как обыч-
ный логический элемент (о конфигурации выводов портов см. главу 3 ).
Самый предпочтительный способ организации сброса при включении питания, как
уже говорилось ранее, - установка внешнего монитора питания. Например, при
5-вольтовом питании подойдет популярная микросхема MC34064 с порогом сраба-
тывания 4,6 В и типовым потреблением около 300 мкА или ее более современный
аналог (например, MAX803L с потреблением 12 мкА). Для трехвольтового питания
пригодна схема MAX803R (2,6 В) или подходящая версия DS1816 с соответствую-
щим напряжением. Все перечисленные микросхемы трехвыводные (питание, "зем-
ля", вывод управления сбросом) и имеют выход с открытым коллектором, т. е. пре-
дусматривают установку "подтягивающего" резистора. Типовое время срабатыва-
ния этих микросхем при снижении напряжения - микросекунды, что обеспечивает
сохранность данных в EEPROM. При повышении напряжения они обеспечивают
большую временную задержку (порядка долей секунды), что позволяет надежно
осуществлять сброс МК без дребезга.
Встроенная схема BOD обеспечивает время срабатывания порядка микросекунд с
задержкой на возврат в рабочее состояние после восстановления напряжения, оп-
ределяющейся теми же установками, что и задержка сброса (ячейки

тактовой частоте 4 МГц) и даже максимально возможное ее значение ~68 мс могут
оказаться недостаточными для обхода дребезга, возникающего при снижении на-
пряжения питания автономного источника. Для выбора режима работы BOD слу-
жат три конфигурационные ячейки

имеющие следующие состояния:

111 (установка по умолчанию) - схема BOD выключена;

101 - включает BOD при пороге срабатывания 2,7 В;

100 - соответствует порогу 4,0 В.

Отметим, что с точки зрения надежности работы, чем меньше разница между на-
пряжением питания и порогом срабатывания монитора питания (внешнего или
встроенной схемы BOD, неважно), тем лучше - при небольших скачках питания,
нечувствительных для монитора, тем не менее могут происходить всяческие не-
приятности вроде самопроизвольного возникновения внешнего прерывания. Одна-
ко эту разницу следует учитывать при питании устройства от батарей: например,
для четырех "пальчиковых" щелочных аккумуляторов и мониторе питания, рассчи-
танном на 4,7 В, остаточное напряжение на элементах после срабатывания монито-

Принципиальная схема программатора на LPT порт показана на рисунке. В качестве шинного формирователя используйте микросхему 74AC 244 или 74HC244 (К1564АП5), 74LS244 (К555АП5) либо 74ALS244 (К1533АП5).

Светодиод VD1 индицирует режим записи микроконтроллера,

светодиод VD2 - чтения,

светодиод VD3 - наличие питания схемы.

Напряжение, необходимое для питания схема берёт с разъёма ISP, т.е. от программируемого устройства. Эта схема является переработанной схемой программатора STK200/300 (добавлены светодиоды для удобства работы), поэтому она совместима со всеми программами программаторов на PC, работающих со схемой STK200/300. Для работы с этим программатором используйтепрограмму CVAVR

Программатор можно выполнить на печатной плате и поместить её в корпус разъёма LPT, как показано на рисунках:




Для работы с программатором удобно использовать удлинитель LPT порта, который несложно изготовить самому (к примеру, из кабеля Centronix для принтера), главное "не жалеть" проводников для земли (18-25 ноги разъёма) или купить. Кабель между программатором и программируемой микросхемой не должен превышать 20-30 см.