Как стать автором
Обновить

Светодиодное сердце на микроконтроллере Atmega16 или программирование AVR на языке Pascal

Время на прочтение 8 мин
Количество просмотров 35K
Однажды я решил сделать подарок своей любимой девушке. Для этого вооружился я паяльником, программатором и компьютером. И, как художник, сотворил светодиодное сердце. Чтобы сердце было особенным, я постарался реализовать всевозможные режимы мигания светодиодами.





Схема



Что из себя представляет схема? Здесь ничего необычного. Управляющим ядром сердца, своеобразным «кардиостимулятором», выступает, всем известный, микроконтроллер AVR Atmega16, окруженный минимально необходимой обвязкой. От кварца тактировать не стал, микроконтроллер работает на внутренней RC-цепочке на частоте 1 МГц.



Каждый светодиод, образующий сердце, подключен к отдельной «ноге» микроконтроллера через токоограничивающий резистор 500 Ом. Всего восемнадцать светодиодов, подключенных к портам A (все выводы), С (все выводы), D (два вывода). Светодиоды управляются «единицей».



Монтаж

Все элементы были спаяны проводом МГТФ на макетной плате.



Макетная плата сзади была «зашита» тонким пенопластом, чтобы защитить монтаж. Также были сделаны «ножки» из стоек, прикрученных винтами.



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



Программа

Для программирования микроконтроллера Atmega16 решил я использовать среду разработки E-LAB. Почему E-LAB? Наверно ответ в том, что в то время платформа Arduino еще не появилась на свет. А для реализации такого проекта требовался сравнительно простой и удобный инструмент. E-LAB — это такой своеобразный «дедушка» Arduino IDE. E-LAB обеспечивает создание программ для микроконтроллеров AVR на языке программирования высокого уровня Pascal, который всем известен. Хотя Pascal и является языком высокого уровня, но знание архитектуры микроконтроллеров AVR и общих принципов их функционирования крайне необходимо для успешного использования этого языка. E-LAB — это кладезь библиотек для работы со встроенными в микроконтроллер периферийными модулями (таймеры, ШИМ, I2C, UART и т.д.), так и с различными внешними периферийными устройствами (клавиатура, знакосинтезирующий ЖКИ, «семисегметнк», Ethernet и т.д.).
Основная логика работы программы заключается в последовательном переключении режимов индикации светодиодов в прерывании программного таймера. Когда я писал эту программу, мною не использовались системы контроля версий, поэтому последняя версия программы не сохранилась. Но я нашел промежуточный вариант программы, в котором основные режимы работы, в частности программный 18-ти канальный ШИМ, присутствуют.

Текст программы на языке Pascal
program Love_Machine;

{$NOSHADOW}
{ $WG}                     {global Warnings off}

//Контроллер ATmega16
//Напряжения питания 3.3 В
Device = mega16, VCC=3.3;
{ $BOOTRST $01C00}         {Reset Jump to $01C00}

Import SysTick, TickTimer;

From System Import LongWord;


Define
  //Рабочая частота 1 MГц (Внутренняя RC-цепочка)
  ProcClock      = 1000000;       {Hertz}
  SysTick        = 10;             {msec}
  StackSize      = $0032, iData;
  FrameSize      = $0032, iData;

  TickTimer      = Timer1;
//Задержка переключения системного
//в милисекундах
Define_USR SysLED_Delay =  500;

Implementation

{$IDATA}

{--------------------------------------------------------------}
{ Type Declarations }

type


{--------------------------------------------------------------}
{ Const Declarations }

 const

 TimeCount: Byte = 70;

 
 MOutBits_L1:array[0..7] of Byte = (

 $00,
 $00,
 $00,
 $00,

 $00,
 $00,
 $00,
 $ff
 );
 
 MOutBits_L2:array[0..7] of Byte = (

 $00,
 $00,
 $00,
 $00,

 $00,
 $00,
 $ff,
 $ff
 );
 
 MOutBits_L3:array[0..7] of Byte = (
 $00,
 $00,
 $00,
 $00,

 $00,
 $ff,
 $ff,
 $ff
 );
 
 MOutBits_L4:array[0..7] of Byte = (

 $00,
 $00,
 $00,
 $00,

 $ff,
 $ff,
 $ff,
 $ff
 );
 
 MOutBits_L5:array[0..7] of Byte = (

 $00,
 $00,
 $00,
 $ff,

 $ff,
 $ff,
 $ff,
 $ff
 );
 
 MOutBits_L6:array[0..7] of Byte = (

 $00,
 $00,
 $ff,
 $ff,

 $ff,
 $ff,
 $ff,
 $ff
 );
 
 MOutBits_L7:array[0..7] of Byte = (

 $00,
 $ff,
 $ff,
 $ff,

 $ff,
 $ff,
 $ff,
 $ff
 );


