Программирование avr микроконтроллеров. На чем начать программировать AVR? Рекомендации. Интерфейс МК в режиме программирования

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

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

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

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

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

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

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

Чем отличается встроенное программирование?

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

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

Первая программа на Си для 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 и вписывай название проекта.

В качестве примера устройство может иметь полный набор инструкций, которые могут работать с 8-битными данными, а также несколько инструкций, которые работают с 16-битными данными. Это устройство должно считаться 8 - разрядный дизайн, даже если отдел маркетинга говорит об ином и называет его 16-разрядным чипом. По объему 8-разрядные микроконтроллеры являются крупнейшим сегментом встроенного рынка. Многие приложения просто не нуждаются в большей мощности, и никогда не будут. 16-разрядные устройства более мощные, но они сжаты между 8-разрядными устройствами на нижнем и 32-разрядном устройствах на высоком конце. 32-разрядные устройства находятся на верхнем уровне встроенного спектра для всех, кроме самых сложных или высокопроизводительных проектов, но они движутся все вниз по цене.

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

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

Какие семейства микроконтроллеров используются в этих учебниках?

Чтобы дать немного обзора различных вариантов доступных микроконтроллеров, этот учебник будет написан вокруг одного 8-битного семейства и одного 32-битного семейства. Эти две семьи были выбраны, чтобы дать достаточно широкую картину устройств и подходов, найденных в мире микроконтроллеров.

Что еще требуется для этих учебных пособий?

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

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

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

Как работает встроенная программа?

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



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

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

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

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

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



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

Выбор языка программирования и среды разработки для программирования

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

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

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

Какие микроконтроллеры существуют?

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

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

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



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

Примечание о примерах программ

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

Тем не менее, 4-битный режим также достаточно быстр и точен, чтобы удовлетворить большую часть нашей потребности, плюс для этого требуется всего 7 контактов. Следовательно, мы будем работать в 4-битном режиме. Для этого вы можете обратиться к следующей схеме. Отнесите его к конфигурации штифта, приведенной выше.

Пишем код

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

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

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

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

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

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

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

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

1 #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 возвращает.

Конечно, в микроконтроллере 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 ; }

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; }

Страшна? На самом деле реалного кода тут всего пять последних строк. Все что #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

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

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

Вот тут, собственно, идет переход к функции main. А через три команды как раз начинается Main. И переход идет через CALL, с сохранением адреса в стеке. При этом бездарно просерается два байта ОЗУ, а их всего 1024. =) Низачот! Впрочем, что то мне подсказывает, что обьявление main как inline int main(void) решит эту проблему, но не пробовал. Можешь сам проверить.

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

1 +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

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

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

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

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

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

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

Основные параметры микроконтроллеров семейства AVR

Микроконтроллер

Память FLASH

Память ОЗУ

Память EEPROM

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

U питания

Дополнительные параметры МК AVR mega:

Рабочая температура: -55…+125*С
Температура хранения: -65…+150*С
Напряжение на выводе RESET относительно GND: max 13В
Максимальное напряжение питания: 6.0В
Максимальный ток линии ввода/вывода: 40мА
Максимальный ток по линии питания VCC и GND: 200мА

Цоколевка выводов моделей ATmega 8X


Цоколевка выводов моделей ATmega48x, 88x, 168x





Расположение выводов у моделей

Цоколевка выводов у моделей ATmega16, 32x


Расположение выводов у моделей ATtiny2313



В конце статьи, во вложении, есть даташиты на некоторые микроконтроллеры

Установочные FUSE биты MK AVR

Запомните, запрограммированный фьюз – это 0, не запрограммированный – 1. Осторожно стоит относиться к выставлению фьюзов, ошибочно запрограммированный фьюз может заблокировать микроконтроллер. Если вы не уверены какой именно фьюз нужно запрограммировать, лучше на первый раз прошейте МК без фьюзов.

Самыми популярными микроконтроллерами у радиолюбителей являются ATmega8 , затем идут ATmega48, 16, 32, ATtiny2313 и другие. Микроконтроллеры продаются в TQFP корпусах и DIP, новичкам рекомендую покупать в DIP. Если купите TQFP, будет проблематичнее их прошить, придется купить или изготовить переходник и паять плату т.к. у них ножки располагаются очень близко друг от друга. Советую микроконтроллеры в DIP корпусах, ставить на специальные панельки (сокеты), это удобно и практично, не придется выпаивать МК если приспичит перепрошить, или использовать его для другой конструкции.

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

Для программирования используется 6 выводов:

RESET - Вход МК
VCC - Плюс питания, 3-5В, зависит от МК
GND - Общий провод, минус питания.
MOSI - Вход МК (информационный сигнал в МК)
MISO - Выход МК (информационный сигнал из МК)
SCK - Вход МК (тактовый сигнал в МК)

Иногда еще используют вывода XTAL 1 и XTAL2, на эти вывода цепляется кварц, если МК будет работать от внешнего генератора, в ATmega 64 и 128 вывода MOSI и MISO не применяются для ISP программирования, вместо них вывода MOSI подключают к ножке PE0, a MISO к PE1. При соединении микроконтроллера с программатором, соединяющие провода должны быть как можно короче, а кабель идущий от программатора на порт LPT так-же не должен быть слишком длинным.

В маркировке микроконтроллера могут присутствовать непонятные буквы с цифрами, например Atmega 8L 16PU, 8 16AU, 8A PU и пр. Буква L означает, что МК работает от более низкого напряжения, чем МК без буквы L, обычно это 2.7В. Цифры после дефиса или пробела 16PU или 8AU говорят о внутренней частоте генератора, который есть в МК. Если фьюзы выставлены на работу от внешнего кварца, кварц должен быть установлен на частоту, не превышающей максимальную по даташиту, это 20МГц для ATmega48/88/168, и 16МГц для остальных атмег.

Первые цифры в названии микроконтроллера обозначают объем FLASH ПЗУ в килобайтах, например ATtiny15 – 1 Кб, ATtiny26 – 2 Кб, AT90S4414 – 4 Кб, Atmega8535 – 8 Кб, ATmega162 – 16Кб, ATmega32 – 32 Кб, ATmega6450 – 64Кб, Atmega128 – 128Кб.

Иногда встречаются схемы, где применены микроконтроллеры с названиями типа AT90S… это старые модели микроконтроллеров, некоторые из них можно заменить на современные, например:

AT90S4433 – ATmega8
AT90S8515 – ATmega8515
AT90S8535 – ATmega8535
AT90S2313 – ATtiny2313
ATmega163 – ATmega16
ATmega161 – ATmega162
ATmega323 – ATmega32
ATmega103 – ATmega64/128

ATmega 8 имеет несколько выводов питания, цифровое – VCC, GND и аналоговое – AVCC, GND. В стандартном включении обе пары выводов соединяют параллельно, т.е. вместе. Микроконтроллеры AVR не любят повышенного напряжения, если питание выше 6 вольт, то они могут выйти из строя. Я обычно применяю маломощный стабилизатор напряжения на 5 вольт, КР142ЕН5 или 78L05. Если напряжение питания слишком низкое, то МК не прошьется, программа будет ругаться и выдавать ошибки (к примеру -24 в PonyProg).

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