Антивирус

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

Министерство образования и науки Российской Федерации

Государственное образовательное учреждение

высшего профессионального образования

«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ

МОРСКОЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ»

Е. В. Коротицкий, Ю. Е. Коротицкая

Основы языка си для микроконтроллеров avr

Учебное пособие

Санкт-Петербург

1. Основы языка Си для микроконтроллеров avr

Универсальный язык С был разработан как инструмент для написания операционной среды UNIX.

Язык С поддерживает процедурно-ориентированную парадигму программирования, т.е. парадигма – взаимосвязанный набор процедур.

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

    1. Препроцессор языка Cи его команды

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

Препроцессор компилятора CodeVisionAVRимеет несколько директив. В Табл. 1 даётся их краткое описание.

Табл. 1 –Директивы препроцессора компилятора CodeVisionAVR

Директива

Назначение

Используется для включения в программу

другого файла

Используется для замены одних лексических единиц языка Си на другие, а также для генерации макросов

Используется для отмены действия директивы #define

Используются для условной компиляции

Используется для изменения встроенных макросов _LINE_и_FILE_

Позволяет остановить компиляцию и отобразить сообщение об ошибках

Используются для включения в исходную программу ассемблерного кода

Разрешает специальные директивы компилятора

ВСЕ директивы препроцессора начинаются со знака #. После директив препроцессора точка с запятой НЕ СТАВИТСЯ.

      1. Директива #include

Пример:

Директива # include

#include "имя_файла" и #include <имя_файла>

Имя_файла состоит из имени файла.

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

#include "имя_файла.h"

      1. Директивы #define, #undef

Директива # define служит для замены часто использующихся одних лексических единиц языка Си (констант, ключевых слов, операторов или выражений) на другие, так называемыеидентификаторы. Идентификаторы, заменяющие текстовые или числовые константы, называютименованными константами. Идентификаторы, заменяющие фрагменты программ, называютмакроопределениями, причём макроопределения могут иметь аргументы.

Директива # define имеет две синтаксические формы:

#define идентификатор текст

#define идентификатор (список параметров) текст

Перед компиляцией программы препроцессор в соответствии с директивой # define заменит все идентификаторы, встречающиеся в программе, на соответствующий им текст.

Пример:

#define А 15 #define В (А+20) // Эти директивы заменят в тексте программы

каждый идентификатор А на число 15, а каждый идентификатор В на выражение (15+20) вместе с окружающими его скобками.

Пример:

#define X(a,b,c) ((а)*(b)-(с)) // Препроцессор в соответствии с этой директивой заменит фрагмент Y=X(k+m,k-m,n); на фрагмент

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

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

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

Как говорил ранее, в качестве подопытного будем использовать МК Atmega8 . Почему именно его?

Данный микроконтроллер может похвастаться наличием 3 портов ввода/вывода. Кроме этого он довольно дешевый.

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

У Atmega8 3 порта. Порт B состоит из 8 ножек-выводов (нумерация 0,1,2,3,4,5,6,7). Порт С состоит из 7 ножек-выводов (нумерация 0,1,2,3,4,5,6). Порт D состоит из 8 ножек-выводов (нумерация 0,1,2,3,4,5,6,7).

Запитывать микроконтроллер можно от 3,3 и 5 В. При напряжении питания 5 В максимальная частота тактирования составляет 16 МГц, а при напряжении питания 3,3 В – максимальная частота тактирования 8 МГц. Пока не будем заморачиваться относительно частот тактирования.

Питания подаётся на 7 ножку-вывод, а «земля» подводится к 8 ножке.

Скачивается бесплатно. Скачали, установили, запустили 🙂

Первое, с чего следует начать знакомство с Atmel Studio – это создание проекта.

Выбираем File -> new -> project .

Откроется окно выбора. Выбираем папку «Browse», в которой будем сохранять написанные проекты. Папку для проектов создал заранее.

Присваиваем имя проекту, в моём случае lesson_avr_1

Обратите внимание на галочку «create directory for solution». Если отметка стоит, то в той папке, которую мы выбрали для сохранения проектов, будет создана отдельная папка под текущий проект.

На этом всё – проект создан.

Займемся настройкой созданного нами проекта. Нажимаем Projest -> lesson_avr_1 properties или (alt+F7)

Переходим на вкладку Tool. Выбираем – симулятор. Совершенные нами действия сделают возможным отлаживать написанный код. Сохраняем изменения. Можно сохранить изменения в одном (текущем) файле или же во всех файлах проекта сразу. Закрываем настройки.

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

