Мигающий светодиод | Программирование микроконтроллеров AVR



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

Мигающий светодиод

Частота работы микроконтроллера

Большинство команд микроконтроллеров AVR выполняются в один такт генератора задающей частоты. В качестве которого широко используют встроенную в МК RC-цепочку или подключают к выводам XTAL1 и XTAL2 кварцевый резонатор.

Подключение кварцевого резонатора к микроконтроллеру

Например, если МК работает с частотой 1 Гц, то одна команда будет выполняться за одну секунду

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

По умолчанию у МК ATmega8 задействован собственный внутренний генератор частоты, а точнее RC-цепочка, которая работает на частоте 1000 000 Гц = 1 МГц. Поэтому время выполнения одной команды равно:

Частота работы микроконтроллера

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

#include <avr/io.h>

int main(void)

{

DDRD = 0b000000011;     

while (1)

    { 

      PORTD = 0b000000001; // Подаем питание на 1-й светодиод

      PORTD = 0b000000010; // Подаем питание на 2-й светодиод

    }

}

Но на самом деле второй LED загорится с разницей во времени 0,000001 секунды от первого. Наши глаза не могут заметить такой малой разницы во времени. Уже при частоте изображений более 24 Гц (t = 1/24 ≈ 0,042 с) наше зрение формирует из отдельных картинок непрерывный фильм. Поэтому в большинстве случаев мы не различаем 25-й кадр.

Для того, чтобы оба светодиода засветились с разницей во времени 0,5 секунды необходимо между соответствующими двумя командами (PORTD = 0b000000001; и PORTD = 0b000000010;) поместит еще 500 000 однотактных пустых команд, т. е. заставить МК полсекунды не выполнять никаких полезных действий. Или, как говорят, нужно “убить” 500 000 тактов. Если код пишется на Ассемблере, то программисты применяют различных циклы, которые “съедают” определенное число тактов и тем самым получают различные интервалы времени.

#include <avr/io.h>

int main(void)

{

DDRD = 0b000000011;

while (1)

    {

     PORTD = 0b000000001; // Подаем питание на 1-й светодиод

/*

Для получения задержки 0,5 секунды сюда нужно вставить

500 000 однотактных команд

*/

     PORTD = 0b000000010; // Подаем питание на 2-й

    }

}

Функция  _delay_ms() и мигающий светодиод

При написании кода на Си в Atmel Studio имеется очень удобная функция _delay_ms(). Для работы данной функции ее необходимо предварительно подключить директивой препроцессора #include <util/delay.h>.

В круглых скобках данной функции можно задавать время в миллисекундах, тогда перед скобками нужно записать ms, или в микросекундах – us:

Функция задержки микроконтроллера

При использовании данной функции для того, чтобы при компиляции Atmel Studio не выдавала никаких предупреждений, следует объявить частоту с помощью оператора #define. Так как по умолчанию для ATmega8 она равна 1 000 000 Гц, то это значение мы и объявим. Это делается следующей строкой:

#define F_CPU 1000000UL

В дальнейшем, когда мы будем подключать к МК кварцевый резонатор, без данной строки уже не обойтись. Структура ее останется прежней, только вместо 1 000 000 нужно будет записать частоту кварцевого резонатора.

Давайте улучшим нашу программу, так, чтобы сначала загорался один светодиод, затем через полсекунды он гаснул и еще через полсекунды загорался второй и снова через 0,5 с гаснул.

Программа для мигания светодиодом на микроконтроллере

#define

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

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

С помощью оператора #define мы присвоим числовому значению какое-либо имя. Это имя называется константа. В отличие от переменной, константа не может изменяться в программе. Выглядит это так:

#define MIG 300

.

.

.

_delay_ms(MIG);

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

После строки с директивой препроцессора #define точка с запятой не ставится. Между именем константы и числовым значением ставится пробел.

Данная строка работает следующим образом. Перед началом компиляции выполняется замена числом 300 всех констант с именем MIG.

#define и регистры

Также оператор #define хорош тем, что с помощью него можно задавать имена регистрам. Например, если мы подключаем к порту D светодиоды, то вместо PORTD мы можем записать, например VD:

#define VD PORTD

.

.

.

VD = 0b00000001;

Давайте перепишем программу, применяю директиву #define:

#define F_CPU 1000000UL

#include <avr/io.h>

#include <util/delay.h>

#define MIG 300

#define VD PORTD

int main(void)

{

    DDRD = 0b000000011;

    while (1)

    {

             VD = 0b000000001; // Включаем 1-й светодиод

             _delay_ms(MIG);     // Ждем 0,5 секунды

             VD = 0b000000000; // Выключаем 1-й

             _delay_ms(MIG);     // Ждем 0,5 секунды

             VD = 0b000000010; // Включаем 2-й

             _delay_ms(MIG);     // Ждем 0,5 секунды

             VD = 0b000000000; // Выключаем 2-й

             _delay_ms(MIG);     // Ждем 0,5 секунды

    }

}

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

Скачать  2 Atmel Studio

Скачать  2 Proteus

2 комментария

Комментировать

Ваш e-mail не будет опубликован. Обязательные поля помечены *