AVR Урок 6. Бегущие огни



Урок 6

 

Бегущие огни

 

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

Для этого нам потребуется уже не один светодиод. У меня на этот счёт имеется светодиодная планка или матрица. Я её поместил в беспаечную макетную плату, катоды всех светодиодов соединил вместе и подключил к общему проводу, а аноды каждый через токоограничивающий резистор подключил к соответствующим ножкам порта D. Вот так это всё выглядит (нажмите на картинку для увеличения изображения)

 

image00_0500

 

Поэтому, как обычно, по старой доброй традиции мы запускаем Atmel Studio, создаём в ней проект, выбрав тот же самый микроконтроллер Atmega8a, назовём проект Test03. Таким же образом в качестве отладчика выберем simulator, и также, чтобы сэкономить наше драгоценное время, скопируем весь код из файла main.c прошлого занятия.

 

 

Начнём писать код. Сначала мы в функции main() создадим целочисленную короткую беззнаковую переменную

 

int main(void)

{

  unsigned char i;

 

Порт также оставляем на выход, и сразу на данном порте включим нулевую ножку в 1

 

DDRD = 0xFF;

PORTD = 0b00000001;

 

А в бесконечном цикле мы создадим цикл другого типа — типа for. Данный цикл уже является конечным и работает он следующим образом

 

image01

 

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

 

while(1)

{

  for(i=0;i<=7;i++)

    {

      _delay_ms(500);

    }

  }

 

В данном цикле у нас будет пока только задержка, остальной код мы уберём. То есть тело нашего цикла будет у нас выполняться до тех пор, пока переменная i у нас не достигнет значения, большего или равного 7. То есть получится, что наше тело будет выполняться ровно 8 раз, затем мы выйдем из данного цикла и благодаря бесконечному циклу заново в него войдём и наш восьмикратный процесс повторится сначала.

 

 

А вот теперь сдвиг. Вставим его до задержки

 

PORTD = (1<<i);

_delay_ms(500);

 

Как мы видим, данный сдвиг мы применяем к регистру, отвечающему за состояния порта D, и в нём мы будем сдвигать единичку влево на величину нашей переменной i, а так как данная переменная с каждым циклом увеличивается на 1 (или инкрементируется), то, соответственно, наша единичка постепенно раз в полсекунды будет двигаться влево, также как и лапки порта, за которые отвечает каждый бит нашего регистра. И тем самым мы и получим эффект бегущего огня.

Давайте соберём наш проект. И, также как и на прошлом занятии скопируем файл протеуса с прошлого занятия и переименуем его в Test03. Откроем его, заменим файл прошивки в свойствах контроллера.

Также добавим ещё 7 светодиодов и 7 резисторов, так как показано на схеме. Можно применять операцию копирования. Как это делается, показано в видеоуроке.

Запустим проект в протеусе и увидим, что наши светодиоды мигают поочерёдно, создавая впечатление эффекта бегущего огня

 

image02

 

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

На следующем занятии мы уже поработаем с ножкой порта не на выход, а на вход. Так как мы уже знаем, что порт — это такая шина, которая умеет работать в разных направлениях. Мы подключим кнопку и попытаемся отследить её состояние. То есть наша задача — узнать в какой-то определённый момент, нажата наша кнопка или отжата. Так что будет интересно.

 

 

Предыдущий урок Программирование МК AVR Следующий урок

 

Исходный код

 

 

Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером

 

Смотреть ВИДЕОУРОК

 

AVR Бегущие огни

 

33 комментария на “AVR Урок 6. Бегущие огни
  1. Panzer:

    еременная i у нас не достигнет значения, меньшего или равного 7

  2. Дмитрий:

    Добрый день! Что за «L» после частоты? В предыдущем проекте её нет.

  3. Alexandr:

    Здравствуйте ! Попробовал развернуть бегущий огонь в другую сторону, код не работает. Не могу понять в чем проблема, по идее должно все работать, но не работает …

    int main(void)
    {
    unsigned char i;

    DDRD = 0xFF;
    PORTD = 0b10000000;
    while(1)
    {
    for (i=7; i>=0; i—)

    {
    PORTD = (1>>i);
    _delay_ms(500);

    }
    }
    }

  4. dimon.0401:

    У тебя в коде много ошибок. Изначально код должен выглядеть как-то так.
    #include
    int main(void)
    {
    unsigned char i;
    DDRD = 0xFF;
    PORTD = 0b00000001;
    while(1)
    {
    for (i=0; i<=7; i++)
    {
    PORTD = (1<<i);
    }
    }
    _delay_ms(500);
    }
    Проверил лично код рабочий компилил в AtmelStudio 6.2, проверял в Протеусе на At mega8 с частотой 8 mhz.
    Если код будет для кого-то кривой, то прошу сильно не пинать, я только учусь. 🙂

  5. dimon.0401:

    В начале должно быть #include , а то не дописал.

  6. Алексей:

    Здравствуйте! На AVR Studio собрал такую программу (выложена часть). Принцип работы следующий: изначально при подачи напряжения на МК светодиоды на портах B и D горят, затем постепенно гаснут. Можно как-то уменьшить программу в плане написания кода.

    #include
    #include

    void main(void)
    {

    mainendloop:
    DDRB=0xFF;
    PORTB=(255);
    DDRD=0xFF;
    PORTD=(255);

    while (1)
    {

    PORTB=(255);
    _delay_ms(255);

    PORTB=(PORTB&0xFE)|(0);
    _delay_ms(255);

    PORTB=(PORTB&0xFC)|(0);
    _delay_ms(255);

    PORTB=(PORTB&0xF8)|(0);
    _delay_ms(255);

    PORTB=(PORTB&0xF0)|(0);
    _delay_ms(255);

    PORTB=(PORTB&0xE0)|(0);
    _delay_ms(255);

    PORTB=(PORTB&0xC0)|(0);
    _delay_ms(255);

    PORTB=(PORTB&0x80)|(0);
    _delay_ms(255);

    PORTB=(PORTB&0x00)|(0);
    _delay_ms(255);

  7. uuu000:

    Не могу добиться бесконечного цикла,с остальными параметрами разобрался.
    Хотел поменять направление и ограничиться пятью ледами.
    Если не трудно -где ошибка?
    Спасибо.

    #define F_CPU 8000000
    #include
    #include

    int main(void)
    { unsigned char i;
    DDRD = 0xFF;
    PORTD = 0b00100000;
    while(1)
    {
    {
    for(i=5;i>=0;i—)
    {
    PORTD = (1<<i);
    _delay_ms(500);
    }
    }

    }
    }

    • Непонятно, для чего после while(1) стоят сразу две открывающие скобки

    • Дмитрий:

      int main(void)
      {
      /* Replace with your application code */
      unsigned char i;
      DDRD = 0xFF;

      while (1)
      {
      PORTD = 0b10000000;
      for (i=0;i> 1);
      }
      _delay_ms(500);
      }
      }

      • Дмитрий:

        int main(void)
        {
        unsigned char i;
        DDRD = 0xFF;

        while (1)
        {
        PORTD = 0b10000000;
        for (i=0;i> 1);
        }
        _delay_ms(500);
        }
        }

        • Дмитрий:

          а что за глюк? не могу нормально код вставить.
          попытка номер 3:
          int main(void)
          {
          /* Replace with your application code */
          unsigned char i;

          DDRD = 0xFF;

          while (1)
          {
          PORTD = 0b10000000;
          for (i=0;i> 1); // пропихиваем 1 с каждой итерацией цикла на 1 двоичный разряд
          }
          _delay_ms(500);
          }
          }

          • Вячеслав:

            У тебя в коде переменная i не учавствует в качестве аргумента какой либо фунции. Она только определена и создано условие. А что использует данную переменную i может быть какой то порт или задержка ?

  8. uuu000:

    Понял,спасибо.

  9. Farmatsevt:

    А зачем мы изначально нулевой бит порта D в единицу опускаем, если PORTD = (1<<i); единица в этой записи означает то же самое?

  10. Саныч:

    Не уверен что все сделал правильно, но в протеусе работает. Гирлянда на 16 портах.

    #define F_CPU 8000000
    #include
    #include
    int main(void)
    {
    unsigned char i;
    unsigned char a;
    DDRB = 0b11111111;
    DDRD = 0b11111111;
    while (1)
    {
    PORTB = 0b00000001;
    for(i=1;i<=8;i++)
    {
    _delay_ms(20);
    PORTB = (1<=7)
    PORTD = 0b00000001;
    {
    for (a=1;a<=8;a++)
    {
    _delay_ms(20);
    PORTD = (1<<a);
    }
    }
    }
    }
    }

  11. Юрий:

    Вернулся,после некоторого обучения,благодаря этим урокам.
    Спасибо за учебу.

    int main(void)
    {

    DDRB=0x00;
    PORTB=0x01;
    DDRD=0xFF;
    PORTD=0x00;
    unsigned char i;
    DDRD = 0xFF;
    PORTD = 0b00000000;
    while(1)
    {

    for(i=7;i>0;i—)
    {
    PORTD = (1<<i);
    _delay_ms(500);
    }

    }
    }

    • Вячеслав:

      Зачем ты два раза до цикла написал DDRD=0xFF; Там наверное должно быть DDRB=0xFF; и потом в цикеле дописать PORTB = (1<<i);

  12. Павел:

    Даже текст программы скопировал из прикрепленного к уроку файла, думал, может ошибку не нахожу. компилирую в Atmel studio. При включенной оптимизации-01 компилятор ошибок не видит и удаляет переменную i, (в окошке Quick watch пишет optimized away) при этом отладка останавливается в функции delay (появляется файл, delay.h, содержащий её описание). При отключении оптимизации (оптимизация-00) компилятор говорит, что функция delay корректно работать не будет, отладка зависает также при обработке задержки, только в другом месте. Что надо сделать, чтобы компилятор нормально обработал текст. квалификатор volatile не помог.

  13. Павел:

    отладчик виснет в delay на второй итерации цикла for.
    при исключении задержки из кода и оптимизации-01 компилятор также не видит ошибок, в окне Watch1 видно сообщение об удалении компилятором глобальной переменной (optimized away), но программа в отладчике работает, в окне IO View видно, как происходит сдвиг активного бита порта D.
    #define F_CPU 8000000L
    #include

    int main(void)
    {
    unsigned char i;
    DDRD = 0xFF;
    PORTD = 0b00000001;
    while(1)
    {
    for( i=0;i<=7;i++)
    {
    PORTD = (1<<i);

    }
    }
    }

  14. Павел:

    Вопрос снят. Не обратил внимания, что при входе в delay индикатор состояния отладчика в левом нижнем углу показывает running. Почему-то задержка длительностью в пол секунды отрабатывается за минуту сорок секунд (засекал по секундомеру), после этого в окне IO View показывается сдвиг бита и индикация состояния отладчика меняется на Ready.
    Может кто-нибудь ответить, почему так долго отрабатывается задержка?

  15. Павел:

    В протеус если записывать файл прошивки с расширением .hex задержка игнорируется, если указать прошивку с расширением .elf задержка отрабатывается как требуется, по 0,5 секунды (видимо учитываются биты фьюзов тактовой частоты). Получается что-то нехорошее с режимом отладки в atmel studio 6.2 .

  16. Михаил:

    Вопрос таков: почему не объявляются типы переменных и почему нельзя написать в цикле вот так: for(int i=0; i<=7; i++)
    {
    PORTD=(1<<PORTD)
    }
    ?
    Спасибо!!!

  17. Юрий:

    Еще один вариант
    #define F_CPU 8000000L
    #include
    #include

    int main(void)
    {
    int i;
    DDRD = 0xFF;
    PORTD = 0x08;
    while(1)
    {
    for(i=7;i>=0;i—)
    {
    PORTD = ((1<<i));
    _delay_ms(500);
    }
    }
    }

  18. C_trvl:

    добрый день!
    начинаю только изучать программирование на AVR.
    при попытке «прогона» программы она всегда выскакивает на надпись:
    __builtin_avr_delay_cycles(__ticks_dc);

    попытки что-либо изменять (в пределах того, что я понял из Ваших предыдущих уроков) ни к чему не привели :(.
    прогу открыл (и пишу) в MicroChip Studio.
    эмулирую в Proteus. после загрузки проги в чип и её запуске, светодиоды не переключаются, горит только первый.
    понимаю, что проблема в задержке (_delay), но что именно не так и как исправить — к сожалению, не знаю.
    прошу помочь.

  19. Матвей:

    А почему нельзя заменить «PORTD = (1<<i)" на "PORTD = (PORTD<<1)"??

    • Роман:

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

  20. Роман:

    Можно, только осторожно. Чтобы единичка не потерялась на следующем круге.

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

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

*