{*
 MOutBits: array[0..24] of LongWord =(
   %010000000000000101,
   %010000000000000101,
   %010000000000000101,
   %010000000000000101,
   %010000000000000101,

   %010000000000000001,
   %010000000000000001,
   %010000000000000001,
   %010000000000000001,
   %010000000000000001,

   %010000000000000001,
   %010000000000000001,
   %010000000000000001,
   %010000000000000001,
   %010000000000000001,

   %000000000000000101,
   %000000000000000101,
   %000000000000000101,
   %000000000000000101,
   %000000000000000101,

   %000000000000000001,
   %000000000000000001,
   %000000000000000001,
   %000000000000000001,
   %000000000000000001


  );
*}

{--------------------------------------------------------------}
{ Var Declarations }
{$IDATA}
 var
 
 OutBitsIndex: Byte;

 St_Level:  Byte;
 St_Timer:  Byte;
 
 PortDataA:    Byte;
 PortDataC:    Byte;
 PortDataD:    Byte;
 ShiftCounter: Byte;

 TimerTickCounter: LongWord;
 

{--------------------------------------------------------------}
{ functions }



//Функци инициализации
//портов ввода-вывода
procedure InitPorts;
begin
  //Первый сегмент сердца ( 8 светодиодов)
  //Порт на вывод
  DDRA:=  %11111111;
  //Записать нули
  PortA:= %00000000;
  
  //Второй сегмент сердца
  //Порт на вывод
  DDRC:=  %11111111;
  //Записать нули
  PortC:= %00000000;

  //Третий сегмент сердца ()
  //Два вывода порта на вывод
  DDRD:=  %00000011;
  //Записать нули
  PortD:= %00000000;
  
  //системный двухцветный светодиод (2 ножки)
  //Порт на вывод
  DDRB:=  %00000011;
  
  

end InitPorts;


//Индикация красным
procedure SysLED_Red;
begin
  //Подтянуть к единице 1-й вывод
  //порта B
  incl(PortB,1);
  //Подтянуть к нулю 0-й вывод
  //порта B
  excl(PortB,0);
end SysLED_Red;

//Индикация зеленым
procedure SysLED_Green;
begin
//Подтянуть к единице 0-й вывод
  //порта B
  incl(PortB,0);
  //Подтянуть к нулю 1-й вывод
  //порта B
  excl(PortB,1);
end SysLED_Green;

//Переключение индикации
//зеленный-красный
procedure SysLED_SwColor;
begin
  //Зажечь красный
  SysLED_Red;
  //Задержка
  mDelay(Word(SysLED_Delay));
  //Зажечь зеленый
  SysLED_Green;
  //Задержка
  mDelay(Word(SysLED_Delay));
end SysLED_SwColor;

//Обработчик прерывания программного таймера
procedure onTickTimer; //(SaveAllRegs);
begin
     //SysLED_SwColor;
