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

Принципиальная схема программатора на 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 см.

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

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 В, остаточное напряжение на элементах после срабатывания монито-

Киселев Роман, Май 2007 Статья обновлена 26 Мая 2014

Итак, что вообще такое микроконтроллер (далее МК)? Это, условно говоря, маленький компьютер, размещенный в одной интегральной микросхеме. У него есть процессор (арифметическо-логическое устройство, или АЛУ), flash-память, EEPROM-память, множество регистров, порты ввода-вывода, а также дополнительные «навороты», такие как таймеры, счетчики, компараторы, USARTы и т. п. Микроконтроллер после подачи питания загружается и начинает выполнять программу, записанную в его flash-памяти. При этом он может через порты ввода/вывода управлять самыми разнообразными внешними устройствами.

Что же это означает? Это значит, что в МК можно реализовать любую логическую схему, которая будет выполнять определенные функции. Это значит, что МК – микросхема, внутреннее содержимое которой, фактически, мы создаем сами. Что позволяет, купив несколько совершенно одинаковых МК, собрать на них совершенно разные схемы и устройства. Если вам захочется внести какие-либо изменения в работу электронного устройства, то не нужно будет использовать паяльник, достаточно будет лишь перепрограммировать МК. При этом не нужно даже вынимать его из вашего дивайса, если вы используете AVR, т. к. эти МК поддерживают внутрисхемное программирование. Таким образом, микроконтроллеры ликвидируют разрыв между программированием и электроникой.

AVR – это 8-битные микроконтроллеры, т. е. их АЛУ может за один такт выполнять простейшие операции только с 8-ми битными числами. Теперь пора поговорить о том, какой МК мы будем использовать. Я работаю с МК ATMega16. Он очень распространенный и приобрести его можно практически в любом магазине радиодеталей где-то за 100 руб. Если вы его не найдете – тогда можно купить любой другой МК серии MEGA, но в этом случае придется искать к нему документацию, т. к. одни и те же «ножки» разных МК могут выполнять разные функции, и, подключив, казалось бы, правильно все выводы, вы, может быть, получите рабочее устройство, а, может быть, лишь облако вонючего дыма. При покупке ATMega16 проверьте, чтобы он был в большом 40-ножечном DIP-корпусе, а также купите к нему панельку, в которую его можно будет вставить. Для работы с ним потребуются также дополнительные устройства: светодиоды, кнопки, разъемы и т. п..

ATMega16 обладает очень большим количеством самых разнообразных функций. Вот некоторые его характеристики:

  • Максимальная тактовая частота – 16 МГц (8 МГц для ATMega16L)
  • Большинство команд выполняются за один такт
  • 32 8-битных рабочих регистра
  • 4 полноценных 8-битных порта ввода/вывода
  • два 8-битных таймера/счетчика и один 16-битный
  • 10-разрядный аналогово-цифровой преобразователь (АЦП)
  • внутренний тактовый генератор на 1 МГц
  • аналоговый компаратор
  • интерфейсы SPI, I2C, TWI, RS-232, JTAG
  • внутрисхемное программирование и самопрограммирование
  • модуль широтно-импульсной модуляции (ШИМ)

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

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

Что для этого понадобится?

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

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

Поясним, какие ножки нас сейчас интересуют.

  • VCC – сюда подается питание (4,5 – 5,5 В) от стабилизированного источника
  • GND – земля
  • RESET – сброс (при низком уровне напряжения)
  • XTAL1, XTAL2 – сюда подключается кварцевый резонатор
  • PA, PB, PC, PD – порты ввода/вывода (A, B, C и D соответственно).

В качестве источника питания можно использовать все, что выдает 7-11 В постоянного тока. Для стабильной работы МК нужно стабилизированное питание. В качестве стабилизатора можно использовать микросхемы серии 7805. Это линейные интегральные стабилизаторы, на вход которых подают 7-11 В постоянного нестабилизированного тока, а на выходе получают 5 В стабилизированного. Перед 7805 и после него нужно поставить фильтрующие конденсаторы (электролитические для фильтрации помех низких частот и керамические для высоких). Если не удается найти стабилизатор, то можно в качестве источника питания использовать батарейку на 4,5 В. От нее МК нужно питать напрямую.

Ниже приведу схему подключения МК:

Давайте теперь разберемся, что здесь для чего.

BQ1 – это кварцевый резонатор, задающий рабочую частоту МК. Можно поставить любой до 16 МГц, но, поскольку мы планируем работать в будущем и с COM-портом, то рекомендую использовать резонаторы на следующие частоты: 14,7456 МГц, 11,0592 МГц, 7,3725 МГц, 3,6864 МГц или 1,8432 МГц (позже станет ясно, почему). Я использовал 11,0592 МГц. Понятное дело, что чем больше частота, тем выше и скорость работы устройства.

R1 – подтягивающий резистор, который поддерживает напряжение 5 В на входе RESET. Низкий уровень напряжения на этом входе означает сброс. После сброса МК загружается (10 – 15 мс) и начинает выполнять программу заново. Поскольку это высокоомный вход, то нельзя оставлять его «болтающимся в воздухе» - небольшая наводка на нем приведет к непредвиденному сбросу МК. Именно для этого и нужен R1. Для надежности рекомендую также установить конденсатор С6 (не более 20 мкФ).

