mikroPascal for AVR. Урок 4. Прерывания, прерывания и еще раз прерывания. Таймеры

Разбираем прерывания в MikroPascal для AVR на примере микроконтроллера ATmega8. Узнайте как работать с прерываниями, таймерами и регистрами.

Как и было указано в описании, этот урок будет посвящен лишь прерываниям. Если вас волнует вопрос о интерфейсах, про которые ничего не было сказано (I2C, OneWire, SPI), то отвечу: просто я с ними сам толком не разобрался, как — то надобности не было. Как разберусь, постараюсь сразу выложить сюда.

В качестве «подопытного кролика» возьмем микроконтроллер ATmega8. Этот МК всем знаком, и хотя бы одна штука есть у каждого радиолюбителя.

Если вы ещё не работали с прерываниями, то первым делом посмотрите таблицу прерываний для выбранного МК. В этой таблице указаны адреса и вектора прерываний. Чем выше адрес, тем ниже приоритет прерывания. К примеру, адрес INT0= 0x01, а TIMER0 OVF = 0x09. Соответственно, если одновременно поступят сигналы, то первым выполнится прерывание по INT0.

Что касается регистров, то очень доходчиво их назначение раскрыто в даташите. Да для различных контроллеров они различны, по этому нет смысла тут их описывать. Так что рассмотим лишь то, что нам потребуется в сегодняшнем уроке. А это регистры TCCR0, TCCR1B, TIMSK, GICR.

1)TCCR0. Это регистр для выбора коэффициента предделения для таймера T0.

MikroPascal for AVR Урок Прерывания Таймеры

2)TCCR1B. 

MikroPascal for AVR Урок Прерывания Таймеры

Регистр, отвечающий как за предделитель (биты 2-0), так и за следующее: подавление дребезга контактов на входе ICP1 (бит 7), выбор фронта срабатывания прерывания по захвату (бит 6), ШИМ (биты 4,3). 

Предделитель настраивается так же как и для T0 (рисунок выше).

3)TIMSK. Это регистр «выбора» прерываний.

MikroPascal for AVR Урок Прерывания Таймеры

Прерывание по совпадению ТС2 (бит 7), прерывание по переполнению ТС2 (бит 6),  прерывание по захвату ТС1 (бит 5), прерывание по совпадению A ТС1 (бит 4), прерывание по совпадению В ТС1 (бит 3), прерывание по переполнению ТС1 (бит 2), не используется (бит 1), прерывание по переполнению ТС0 (бит 0).

4)GICR (он же GIMSK) — запрет/разрешение прерываний по сигналам на входах INT0, INT1

  

MikroPascal for AVR Урок Прерывания Таймеры

Разобравшись с даташитом, можно перейти к mikroPascal. Итак, конфигурация регистров, разрешение и запрет прерывания.

TCCR1B:=0x02; // Можно сделать так (предделитель 8);
TCCR1B.B0:=1; // А можно так. То же самое, предделитель на 8;
TCCR1B.CS10:=1; // И это тоже установка предделителя на 8!

asm cli end; // Запрет прерываний.
asm sei end; // Разрешение прерывний.

// Использование ассемблерных вставок может сильно снизить переносимость вашей программы с одного МК на другой.

Вот так, несколькими способами можно сконфигурировать не лишь предделитель, но все остальное, например прерывание по входу INT0.

GICR:=0x64;
GICR.B7:=1;
GICR.INT0:=1;

Теперь, когда основы просмотрены, можно перейти к практике.

1. Прерывание по переполнению T0.

program timerInt_0;

var count,i:integer;

procedure timer0_ovf(); iv IVT_ADDR_TIMER0_OVF;
begin
if i>=1 then begin
if count>=4 then begin // Так как таймер 8-ми битный, то макс. значение = 256.
PORTB0_bit:=1; // Тгогда берем частоту кварца, делим на 1024 , а потом ещё на 256.
count:=0;
i:=0; // Получаем 3,8. Слишь раз в секунду будет переполняться таймер T0.
end else inc(count);
end else begin
if count>=4 then begin
PORTB0_bit:=0;
count:=0;
i:=1;
end else inc(count);
end;
end;

