|
Затраты на обработку прерывания по переполнению таймера. |
|
|
|
Jan 21 2010, 18:11
|
Гуру
     
Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164

|
Господа, пишу программку для подсчета периода импульсов. Могу применять только 8-битный таймер, ибо 16-ти битный таймер занят. Всвязи с тем что переполнение его наступает через каждые 256 тактов, их приходится точно просчитывать. Вот такой вот код: Код interrupt [TIM0_OVF] void timer0_ovf_isr(void) { //tacts++; a=TCNT0; TCCR0=0x00; lcd_gotoxy(0,0); lcd_putsf(" ");
lcd_gotoxy(0,0); sprintf(s," %i", a); lcd_puts(s);
TCCR0=0x01;
} То есть сразу по прерывания по переполнения таймер по идее должен сбрасываться. В самом начале обработчика значение TCNT0 записывается в переменную a. И ее значение выводится на экран. Так вот в данном случае оно равно 32 или 33 (в зависимости от фазы луны и того, чем я похмелялся )))) ). Что контроллер делает эти 32 такта? ПРичем 32 такта выдается хоть при 16 000 кГц, хоть при делении на 8 (2000 кГц). Инициализирую таймер так: Код // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 16000,000 kHz // Mode: Normal top=FFh // OC0 output: Disconnected TCCR0=0x01; TCNT0=0x00; OCR0=0x00; Что делать и кто виноват? У кого нибьудь есть опыт написания таких программ, когда приходилось несколько циклов переполнения таймера складывать. Как-нибудь компенсировали время, затрачиваемое на обработку прерывания?
|
|
|
|
|
 |
Ответов
(15 - 29)
|
Jan 22 2010, 14:39
|
Гуру
     
Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164

|
Все господа.... мысли кончились. Не гонясь за точностью, пытаюсь сделать хоть что-то. Завел Timer0 на 16 МГц. ПО переполнению инкрементирую OVF_count - счетчик переполнений. Timer1 в режиме PWM настроен на период 20 мс, выход поступает на вход INT0, фронту импульса запускается обработчик, который умножает OVF_count на 256 , прибавляет текущее значение TCNT0, после чего делит на 16 - получаем период в микросекундах. У меня же проказывает не 20 000 мкс, а 1610 либо 1594. Вот код, где я не прав? CODE #include <mega32.h> #include <delay.h> #include <stdio.h>
#asm .equ __lcd_port=0x15;PORTC #endasm #include <lcd.h>
#define FAN PORTB.4
#define FINEUP PIND.7 #define FINEDOWN PIND.4
#define SPEED_MINUS PIND.6 #define SPEED_PLUS PIND.1
#define SPEED_MIN 0x0490 #define SPEED_MAX 0x07D0
#define SPEED OCR1A
char lcd_buffer[33]; unsigned char s[8];
unsigned int a,b;
unsigned int OVF_count;
unsigned long int time;
// External Interrupt 0 service routine interrupt [EXT_INT0] void ext_int0_isr(void) { a=TCNT0; time=(OVF_count*256+a)/16; OVF_count=0; lcd_clear(); lcd_gotoxy(0,0); sprintf(s,"%u", time); lcd_puts(s);
}
interrupt [TIM0_OVF] void timer0_ovf_isr(void) { OVF_count++; }
main() {
unsigned char i,j, devices; int temp;
OVF_count=0;
// Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 16000,000 kHz // Mode: Normal top=FFh // OC0 output: Disconnected TCCR0=0x01; TCNT0=0x00; OCR0=0x00;
// Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 2000,000 kHz // Mode: Ph. & fr. cor. PWM top=ICR1 // OC1A output: Non-Inv. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x80; TCCR1B=0x12; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x4E; ICR1L=0x00; OCR1BH=0x00; OCR1BL=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0b00000001;
// External Interrupt(s) initialization // INT0: On // INT0 Mode: Rising Edge // INT1: Off // INT2: Off GICR|=0x40; MCUCR=0x03; MCUCSR=0x00; GIFR=0x40;
SPEED=SPEED_MIN;
DDRA=0xFF; DDRD=0x00; PORTD=0xFF;
DDRD.5=1;
DDRB.4=1; FAN=1; delay_ms(500); FAN=0;
DDRD.2=0; PORTD.2=0;
DDRD.0=0; PORTD.0=1;
lcd_init(8); lcd_clear();
#asm sei #endasm
while (1) { if (!SPEED_PLUS) { if (SPEED<SPEED_MAX) SPEED++; delay_ms(2); }
if (!SPEED_MINUS) {
if (SPEED>SPEED_MIN) SPEED--; delay_ms(2); }
}
}
Сообщение отредактировал rezident - Jan 22 2010, 15:07
Причина редактирования: Нарушение п.3.4 Правил форума.
|
|
|
|
|
Jan 22 2010, 19:14
|
Гуру
     
Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164

|
Хм.. а вот тут уже незнание мной матчасти, вопрос вообще можно было бы выделить в отдельную тему. Формула у меня такая: time=(OVF_count*256+a)/16; time имеет тип long int. Я думал этого достаточно. Разве умножаемая величина должна быть подходящего типа? Я думал, что только произведение...
Да, кстати, отключил таймер1, заменил сигнал с него на цикл PORTD.5=1;PORTD.5=0; с задержкой в 1 мс (период 2 мс). На осциллографе длительность периода составила 2,1 мс. Запретил обработку прерываний (внешнего и переполнения T0) - стало ровно 2 мс. Кроме того с вычислениями все в порядке стало, в пределах 2 мс показывает отлично, остальные диапазоны не проверял. Может быть таймер1 вносит в работу программы какую-то разножопицу? Хотя прерывания с него запрещены и работает он в режиме phase & frequency correction PWM.
Сообщение отредактировал zheka - Jan 22 2010, 19:17
|
|
|
|
|
Jan 22 2010, 21:10
|
Местный
  
Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017

