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

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

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

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

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

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

2)TCCR1B. 

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

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

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

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

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

  

Разобравшись с даташитом, можно перейти к 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.

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

.

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.

И схема:

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.

И схема:

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


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

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

Ваш адрес email не будет опубликован.