Программирование микроконтроллеров avr нуля

Простая программа для AVR микроконтроллера на языке Си

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

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

Кратко о языке программирования Си

Язык Си является компилируемым статически типизированным языком программирования, который был разработан сотрудниками Bell Labs — Кеном Томпсоном (Ken Thompson) и Деннисом Ритчи (Dennis MacAlistair Ritchie) в начале 1970-х годов.

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

Изначально язык Си использовался в операционной системе Unix для написания приложений и самого ядра ОС. Позже он был портирован и на другие платформы, что принесло ему очень широкую популярность.

При разработке программ на языке Си для AVR микроконтроллеров используется набор библиотек avr-libc и компилятор avr-gcc, с установкой которых в Linux мы уже разобрались в одной из прошлых статей.

Исходный код программы на языке Си

Вполне может быть что вы никогда не писали программ на языке Си — в этом нет ничего страшного. Для того чтобы написать первую программу на AVR C и провести с ней эксперименты вполне достаточно базовых знаний по работе с консолью в Linux. Позже вы сами сможете найти недостающую информации и изучить все необходимое самостоятельно.

Читайте также:  Мобильная разработка порог входа

Приведенный ниже код программы на Си будет выполнять следующие действия (алгоритм):

  1. Зажечь светодиод 1 и погасить его с небольшой задержкой (два раза подряд);
  2. Выполнить более длительную задержку;
  3. Зажечь светодиод 2 и погасить его с небольшой задержкой (два раза подряд);
  4. Начать все сначала.

Вот исходный код программы который работает по приведенному выше алгоритму:

/* Светодиодная мигалка на микроконтроллере ATmega8 * https://ph0en1x.net */ #define F_CPU 1000000UL // укажем компилятору частоту ЦПУ #include // Подключим файл io.h #include // Подключим файл delay.h void main(void) < // начало программы // -- установим параметры -- int delay_ms_1 = 100; // задержка для светодиода int delay_ms_2 = 300; // задержка между светодиодами // -- настроим пины порта -- DDRD |= (1 > 

Рассмотрим все в исходном коде более подробно.

Строки или части строк что начинаются с двух слешей «//«, а также блоки текста что начинается с символов «/*» и заканчивается символами «*/» — это комментарии. В комментариях может размещаться полезная информация и примечания.

Строкой «#define F_CPU 1000000UL» мы объявляем константу, которая говорит компилятору что частота ЦПУ нашего микроконтроллера равна 1000000Гц (1МГц). Данное объявление необходимо для правильной работы некоторых функций, в нашей программе это функция «_delay_ms». В моем примере микроконтроллер ATmega8 без установки битов-фьюзов по умолчанию работает на внутреннем тактовом RC-генераторе с частотой 1МГц.

Строка «#include » производит подключение файла «io.h» к текущему файлу исходного кода, а строка «#include » — подключает файл «delay.h».

Узнать где размещаются данные файлы в Linux можно с помощью программы «locate». Установим ее и обновим индекс файлов для поиска:

sudo apt-get install locate sudo updatedb

В качестве примера, выполним поиск путей где размещаются файлы «io.h» и оставим только те результаты, в которых содержится сочетание символов «avr»:

В результате получим список путей ко всем файлам где в имени встречается «io.h», а также путь содержит подстроку «avr»:

/usr/lib/avr/include/stdio.h /usr/lib/avr/include/avr/io.h /usr/share/doc/avr-libc/avr-libc-user-manual/group__avr__io.html /usr/share/doc/avr-libc/avr-libc-user-manual/group__avr__stdio.html /usr/share/man/man3/io.h.3avr.gz /usr/share/man/man3/stdio.h.3avr.gz

Здесь мы можем видеть что нужный нам файл находится по пути «/usr/lib/avr/include/avr/io.h». Посмотрев его содержимое можно увидить что он содержит в себе включение других файлов с определениями (AVR device-specific IO definitions), которые в свою очередь зависят от выбранного нами типа микроконтроллера.

Тип микроконтроллера (MCU Type) в даном случае указывается как параметр «-mmcu=atmega8» (для ATmega8) при вызове команды-компилятора «avr-gcc».

В моем случае для микроконтроллера ATmega8 через файл «io.h» подключается следующий файл — «iom8.h» (Input Output Mega8), в нем хранятся все определения таких переменных как PD0, PD1, PB8, DDRD, DDRB, RAMSTART, RAMEND и много всего другого.

Файлы с определениями IO (io*.h) для каждого типа МК хранятся в директории по адресу «/usr/lib/avr/include/avr/», рекомендую зайти туда и посмотреть что в ней творится для более глубокого понимания.

Полистать содержимое файла iom8.h можно в редакторе nano, для этого выполним команду:

nano /usr/lib/avr/include/avr/iom8.h

Для поиска в редакторе nano используйте комбинацию клавиш CTRL+W (для запоминания: where, где).

Также используя команду «cat» можно вывести только те строчки, которые содержат в файле указанное сочетание символов или слово:

cat /usr/lib/avr/include/avr/iom8.h | grep RAM

Данная команда выведет вот такой текст:

#define RAMSTART (0x60) #define RAMEND 0x45F #define XRAMEND RAMEND

Таким образом можно посмотреть какие есть константы и определения в библиотеке avr-gcc для работы с операциями ввода-вывода(Input-Output), их значения и многое другое для вашего типа микроконтроллера!

Файл «delay.h» содержит в себе определения функций задержки, в частности там содержится код функции «_delay_ms», которую мы будем использовать в примере. Для просчета временной задержки такие функции используют константу «F_CPU», которую мы объявили раньше в начале кода.