case  St_Timer  of
 0:
     toggle(PortA,0);
     toggle(PortA,1);
     toggle(PortA,2);
     toggle(PortA,3);
     toggle(PortA,4);
     toggle(PortA,5);
     toggle(PortA,6);
     toggle(PortA,7);
     
     toggle(PortC,0);
     toggle(PortC,1);
     toggle(PortC,2);
     toggle(PortC,3);
     toggle(PortC,4);
     toggle(PortC,5);
     toggle(PortC,6);
     toggle(PortC,7);
     
     toggle(PortD,0);
     toggle(PortD,1);
 |
 1:
      PortDataA := PortDataA ror 1;
      PortA := PortDataA;
      PortDataC := PortDataC ror 1;
      PortC := PortDataC;
      PortDataD := PortDataD ror 1;
      PortD := PortDataD;
 |
 2:


     // PortDataC := PortDataC ror 1;
     // PortC := PortDataC;
     // PortDataD := PortDataD ror 1;
     // PortD := PortDataD;
      
      if  (ShiftCounter = 0) or (ShiftCounter = 18)
      then
            PortD := $00;
            ShiftCounter := 0;
            PortDataA := $01;
            PortA := PortDataA;
            inc(ShiftCounter);
            
      elsif (ShiftCounter < 8) and (ShiftCounter > 0)
      then
            PortDataA := PortDataA rol 1;
            PortA := PortDataA;
            inc(ShiftCounter);
            
      elsif (ShiftCounter = 8)
      then
            PortA := $00;
            PortDataC := $01;
            PortC := PortDataC;
            inc(ShiftCounter);
            
      elsif (ShiftCounter > 8) and (ShiftCounter < 16)
      then

            PortDataC := PortDataC rol 1;
            PortC := PortDataC;
            inc(ShiftCounter);
            
      elsif (ShiftCounter = 16)
      then
            PortC := $00;
            PortDataD := $01;
            PortD := PortDataD;
            inc(ShiftCounter);
            
      elsif (ShiftCounter > 16) and (ShiftCounter < 18)
      then
            PortDataD := PortDataD rol 1;
            PortD := PortDataD;
            inc(ShiftCounter);
      endif;




 
 |
 3:
    inc(TimerTickCounter);


    if ( ( TimerTickCounter mod TimeCount ) = 0 )
    then

      inc(St_Level);

      if ( St_Level >= 16)
      then
          St_Level := 1;
      endif;
      
    endif;

    
    case St_Level   of
    0:
    PortA := $00;
    PortC := $00;
    PortD := $00;
    |
    1:
    PortA := MOutBits_L1[OutBitsIndex];
    PortC := MOutBits_L1[OutBitsIndex];
    PortD := MOutBits_L1[OutBitsIndex];
    |
    2:
    PortA := MOutBits_L2[OutBitsIndex];
    PortC := MOutBits_L2[OutBitsIndex];
    PortD := MOutBits_L2[OutBitsIndex];
    |
    3:
    PortA := MOutBits_L3[OutBitsIndex];
    PortC := MOutBits_L3[OutBitsIndex];
    PortD := MOutBits_L3[OutBitsIndex];
    |
    4:
    PortA := MOutBits_L4[OutBitsIndex];
    PortC := MOutBits_L4[OutBitsIndex];
    PortD := MOutBits_L4[OutBitsIndex];
    |
    5:
    PortA := MOutBits_L5[OutBitsIndex];
    PortC := MOutBits_L5[OutBitsIndex];
    PortD := MOutBits_L5[OutBitsIndex];
    |
    6:
    PortA := MOutBits_L6[OutBitsIndex];
    PortC := MOutBits_L6[OutBitsIndex];
    PortD := MOutBits_L6[OutBitsIndex];
    |
    7:
    PortA := MOutBits_L7[OutBitsIndex];
    PortC := MOutBits_L7[OutBitsIndex];
    PortD := MOutBits_L7[OutBitsIndex];
    |
    8:
    PortA := $FF;
    PortC := $FF;
    PortD := $FF;
    |
    9:
    PortA := MOutBits_L7[OutBitsIndex];
    PortC := MOutBits_L7[OutBitsIndex];
    PortD := MOutBits_L7[OutBitsIndex];
    |
    10:
    PortA := MOutBits_L6[OutBitsIndex];
    PortC := MOutBits_L6[OutBitsIndex];
    PortD := MOutBits_L6[OutBitsIndex];
    |
    11:
    PortA := MOutBits_L5[OutBitsIndex];
    PortC := MOutBits_L5[OutBitsIndex];
    PortD := MOutBits_L5[OutBitsIndex];
    |
    12:
    PortA := MOutBits_L4[OutBitsIndex];
    PortC := MOutBits_L4[OutBitsIndex];
    PortD := MOutBits_L4[OutBitsIndex];
    |
    13:
    PortA := MOutBits_L3[OutBitsIndex];
    PortC := MOutBits_L3[OutBitsIndex];
    PortD := MOutBits_L3[OutBitsIndex];
    |
    14:
    PortA := MOutBits_L2[OutBitsIndex];
    PortC := MOutBits_L2[OutBitsIndex];
    PortD := MOutBits_L2[OutBitsIndex];
    |
    15:
    PortA := MOutBits_L1[OutBitsIndex];
    PortC := MOutBits_L1[OutBitsIndex];
    PortD := MOutBits_L1[OutBitsIndex];
    |
    endcase;
    
    inc( OutBitsIndex );
    
    if OutBitsIndex >= 8
    then
        OutBitsIndex := 0;
    endif;
  |
  endcase;