SB1 – кнопка сброса.

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

Синим прямоугольником на схеме обведен собственно программатор. Его удобно выполнить в виде провода, один конец которого втыкается в LPT порт, а другой – в некий разъем рядом с МК. Провод не должен быть чрезмерно длинным. Если возникнут проблемы с этим кабелем (обычно не возникают, но всякое бывает) то придется спаять адаптер Altera ByteBlaster. О том, как это сделать, написано в описании к программатору AVReal.

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

Для программирования AVR есть несколько сред разработки. Во-первых, это AVR Studio – официальная система программирования от Atmel. Она позволяет писать на ассемблере и отлаживать программы, написанные на ассемблере, С и С++. IAR – это коммерческая система программирования на C, С++ и ассемблере. WinAVR – компилятор с открытыми исходниками. AtmanAVR – система программирования для AVR с интерфейсом, почти «один в один» таким же, как у Visual C++ 6. AtmanAVR также позволяет отлаживать программы и содержит множество вспомогательных функций, облегчающих написание кода. Эта система программирования коммерческая, но, согласно лицензии, ее можно в течение месяца использовать «нахаляву».

Я предлагаю начать работу с IAR как с наиболее «прозрачной» средой разработки. В IAR проект целиком создается «ручками», соответственно, сделав несколько проектов, вы уже будете четко знать, что означает каждая строчка кода и что будет, если ее изменить. При работе же с AtmanAVR придется либо пользоваться предварительно созданным шаблоном, который очень громоздкий и трудный для понимания для человека, не имеющего опыта, либо иметь множество проблем с заголовочными файлами при сборке проекта «с нуля». Разобравшись с IAR, мы впоследствии рассмотрим другие компиляторы.

Итак, для начала раздобудьте IAR. Он очень распространен и его нахождение не должно быть проблемой. Скачав где-либо IAR 3.20, устанавливаем компилятор / рабочую среду, и запускаем его. После этого можно начинать работу.

Запустив IAR, выбираем file / new / workspace , выбираем путь к нашему проекту и создаем для него папку и даем имя, например, «Prog1». Теперь создаем проект: Project / Create new project… Назовем его также – «Prog1». Щелкаем правой кнопкой мыши на заголовке проекта в дереве проектов и выбираем «Options»

Здесь будем настраивать компилятор под конкретный МК. Во-первых, нужно выбрать на вкладке Target тип процессора ATMega16, на вкладке Library Configuration установить галочку Enable bit definitions in I/O-include files (чтобы можно было использовать в коде программы имена битов различных регистров МК), там же выбрать тип библиотеки С/ЕС++. В категории ICCAVR нужно на вкладке Language установить галочку Enable multibyte support, а на вкладке Optimization выключить оптимизацию (иначе она испортит нашу первую программу).

Далее выбираем категорию XLINK. Здесь нужно определить формат откомпилированного файла. Поскольку сейчас мы задаем опции для режима отладки (Debug), о чем написано в заголовке, то на выходе нужно получить отладочный файл. Позже мы его откроем в AVR Studio. Для этого нужно выбрать расширение.cof, а тип файла – ubrof 7.

Теперь нажимаем ОК, после чего меняем Debug на Release.

Снова заходим в Options, где все параметры, кроме XLINK, выставляем те же. В XLINK меняем расширение на.hex, а формат файла на intel-standart.

Вот и все. Теперь можно приступать к написанию первой программы. Создаем новый Source/text и набираем в нем следующий код:

#include "iom16.h" short unsigned int i; void main (void ) { DDRB = 255; PORTB = 0; while (1) { if (PORTB == 255) PORTB = 0; else PORTB++; for (i=0; i

Файл «iom16.h» находится в папке (C:\Program Files)\IAR Systems\Embedded Workbench 3.2\avr\inc . Если вы используете другой МК, например, ATMega64, то выбирайте файл «iom64.h». В этих заголовочных файлах хранится информация о МК: имена регистров, битов в регистрах, определены имена прерываний. Каждая отдельная «ножка» порта A, B, C или D может работать либо как вход, либо как выход. Это определяется регистрами Data Direction Register (DDR). 1 делает «ножку» выходом, 0 – входом. Таким образом, выставив, например, DDRA = 13, мы делаем выходами «ножки» PB0, PB2, PB3, остальные – входы, т.к. 13 в двоичном коде будет 00001101.

PORTB – это регистр, в котором определяется состояние «ножек» порта. Записав туда 0, мы выставляем на всех выходах напряжение 0 В. Далее идет бесконечный цикл. При программировании МК всегда делают бесконечный цикл, в котором МК выполняет какое-либо действие, пока его не сбросят или пока не произойдет прерывание. В этом цикле пишут как бы «фоновый код», который МК выполняет в самую последнюю очередь. Это может быть, например, вывод информации на дисплей. В нашем же случае увеличивается содержимое регистра PORTB до тех пор, пока он не заполнится. После этого все начинается сначала. Наконец, цикл for на десять тысяч тактов. Он нужен для формирования видимой задержки в переключении состояния порта В.



Теперь сохраняем этот файл в папке с проектом как Prog1.c, копируем в папку с проектом файл iom16.h, выбираем Project/Add Files и добавляем «iom16.h» и «Prog1.c». Выбираем Release, нажимаем F7, программа компилируется и должно появиться сообщение:


