Первое, что хочется сделать после сборки программатора это помигать светодиодиком. В этой статье я подробно опишу, как написать такую простенькую программку и расскажу как она работает.
Для начала я познакомлю Вас со средой программирования Algorithm Builder, которая на мой взгляд очень удобна для написания программ на языке ассемблер. Скачать её можно на сайте автора. Она полностью бесплатна. Далее установите её и после того, как установили, откройте её и появится вот такое окно:
Эта программа является хорошей графической средой программирования на ассемблере. В отличии от классического ассемблера здесь пишется не сам код программы на ассемблере, а рисуется алгоритм работы этой программы, а компилятор при компиляции сам составит и скомпилирует код. Инструкций по работе с программой давать не буду – основы работы с ней описаны в родном руководстве на русском языке. Для того, чтобы открыть руководство по программе нужно нажать на знак вопроса, который находится на верху программы и нажать на пункт “manual.pdf”. В руководстве всё отлично расписано и даже есть пример вольтметра.
Теперь перейдем к нашей программке, которая будет мигать светодиодом. Для начала разберём алгоритм работы программы. Для того, чтобы помигать светодиодом нужно его подключить к какому-либо порту ввода-вывода микроконтроллера. Что же такое порты ввода-вывода? Порты ввода-вывода (далее порты в/в) это выводы микроконтроллера, состоянием которых можно управлять из программы, прошитой в мк. Они могут быть в трёх состояниях:
- Высокий уровень – логическая единица (это состояние вывода, при котором порт в/в подключён напряжению питания, то есть на этом выводе уровень напряжения, равный напряжению питания)
- Низкий уровень – логический ноль ( в этом состоянии порт в/в подключён напрямую к земле (минусу питания) и напряжение на нём равно 0 Вольт)
- Высокоомное HI-Z состояние (в этом состоянии ножка не подключена ни к чему т.е на ней не 0 и не 1)
Эти порты есть у каждого микроконтроллера AVR и почти все выводы контроллера (кроме земли и питания) могут работать в режиме порта в/в. А теперь откроем даташит на микроконтроллер, и посмотрим где же у него располагаются эти сами порты в/в. Для примера будем использовать микроконтроллер ATmega16. Итак, заходи на atmel.com, находим даташит на ATmega16 и открываем его. Далее идём на вторую страницу документа и видим там распиновку микроконтроллера в разных корпусах. Нас интересует распиновка контроллера в DIP корпусе. Вот она:
Видите выводы PD1,2,3 PC1,2,3, и т.д.? Вот это и есть порты в/в. Например, PC0 расшифровывается как Port C 0. Как видно, у данного микроконтроллера 4 порта в/в и каждый из них имеет по 8 выводов, значит всего в нашем распоряжении 32 ножки.
Теперь раздерём как эти выводы настраиваются и как с ними работать. Порты в/в можно настроить на вход или на выход. Делается это установкой соответствующих бит в регистре DDR (Data Direction Register – регистр направления порта в/в). Для каждого порта есть свой регистр DDR. Именуются они так: в начале идёт префикс DDR, а после него имя порта. Например, для порта A регистр будет называться DDRA. Также есть ещё регистры PORT и PIN, но о них позже. Регистр состоит из 8-ми бит, которые соответствуют ножке, например, бит 0 в регистре DDRA будет определять направление вывода PA0. Если записать в этот бит 1, то вывод PA1 будет портом вывода т.е. выходом, а если записать 0 – входом.
Итак, создадим уже наконец новый проект в Algorithm Builder и настроим вывод PA0 в режим выхода. Для этого, как я и говорил нужно записать в 0 бит регистра DDRA единицу. Для начала настроим наш проект. Заходим в опции проекта и указываем микроконтроллер и тактовую частоту в герцах (в нашем случае это 8000000 Гц = 8 мГц):
Нажимаем применить и начинаем рисовать алгоритм. Как я уже говорил нужно в бит 0 регистра DDRA записать 1. Делается это вот так:
Чёрточка, которая находится наверху – это вершина блока с алгоритмом (элемент Vertex). Разберём команду, которую я написал. DDRA.0 означает, что мы записываем в регистр DDRA, бит 0, а 1 -> — записываем единицу. Проще говоря, записываем единицу в 0 бит регистра DDRA.
Порт на выход мы настроили, теперь нужно записать в него единичку, тем самым зажечь светодиод. Чтобы записать единицу в порт, нужно записать единицу в регистр PORT. Именуется он также, как и DDR т.е. порту А соответствует регистр PORTA. Запишем в 0 бит порта A единичку:
Скомпилируем программу, нажав в верхнем меню Программа -> Компилировать. Если в нашем алгоритме нет ошибок, программа скомпилируется и вылезет вот такое окошко:
Как видно, программа занимает в памяти контроллера 2 слова. 1 слово – это 2 байта, значит наша программа занимает 4 байта. Теперь при помощи программатора загрузим нашу прогу в мк и подключим светодиод к порту A0 через резистор, сопротивлением 330 ом. Светодиод будет непрерывно гореть.
Далее давайте заставим светодиод не просто гореть, а мигать! Для этого нужно добавить ещё пару строк кода. Итак, продолжим. Чтобы потушить светодиод, нужно подать 0 на вывод PA0. Это делается точно также, как и запись единицы, но записываем мы 0:
Если сейчас скомпилить программу и зашить её в контроллер, то светодиод то мигать будет, но с бешеной скоростью, поэтому нам будет казаться, что он горит. Для того, чтобы снизить скорость мигания, нужно ввести какую-либо задержку между подачей 0 и 1. Проще всего реализовать её так: мы загружаем в три рабочих регистра (кстати, забыл упомянуть, что рабочие регистры – это, можно сказать, ячейки, в которые можно загрузить переменные. Это могут быть какие-нибудь числа или же буквы. В AVR есть 32 8-и разрядных регистра, значит в каждый из них можно загрузить по одному байту т.е. число от 0 до 255 или одну букву) r16, r17, r18 числа, а потом вычитаем их оттуда. Пока не появится 0, как только в регистре появится 0, переходим к разорению следующего регистра 🙂 и т.д., пока не опустошим все. Суть этой задержки в том, чтобы занять процессор бесполезной работой и потратить на это время. Для этой задержки мы создадим подпрограмму, которую можно будет вызывать между записью 1 и 0 в порт, а не прописывать каждый раз. Для этого сначала нужно инициализировать стек – ОЗУ. О стеке и о подпрограммах я расскажу позже, а сейчас напишем нашу подпрограмму:
Подпрограмма Delay – это и есть наша задержка. r16– означает, что мы вычитаем 1 из регистра 16, а стрелочки и -= означает, что если результат не равен нулю мы переходим по метке. Стрелочка указывает на метку. Это называется условный переход т.е. переход при каком-то условии. А теперь разберём основную программу. Между подачей 1 и 0 я написал Delay, это означает, что в этом месте мы подключаем нашу подпрограмму. А стрелочка – это безусловный переход, то есть переход произойдёт вне зависимости от чего-либо. Если сейчас прошить эту программу в мк, то светодиод опят-же будет лишь гореть т.к. мы ещё не инициализировали стек. Стек можно инициализировать с помощью настройщика. Для этого нажимаем на эту кнопочку:
Далее в появившемся меню выбираем “Stack Pointer (SP) и появится вот такое окно:
Ставим галочку и нажимаем ОК. Теперь в начале программы появится такой блок:
Это так называемый блок настройки периферии. Таким-же образом можно настроить и другую периферию, например, АЦП.
Всё компилируем программу и светодиод замигает! Кстати, о зашивке программы. После компиляции в папке с проектом появятся ещё 2 файла: один из них называется также, как и сам проект, а ко второму добавлено _EE. Первый файл нужно прошивать во flash память микроконтроллера, а второй не трогать вовсе.
Удачи Вам в прошивке!