begin
TCCR0:=0x05; //Èëè ìîæíî òàê CS00_bit:=1; CS01_bit:=0; CS02_bit:=1;
TIMSK:=0x01;
SREG_I_bit:=1;

DDB0_bit:=1;

while true do begin
end;
end.

В приложении к статье есть проекты в Proteus для каждого примера кода. Я не буду приводить скриншоты всех схем, потому что в этом нет необходимости. Если вам интересно, что получилось, то можете посмотреть сам.

2. Прерывание по переполнению T1.

program timerInt_1_ofv;

//Использование прерывания по переполнению счетчика 1-го таймера кардинально от такой же процедуры,
//применительно к 0 таймеру, не отличается. Различия лишь в разрядности (16 против 8).

var count,i:integer;

procedure timer1_ofv(); iv IVT_ADDR_TIMER1_OVF; ics ICS_AUTO;
begin
if i>=1 then begin
if count>=15 then begin //Так как счетчик таймера 16-ти разрядный, то предел счета — 65535. Значит мы должны
count:=0; //разделить частоту тактового генератора на коэф. предделения и ещё на 65535.
i:=0; //В итоге получаем 15.
PORTB0_bit:=0;
PORTB1_bit:=1;
end else inc(count);
end else begin
if count>=15 then begin
count:=0;
i:=1;
PORTB0_bit:=1;
PORTB1_bit:=0;
end else inc(count);
end;
end;

begin

TCCR1B:=0x01;
TIMSK:=0x04;
SREG_I_bit:=1;

DDB0_bit:=1;
DDB1_bit:=1;

while true do begin

end;
end.

 

3. Использование прерывания по совпадению (T1).

program timer1_compare;

uses AADL;

var LCD_RS : sbit at PORTC4_bit; //Указываем куда подключен LCD
var LCD_EN : sbit at PORTC5_bit;
var LCD_D4 : sbit at PORTC0_bit;
var LCD_D5 : sbit at PORTC1_bit;
var LCD_D6 : sbit at PORTC2_bit;
var LCD_D7 : sbit at PORTC3_bit;

var LCD_RS_Direction : sbit at DDC4_bit;
var LCD_EN_Direction : sbit at DDC5_bit;
var LCD_D4_Direction : sbit at DDC0_bit;
var LCD_D5_Direction : sbit at DDC1_bit;
var LCD_D6_Direction : sbit at DDC2_bit;
var LCD_D7_Direction : sbit at DDC3_bit;

var count, time:word;
i:integer;
tstr: string [23];

procedure timer1_comp_a(); iv IVT_ADDR_TIMER1_COMPA; ics ICS_AUTO;
begin
TCCR1B:=0x00;
inc(time);
IntToStr(time,tstr);
TCCR1B:=0x04;
TCNT1L:=0x00;
TCNT1H:=0x00;
end;

begin
TCCR1B:=0x04;

TIMSK:=0x10;

OCR1AH:=0x7a;
OCR1AL:=0x12;

SREG_I_bit:=1;

DDRC:=0xff;
DDRD:=0xff;
DDB0_bit:=0;
DDB1_bit:=0;
DDB2_bit:=0;
PORTB:=0x07;

lcd_init; //Инициализация дисплея.
lcd_cmd(_lcd_clear); //»Чистим» дисплей.
lcd_cmd(_lcd_cursor_off); //Отключаем мигающуюю штуковину,
//////////////////////////////////////////////////////////////////////////////////которую обозвали курсором.
TCCR1B:=0x00; //А это «лого» с адресом сайта //и названием «прибора» 🙂
lcd_out(1,16,’/me’);
lcd_out(2,16,’Easy Timer’);
delay_ms(150);
for i:=0 to 14 do begin //Кому интереснogo
lcd_cmd(_lcd_shift_right); //наведите курсор на сслыку выше.
delay_ms(150);
end;
lcd_cmd(_lcd_clear);
delay_ms(2000);
TCCR1B:=0x04;
/////////////////////////////////////////////////////////////////////////////////