|
Цитата Timer1 в режиме PWM настроен на период 20 мс, выход поступает на вход INT0 А почему не используете одно из прерываний таймера? зачем такой обходной путь? Цитата time=(OVF_count*256+a)/16; Установите предделитель таймера на 8 и Вам для 20 мс хватит разрядности unsigned int. Тогда time=(OVF_count*256+a)/2;
|
|
|
|
|
Jan 25 2010, 11:38
|
Участник

Группа: Свой
Сообщений: 54
Регистрация: 4-11-05
Пользователь №: 10 480

|
Вот пример реализации измерения частоты импульсов на CPU188-MX5. Основная идея способа - считать количество импульсов и время между передними фронтами импульсов. Если время между фронтами превышает заданное (в данном случае 3 секунды), то полученые значения кол-ва импульсов и время между фронтами копируется в соответствующие переменные, из которых внешняя функция вычисляет уже частоту импульсов. CODE //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
namespace FI { word timeMS = -1; word freqCount = 0;
void __declspec(naked) interrupt IntHandler(); };
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void __declspec(naked) interrupt FI::IntHandler() { static word sMS = 0; static word count = 0;
__asm { push ax // 14 push dx // 14 push ds // 13
mov ax, seg millisecondsCount // 4 mov ds, ax // 2
inc [count] // 19 mov ax, [word ptr millisecondsCount] // 2 mov dx, [sMS] // 2 sub ax, dx // 3 cmp ax, 3000 // 3
jnc loc2 // 4, 13
locret:
pop ds pop dx pop ax iret
loc2:
add dx, ax
mov [byte ptr sMS], dl // 2 mov [byte ptr sMS+1], dh // 2 mov [byte ptr timeMS], al // 2 mov [byte ptr timeMS+1], ah // 2
mov al, [byte ptr count] // 2 mov ah, [byte ptr count+1] // 2
mov [byte ptr freqCount], al // 2 mov [byte ptr freqCount+1], ah // 2
xor al, al // 3 mov [byte ptr count], al // 2 mov [byte ptr count+1], al // 2
pop ds // 12 pop dx // 14 pop ax // 14 iret // 28
} }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
float FI::GetFreq() { TRACE float f1; float f2; _cli(); f1 = FI::freqCount; f2 = FI::timeMS; FI::freqCount = 0; FI::timeMS = -1;
_sti();
return 1000.0 * f1 / f2; }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Сообщение отредактировал fantex - Jan 25 2010, 11:39
|
|
|
|
|
Jan 25 2010, 13:59
|
Гуру
     
Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164

|
Камень ATMega32. Вот, господа, упростил до безобразия: По прерыванию INT0 сразу запоминаю значение TCNT0 и количество переполнений таймера0 (всего-то 256 тиков, вдруг чего...) Затем обнуляю таймер: если импульс первый, то начинаем жихнь с чистого листа, если второй, то пофиг, мы то уже запомнили его значение. Далее, чтобы свести к минмимуму влияние вывода на LCD на время, проверяю, первый ли импусьс? Если первый, то обнуляю счетчик переполнений и сбрасываю флаг "первого импульса", дабы при следующем срабатывании он считался вторым. Код // External Interrupt 0 service routine interrupt [EXT_INT0] void ext_int0_isr(void) {
a=TCNT0; b=OVF_count; TCNT0=0x00;
if (!first_pulse) { // time=((OVF_count*256+a)/16); lcd_clear(); lcd_gotoxy(1,0); sprintf(s,"%u", b); lcd_puts(s); lcd_gotoxy(1,1); sprintf(s,"%u", a); lcd_puts(s); OVF_count=0; first_pulse=1; TCNT0=0x00; } else { first_pulse=0; OVF_count=0; }
}
interrupt [TIM0_OVF] void timer0_ovf_isr(void) { OVF_count++; } Генерирую импульсы с периодом 1024 мкс. По идее таймер должен переполниться 64 раза на частоте 16 МГц. Показывает 80. Ну да ладно. Далее - запускаю таймер1. События на нем не обрабатываются, да и вообще, прерывания с него запрещены: TIMSK=0b00000001; Тем не менее, когда я его запускаю: Код / Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 2000,000 kHz // Mode: Ph. & fr. cor. PWM top=ICR1 // OC1A output: Non-Inv. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x80; TCCR1B=0x12; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x4E; ICR1L=0x00; OCR1BH=0x00; OCR1BL=0x00; на экран выводится число переполнений 1248. Если упрощаю поведение таймера : TCCR1A=0x00;TCCR1B=0x01;, то ничего такого нет, показывает 80. Господа, мысли кончились. Почему таймеры не уживаются? кстати, чем больше делитель TIMER1, тем больше улетает величина OVF_count. Если ее не делить, то показывает 96. А уж если включаю TOIE1, то контроллер начинает ресетиться. В TIFR при проверке все время висит 0x3F.
Сообщение отредактировал zheka - Jan 25 2010, 13:39
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|