Перейти к содержанию
    

Затраты на обработку прерывания по переполнению таймера.

Все господа.... мысли кончились. Не гонясь за точностью, пытаюсь сделать хоть что-то.

Завел Timer0 на 16 МГц. ПО переполнению инкрементирую OVF_count - счетчик переполнений. Timer1 в режиме PWM настроен на период 20 мс, выход поступает на вход INT0, фронту импульса запускается обработчик, который умножает OVF_count на 256 , прибавляет текущее значение TCNT0, после чего делит на 16 - получаем период в микросекундах. У меня же проказывает не 20 000 мкс, а 1610 либо 1594. Вот код, где я не прав?

#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
Нарушение п.3.4 Правил форума.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот код, где я не прав?
Прикиньте: какое значение переменной OVF_count ? А, если умножить это значение на 256 ? А, поместиться ли полученное значение в int ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хм.. а вот тут уже незнание мной матчасти, вопрос вообще можно было бы выделить в отдельную тему.

Формула у меня такая: 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Timer1 в режиме PWM настроен на период 20 мс, выход поступает на вход INT0

А почему не используете одно из прерываний таймера? зачем такой обходной путь?

time=(OVF_count*256+a)/16;

Установите предделитель таймера на 8 и Вам для 20 мс хватит разрядности unsigned int.

Тогда time=(OVF_count*256+a)/2;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Установите предделитель таймера на 8 и Вам для 20 мс хватит разрядности unsigned int.
Разрядности и так хватит. Можно записать выражение, например, так : time=OVF_count*16+a/16;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А почему не используете одно из прерываний таймера? зачем такой обходной путь?

 

Александр, если вы мне подскажете способ, как сделать на 8-битном таймере PWM периодом 20 мс, чтобы освободить Timer1, или как на одном Timer1 сделать одновременно и PWM и измерение периода импульсов, то я откажусь от INT0

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дискретность? думаю, что той что обеспечит 8-битный таймер хватит. ДЛина импульса от 0.8 до 2 мс.

Насколько я понял, на 8-битном таймере максимальный период при наибольшем коэффициенте деления будет 16 мс. БОльше не сделаешь? МОжет добавить в обработчик переполнения задержку в 4 мс?

Изменено пользователем zheka

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем, конфликтуют у меня два таймера. Переделал на TImer1 - все нормально, генерирую в цикле импульс периодом 500 мкс (через delay_ms) показывает 504 мкс, и даже не знаю, может быть именно delay_us вносит эту погрешность.

Но, как только разрешаю прерывание по переполнению Timer0 - сразу скачет на 553 мкс, причем показания нестабильны. Даже запрет прерываний на время обработки и вывода на экран не помогают...

Изменено пользователем zheka

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот пример реализации измерения частоты импульсов на CPU188-MX5.

Основная идея способа - считать количество импульсов и время между передними фронтами импульсов.

Если время между фронтами превышает заданное (в данном случае 3 секунды), то полученые значения кол-ва импульсов

и время между фронтами копируется в соответствующие переменные, из которых внешняя функция вычисляет уже частоту импульсов.

 

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Fantex, спасибо, но, с алгоритмом я разобрался, у меня не получаются детали и конкретная реализация на конкретном камне. КРоме того, я пишу на С.

Метод подсчета импульсов мне ну никак не подходит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Камень 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Почему таймеры не уживаются?
Вы предлагаете погадать? Ну, например, разрешены прерывания от таймера 1, а процедура обработки прерывания не написана. При прерываниях от таймера 1 МК попадает на вектора 7-10, но там не команды перехода. МК добирается до векторов таймера 0 (на вектор 12, например), и благополучно попадает на функцию обработки его (таймера 0) прерывания по переполнению...

 

Совет: выбросить вывод на LCD из обработчика прерывания таймера: поставьте взведение флага вывода в прерывании - перенесите сам вывод в main во внутрь бесконечного цикла с проверкой флага.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Палыч, вообще-то прерывания тапймера1 запрещены. ПОпробую ваш совет про вывод в main

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...