Total number of errors: 0
Total number of warnings: 0

Приведу фотографию своего программатора:

Скачиваем программатор AVReal. Копируем его (AVReal32.exe) в папку Release/exe, где должен лежать файл Prog1.hex. Подаем питание на МК, подключаем кабель-программатор. Открываем Far Manager (в нем наиболее удобно прошивать МК), заходим в эту папку, нажимаем Ctrl+O. Поскольку у нас совершенно новый МК, то набиваем

avreal32.exe +MEGA16 -o11.0592MHZ -p1 -fblev=0,jtagen=1,cksel=F,sut=1 –w

Не забудьте правильно указать частоту, если используете не 11059200 Гц! При этом в МК прошиваются т.н. fuses – регистры, управляющие его работой (использование внутреннего генератора, Jtag и т.п.). После этого он готов к приему первой программы. Программатору в качестве параметров передают используемый LPT-порт, частоту, имя файла и другие (все они перечислены в описании к AVReal). Набираем:

Avreal32.exe +Mega16 -o11.0592MHz -p1 -e -w -az -% Prog1.hex

В случае правильного подключения программатор сообщит об успешном программировании. Нет гарантии, что это получится с первого раза (при первом вызове программы). У меня самого бывает программируется со второго раза. Возможно, LPT-порт глючный или возникают наводки в кабеле. При возникновении проблем тщательно проверьте свой кабель. По своему опыту знаю, что 60% неисправностей связаны с отсутствием контакта в нужном месте, 20% - с наличием в ненужном и еще 15% - с ошибочной пайкой не того не к тому. Если ничего не получится, читайте описание к программатору, попробуйте собрать Byte Blaster.

Предположим, у вас все работает. Если теперь подключить к порту В МК восемь светодиодов (делайте это в выключенном состоянии МК, и желательно последовательно со светодиодами включить резисторы в 300-400 Ом) и подать питание, то произойдет маленькое чудо – по ним побежит «волна»!

© Киселев Роман
Май 2007

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

Общая информация

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

Немного о AVR

В рамках статьи будет рассмотрено семейство микроконтроллеров, выпускаемых фирмой Atmel. Они имеют довольно неплохую производительность, что позволяет использовать их во многих любительских устройствах. Широко применяются и в промышленности. Можно встретить в такой технике:

  1. Бытовой. Стиральные машины, холодильники, микроволновые печи и прочее.
  2. Мобильной. Роботы, средства связи и так далее.
  3. Вычислительной. Системы управления периферийными устройствами, материнские платы.
  4. Развлекательной. Украшения и детские игрушки.
  5. Транспорт. Системы безопасности и управления двигателем автомобиля.
  6. Промышленное оборудование. Системы управления станками.

Это, конечно же, не все сферы. Они применяются там, где выгодно использовать не набор управляющих микросхем, а один микроконтроллер. Это возможно благодаря низкому энергопотреблению и Для написания программ используются языки С и Assembler, немного изменённые под семейство микроконтроллеров. Такие изменение необходимы из-за слабых вычислительных возможностей, которые исчисляются, как правило, в десятках килобайт. AVR-программирование без изучения этих языков не представляется возможным.

Как получить свой первый микроконтроллер?

AVR-программирование требует:

  1. Наличия необходимой среды разработки.
  2. Собственно самих микроконтроллеров.

Второй пункт рассмотрим подробнее. Существует три возможности обзавестись требуемым устройством:

  1. Купить непосредственно сам микроконтроллер.
  2. Обзавестись устройством в составе конструктора (например - Arduino).
  3. Собрать микроконтроллер самостоятельно.

В первом пункте ничего сложного нет, поэтому сразу перейдём ко второму и третьему.

Обзавестись устройством в составе конструктора

В качестве примера будет выбран известный Arduino. Это по совместительству удобная платформа для быстрой и качественной разработки различных электронных устройств. Плата Arduino включает в себя определённый набор компонентов для работы (существуют различные конфигурации). В неё обязательно входит AVR-контроллер. Этот подход позволяет быстро начать разработку устройства, не требует специальных умений и навыков, имеет значительные возможности в плане подключения дополнительных плат, а также в интернете можно найти много информации на интересующие вопросы. Но не обошлось и без минусов. Покупая Arduino, человек лишает себя возможности более глубоко окунуться в AVR-программирование, лучше узнать микроконтроллер, специфику его работы. Также негатива добавляет и относительно узкая линейка моделей, из-за чего часто приходится покупать платы под конкретные задачи. Особенностью также является и то, что программирование на "СИ" здесь отличается довольно сильно от стандартной формы. Несмотря на все свои недостатки, Arduino подходит для изучения новичкам. Но злоупотреблять не стоит.

Самостоятельная сборка

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

Как работать?

Итак, допустим, что вопрос с микроконтроллером решился. Далее будет считаться, что он был приобретён или же куплен самостоятельно. Что ещё нужно, чтобы освоить AVR-программирование? Для этой цели нужна среда разработки (в качестве базиса подойдёт и обычный блокнот, но рекомендую остановиться на Notepad++). Хотя существуют и другие программы для программирования AVR, приведённое обеспечение сможет справиться со всеми требованиями. Также необходим программатор. Его можно приобрести в ближайшем магазине, заказать по интернету или собрать самостоятельно. Не помешает и печатная плата. Она не обязательна, но её использование позволяет сэкономить свои нервы и время. Также покупается/создаётся самостоятельно. И последнее - это источник питания. Для AVR необходимо обеспечить поступление напряжения на 5В.

