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

Реализация частотомера обратного счёта на PIC24H

Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи.

Итак, пишется реализация частотомера по алгоритму, приведённому здесь

Входные импульсы считаются таймером Т2, опорные - Т3. Входы IC1 IC2 T2CK - физически один пин, мэппинг с помощью PPS. IC1 захватывает Т2, IC2 захватывает T3.

Функция F_init() инициализирует переферию.

Функция F_start() запускает цикл измерения частоты.

По первому прерывнию от IC1 сохраняются пары значений F_osc_start, F_in_start и счётчики переполнения таймеров over2_start, over3_start.

Далее модули IC1 IC2 выключаются и запускается на время gate_time таймер Т5. По его прерыванию модули IC1 IC2 опять включаются, и после очередного фронта входного сигнала в прерывании IC1

сохраняются пары значений F_osc_end, F_in_end и счётчиков переполнения таймеров over2_end, over3_end. Устанавливается флаг new_data.

В main'е крутиться простейшая state machine, после установки флага new_data значение частот вычисляется функцией F_calc() и отображается на ЛСД.

 

main() {

F_init();
while(1)
{


switch (main_state)
	{
	case 0: 

			F_start(200);	
			main_state=1;
			break;

	case 1: 	

			if (new_data)
			{
				Freq=F_calc();
				LCDWriteCmd(0x01); // clear display
				delay_ms(2);
				if (Freq == 0)
					{
						LCDputstr ("NO SIGNAL");
					}				
				else 
					{
						sprintf(F_display, "%15f", Freq);
						LCDputstr(F_display);

					}
				new_data=0;
				main_state=0; 
			}

			break;

	}
}

 

 

void F_init(void)
{
// init timer2 for external clock

T2CONbits.TON = 0; // Disable Timer
T2CONbits.TCS = 1; // Select external clock
T2CONbits.TGATE = 0; // Disable Gated Timer mode
T2CONbits.TCKPS = 0b00; // 1:1 prescaler
T2CONbits.T32 = 0; // 16bit mode
TMR2 = 0x00; // Clear timer register
PR2=0xffff;
IPC1bits.T2IP = 6; // INT priority=6
IFS0bits.T2IF = 0; // Clear Timer1 Interrupt Flag
IEC0bits.T2IE = 1; // Enable Timer1 interrupt


// init timer 3

T3CONbits.TON = 0; // Disable Timer
T3CONbits.TCS = 0; // Select internal clock
T3CONbits.TCKPS = 0b00; // 1:1 prescaler
T3CONbits.TGATE = 0; // Disable Gated Timer mode
TMR3 = 0x00; // Clear timer register
PR3=0xffff;
IPC2bits.T3IP = 6; //INT priorty=6
IFS0bits.T3IF = 0; // Clear Timer1 Interrupt Flag
IEC0bits.T3IE = 1; // Enable Timer1 interrupt	


// init timer 5 for 0.2c 

T5CONbits.TON = 0; // Disable Timer
T5CONbits.TCS = 0; // Select internal clock
T5CONbits.TCKPS = 0b11; // 1:256 prescaler
T5CONbits.TGATE = 0; // Disable Gated Timer mode
TMR5 = 0x00; // Clear timer register
PR5=2879;
IPC7bits.T5IP = 4; //INT priorty=4
IFS1bits.T5IF = 0; // Clear Timer1 Interrupt Flag
IEC1bits.T5IE = 1; // Enable Timer1 interrupt	


// init input capture 1 on Timer 2

IC1CONbits.ICM= 0b000; // Disable Input Capture 1 module
IC1CONbits.ICTMR= 1; // Select Timer2 as the IC2 Time base
IC1CONbits.ICI= 0b00; // Interrupt on every capture event
IPC0bits.IC1IP = 6; // Setup IC1 interrupt priority level
IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupt


// init input capture 1 on Timer 3

IC2CONbits.ICM= 0b000; // Disable Input Capture 2 module
IC2CONbits.ICTMR= 0; // Select Timer3 as the IC2 Time base
IC2CONbits.ICI= 0b00; // Interrupt on every capture event - INT from IC2 DISABLED!!!
//	IPC0bits.IC1IP = 6; // Setup IC1 interrupt priority level
//	IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
//	IEC0bits.IC1IE = 1; // Enable IC1 interrupt

gate_flag=0;




}


void F_start (unsigned int gate_time)

{
unsigned int scale;



T5CONbits.TON = 0;
scale = gate_time*(3125/(TOSC*8)) ;
TMR5 = 0x00; 			// Clear timer register
PR5=scale;
new_data=0;
T2_over=0;				//Clear TMR2 overflow
T3_over=0;				//Clear TMR3 overflow
TMR2 = 0x00;  			//Clear timer 2
TMR3 = 0x00;  			//Clear timer 3
T2CONbits.TON = 1; 		//Start Timer 2
T3CONbits.TON = 1;		//Start Timer 3
IC1CONbits.ICM= 0b011; 	// Enable IC1, every rising edge
IC2CONbits.ICM= 0b011; 	// Enable IC2, every rising edge

}

void __attribute__((__interrupt__)) _T2Interrupt( void )
{
	T2_over++ ;
		IFS0bits.T2IF = 0;				/* reset timer interrupt flag	*/
}


void __attribute__((__interrupt__)) _T3Interrupt( void )
{
	T3_over++ ;
		IFS0bits.T3IF = 0;				/* reset timer interrupt flag	*/
}


void __attribute__((__interrupt__)) _T5Interrupt( void )
{

	T5CONbits.TON = 0; 
	IC1CONbits.ICM= 0b011; //Enable IC1 , on every rising edge
	IC2CONbits.ICM= 0b011; //Enable IC2 , on every rising edge
	IFS1bits.T5IF = 0;				/* reset timer interrupt flag	*/
}

// Capture Interrupt Service Routine

void __attribute__((__interrupt__)) _IC1Interrupt(void)
{
unsigned int tmp;




if (!gate_flag) 
{
	F_osc_start=IC2BUF;
	IC2CONbits.ICM= 0b000; //Turn OFF IC2
	F_in_start=IC1BUF;
	IC1CONbits.ICM= 0b000; //Turn OFF IC1 module
	over2_start=T2_over;
	over3_start=T3_over;
	while (IC1CONbits.ICBNE)
	{
		tmp=IC1BUF;
	}
	while (IC2CONbits.ICBNE)
	{
		tmp=IC2BUF;
	}	
	gate_flag=1;
	T5CONbits.TON = 1; //start timer5
}
else
{
	F_osc_end=IC2BUF;
	IC2CONbits.ICM= 0b000; //Turn OFF IC2
	F_in_end=IC1BUF;
	IC1CONbits.ICM= 0b000; //Turn OFF IC1 module
	over2_end=T2_over;
	over3_end=T3_over;
	T2CONbits.TON = 0; 		//Stop Timer 2
	T3CONbits.TON = 0;		//Stop Timer 3
	while (IC1CONbits.ICBNE)
	{
		tmp=IC1BUF;
	}
	while (IC2CONbits.ICBNE)
	{
		tmp=IC2BUF;
	}	
	gate_flag=0;	
	new_data=1;
}

IFS0bits.IC1IF=0; //reset interrupt flag
}

float F_calc(void)

{
float F;
float M;	//input pulse count
float N;	//clock pulse count
unsigned long A1,A2 ;




A1=((unsigned long)(over2_end-over2_start))<<16;
A2=((unsigned long)(over3_end-over3_start))<<16;
M=A1+(F_in_end-F_in_start);
N=A2+(F_osc_end-F_osc_start);
if (M == 0) F=0;
else F=(OSCIL*M)/N;
return F;
}

 

 

Проблема заключается в том, что данный код примерно в 80% циклов выдаёт правильное значение частоты, а в 20% - таймер Т2 пропускает 1 входной импульс, причем это происходит совершенно хаотично, никакой закономерности я уловить не смог.

Уже довольно долго бьюсь с отладкой, но всё мимо. Будут ли какие-нибудь идеи о причинах такого поведения?

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


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

Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи.

 

А почему такой извращенный подход? четыре прерывания используются! Зачем опорную частоту надо заводить на пин и считать, когда от нее можно просто затактировать сам процессор?

 

Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения.

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


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

Зачем опорную частоту надо заводить на пин и считать, когда от нее можно просто затактировать сам процессор?

Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения.

 

Опора на внешний пин не заводится, Т3 считает внутренний клок.

Вы ссылку на алгоритм посмотрели, которую я дал? Там извращенность подхода тщательно обсуждается.

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


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

А при каких порядках входной частоты наблюдается проблема ? Всегда или только в области больших величин ? PIC24 я не знаю, а вот что до реализации на AVR из темы по ссылке - там не все так просто, хотя решение есть.

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


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

При любых. Даже очень низких, хоть 1 Гц.

Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то

иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой.

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


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

При любых. Даже очень низких, хоть 1 Гц.

Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то

иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой.

Тогда надо внимательно разглядывать, как устроена синхронизация периферии у этого семейства. А исследовать можно, заведя в качестве счетного какой-нибудь собственный сигнал (OC, PWM) через регулируемую линию задержки (интегрирующую цепочку с формирователем). Тогда можно удостовериться, что проблема в гонках. А вот можно ли обойти - надо подумать...

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


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

Написал простейшую тестовую программу (инициализацию опускаю). IC1 захватывает TMR3.

 


int main()
{
while (1) {};
}

void __attribute__((__interrupt__)) _IC1Interrupt(void)
{
unsigned int a,b;


b=TMR3;
a=IC1BUF;
if (a==b ) 
{
	ok1++;
}
else
{
	error1++;
}
counter++;
IFS0bits.IC1IF=0; //reset interrupt flag
}

 

 

независимо от входной частоты этот код дает 75% счётчика ок и 25% счётчика error от общего числа циклов. Я в ах.... прострации :wacko:

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

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


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

Вдумчиво изучать работу схемы синхронизации. Муторно, противно, но придется...

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


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

Так изучай-не изучай, повлиять на работу ея я всё равно не могу, отключить - тоже. С таймером всё более-менее понятно и оговорено в даташите - время от входного фронта до изменения состояния 0.75-1.75 цикла.

0-1 цикл - задержка синхронизации, то есть обычный Д-триггер, 0.75 цикла - сам таймер. А вот с input capture тайминги не оговариваюся. есть старый аппнот 96 года по семейству pic17, appnote где на стр.8 нарисованы тайминги работы, так как я их себе представлял (но не так, как на самом деле в pic24)

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


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

Я бы: 1) разобрался с синхронизацией IC (в том числе для экспериментов используя регулируемую задержку импульса от какого-нибудь собственного выхода); 2) попробовал бы общую внешнюю синхронизацию, отдельной схемой. Но тут надо чесать репу, я PIC24 не знаю совершенно...

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

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


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

