Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на сентябрь 2023 г.
1. 1000 руб
Сайт Паяльник

Реклама ⓘ

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,'http://cxem.net');
                    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,'http://cxem.net');
                    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,'http://cxem.net');
                    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.

И схема:

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

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

Теги:

Опубликована: 0 0
Я собрал 0 1
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 1 чел.

Комментарии (3) | Я собрал (0) | Подписаться

0
kaj62 #
Большое спасибо за уроки!
Подскажите, что за процедура "abs" в строке freq:=abs(31250/temp); ?
Ответить
0

[Автор]
zeconir #
Если не изменяет память - округление.
Ответить
0
Dusin #
Вот беда! Больше года прошло и никто не поправил!
abs(x) - получение абсолютного значения величины 'x'
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Pickit 2 - USB-программатор PIC-микроконтроллеров
Pickit 2 - USB-программатор PIC-микроконтроллеров
AVR-программатор USB ASP Радиореле 220В
вверх