Где и как учиться?

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

Порты ввода/вывода

Это чрезвычайно важная тема. Без понимания того, как работают порты ввода/вывода, не представляется возможным внутрисхемное программирование AVR вообще. Ведь взаимодействие микроконтроллера с внешними устройствами осуществляется именно при их посредничестве. На первый взгляд новичка может показаться, что порт - это довольно запутанный механизм. Чтобы избежать такого впечатления, не будем детально рассматривать схему его работы, а только получим общее представление об этом. Рассмотрим программную реализацию. В качестве примера устройства был выбран микроконтроллер AtMega8 - один из самых популярных из всего семейства AVR. Порт ввода/вывода представляет собой три регистра, которые отвечают за его работу. На физическом уровне они реализовываются как ножки. Каждой из них соответствует определённый бит в управляющем реестре. Каждая ножка может работать как для ввода информации, так и для её вывода. Например, на неё можно повесить функцию зажигания светодиода или обработку нажатия кнопки. Кстати, три регистра, о которых говорилось, это: PORTx, PINx и DDRx. Каждый из них является восьмиразрядным (не забываем, что мы рассматриваем AtMega8). То есть один бит занимается определённой ножкой.

Работа регистров

Наиболее весомым в плане ориентации является управляющий DDRx. Он также является восьмиразрядным. Значения для него могут быть записаны 0 или 1. Как меняется работа контроллера при использовании нулей и единицы? Если в определённом бите выставить 0, то соответствующая ему ножка будет переключена в режим входа. И с неё можно будет считывать данные, что идут с внешних устройств. Если установить 1, то микроконтроллер сможет управлять чем-то (например, дать приказ транзистору пропустить напряжение и зажечь светодиод). Вторым по важности является PORTx. Он занимается управлением состояния ножки. Давайте рассмотрим пример. Допустим, у нас есть порт вывода. Если мы устанавливаем логическую единицу в PORTx, то посылается сигнал от микроконтроллера управляющему устройству начать работу. Например, зажечь светодиод. При установлении нуля он будет гаситься. То есть работать с управляющим регистром DDRx постоянно, нет надобности. И напоследок давайте о PINx. Этот регистр отвечает за отображение состояния ножки контроллера, когда она настроена на состояние ввода. Следует отметить, что PINx может работать исключительно в режиме чтения. Записать в него ничего не получится. Но вот прочитать текущее состояние ножки - это без проблем.

Работа с аналогами

AVR не являются единственными микроконтроллерами. Этот рынок поделен между несколькими крупными производителями, а также между многочисленными китайскими имитирующими устройствами и самоделками. Во многом они подобны. К примеру, программирование PIC/AVR сильно не отличается. И если есть понимание чего-то одного, то понять всё остальное будет легко. Но начинать путь рекомендуем всё же с AVR благодаря его грамотной структуре, дружелюбности к разработчику и наличию большого количества вспомогательных материалов, из-за чего процесс разработки можно значительно ускорить.

Техника безопасности

Когда будет вестись программирование микроконтроллеров AVR на "СИ" или на "Ассемблере", то необходимо работать очень осторожно. Дело в том, что выставив определённую комбинацию регистров и изменив внутренние настройки, можно спокойно заблокировать микроконтроллер. Особенно это касается фьюзов. Если нет уверенности в правильности своих действий, то лучше отказаться от их использования. Это же относится и к программаторам. Если покупать заводскую аппаратуру, то она будет прошивать микроконтроллеры без проблем. При сборке своими руками может возникнуть печальная ситуация, при которой программатор заблокирует устройство. Это может произойти как из-за ошибки в программном коде, так и через неполадки в нём самом. Кстати, об ещё одном (на этот раз позитивном) моменте, который ранее вскользь упоминался, но так и не был раскрыт полностью. Сейчас практически все современные микроконтроллеры обладают функцией внутрисхемного программирования. Что это значит? Допустим, что устройство было запаяно на плате. И чтобы сменить его прошивку, сейчас не нужно его выпаивать, ведь такое вмешательство может повредить сам микроконтроллер. Достаточно подключиться к соответствующим выводам и перепрограммировать его при их посредстве.

Какую модель выбрать?

В рамках статьи была рассмотрена AtMega8. Это довольно посредственный за своими характеристиками микроконтроллер, которого, тем не менее, хватает для большинства поделок. Если есть желание создать что-то масштабное, то можно брать уже своеобразных монстров вроде Atmega128. Но они рассчитаны на более опытных разработчиков. Поэтому, если нет достаточного количества опыта, то лучше начинать с небольших и простых устройств. К тому же они и значительно дешевле. Согласитесь, одно дело случайно заблокировать микроконтроллер за сто рублей, а совсем иное - за полтысячи. Лучше набить себе руку и разобраться в различных аспектах функционирования, чтобы в последующем не терять значительные суммы. Первоначально можно начать с AtMega8, а потом уже ориентироваться по своим потребностям.

Заключение

