Главная > AVR > Краткое пособие по микроконтроллерам AVR. Часть 1.

Краткое пособие по микроконтроллерам AVR. Часть 1.

Базовые понятия, порты ввода-вывода.

1. Основные отличия С для микроконтроллеров.
Базовая структура программы.

Всем известно, что компилятор С без стандартных библиотек — унылая штука. При программировании для x86 практически всегда подключаются как минимум два заголовочных файла — stdio.h и conio.h, обеспечивающие базовые возможности ввода с клавиатуры/вывода на экран, при этом самыми популярными функциями являются printf(), scanf() и getch(). Кроме этого обычно подключается множество дополнительных библиотек для более специфичных функций вроде математики. Однако очевидно, что у микроконтроллера нет ни клавиатуры, ни экрана (если только мы не подключим их сами, а это уже совсем другая история). Зато у него есть много другой стандартной периферии, доступ к которой возможен «из коробки».


Внимание,

большинство дальнейших рассуждений и постулатов приведено для компилятора AVR GCC и среды AVR Studio.


Итак, функцию stdio.h здесь выполняет avr/io.h. Да, именно так, со слешем в имени. В этом файле находятся определения констант, имен регистров и всего прочего, что может понадобиться для выполнения базового ввода-вывода. С conio.h же можно сравнить avr/interrupt.h — второй по важности заголовочный файл. Как ясно из названия, он предоставляет возможности для обработки прерываний.


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

Еще один момент, специфичный для встроенных систем. Чаще всего обычная программа для «большого» компьютера выполняется под управлением операционной системы, и потому функция main() завершается командой возврата т.н. кода завершения — знакомый всем return 0 в ее конце. Кроме того, main() может принимать аргументы — параметры командной строки. Однако ясно, что в микроконтроллере нет операционной системы — с самого начала управление всеми ресурсами отдается единственной управляющей программе. Поэтому стандартное завершение не имеет смысла — возвращать код некуда. И, естесственно, main() в программе для МК не может принимать никаких аргументов — им просто неоткуда взяться.
Кстати, поскольку микроконтроллером исполняется всего одна программа, оная должна иметь вид бесконечного цикла или кончаться им, потому что поведение устройства за пределами управляющей программы не определено, и пока устройство включено, постоянно должен исполняться пользовательский код.

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

#include <avr/io.h>

void main(void)
{
  /*Здесь обычно производится настройка периферии*/

  while (1);
}

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

2. Битовые операции.

При программировании МК достаточно часто приходится манипулировать отдельными битами в регистрах и переменных. Например, установить или сбросить определенный бит, не трогая остальные. Достичь этого можно, используя побитовые логические операции — сдвиг ( <<, >>), побитовое ИЛИ ( | ), побитовое И ( & ) а также побитовую инверсию ( ~ ).

Установка отдельного бита в переменной:

var|=(1<<номер_бита);

Очистка отдельного бита:

var&=~(1<<номер_бита);

Очевидно, так же можно устанавливать/сбрасывать группу бит:

var|=(1<<бит1) | (1<<бит2) | (1<<бит3) ...;
var&=~((1<<бит1) | (1<<бит2) | (1<<бит3) ...);

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

Проверка бита в переменной var на равенство единице:

if (var & (1<<номер_бита))
{
  /*этот бит установлен (единица)*/
}
else
{
  /*бит сброшен (ноль)*/
}

Очевидно, что подобным образом можно одновременно проверять на равенство единице группу бит.

3. Порты ввода-вывода.

Порты ввода-вывода — основные периферийные модули, которые всегда есть в любом микроконтроллере. В некоторых МК может не быть почти ничего, но порты есть обязательно. Собственно, это те самые ножки, которые торчат из эпоксидного корпуса микросхемы и через которые осуществляется обмен данными кристалла с внешним миром. Для программы порты, как и любая другая периферия, представлены регистрами ввода-вывода. С точки зрения программирования на С регистры можно рассматривать как некие предопределенные переменные, объявленные в стандартном заголовочном файле (avr/io.h). Каждый бит в регистре управления однозначно сопоставлен физической ножке контроллера.

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


Вообще, обозначение регистров обычно имеет следующий формат:

<сокращенное название функции><буква/номер устройства>
т.о., DDRBData Direction Register of port B.


Единичный бит в DDRx означает, что соответствующая ножка настроена на выход, ноль — на вход. Для управления ножками, настроенными на выход, служит регистр PORTx. Единица в PORTx означает, что на сопоставленную соответствующему биту регистра ножку будет подано напряжение питания; в случае записи туда нуля ножка будет подключена к общему проводу. Для чтения состояния ножек, настроенных на вход, служит регистр PINx. Аналогично, прочитанная единица означает, что на ножку извне подано напряжение более трех вольт (примерно), ноль — что на ножку подано менее полутора вольт (также приблизительно).

Отдельный случай — запись единицы в бит PORTx, соответствующий выводу, настроенному на вход с помощью DDRx. В этом случае к ножке будет подключен внутренний подтягивающий резистор (к питанию).


Подтягивающий резистор («подтяжка») — резистор, подключенный между точкой схемы и питанием (либо землей) для фиксации потенциала этой точки. Говорят, что он «подтягивает» ее к питанию (земле).

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

Итак, упрощенную блок-схему вывода микроконтроллера можно представить следующим образом:

Пример — простое мигание светодиодом на выводе контроллера.


В примере ниже используется стандартная библиотека util/delay. Она содержит функции простых (подсчет тактов) задержек — _delay_ms() (задержка в миллисекундах), _delay_us() (задержка в микросекундах) и т.п.

Важно — параметр указанных функций может быть только константой! Это связано с особенностями их компиляции.


#include <avr/io.h>
#include <util/delay.h>

void main(void)
{
  DDRB=0xFF;
  PORTB=0;

  while (1)
  {
    PORTB|=(1<<PB0);
    _delay_ms(250);
    PORTB&=~(1<<PB0);
    _delay_ms(250);
  }
}

Рубрики:AVR
  1. Dinsul
    19/03/2016 в 21:44

    что за хедер коньйо.х? Его нет в стандартной библиотеке!!!!

    • YS
      19/03/2016 в 22:35

      Есть такой заголовочник. Формально он не входит в стандартную библиотеку, но де-факто всегда присутствует в поставке компиляторов, как минимум в случае x86/Win32.

  1. No trackbacks yet.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s