Нашёл я ответ в документации на pic24f

post-37149-1310643635_thumb.png

 

Note1 просто убило.

-Таймер, ты сколько импульсов сосчитал?

-Ну, сэм-восэм, не больше.

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


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

Ну, может быть, не все так страшно, если сделать внешнюю схему синхронизации IC (половинка 74). Вот только поэкспериментировать с фазировкой. Проверять надо, конечно...

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


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

Отказался в итоге от захвата таймера, считающего входную частоту. Захватываю только таймер, считающий опору, а в прерывании от IC1 просто читаю входной таймер первой командой. При высокой входной частоте этот таймер, конечно, успевает сосчитать несколько фронтов за время входа в прерывание, но, поскольку это время однинаково в начале и конце цикла измерения, то ошибка не возникает. Ессно, при такой схеме требуется , что приоритет прерывания IC1 был наивысшим.

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

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


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

Совершенно странное решение, прямо следующее из лени ТС прочитать даташит и референсный мануал на PIC24H.

Первое. Нет необходимости дважды входить в прерывание по IC. Для этого есть стек FIFO у этого модуля на 4 захвата. Ставим прерывание по 2 событиям захвата и дважды читаем буфер. Из второго чтения вычитаем первое.

Второе. Метод в том виде, в котором его применил ТС абсурден. Он имеет смысл лишь при ВНЕШНЕЙ опоре. Т.е. в этом случае частота МК не имеет значения, поскольку метод ратиометрический - одним и тем же интервалом меряется и опора и измеряемый сигнал. Если опора - это системная частота, то результат ее захвата будет КОНСТАНТОЙ. Ну какого ... ее мерять???

Третье. Модуль захвата. НИКОГДА захват не может быть асинхронным. ВСЕГДА захват происходит не фронтом сигнала, а ближайшим предыдущим (или последующим, зависит от схемотехники модуля) фронтом системной частоты. Иначе можно защелкнуть гонки фронтов таймера/счетчика.

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


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

Уважаемый, читайте внимательно алгоритм по ссылке из первого поста, вы его нифига не поняли.

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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