Вот и была рассмотрена тема программирования AVR в самых общих чертах. Конечно, ещё о многом можно рассказывать. Так, к примеру, не было рассмотрено маркирование микроконтроллеров. А оно может о многом сказать. Так, в основном микроконтроллеры работают на напряжении в 5В. Тогда как наличие, к примеру, буквы L может сказать о том, что для работы устройства достаточно только 2,7 В. Как видите, порой знания о маркировке могут сыграть очень важную роль в плане корректной и долговечной работы устройств. Время функционирования микроконтроллеров - это тоже интересная тема. Каждое устройство рассчитано на определённый период. Так, некоторые могут отработать тысячу часов. Другие же имеют гарантийный запас в 10 000!

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

К сожалению все хотят результат немедленно. Поэтому я решил пойти с другой стороны — сделать обучалку по Си, но с показом его нижнего белья. Хороший программист-эмбеддер всегда крепко держит свою железку за шкварник, не давая ей ни шагу ступить без разрешения. Так что будет вначале Си код, потом то что родил компилятор и как все это работает на самом деле:)

С другой стороны у Си сильная сторона это переносимость кода. Если, конечно, писать все правильно. Разделяя алгоритмы работы и их железные реализации в разные части проекта. Тогда для переноса алгоритма в другой МК достаточно будет переписать только интерфейсный слой, где прописано все обращение к железу, а весь рабочий код оставить как есть. И, конечно же, читаемость. Сишный исходник проще понять с первого взгляда (хотя.. мне, например, уже пофигу на что фтыкать — хоть си, хоть асм:)), но, опять же, если правильно все написать. Этим моментам я тоже буду уделять внимание.

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

Первая программа на Си для AVR

Выбор компилятора и установка среды
Для AVR существует множество разных компиляторов Си:
В первую очередь это IAR AVR C — почти однозначно признается лучшим компилятором для AVR, т.к. сам контроллер создавался тесном сотрудничистве Atmel и спецов из IAR. Но за все приходится платить. И этот компилятор мало того, что является дорогущим коммерческим софтом, так еще обладает такой прорвой настроек, что просто взять и скомпилить в нем это надо постраться. У меня с ним правда не срослось дружбы, проект загнивал на странных ошибках на этапе линковки (позже выяснил, что это был кривой кряк).

Вторым идет WinAVR GCC — мощный оптимизирующий компилятор. Полный опенсорц, кроссплатформенный, в общем, все радости жизни. Еще он отлично интегрируется в AVR Studio позволяя вести отладку прямо там, что адски удобно. В общем, я выбрал его.

Также есть CodeVision AVR C — очень популярный компилятор. Стал популярен в связи со своей простотой. Рабочую программу в нем получить можно уже через несколько минут — мастер стартового кода этом сильно способствует, штампуя стандартыне инициализации всяких уартов. Честно говоря, я как то с подозрением к нему отношусь — как то раз приходилось дизасмить прогу написаную этим компилером, каша какая то а не код получалась. Жуткое количество ненужных телодвижений и операций, что выливалось в неслабый обьем кода и медленное быстродействие. Впрочем, возможно тут была ошибка в ДНК писавшего исходную прошивку. Плюс он хочет денег. Не так много как IAR, но ощутимо. А в деморежиме дает писать не более чем 2кб кода.
Кряк конечно есть, но если уж воровать, так миллион, в смысле IAR:)

Еще есть Image Craft AVR C и MicroC от микроэлектроники. Ни тем ни другим пользоваться не приходилось, но вот SWG очень уж нахваливает MicroPascal , мол жутко удобная среда программирования и библиотеки. Думаю MicroC не хуже будет, но тоже платный.

Как я уже сказал, я выбра WinAVR по трем причинам: халявный, интегрируется в AVR Studio и под него написана просто прорва готового кода на все случаи жизни.

Так что качай себе инсталяху WinAVR с и AVR Studio. Далее вначале ставится студия, потом, сверху, накатывается WinAVR и цепляется к студии в виде плагина. Настоятельно рекомендую ставить WinAVR по короткому пути, что то вроде C:\WinAVR тем самым ты избежишь кучи проблем с путями.

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

Открывается рабочее поле с пустым *.c файлом.

Теперь не помешает настроить отображение путей в закладках студии. Для этого слазь по адресу:
Меню Tools — Options — General — FileTabs и выбираем в выпадающем списке «Filename Only». Иначе работать будет невозможно — на вкладке будет полный путь файла и на экране будет не более двух трех вкладок.

Настройка проекта
Вообще, классическим считается создание make файла в котором бы были описаны все зависимости. И это, наверное, правильно. Но мне, выросшему на полностью интегрированных IDE вроде uVision или AVR Studio этот подход является глубоко чуждым. Поэтому буду делать по своему, все средствами студии.

Тыкай в кнопку с шестеренкой.


Это настройки твоего проекта, а точнее настройки автоматической генерации make файла. На первой странице надо всего лишь вписать частоту на которой будет работать твой МК. Это зависит от фьюз битов, так что считаем что частота у нас 8000000Гц.
Также обрати внимание на строку оптимизации. Сейчас там стоит -Os это оптимизация по размеру. Пока оставь как есть, потом можешь попробовать поиграться с этим параметром. -O0 это отстутсвие оптимизации вообще.