Строкой «void main(void) » с левосторонней фигурной скобки начинается тело нашей программы и заканчивается оно правосторонней фигурной скобкой «>» в самом низу листинга. Таким образом мы объявили основную функцию «main» с которой начнется выполнение программы, тело функции взято в фигурные скобки, а ключевые слова «void» означают что функция не принимает и не возвращает никаких данных, в данном случае.

Важно знать что в языке Си символ точка с запятой «;» является специальным символом — пустым оператором (который ничего не выполняет) и используется для указанию компилятору что это конец команды.

В строчке «int delay_ms_1 = 100;» мы объявили новую переменную «delay_ms_1» с типом «int» (Integer, Целый тип, значения от -32768 до 32767) и присвоили ей значение 100. Служит она в нашей программе для установки задержки в миллисекундах при мелькании каждого из светодиодов.

В следующей строке «int delay_ms_2 = 300;» мы также выполнили инициализацию переменной, которая будет служить для установки времени задержки между мельканиями отдельных светодиодов — 300 миллисекунд.

Следующая команда в коде идентична предыдущей за исключением того что она устанавливает на вывод канал PD1 порта DDRD.

К каналам PD0 и PD1 (ножки 2 и 3 чипа) у нас подключены светодиоды, свечением которых мы и будем управлять.

Также данную запись можно представить вот так:

Здесь мы присваиваем переменной PORTD содержимое этой же переменной, применив к ней битовую операцию «|» (логическое ИЛИ), в качестве аргумента которой выступает результат выражения «1 число один, биты которого сдвинуты на 0 (PD0 = 0) разрядов влево.

Дальше мы выполняем небольшую задержку по времени «_delay_ms(delay_ms_1);» вызывая функцию «_delay_ms» и передав ей в качестве аргумента переменную «delay_ms_1», которая уже содержит число 100.

Более развернуто данную строку можно записать так:

Здесь мы выполняем запись в порт PORTD его начального значения, перед тем применив к последнему операцию «&» (логическое И), в качестве второго аргумента которой передаем результат комплексного выражения «~(1 оператора инверсии «~» (смена значений всех бит на противоположные).

В следующих строках в коде мы снова выполняем установку (запись 1) и сброс (запись 0) бита PD0 в байте регистра для порта PORTD с установленной задержкой «delay_ms_1», чем мы заставляем светодиод подключенный к пину канала PD0 зажечься и погаснуть (мелькнуть, blink).

Строкой «_delay_ms(delay_ms_2);» выполняется более длительная задержка по времени с использованием значения переменной «delay_ms_2» которая выше получила значение 300 (задержка в 300 миллисекунд).

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

По завершению приведенных команд все начинается снова в бесконечном цикле «while (1)«.

Самое сложное к пониманию здесь это, пожалуй, работа с установкой нужных битов в портах. Более подробно данная тема освещена в статье: Работа с регистрами AVR микроконтроллера на Си, битовые операции.

Компиляция и прошивка программы в МК

Для компиляции программы сохраним исходный код в файле под названием «leds_blinking.c». Если у вас уже настроена среда Geany то для компиляции достаточно нажать на панели инструментов кнопку «Compile».

Для компиляции файла с программой на Си в консоли нужно выполнить команду:

avr-gcc -mmcu=atmega8 -Os leds_blinking.c -o leds_blinking.o

В результате работы, если нет ошибок, получим объектный файл leds_blinking.o с которого нам нужно извлечь необходимые данные для прошивки нашего микроконтроллера (в моем случае ATmega8, параметр «-mmcu=atmega8»).

Для извлечения данных и построения файла прошивки в формате Intel Hex нужно нажать в Geany кнопку «Build». Из консоли получить нужный файл можно при помощи команды:

avr-objcopy -j .text -j .data -O ihex leds_blinking.o leds_blinking.hex

Теперь, когда у нас есть файл с прошивкой в формате Intel HEX останется записать его содержимое (прошить) во флешь-памяти микроконтроллера, выполнить эту операцию можно нажав в подготовленной нами среде Geany кнопку «Run or view current file» (Execute).

В консоли выполнить прошивку можно при помощи avrdude командой (для ATmega8 параметр «-p m8», программатор USBAsp «-c usbasp»):

avrdude -c usbasp -p m8 -P usb -U flash:w:leds_blinking.hex

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

Желательно выполнять все шаги (компиляция+построение hex-файла + прошивка) поочередно и вести наблюдение за информацией что появляется в консоли или на информационной панели Geany. Это поможет обнаружить ошибки и замечания если что-то не будет работать так как нужно.

Документация по языку Си и AVR Си

Брайан Керниган и Dennis Ritchie — Язык программирования C: brian-kernighan-and-dennis-ritchie-c-language.pdf.zip (2,1Мб, PDF).

Герберт Шилдт — Полный справочник по C: gerbert-shildt-c-complete-guide.zip (912Кб, HTML).

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

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

Библиотека Си для AVR микроконтроллеров (AVR C Runtime Library) — https://savannah.nongnu.org/projects/avr-libc/

По приведенной выше ссылке можно почитать документацию (на английском языке) прямо на сайте или же скачать ее одним файлом в форматах HTML и PDF, там есть вся необходимая информация по использованию библиотеки avr-libc для программирования AVR микроконтроллеров.

Заключение

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

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

В этом видео программатор USBAsp уже отключен, а питание схемы на микроконтроллере осуществляется от батареи КРОНА с напряжением 9В через схему стабилизатора напряжения которая обеспечивает на выходе стабильные 5В.

Источник

Оцените статью