Главная » Микроконтроллеры
Призовой фонд
на октябрь 2020 г.
1. 1500 руб
Сайт Паяльник
2. Тестер компонентов MG328
Сайт Паяльник
3. 150 руб.
От пользователей


Набор качественных светодиодов, 100 шт.

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

Программатор Pickit3
Программатор Pickit3
ELM327 OBD II — адаптер с поддержкой CAN Лазерный модуль 650нм 5мВт
вверх