Следующим шагом будет настройка путей. Первым делом добавь туда директорию твоего проекта — будешь туда подкладывать сторонние библиотеки. В списке появится путь «.\»

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


На этом пока все. Жми везде ОК и переходи в исходник.

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

Работать будет так:
При приходе по COM порту единички (код 0х31) будем зажигать диодик, а при приходе нуля (код 0х30) гасить. Причем сделано будет все на прерываниях, а фоновой задачей будет мигание другого диода. Простенько и со смыслом.

Собираем схему
Нам надо соединить модуль USB-USART конвертера с выводами USART микроконтроллера. Для этого берем перемычку из двух проводков и накидывам на штырьки крест накрест. То есть Rx контроллера соединяем с Tx конвертера, а Tx конвертера с Rx контроллера.

Получится, в итоге вот такая схема:


Подключение остальных выводов, питания, сброса не рассматриваю, оно стандартное

Пишем код

Сразу оговорюсь, что я не буду углубляться конкретно в описание самого языка Си. Для этого существует просто колоссальное количество материала, начиная от классики «Язык программирования Си» от K&R и заканчивая разными методичками.

Одна такая метода нашлась у меня в загашнике, я когда то именно по ней изучал этот язык. Там все кратко, понятно и по делу. Я ее постепенно верстаю и перестаскиваю на свой сайт.

Там правда еще не все главы перенесены, но, думаю, это ненадолго.

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

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

1 #include

#include

Этот файл находится в папке WinAVR и в нем содержится описание всех регистров и портов контроллера. Причем там все хитро, с привязкой к конкретному контроллеру, который передается компилятором через make файл в параметре MCU и на основании этой переменной в твой проект подключается заголовочный файл с описанием адресов всех портов и регистров именно на этот контроллер. Во как! Без него тоже можно, но тогда ты не сможешь использовать символические имена регистров вроде SREG или UDR и придется помнить адрес каждого вроде «0xC1», а это голову сломать.

Сама же команда #include <имя файла> позволяет добавить в твой проект содержимое любого текстового файла, например, файл с описанием функций или кусок другого кода. А чтобы директива могла этот файл найти мы и указывали пути к нашему проекту (директория WinAVR там уже по дефолту прописана).

Главная функция.
Программа на языке Си вся состоит из функций. Они могут быть вложенными и вызываться друг из друга в любом порядке и разными способами. Каждая функция имеет три обязательных параметра:

  • Возвращаемое значение, например, sin(x) возвращает значение синуса икс. Как в математике, короче.
  • Передаваемые параметры, тот самый икс.
  • Тело функции.

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

Любая программа на Си должна содержать функцию main как точку входа в главную прогрмму, иначе это нифига не Си:). По наличию main в чужом исходнике из миллиона файлов можно понять, что это и есть головная часть программы откуда начинается все. Вот и зададим:

1 2 3 4 5 int main(void ) { return 0 ; }

int main(void) { return 0; }

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

Разберем что же мы сделали.
int это тип данных которая функция main возвращает.

Конечно, в микроконтроллере main ничего вернуть в принципе не может и по идее должна быть void main(void) , но GCC изначально заточен на PC и там программа может вернуть значение операционной системе по завершении. Поэтому GCC на void main(void) ругается Warning’ом.

Это не ошибка, работать будет, но я не люблю варнинги.

void это тип данных которые мы передаем в функцию, в данном случае main также не может ничего принять извне, поэтом void — пустышка. Заглушка, применяется тогда когда не надо ничего передавать или возвращать.

Вот такие вот { } фигурные скобочки это программный блок, в данном случае тело функции main , там будет распологаться код.

return — это возвращаемое значение, которое функция main отдаст при завершении, поскольку у нас int, то есть число то вернуть мы должны число. Хотя это все равно не имеет смысла, т.к. на микроконтроллере из main нам выходить разве что в никуда. Я возвращаю нуль. Ибо нефиг. А компилятор обычно умный и на этот случай код не генерит.
Хотя, если извратиться, то из main на МК выйти можно — например вывалиться в секцию бутлоадера и исполнить ее, но тут уже потребуется низкоуровневое ковыряние прошивки, чтобы подправить адреса перехода. Ниже ты сам увидишь и поймешь как это сделать. Зачем? Вот это уже другой вопрос, в 99.999% случаев это нафиг не надо:)

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

1 2 3 4 5 6 int main(void ) { unsigned char i; return 0 ; }

int main(void) { unsigned char i; return 0; }

unsigned значит беззнаковый. Дело в том, что в двоичном представлении у нас старший бит отводится под знак, а значит в один байт (char) влазит число +127/-128, но если знак отбросить то влезет уже от 0 до 255. Обычно знак не нужен. Так что unsigned .
i — это всего лишь имя переменной. Не более того.

Теперь надо проинициализировать порты и UART . Конечно, можно взять и подключить библиотеку и вызвать какой нибудь UartInit(9600); но тогда ты не узнаешь что же произошло на самом деле.

Делаем так:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main(void ) { unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1) #define HI(x) ((x)>>8) #define LO(x) ((x)& 0xFF) UBRRL = LO(bauddivider) ; UBRRH = HI(bauddivider) ; UCSRA = 0 ; UCSRB = 1 << RXEN| 1 << TXEN| 1 << RXCIE| 0 << TXCIE; UCSRC = 1 << URSEL| 1 << UCSZ0| 1 << UCSZ1; }

