Как и было указано в описании, этот урок будет посвящен только прерываниям. Если вас волнует вопрос о интерфейсах, про которые ничего не было сказано (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.
И схема:
Ну вот и все. Если где-то заметили ошибку, пишите в комментариях, я ведь тоже учусь вместе с вами. Удачи вам в ваших начинаниях!
Прикрепленные файлы:
- less4.rar (650 Кб)
Комментарии (3) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
Подскажите, что за процедура "abs" в строке freq:=abs(31250/temp); ?
[Автор]
abs(x) - получение абсолютного значения величины 'x'