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

в 16:15, , рубрики: Без рубрики

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

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

Схема

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

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

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

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

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

Монтаж

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

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

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

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

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

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

Программа

Для программирования микроконтроллера 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, являлось и является то, что для программирования из среды нужен особый фирменный программатор. За не имением такового, я зашивал hex-файл в микроконтроллер «народным» программатором AVR910 собственного производства.

Заключение

Думаю, что сейчас, похожий подарок на платформе Arduino может сделать каждый, и тем самым порадовать своих любимых. Было бы желание.

Автор: love_energy

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js