int main(void) { unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1) #define HI(x) ((x)>>8) #define LO(x) ((x)& 0xFF) UBRRL = LO(bauddivider); UBRRH = HI(bauddivider); UCSRA = 0; UCSRB = 1<

Страшна? На самом деле реалного кода тут всего пять последних строк. Все что #define это макроязык препроцессора. Почти та же ботва, что и в Ассемблере, но синтаксис несколько иной.

Они облегчат твои рутинные операции по вычислении нужных коэффициентов. В первой строке мы говорим что вместо XTAL можно смело подставлять 8000000, а L — указание типа, мол long — это тактовая частота процессора. То же самое baudrate — частота передачи данных по UART.

bauddivider уже сложней, вместо него будет подставлятся выражение вычисленное по формуле из двух предыдущих.
Ну, а LO и HI из этого результата возьмут младший и старший байты, т.к. в один байт оно явно может не влезть. В HI делается сдвиг икса (входной параметр макроса) восемь раз в вправо, в результате от него останется только старший байт. А в LO мы делаем побитовое И с числом 00FF, в результате останется только младший байт.

Так что все что сделано как #define можно смело выкинуть, а нужные числа подсчитать на калькуляторе и сразу же вписать их в строки UBBRL = …. и UBBRH = …..

Можно. Но! Делать этого КАТЕГОРИЧЕСКИ НЕЛЬЗЯ !

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

Дальше все просто:
Все эти «UBRRL и Со» это регистры конфигурации UART передатчика с помощью которого мы будем общаться с миром. И сейчас мы присвоили им нужные значения, настроив на нужную скорость и нужный режим.

Запись вида 1< Означает следующее: взять 1 и поставить ее на место RXEN в байте. RXEN это 4й бит регистра UCSRB , так что 1< образует двоичное число 00010000, TXEN — это 3й бит, а 1< даст 00001000. Одиночная «|» это побитовое ИЛИ , так что 00010000 | 00001000 = 00011000. Таким же образом выставляются и добавляются в общуюу кучу остальные необходимые биты конфигурации. В итоге, собраное число записывается в UCSRB. Подробней расписано в даташите на МК в разделе USART. Так что не отвлекаемся на технические детали.

Готово, пора бы посмотреть что получилось. Жми на компиляцию и запуск эмуляции (Ctrl+F7).

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

Дело в том, что изначально, на самом деле, она стояла на строке UBRRL = LO(bauddivider); Ведь то что у нас в define это не код, а просто предварительные вычисления, вот симулятор немного и затупил. Но теперь он осознал, первая инструкция выполнена и если ты залезешь в дерево I/O View , в раздел USART и поглядишь там на байт UBBRL то увидишь, что там значение то уже есть! 0х33.

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

Вскрытие
Теперь сбрось симуляцию в ноль. Нажми там Reset (Shift+F5) . Открывай дизассемблированный листинг, сейчас ты увидишь что происходит в контроллере в самом деле. View -> Disassembler . И не ЫЫАААА!!! Ассемблер!!! УЖОС!!! А НАДО. Чтобы потом, когда что то пойдет не так, не тупил в код и не задавал ламерских вопросах на форумах, а сразу же лез в потроха и смотрел где у тебя затык. Ничего там страшного нет.

Вначале будет ботва из серии:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 +00000000: 940C002A JMP 0x0000002A Jump +00000002: 940C0034 JMP 0x00000034 Jump +00000004: 940C0034 JMP 0x00000034 Jump +00000006: 940C0034 JMP 0x00000034 Jump +00000008: 940C0034 JMP 0x00000034 Jump +0000000A: 940C0034 JMP 0x00000034 Jump +0000000C: 940C0034 JMP 0x00000034 Jump +0000000E: 940C0034 JMP 0x00000034 Jump +00000010: 940C0034 JMP 0x00000034 Jump +00000012: 940C0034 JMP 0x00000034 Jump +00000014: 940C0034 JMP 0x00000034 Jump +00000016: 940C0034 JMP 0x00000034 Jump +00000018: 940C0034 JMP 0x00000034 Jump +0000001A: 940C0034 JMP 0x00000034 Jump +0000001C: 940C0034 JMP 0x00000034 Jump +0000001E: 940C0034 JMP 0x00000034 Jump +00000020: 940C0034 JMP 0x00000034 Jump +00000022: 940C0034 JMP 0x00000034 Jump +00000024: 940C0034 JMP 0x00000034 Jump +00000026: 940C0034 JMP 0x00000034 Jump +00000028: 940C0034 JMP 0x00000034 Jump

