пятница, 6 января 2012 г.

Светодиодная мигалка на микроконтроллере ATmega8.


Использование микроконтроллеров существенно упрощает построение светодиодных мигалок. Светодиодная мигалка - это, наверное, одно из самых подходящих устройств для изготовления новичками в области построения схем на микроконтроллерах. Здесь приведен пример построения мигалки и исходный код для микроконтроллера ATmega8 на языке C++.
На сайте
myrobot.ru в разделе "шаг за шагом" понятным языком описано как программировать микроконтроллеры и часть информации, приведенной здесь, взята от туда. Рассмотрим схему приведенную на рисунке:

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


 int main(void)        
   {
DDRD = 0xff;            /* все выводы порта D сконфигурировать как выходы */
DDRC = 0x00;      //выводы порта С сконфигурировать как входы

PORTC = 0xff; // установить "1" на всех выводах порта C,
// включаем подтягивающие резисторы

int i=0; //для задержек
int j=0; //для смены битов порта D
int d=20; //для изменения периода миганий
int sw=0; //для изменения режимов миганий

while (1)          // Бесконечный цикл
{
if (!(PINC & (1<<PINC4))) //если логический ноль на 4 бите порта С
{
sw++; //преключить режим миганий
if(sw>4)sw=0;
}
if (!(PINC & (1<<PINC3))) //если логический ноль на 3 бите порта С
{
d+=20; //увеличить период миганий
if(d>260)d=20;
}

switch(sw)
{
case 0:
DDRD |= 1<<j;
for(i=0;i<d;i++)_delay_us(50);
DDRD &= ~(1<<j);
for(i=0;i<d;i++)_delay_us(50);
j++;
if(j>3)j=0;
break;
case 1:
DDRD |= 1<<j;
for(i=0;i<d;i++)_delay_us(50);
DDRD &= ~(1<<j);
for(i=0;i<d;i++)_delay_us(50);
j--;
if(j<0)j=3;
break;
case 2:
DDRD &= ~(1<<j);
for(i=0;i<d;i++)_delay_us(50);
DDRD |= 1<<j;
for(i=0;i<d;i++)_delay_us(50);
j++;
if(j>3)j=0;
break;
case 3:
DDRD &= ~(1<<j);
for(i=0;i<d;i++)_delay_us(50);
DDRD |= 1<<j;
for(i=0;i<d;i++)_delay_us(50);
j--;
if(j<0)j=3;
break;
case 4:
DDRD=0xff;
for(i=0;i<d;i++)_delay_us(50);
DDRD=0x00;
for(i=0;i<d;i++)_delay_us(50);
break;
}
}

   }

При каждой итерации цикла while проверяется нажатие кнопок. Если нажата кнопка SB1 то переменная sw инкрементируется. Если переменная sw равна нулю (это проверяет команда switch) то в нулевой бит порта D записывается единица, потом происходит задержка, после чего в этот бит записывается ноль, затем снова задержка, потом переменная j инкрементируется и, если кнопка SB1 не была нажата, то все повторяется для следующего бита и таким образом происходит "движение светодиода", когда переменная j становится больше трех, ей присваивается ноль и все повторяется. Если нажимается кнопка SB1 то sw увеличивается и если она была равна нулю то она становиться равной единице и тогда происходит тоже что и при sw равной нулю только j теперь не инкрементируется а декрементируется (за счёт этого "движение светодиода" происходит в другую сторону) а при становлении j меньше нуля ей присваивается 3 и всё повторяется. Если sw равно двум или трем то происходит тоже только "бегущий светодиод" инвертирован, потому что порядок записи нуля и единицы в биты портов изменен. Если sw равно четырем то мигают все светодиоды. Если нажата кнопка SB2 то переменная d увеличивается на 20 и т.о. увеличиваются все задержки, следовательно увеличивается период мигания светодиодов при всех режимах. Если d>260 то ей снова присваивается значение 20 и период миганий резко уменьшается (частота увеличивается).

Исходный код компилировался в hex файл программой WinAVR. hex файл записывался в микроконтроллер программой PonyProg.

8 комментариев:

  1. Как бы выглядел данный код будь он написан на
    С++??? (если С++ в примере нет).
    Ни смотря на то как, чем и т.д. был написан этот код он был записан в микроконтроллер и всё заработало как надо! Цель была достигнута!

    ОтветитьУдалить
  2. Зачем выделять память под int если хватит unsigned char?

    ОтветитьУдалить
  3. И не плохо бы на нажатие кнопки сделать проверку на залипание... короче, код для новичка просто ужасен, плохому со старта научится.

    ОтветитьУдалить
    Ответы
    1. И не _delay_us(), а _delay_ms()

      Удалить
  4. _delay_us() -для микросекунд,
    _delay_ms() -для миллисекунд
    (если я не ошибаюсь).
    unsigned char занимает 1 байт,
    int занимает 2 байт (или 4 точно не знаю)
    поэтому для экономии памяти лучше использовать unsigned char.
    В atmeg8 8Мб доступной памяти поэтому для данной простой программы это не так важно, это просто надо учитывать.
    Дребезг контактов при данном коде ПОЧТИ не страшен так как состояние вывода к которому подключена кнопка проверяется относительно редко и контакты, в большинстве случаев, успевают "продребезжать" прежде чем кнопка провериться.

    А что подразумевается под залипанием?
    Когда контакты не размыкаются после нажатия или что-то программное или что-то ещё?

    ОтветитьУдалить
  5. Что-то типа:
    if (PINC.1 ==1)
    {
    while (PINC.1 == 1) { }
    ... код для выполнения
    }

    ОтветитьУдалить
  6. Спасибо за ответ!

    Поправка:
    У atmega8 8кбайт а не 8Мбайт доступной флеш-памяти, согласно даташиту: http://www.atmel.com/images/doc2486.pdf

    ОтветитьУдалить