Более подробную информацию можно посмотреть на английском языке в CodeVision User Manual, а также рекомендую сайт http://somecode.ru с видеоуроками по Си для микроконтроллеров и книгу «Как программировать на С» автор Дейтель, это единственная годная книга, с которой я сам начинал.

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

Сама прошивка имеет расширение.hex и представляет собой набор инструкций, в виде единиц и нулей, который понятен микроконтроллеру. Откуда взять прошивку? Ее можно скачать с сайтов по электронике, либо написать самому. Написать ее можно в специальных программах, которые называются средой разработки. Из наиболее известных мне AVR Studio, IAR, CodeVision, WinAVR… Нельзя сказать, что какая из этих сред лучше или хуже, каждому свое. Можно сказать, что различаются эти программы в основном удобством, языком программирования и ценой. В пределах данного сайта, рассматривается только CodeVision.

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

Файл с исходным кодом является набором команд на языке программирования, задача CodeVision`а перевести эти команды в двоичный код, ваша задача написать этот исходный код. CodeVision понимает язык Си, файлы с исходным кодом имеют расширение «.c». Но у CodeVision есть некоторые конструкции, которые не используются в Си, за это его многие программисты не любят, а используемый язык называют Си подобным. Однако, это не мешает писать серьезные проекты. Множество примеров, генератор кода, большой набор библиотек дает большой плюс CodeVision`у. Единственный минус, то что он платный, хотя есть бесплатные версии с ограничением кода.

Исходный код должен содержать заголовок с используемым типом микроконтроллера и функцию main. Например, используется ATtiny13

#include void main(void ) { } ;

#include void main(void) { };

До функции main можно подключить необходимые библиотеки, объявить глобальные переменные, константы, настройки. Библиотека это отдельный файл, обычно с расширением «.h», в котором уже есть заранее написанный код. В одних проектах этот код может быть нам нужен, а в других не нужен. Например, в одном проекте мы используем жк дислей, а в другом не используем. Подключить библиотеку для работы с жк дисплеем «alcd.h», можно так:

#include #include void main(void ) { } ;

#include #include void main(void) { };

Переменные это участки памяти, в которые можно поместить некие значения. Например, сложили два числа, результат нужно, где то сохранить, чтобы использовать в дальнейшем. Сначала необходимо объявить переменную, т.е. выделить под нее память, например:
int i=0;
т.е. мы объявили переменную i и поместили в нее значение 0, int – тип переменной, или проще говоря, означает размер выделенной памяти. Каждый тип переменных может хранить только определенный диапазон значений. Например, int можно записать числа от -32768 до 32767. Если нужно использовать числа с дробной частью значит, переменную нужно объявлять как float, для символов используют тип char.

bit, _Bit 0 или 1 char от -128 до 127 unsigned char от 0 до 255 int от -32768 до 32767 unsigned int от 0 до 65535 long int от -2147483648 до 2147483647 unsigned long int от 0 до 4294967295 float от ±1.175e-38 до ±3.402e38

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

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

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

В любой части исходного кода можно написать комментарий, на работу программы он влиять никак не будет, но будет помогать сделать пометки к написанному коду. Закомментировать строку можно двумя слешами //после этого компилятор будет игнорировать всю строку, либо несколько строк /**/, например:

/*Основные математические операции:*/ int i= 0 ; //объявляем переменную i и присваиваем ей значение 0 //Сложение: i = 2 + 2 ; //Вычитание: i = 2 - 2 ; //после выполнения данного выражения переменная i будет равна 0 //Умножение: i = 2 * 2 ; //после выполнения данного выражения переменная i будет равна 4 //Деление: i = 2 / 2 ; //после выполнения данного выражения переменная i будет равна 1

/*Основные математические операции:*/ int i=0; //объявляем переменную i и присваиваем ей значение 0 //Сложение: i = 2+2; //после выполнения данного выражения переменная i будет равна 4 //Вычитание: i = 2-2; //после выполнения данного выражения переменная i будет равна 0 //Умножение: i = 2*2; //после выполнения данного выражения переменная i будет равна 4 //Деление: i = 2/2; //после выполнения данного выражения переменная i будет равна 1

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

if(i>3) //если i больше 3, то присвоить i значение 0 { i=0; } /*если i меньше 3, то перейти к коду следующему после тела условия, т.е. после скобок {}*/

Также if можно использовать совместно с else – иначе