00000000: 940C002A JMP 0x0000002A Jump +00000002: 940C0034 JMP 0x00000034 Jump +00000004: 940C0034 JMP 0x00000034 Jump +00000006: 940C0034 JMP 0x00000034 Jump +00000008: 940C0034 JMP 0x00000034 Jump +0000000A: 940C0034 JMP 0x00000034 Jump +0000000C: 940C0034 JMP 0x00000034 Jump +0000000E: 940C0034 JMP 0x00000034 Jump +00000010: 940C0034 JMP 0x00000034 Jump +00000012: 940C0034 JMP 0x00000034 Jump +00000014: 940C0034 JMP 0x00000034 Jump +00000016: 940C0034 JMP 0x00000034 Jump +00000018: 940C0034 JMP 0x00000034 Jump +0000001A: 940C0034 JMP 0x00000034 Jump +0000001C: 940C0034 JMP 0x00000034 Jump +0000001E: 940C0034 JMP 0x00000034 Jump +00000020: 940C0034 JMP 0x00000034 Jump +00000022: 940C0034 JMP 0x00000034 Jump +00000024: 940C0034 JMP 0x00000034 Jump +00000026: 940C0034 JMP 0x00000034 Jump +00000028: 940C0034 JMP 0x00000034 Jump

Это таблица векторов прерываний. К ней мы еще вернемся, пока же просто посмотри и запомни, что она есть. Первая колонка — адрес ячейки флеша в которой лежит команда, вторая код команды третья мнемоника команды, та самая ассемблерная инструкция, третья операнды команды. Ну и автоматический коммент.
Так вот, если ты посмотришь, то тут сплошные переходы. А код команды JMP четырех байтный, в нем содержится адрес перехода, записанный задом наперед — младший байт по младшему адресу и код команды перехода 940C

0000002B: BE1F OUT 0x3F,R1 Out to I/O location

Запись этого нуля по адресу 0x3F, Если ты поглядишь в колонку I/O view, то ты увидишь что адрес 0x3F это адрес регистра SREG — флагового регистра контроллера. Т.е. мы обнуляем SREG, чтобы запустить программу на нулевых условиях.

1 2 3 4 +0000002C: E5CF LDI R28,0x5F Load immediate +0000002D: E0D4 LDI R29,0x04 Load immediate +0000002E: BFDE OUT 0x3E,R29 Out to I/O location +0000002F: BFCD OUT 0x3D,R28 Out to I/O location

0000002C: E5CF LDI R28,0x5F Load immediate +0000002D: E0D4 LDI R29,0x04 Load immediate +0000002E: BFDE OUT 0x3E,R29 Out to I/O location +0000002F: BFCD OUT 0x3D,R28 Out to I/O location

Это загрузка указателя стека. Напрямую грузить в I/O регистры нельзя, только через промежуточный регистр. Поэтому сначала LDI в промежуточный, а потом оттуда OUT в I/O. О стеке я тоже еще расскажу подробней. Пока же знай, что это такая динамическая область памяти, висит в конце ОЗУ и хранит в себе адреса и промежуточные переменные. Вот сейчас мы указали на то, откуда у нас будет начинаться стек.

00000032: 940C0041 JMP 0x00000041 Jump

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

1 2 +00000041: 94F8 CLI Global Interrupt Disable +00000042: CFFF RJMP PC-0x0000 Relative jump

00000041: 94F8 CLI Global Interrupt Disable +00000042: CFFF RJMP PC-0x0000 Relative jump

Это на случай непредвиденых обстоятельств, например выхода из функции main. Из такого зацикливания контроллер можно вывести либо аппаратным сбросом, либо, что вероятней, сбросом от сторожевой собаки — watchdog. Ну или, как я говорил выше, подправить это мест в хекс редакторе и ускакать куда нам душе угодно. Также обрати внимание на то, что бывает два типа переходов JMP и RJMP первый это прямой переход по адресу. Он занимает четыре байта и может сделать прямой переход по всей области памяти. Второй тип перехода — RJMP — относительный. Его команда занимает два байта, но переход он делает от текущего положения (адреса) на 1024 шага вперед или назад. И в его параметрах указывается смещение от текущей точки. Используется чаще, т.к. занимает в два раза меньше места во флеше, а длинные прееходы нужны редко.

1 +00000034: 940C0000 JMP 0x00000000 Jump

00000034: 940C0000 JMP 0x00000000 Jump

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

Функция main. Все аналогично, даже можно и не описывать. Посмотри только что в регистры заносится уже вычисленное число. Препроцессор компилятора рулит!!! Так что никаких «магических» чисел!

1 2 3 4 5 6 7 8 9 10 11 12 <

00000036: E383 LDI R24,0x33 Load immediate +00000037: B989 OUT 0x09,R24 Out to I/O location 15: UBRRH = HI(bauddivider); +00000038: BC10 OUT 0x20,R1 Out to I/O location 16: UCSRA = 0; +00000039: B81B OUT 0x0B,R1 Out to I/O location 17: UCSRB = 1<

А вот тут косяк:

1 2 3 +0000003E: E080 LDI R24,0x00 Load immediate +0000003F: E090 LDI R25,0x00 Load immediate +00000040: 9508 RET Subroutine return

0000003E: E080 LDI R24,0x00 Load immediate +0000003F: E090 LDI R25,0x00 Load immediate +00000040: 9508 RET Subroutine return

Спрашивается, для чего это компилятор добавляет такую ботву? А это не что иное, как Return 0, функцию то мы определили как int main(void) вот и просрали еще целых четыре байта не пойми на что:) А если сделать void main(void) то останется только RET, но появится варнинг, что мол у нас функция main ничего не возвращает. В общем, поступай как хошь:)

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

Продолжение следует через пару дней …

Offtop:
Alexei78 сварганил плагинчик для файрфокса облегчающий навигацию по моему сайту и форуму.
Обсуждение и скачивание,