end;
{--------------------------------------------------------------}
{ Main Program }
{$IDATA}
//Код выполняемый сразу после Reset'a
begin
  //Инициализировать порты ввода/вывода
  InitPorts;
  //Настроить программный таймер
  // Период = 1 мс
  // Частота = 1 кГц
  TickTimerTime(1000);
  // Запустить таймер
  TickTimerStart;
  
  // Остановить таймер
  TickTimerStop;
  
  //Разрешить прерывания
  EnableInts;

  {
  //Последовательное включение
  //светодиодов  c секундным интервалом
  incl(PortA,0);
  mDelay(1000);
  incl(PortA,1);
  mDelay(1000);
  incl(PortA,2);
  mDelay(1000);
  incl(PortA,3);
  mDelay(1000);
  incl(PortA,4);
  mDelay(1000);
  incl(PortA,5);
  mDelay(1000);
  incl(PortA,6);
  mDelay(1000);
  incl(PortA,7);
  mDelay(1000);
  incl(PortC,0);
  mDelay(1000);
  incl(PortC,1);
  mDelay(1000);
  incl(PortC,2);
  mDelay(1000);
  incl(PortC,3);
  mDelay(1000);
  incl(PortC,4);
  mDelay(1000);
  incl(PortC,5);
  mDelay(1000);
  incl(PortC,6);
  mDelay(1000);
  incl(PortC,7);
  mDelay(1000);
  incl(PortD,0);
  mDelay(1000);
  incl(PortD,1);
  mDelay(1000);

  //Переход в первый режим таймера (Toggle)
  St_Timer := 0;

  //Период 200 мс
  TickTimerTime(200000);
  // Запустить таймер
  TickTimerStart;
  //Задержка 2 секунды
  mDelay(2000);
  
  //Период 150 мс
  TickTimerTime(150000);
  // Запустить таймер
  TickTimerStart;
  mDelay(2000);

  // Остановить таймер
  TickTimerStop;
  //Период 100 мс
  TickTimerTime(100000);
  // Запустить таймер
  TickTimerStart;
  //Задержка 2 секунды
  mDelay(2000);
  
  // Остановить таймер
  TickTimerStop;
  //Период 50 мс
  TickTimerTime(50000);
  // Запустить таймер
  TickTimerStart;
  //Задержка 2 секунды
  mDelay(2000);
  
  // Остановить таймер
  TickTimerStop;
  //Период 25 мс
  TickTimerTime(25000);
  // Запустить таймер
  TickTimerStart;
  //Задержка 2 секунды
  mDelay(2000);

  }
  // Остановить таймер
  TickTimerStop;
  
  PortDataA := $AA;
  PortDataC := $AA;
  PortDataD := $AA;

  //Переход во второй режим (Shift Inv)
  St_Timer := 1;

  //Период 200 мс
  TickTimerTime(200000);
  // Запустить таймер
  TickTimerStart;
  
  //Задержка 5 секунд
  mDelay(2000);
  
  // Остановить таймер
  TickTimerStop;
  
  PortA := $00;
  PortC := $00;
  PortD := $00;
  
  PortDataA := $00;
  PortDataC := $00;
  PortDataD := $00;
  

  ShiftCounter := 0;

  //Переход во второй режим (Shift One)
  St_Timer := 2;
  
  //Период 200 мс
  TickTimerTime(200000);
  // Запустить таймер
  TickTimerStart;
  
  //Задержка 5 секунд
  mDelay(5000);

  // Остановить таймер
  TickTimerStop;
  //Переход в третий режим таймера (PWM)
  St_Timer := 3;
  // Частота = 1 кГц
  TickTimerTime(1000);
  // Запустить таймер
  TickTimerStart;

  //Основной цикл
  loop


        //inc(St_Level);
        //if ( St_Level >= 9)
        //then
        //    St_Level := 0;
        //endif;
        //mDelay(200);

       //SysLED_SwColor;
      
      // incl(PortC,1);
     // mDelay(1);
  endloop;
end Love_Machine.


Файлы проекта E-LAB можно скачать здесь.
Серьезным недостатком E-LAB, являлось и является то, что для программирования из среды нужен особый фирменный программатор. За не имением такового, я зашивал hex-файл в микроконтроллер «народным» программатором AVR910 собственного производства.

Видео


Заключение

Думаю, что сейчас, похожий подарок на платформе Arduino может сделать каждый, и тем самым порадовать своих любимых. Было бы желание.
Теги:
Хабы:
+7
Комментарии 19
Комментарии Комментарии 19

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн