Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Реализация частотомера обратного счёта на PIC24H
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Все остальные микроконтроллеры > PIC
shkal
Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи.
Итак, пишется реализация частотомера по алгоритму, приведённому здесь
Входные импульсы считаются таймером Т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() и отображается на ЛСД.

CODE
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;

}
}



CODE
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 входной импульс, причем это происходит совершенно хаотично, никакой закономерности я уловить не смог.
Уже довольно долго бьюсь с отладкой, но всё мимо. Будут ли какие-нибудь идеи о причинах такого поведения?
ar__systems
Цитата(shkal @ Jul 12 2011, 08:13) *
Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи.


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

Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения.
shkal
Цитата(ar__systems @ Jul 13 2011, 00:43) *
Зачем опорную частоту надо заводить на пин и считать, когда от нее можно просто затактировать сам процессор?
Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения.


Опора на внешний пин не заводится, Т3 считает внутренний клок.
Вы ссылку на алгоритм посмотрели, которую я дал? Там извращенность подхода тщательно обсуждается.
rx3apf
А при каких порядках входной частоты наблюдается проблема ? Всегда или только в области больших величин ? PIC24 я не знаю, а вот что до реализации на AVR из темы по ссылке - там не все так просто, хотя решение есть.
shkal
При любых. Даже очень низких, хоть 1 Гц.
Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то
иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой.
rx3apf
Цитата(shkal @ Jul 13 2011, 12:37) *
При любых. Даже очень низких, хоть 1 Гц.
Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то
иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой.

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

CODE


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.gif
rx3apf
Вдумчиво изучать работу схемы синхронизации. Муторно, противно, но придется...
shkal
Так изучай-не изучай, повлиять на работу ея я всё равно не могу, отключить - тоже. С таймером всё более-менее понятно и оговорено в даташите - время от входного фронта до изменения состояния 0.75-1.75 цикла.
0-1 цикл - задержка синхронизации, то есть обычный Д-триггер, 0.75 цикла - сам таймер. А вот с input capture тайминги не оговариваюся. есть старый аппнот 96 года по семейству pic17, appnote где на стр.8 нарисованы тайминги работы, так как я их себе представлял (но не так, как на самом деле в pic24)
rx3apf
Я бы: 1) разобрался с синхронизацией IC (в том числе для экспериментов используя регулируемую задержку импульса от какого-нибудь собственного выхода); 2) попробовал бы общую внешнюю синхронизацию, отдельной схемой. Но тут надо чесать репу, я PIC24 не знаю совершенно...
shkal
Нашёл я ответ в документации на pic24f
Нажмите для просмотра прикрепленного файла

Note1 просто убило.
-Таймер, ты сколько импульсов сосчитал?
-Ну, сэм-восэм, не больше.
rx3apf
Ну, может быть, не все так страшно, если сделать внешнюю схему синхронизации IC (половинка 74). Вот только поэкспериментировать с фазировкой. Проверять надо, конечно...
shkal
Отказался в итоге от захвата таймера, считающего входную частоту. Захватываю только таймер, считающий опору, а в прерывании от IC1 просто читаю входной таймер первой командой. При высокой входной частоте этот таймер, конечно, успевает сосчитать несколько фронтов за время входа в прерывание, но, поскольку это время однинаково в начале и конце цикла измерения, то ошибка не возникает. Ессно, при такой схеме требуется , что приоритет прерывания IC1 был наивысшим.
Там есть один тонкий момент, связаный с возможностью совпадения прерывания от IC1 и прерывания от переполнения таймеров, но это вроде решаемо, тут где-то была ветка на эту тему.
Марк_Я
Совершенно странное решение, прямо следующее из лени ТС прочитать даташит и референсный мануал на PIC24H.
Первое. Нет необходимости дважды входить в прерывание по IC. Для этого есть стек FIFO у этого модуля на 4 захвата. Ставим прерывание по 2 событиям захвата и дважды читаем буфер. Из второго чтения вычитаем первое.
Второе. Метод в том виде, в котором его применил ТС абсурден. Он имеет смысл лишь при ВНЕШНЕЙ опоре. Т.е. в этом случае частота МК не имеет значения, поскольку метод ратиометрический - одним и тем же интервалом меряется и опора и измеряемый сигнал. Если опора - это системная частота, то результат ее захвата будет КОНСТАНТОЙ. Ну какого ... ее мерять???
Третье. Модуль захвата. НИКОГДА захват не может быть асинхронным. ВСЕГДА захват происходит не фронтом сигнала, а ближайшим предыдущим (или последующим, зависит от схемотехники модуля) фронтом системной частоты. Иначе можно защелкнуть гонки фронтов таймера/счетчика.
shkal
Уважаемый, читайте внимательно алгоритм по ссылке из первого поста, вы его нифига не поняли.
Марк_Я
Да нет, любезный автор... Это Вы меряете в качестве опоры кварц МК и формируете интервал счета тоже от кварца МК. Очевидно, что результат такого измерения будет КОНСТАНТА.
Интервал измерения опоры и сигнала один и тот же. Это и есть РАТИОМЕТРИЯ.
shkal
Интервал счета формируется от фронтов входного сигнала
Марк_Я
Да ужжж.
Тогда виноват - не понял источника на ссылке.
В таком варианте - это классический периодомер с делителем по входу.
Я за последний год на 24-х и 33-х пиках таких сделал четыре в составе разных изделий.
Всегда использовал модуль захвата с парными событиями захвата при формировании прерываний.
Делал скользящее окно по кольцевому буферу массива результатов по максимуму (исходя из потребной реакции системы) и получал от 5 до 7 разрядов вполне стабильных показаний (в зависимости от стабильности источника сигнала)ю
Как уже ранее говорил, несинхронность опоры и сигнала приводит к ошибке счета в один период опорной частоты. Но при увеличении интервала (коэффициента деления и длины массива накопления) счета эта ошибка становится несущественной.
Посути это увеличение разрешения через оверсемплинг.
shkal
Если я правильно понял, у вас захват двух последовательных фронтов входного сигнала, т.е. измеряется время одного периода. В этом случае нужна информация о приблизительной входной частоте, иначе как определить нужный коэфициент деления? В алгоритме , описанном GM, изменение коэфициента деления происходит как-бы автоматически при изменении входной частоты таким образом, чтобы время измерения оставалось постоянным. Кроме того, этот алгоритм не имеет нижней граничной частоты - он просто ждет второго фронта, если он не появился за время гейта. Еще вот какой вопрос-очищается ли у ПИК24 ФИФО захвата при выключении модуля захвата?
Марк_Я
С переполнениями таймера захвата можно решить вопрос путем их счета. Есть разные варианты. Или программный стек для них, либо просто таймаут (фиксация нижней частоты).
Измеряю я не обязательно один входной период, а несколько - это по сути накопление.
Коэффициент деления должен быть предварительно расчитан, либо вначале произведено грубое измерение, а затем выбран делитель для входной частоты (или опорной).
Сброс стека скорее всего есть, но для меня это было неактуально - модуль нигде не выключается.
Все мои случаи меряют достаточно узкий диапазон частот - это специальные частотомеры. Один на 5...10 МГц, остальные низкочастотные (10...30 кГц и два на 58 +/-5кГц).
PS. Вспомнил... Еще одним я меряю период сети для формирования трех фаз из одной. Там разрешение в 32 двоичных разряда и диапазон работы 40...80 Гц.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.