Бегущие огни на ATtiny2313, первая программа для микроконтроллера

Моим первым устройством на микроконтроллере была схема бегущих огней. Линейка из 15-ти светодиодов, которые попеременно загораются. Принципиальная схема такого устройства предельно проста. По сравнению с макетной платой здесь появились только 15 светодиодов, у которых анод общий, а катоды подсоединены к портам ввода/вывода микроконтроллера и столько же токоограничительных резисторов R3-R17 по 360 Ом.

Светодиоды управляются низким уровнем на выводе микроконтроллера (так как для низкого уровня микроконтроллер может выдать больший ток, около 20мА, чем для высокого, но не стоит забывать про суммарный ток, который может выдать порт).

Линейка светодиодов у меня организована на SMD компонентах, но это дело вкуса.
фото бегущих огней
Теперь рассмотрим порты ввода/вывода. У ATtiny2313 есть 15 линий ввода/вывода, которые сгруппированы в 2 порта: PortB – 8 выводов и PortD – 7 выводов. Вся работа микроконтроллера состоит из правильного шевеления ножками, а искусство программирования – заставить его так делать. Также ножки портов по совместительству могут выполнять другие функции: быть входом встроенного АЦП, внешнего прерывания, выходом USART’а и множество других функций, но их использование рассмотрим немного попозже. Для использования портов ввода вывода их нужно сперва сконфигурировать, за это отвечают регистры PORTX и DDRX, где X – имя порта. Приведем табличку их возможных состояний (n=0..7 – номер вывода в порту):
таблица состояния портов
Как видно из таблицы при значении DDRXn =1 порт работает как выход, и логический уровень на его выходе равен биту PORTXn. При значении DDRXn =0 вывод PXn будет работать на вход, а бит PORTXn определяет, подключен ли к входу внутренний подтягивающий резистор или нет. Логический уровень на выводе PXn можно считать из бита PINXn. Перейдем к практике, предлагаю написать программу, которая будет по очереди зажигать каждый из 15 светодиодов, сперва напишем программу на С, а потом для разнообразия на ассемблере. Программа на С выглядит так:

  1. #include <avr/io.h>
  2.  
  3. #define SPEED 3000 //Скорость бегущих огней
  4.  
  5. void pause (unsigned int a) //Задержка
  6. { unsigned int i;
  7. for (i=a;i>0;i--);
  8. }
  9.  
  10. int main(void)
  11. {
  12. unsigned char i;
  13.  
  14. DDRB=0xff; //Инициализация портов ввода/вывода
  15. DDRD=0x7f;
  16. PORTB=0xff;
  17. PORTD=0x7f;
  18.  
  19. while (1) //Вечный цикл
  20. {
  21. for (i=0;i<8;i++) //По одному зажигаем светодиоды на PORTB
  22. {
  23. PORTB=~_BV(i);
  24. pause(SPEED);
  25. }
  26. PORTB=0xff;
  27.  
  28. for (i=0;i<7;i++) //По одному зажигаем светодиоды на PORTD
  29. {
  30. PORTD=~_BV(i);
  31. pause(SPEED);
  32. }
  33. PORTD=0x7f;
  34. }
  35. }

Теперь немного пояснений. Думаю, для тех, кто писал на С для ПК особых затруднений не возникнет. Строка #include подключает внешний модуль avr/io.h, который необходим для работы с портами ввода/вывода. Строка #define SPEED 3000 отвечает за скорость бегущей строки, означает, что в тексте программы все вхождения слова SPEED будут заменены на число 3000 (некий аналог задания констант в программе). Функция pause (unsigned int a) определяет задержку примерно (8*a)/f, где a – аргумент функции f – частота тактирования микроконтроллера. В вечном цикле while происходит попеременное включение светодиодов для каждого порта (так как управление осуществляется низким уровнем, то стоит символ НЕТ - «~»). Функция unsigned char _BV(b) возвращает байт, в котором бит под номером b устрановлен в единицу, а все остальные равны 0. Например, _BV(2)==0x04.
Попробует теперь написать аналогичную программу на ассемблере:

  1. ;********************Define chapter******************************************
  2. .def temp =r16
  3. .def temp2 =r17
  4.  
  5. .def coarse =r21
  6. .def medium =r22
  7. .def fine =r23
  8. ;**********************Const chapter*****************************************
  9. .equ c_del =1
  10. .equ m_del =100
  11. .equ f_del =255
  12.  
  13. .include "2313def.inc"
  14. ;*********************************Code segment*******************************
  15. .cseg
  16. .org 0
  17. rjmp reset
  18.  
  19. reset:
  20. ldi Temp,RamEnd ;Инициализация стека
  21. out SPL,Temp
  22.  
  23. ldi temp,0xff ;Инициализация портов ввода/вывода
  24. out DDRB, temp
  25. out PORTB, temp
  26. ldi temp,0x7f
  27. out DDRD, temp
  28. out PORTD, temp
  29. main:
  30. ldi temp,0xfe
  31. ldi temp2,0x08
  32. for1: ;По одному зажигаем светодиоды на PORTB
  33.  
  34. out PORTB,temp
  35. sec
  36. rol temp
  37. rcall delay
  38.  
  39. dec temp2
  40. cpi temp2, 0x00
  41. brne for1
  42.  
  43. ldi temp,0xff
  44. out PORTB, temp
  45.  
  46. ldi temp,0x7e
  47. ldi temp2,0x07
  48. for2: ;По одному зажигаем светодиоды на PORTD
  49.  
  50. out PORTD,temp
  51. sec
  52. rol temp
  53. rcall delay
  54.  
  55. dec temp2
  56. cpi temp2, 0x00
  57. brne for2
  58.  
  59. ldi temp,0x7f
  60. out PORTD, temp
  61.  
  62. rjmp main
  63. ;*********************Delay**************************************************
  64. delay:
  65.  
  66. ldi coarse, c_del
  67. cagain: ldi medium, m_del
  68. magain: ldi fine, f_del
  69. fagain: dec fine
  70. brne fagain
  71. dec medium
  72. brne magain
  73. dec coarse
  74. brne cagain
  75. ret

Секция Define chapter сопоставляет имена регистров с именами переменных. В секции Const chapter задаются константы, которые влияют на скорость бегущих огней. Далее идет сегмент кода Code segment в котором собственно и содержится код нашей программы. В коде программы сперва инициализируем стек, порты ввода/вывода конфигурируем как выхода, после чего в вечном цикле по одному светодиоды перебираем. Структура кода очень похожа на написанную выше программу С. Код на С более прост для понимания и компактен, но по размеру программы ассемблер обгоняет С (оптимизация не включена): 84 против 312 байт кода у С. Если же включить максимальную оптимизацию для С, то получим 184 байт кода, но придется совсем под другому переписать функцию задержки. Итого имеем, ассемблер более сложен, но код компактен и быстр, а С более прост, но код получившейся программы значительно больше. В заключение привожу видео готового устройства:

Советую не ограничиваться просто бегущим огоньком, а немного поэкспериментировать:

Скачать проект для AVR Studio

Расскажите как сделать

Расскажите как сделать бегущие огни как в сериале рыцарь дорог

А как в сериале "Рыцарь

А как в сериале "Рыцарь дорог"?

Начинающие, ну неужели никто

Начинающие, ну неужели никто не собрал до сих пор это?
расскажите какие проблемы были, что сложно было сделать, что легко.
Свои мнения!

Ха-ха-ха!

Ну я начинающий! :))) Ну какие проблемы?? Собрал, прошил МК - заработало. Ну чего тут сложного? Повесить на каждую ногу светодиод с резистором!? Делай уже!