if(i<3) //если i меньше 3, то присвоить i значение 0 { i=0; } else { i=5; //иначе, т.е. если i больше 3, присвоить значение 5 }

Также имеется операция сравнения «==» ее нельзя путать с «=» присвоить. Обратная операция не равно «!=», допустим

if(i==3)//если i равно 3, присвоить i значение 0 { i=0; } if(i!=5) //если i не равно 5, присвоить i значение 0 { i=0; }

Перейдем к более сложным вещам – функциям. Допустим, у вас есть некий кусок кода, который повторяется несколько раз. Причем этот код довольно большой в размерах. Писать его каждый раз неудобно. Например, в программе, каким то образом изменяется переменная i, при нажатии на кнопку 0 и 3 порта D выполняется одинаковый код, который в зависимости от величины переменной i включает ножки порта B.

void main(void ) { if (PIND.0== 0 ) //проверяем нажата ли кнопка на PD0 { if (i== 0 ) //если i==0 включить PB0 { PORTB.0= 1 ; } if (i== 5 ) // если i==5 включить PB1 { PORTB.1= 1 ; } } … if (PIND.3== 0 ) // выполняем тоже самое, при проверке кнопки PD3 { if (i== 0 ) { PORTB.0= 1 ; } if (i== 5 ) { PORTB.1= 1 ; } } }

void main(void) { if(PIND.0==0) //проверяем нажата ли кнопка на PD0 { if(i==0) //если i==0 включить PB0 { PORTB.0=1; } if(i==5) // если i==5 включить PB1 { PORTB.1=1; } } … if(PIND.3==0)// выполняем тоже самое, при проверке кнопки PD3 { if(i==0) { PORTB.0=1; } if(i==5) { PORTB.1=1; } } }

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

void i_check() { if (i== 0 ) { PORTB.0= 1 ; } if (i== 5 ) { PORTB.1= 1 ; } }

void i_check() { if(i==0) { PORTB.0=1; } if(i==5) { PORTB.1=1; } }

void означает что функция ничего не возвращает, об этом чуть ниже i_check() – это название нашей функции, можете назвать как угодно, я назвал именно так – проверка i. Теперь мы можем переписать наш код:

void i_check() { if(i==0) { PORTB.0=1; } if(i==5) { PORTB.1=1; } } void main(void) { if(PIND.0==0) //проверяем нажата ли кнопка на PD0 { i_check(); } … if(PIND.3==0) { i_check(); } }

Когда код будет доходить до строчки i_check(); то он перепрыгнет внутрь функции и выполнит код внутри. Согласитесь, код компактнее и нагляднее, т.е. функции помогают заменить одинаковый код, всего на одну строчку. Обратите внимание, что функция объявляется вне основного кода, т.е. до функции main. Можно сказать, да зачем мне это надо, но изучая уроки вам часто будут попадаться функции, например очистка жк экрана lcd_clear() — функция не принимает никаких параметров и ничего не возвращает, однако очищает экран. Иногда эта функция используется чуть ли не через строчку, так что экономия кода очевидна.

Намного интереснее выглядит применение функции, когда она принимает значения, например, есть переменная c и есть функция sum, которая принимает два значения типа int. Когда основная программа будет выполнять эту функцию, то в скобках уже указаны аргументы, таким образом «a» станет равной двум, а «b» станет равной 1. Функция выполнится и «с» станет равна 3.

int c= 0 ; void sum(int a, int b) { c= a+ b; } void main(void ) { sum(2 , 1 ) ; }

int c=0; void sum(int a, int b) { c=a+b; } void main(void) { sum(2,1); }

Одна из часто встречаемых подобных функций это перевод курсора у жк дисплея lcd_gotoxy(0,0); которая, кстати, тоже принимает аргументы – координаты по х и у.

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

int c= 0 ; int sum(int a, int b) { return a+ b; } void main(void ) { с= sum(2 , 1 ) ; }

int c=0; int sum(int a, int b) { return a+b; } void main(void) { с=sum(2,1); }

Результат будет такой же как и в прошлый раз c=3, однако обратите внимание, мы переменной «с» присваиваем значение функции, которая уже не void, а возвращает сумму двух чисел типа int. Таким образом мы не привязываемся к конкретной переменной «с», что добавляет гибкости в использовании функций. Простой пример подобной функции — чтение данных АЦП, функция возвращает измеренное значение result=read_adc();. На этом закончим с функциями.

Теперь перейдем к массивам. Массив это связанные переменные. Например, у вас есть таблица синуса с несколькими точками, не будете же вы создавать переменные int sinus1=0; int sinus2=1; и т.д. для этого используют массив. Например, создать массив из трех элементов можно так:
int sinus={0,1,5};
в квадратных скобках указывается общее количество элементов массива. Присвоить переменной «с» значение третьего элемента можно таким образом:
с=sinus;
Обратите внимание, нумерация элементов массива начинается с нуля, т.е. «с» станет равна пяти. У данного массива элемента sinus не существует!!!
Отдельному элементу можно присвоить значение так:
sinus=10;

Возможно, вы уже успели заметить, что в CodeVision нет строковых переменных. Т.е. нельзя создать переменную string hello=”привет”; для этого придется создавать массив из отдельных символов.

lcd_putchar(hello); lcd_putchar(hello); lcd_putchar(hello);

и т.д.
Получается довольно громоздко, тут на помощь приходят циклы.
Например цикл while

while(PINB.0!=0) { }

Пока кнопка не нажата ничего не делать – гонять пустой цикл.

Еще один вариант цикл for

int i; for (i= 0 ; i< 6 ; i++ ) { lcd_putchar(hello[ i] ) ; }

int i; for(i=0;i<6;i++) { lcd_putchar(hello[i]); }

Смысл точно такой же, как и у while только добавлено начальное условие i=0 и условие, выполняемое каждый цикл i++. Код внутри цикла максимально упрощен.

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

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

Киселев Роман, Май 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 очень здорово поможет. К сожалению их очень мало. Если Вам в руки попалась книга, и вы считаете что в ней очень доступно все расписано – попробуйте. Не советую учиться по электронным книгам, в крайнем случае, распечатайте. Очень неудобно переключаться между средой и текстом файла книги. Гораздо приятнее читая книгу тут же пробовать, не отвлекаясь на переключения, кроме того, на полях можно делать пометки, записывать возникшие идеи.

Среда программирования попроще. Если есть на выбор несколько сред программирования Вашего языка – не сомневайтесь, выбирайте ту, что проще. Пусть она менее функциональна. Пусть она компилирует страшно раздутый код. Главное чтобы было просто начать работать. После того как Вы освоитесь в простой среде вы с легкостью перейдете на более продвинутую и «правильную» среду. И не слушайте тех, кто говорит, что вы потеряете больше времени – они не правы. Ученикам младших классов не задают читать «Войну и мир» им дают книги попроще – с картинками.

Библиотеки. Наличие библиотек спорно для изучения языка. Конечно, позже они очень облегчат жизнь, но поначалу «Черные ящики»-библиотеки непонятны и не очень способствуют пониманию языка. С другой стороны облегчают чтение программы и позволяют новичку, не особо напрягаясь, строить сложные программы. Так что, их наличием особо не заморачивайтесь. По крайней мере, по началу.

Эффективный код. Выбор среды программирования для изучения программирования только по тому, насколько эффективный код та компилит – плохая идея. Вам главное комфортно начать изучение – что там получается «на выходе» дело десятое. Конечно, позже можно над этим и поработать.

Визарды. Любое устройство на борту кристалла нуждается в настройке при помощи портов. Процедура довольно муторная и даташиты обязательны. Кроме того, есть нюансы, в которые новичку не просто вкурить. Поэтому в среде очень желательно наличие визардов. Вызарды это автоматические настройщики SPI, I2C, USART и т.д. Чем больше устройств поддерживается, тем лучше. Выставляешь необходимые параметры периферии, а визард сам генерирует код, который обеспечит заданные параметры. Очень упрощает жизнь.


Общие рекомендации такие – программирование на начальном этапе должно быть максимально простым (пусть даже примитивным). Среда программирования должна быть легка в освоении (так как Вам надо, для начала, освоить программирование а не тратить время на ковыряние в настройках). Желательно русифицирована. Также не помешает русский мануал и примеры программ. Желательна возможность прошивки кристалла из среды. Далее при освоении основ программирования можно переходить и на более сложные оболочки.


Еще одна рекомендация, напоследок – работайте с реальным кристаллом. Не бойтесь его спалить. Нарабатывайте практический опыт. Работа с эмуляторами (например Proteus) хоть и освободит от возни с паяльником, но никогда не сможет дать то удовлетворение которое Вы получите от заработавшей программы, первых помигиваний светодиодом! Понимание того, что вы сделали своими руками реальную рабочую схему вселяет уверенность и стимул двигаться дальше!

(Visited 7 377 times, 1 visits today)