While true do begin
if Button(PINB,0,100,0) then TCCR1B:=0x04;
if Button(PINB,1,100,0) then TCCR1B:=0x00;
if Button(PINb,2,100,0) then begin
time:=0;
tstr:=’0′;
lcd_cmd(_lcd_clear);
lcd_out(1,1,tstr + ‘ sec’);
end;
lcd_out(1,1,tstr + ‘ sec’);
delay_ms(100);
end;
end.

Вот тут уже схема необходима.

MikroPascal for AVR Урок Прерывания Таймеры

.

4. Прерывание по переполнению T0 и INT0. Частотомер.

program timer0_ovf_int0;

uses AADL;

var LCD_RS : sbit at PORTC4_bit; //Указываем куда подключили LCD
var LCD_EN : sbit at PORTC5_bit;
var LCD_D4 : sbit at PORTC0_bit;
var LCD_D5 : sbit at PORTC1_bit;
var LCD_D6 : sbit at PORTC2_bit;
var LCD_D7 : sbit at PORTC3_bit;

var LCD_RS_Direction : sbit at DDC4_bit;
var LCD_EN_Direction : sbit at DDC5_bit;
var LCD_D4_Direction : sbit at DDC0_bit;
var LCD_D5_Direction : sbit at DDC1_bit;
var LCD_D6_Direction : sbit at DDC2_bit;
var LCD_D7_Direction : sbit at DDC3_bit;

var freq, temp, count : word;
lcdout : string [23];
i:integer;

procedure timer0_ovf(); iv IVT_ADDR_TIMER0_OVF; ics ICS_AUTO;
begin
if count>=3905 then begin //Проверяем, прошла 1 с , или нет.
TCCR0:=0x00; //Если прошла, отключаем прерывания.
GICR:=0x00; //И производим вычисления…

Word2Str(freq, lcdout); //
freq:=0; //Обнуляем переменные.
i:=0;
count:=0;

TCCR0:=0x02; //Включаем прерывания.
GICR:=0x64;
end else inc(count);
end;

procedure int0_(); iv IVT_ADDR_INT0; ics ICS_AUTO;
begin
inc(freq); //Увеличиваем с каждым импульсом значение переменной freq.
end;

begin

TCCR0:=0x02; //Конфигурируем регистры
TIMSK:=0x01;
GICR:=0x64; //Разрешаем прерывания по INT0.
SREG_I_bit:=1;

DDRC:=0xFF; // Для INT1 нужно записать GICR:=0x128;
DDD2_bit:=0;

lcd_init; //Инициализация дисплея
lcd_cmd(_lcd_clear); //
lcd_cmd(_lcd_cursor_off); //
//////////////////////////////////////////////////////////////////////////////////
TCCR0:=0x00;
GICR:=0x00; //Пасхалка 🙂 Выводит адрес сайта Паяльник и название «прибора».
lcd_out(1,16,’/me’);
lcd_out(2,16,’Easy Freq’);
delay_ms(150);
for i:=0 to 14 do begin
lcd_cmd(_lcd_shift_right);
delay_ms(150);
end;
delay_ms(2000);
TCCR0:=0x02;
GICR:=0x64;
/////////////////////////////////////////////////////////////////////////////////
lcd_cmd(_lcd_clear);
While true do begin //Основной цикл.
lcd_out(1,1,’Frequrecy’); //Выводим на дисплей, то над чем так мучались 🙂
lcd_out(2,1,lcdout+’ Hz’);
delay_ms(100);
lcd_cmd(_lcd_clear);
end;
end.

И схема:

