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 2.0

 

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

 

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 не будет опубликован. Обязательные поля помечены *

*