|
Затраты на обработку прерывания по переполнению таймера. |
|
|
|
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; Что делать и кто виноват? У кого нибьудь есть опыт написания таких программ, когда приходилось несколько циклов переполнения таймера складывать. Как-нибудь компенсировали время, затрачиваемое на обработку прерывания?
|
|
|
|
|
Jan 21 2010, 18:34
|

Местный
  
Группа: Свой
Сообщений: 211
Регистрация: 6-08-07
Из: Приднестровье, Тирасполь
Пользователь №: 29 581

|
Цитата(zheka @ Jan 21 2010, 22:11)  ...Всвязи с тем что переполнение его наступает через каждые 256 тактов, их приходится точно просчитывать... Просчитыванию очень способствуют функции типа: Цитата(zheka @ Jan 21 2010, 22:11)  Код interrupt [TIM0_OVF] void timer0_ovf_isr(void) { ... lcd_gotoxy(0,0); lcd_putsf(" "); lcd_gotoxy(0,0); sprintf(s," %i", a); lcd_puts(s); ...} Как Вы думаете, за сколько тактов они выполнятся и сколько раз за это время переполнится таймер?..
--------------------
Любой, заслуживающий внимания, опыт приобретается себе в убыток...
|
|
|
|
|
Jan 21 2010, 18:52
|
Гуру
     
Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164

|
Код А листинг ассемблера посмотреть не догадались? К сожалению, китайский не изучал))) Никак нельзя этот период стабилизировать? У меня две цифры отображаются - 32 и 33. Опять таки-чисто практический вопрос - имея таймер 8 бит, как посчитать период импульсов? Длительность от 500 мкс до 1 сек. Точность - 1 такт (клок 160 МГц).
|
|
|
|
|
Jan 21 2010, 19:07
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(zheka @ Jan 21 2010, 23:52)  К сожалению, китайский не изучал))) Ну сюда его (листинг) выложите, тут его поймут. Цитата(zheka @ Jan 21 2010, 23:52)  Никак нельзя этот период стабилизировать? Попробуйте использовать для сохранения TCNT0 глобальную или статическую переменную и вынести из обработчика прерывания вызовы любых функций. Пока в обработчике прерывания есть вызовы других функций компилятор будет формировать пролог из блока сохранения всех регистров.
|
|
|
|
|
Jan 21 2010, 19:14
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(zheka @ Jan 21 2010, 20:52)  Никак нельзя этот период стабилизировать? Можно, но для этого придется выучить "китайский" и на китайском переписать пролог, да и собсно тело обработчика тоже. Цитата Опять таки-чисто практический вопрос - имея таймер 8 бит, как посчитать период импульсов? Длительность от 500 мкс до 1 сек. Точность - 1 такт (клок 160 МГц). 160Mhz это не опечатка? Вроде в ветке по AVR находимся... стало быть 16Mhz. В свете того что 8-ми битный таймер как правило обделенный, то есть только один вариант - очень и очень долго мерять и с помощью мат статисткики отсеивать результаты с большой погрешностью. А если хотите быстро, тогда обязательно нужен input capture и хотя бы поверхностное знание китайского.
|
|
|
|
|
Jan 22 2010, 04:54
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(zheka @ Jan 22 2010, 07:54)  на каждые 256 тактов будет приходиться 32 такта бесполезной работы, то есть 12,5% времени псу под хвост.  Вы чего там надумали? Код // Registers used // SaveSREG - keep SREG during ISR execution // TickL, TickH - store 16-bit overflow data timer0_ovf_isr: in SaveSREG,SREG //1 inc TickL//1 brne label1//2 inc TickH//1 label1: out SREG,SaveSREG //1 reti //4 //Total: 4+1+1+2+1+1=10 clocks + interrupt response time Комментировать очевидное не буду. в помощь
|
|
|
|
|
Jan 22 2010, 06:15
|
Местный
  
Группа: Свой
Сообщений: 256
Регистрация: 6-03-06
Из: Украина, г. Винница
Пользователь №: 15 017

|
Цитата Длительность от 500 мкс до 1 сек. Точность - 1 такт (клок 160 МГц). Такую точность можно получить только используя регистр захвата + прерывание по захвату. И, само собой разумеется, считать прерывания по переполнению. А в прерывании по захвату (после второго перепада) анализировать значения TCNT и ICR. Если TCNT < ICR, то последнее переполнение не учитывать.
|
|
|
|
|
Jan 22 2010, 09:09
|
Участник

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

|
Цитата Опять таки-чисто практический вопрос - имея таймер 8 бит, как посчитать период импульсов? Длительность от 500 мкс до 1 сек. Точность - 1 такт (клок 16 МГц). При периоде импульсов 1 сек, получается точность измерения 0.00000625%. Что это за такие импульсы, которые требуют такую точность измерения?
|
|
|
|
|
Jan 22 2010, 10:41
|
Участник

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

|
Тогда какой смысл измерять с таким разрешением (0.0625 мкс), когда достаточно для точности 0.005% разрешения 25мкс? Или может у автора стоит прецизионный кварцевый генератор с точностью 1ppm и он делает прецизионный частотомер?
Сообщение отредактировал fantex - Jan 22 2010, 10:43
|
|
|
|
|
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
|
|
|