MikroPascal for AVR Урок Прерывания Таймеры

5. Последнее на сегодня. Прерывание по захвату. С помощью этого прерывания, доступного для T1, можно вычислить длительность импульсов, и как следствие частоту. К сожалению, у меня с помощью Proteus’а не получилось корректно воспроизвести картину… Возможно где-то в коде ошибка, но факт остается фактом — чем больше частота, тем громаднее погрешность. Итак, код:

program timer1_capt;

var LCD_RS : sbit at PORTC4_bit; //Указываем куда подключен LCD
var LCD_EN : sbit at PORTC5_bit;
var LCD_D4 : sbit at PORTC0_bit;
var LCD_D5 : sbit at PORTC1_bit;
var LCD_D6 : sbit at PORTC2_bit;
var LCD_D7 : sbit at PORTC3_bit;

var LCD_RS_Direction : sbit at DDC4_bit;
var LCD_EN_Direction : sbit at DDC5_bit;
var LCD_D4_Direction : sbit at DDC0_bit;
var LCD_D5_Direction : sbit at DDC1_bit;
var LCD_D6_Direction : sbit at DDC2_bit;
var LCD_D7_Direction : sbit at DDC3_bit;

var a,b,c,i:integer;
temp,temp_1,temp_2:integer;
freq:word;
lcdout:string [23];

const T = 0.000032;

procedure timer1_capt(); iv IVT_ADDR_TIMER1_CAPT;
begin
asm cli end; //Ассемблерные вставки —
//Полезная вещь. Но их
if c=0 then begin //использовать нужно крайне
b:=TCNT1L; //аккуратно, иначе код будет
a:=TCNT1H; //»не переносимым» на другие
temp_1:=a shl 8; //контроллеры.
temp_1:=temp_1 xor b; //Далее мы считываем данные
inc(c); //из регистра TCNT1. И приводим
end else begin //их в читабельный вид.
b:=TCNT1L; //По следующему вх. импульсу
a:=TCNT1H; //Опять дергаем счетный регистр
temp_2:=a shl 8; //и вычисляем разницу.
temp_2:=temp_2 xor b;
temp:=temp_2-temp_1;
c:=0; //Обнуляем регистр и переменные.
TCNT1H:=0;
TCNT1L:=0;
end;

asm sei end;

end;

begin
TCCR1B.B2:=1; //Тут мы ставим предделитель 256
TIMSK.B5:=1; //Прерывание по захвату.
SREG_I_bit:=1; //Прерывания разрешены.

DDB0_bit:=0; //Пин 0 порта B на вход.

lcd_init;
lcd_cmd(_lcd_clear);
lcd_cmd(_lcd_cursor_off); //Отключаем мигающуюю штуковину,
//////////////////////////////////////////////////////////////////////////////////которую обозвали курсором.
asm cli end; //А это «лого» с адресом сайта и названием «прибора» 🙂
lcd_out(1,16,’/me’);
lcd_out(2,16,’Starter Freq’);
delay_ms(150);
for i:=0 to 14 do begin //Кому интереснo ac:logo
lcd_cmd(_lcd_shift_right); //наведите курсор на сслыку выше.
delay_ms(150);
end;
lcd_cmd(_lcd_clear);
delay_ms(2000);
asm sei end;
/////////////////////////////////////////////////////////////////////////////////
While true do begin
freq:=abs(31250/temp); //Считаем частоту.
FloatToStr(freq,lcdout); //Копируем в строковую переменную
lcd_out(1,1,’Frequrency’); //Выводим на дисплей.
lcd_out(2,1,lcdout+’ Hz’);
delay_ms(300);
lcd_cmd(_lcd_clear);
end;

end.

И схема:

MikroPascal for AVR Урок Прерывания Таймеры

Ну вот и все. Если где-то заметили ошибку, пишите в комментариях, я ведь тоже учусь вместе с вами. Удачи вам в ваших начинаниях!


Прикрепленные файлы:

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

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