shkal 0 12 июля, 2011 Опубликовано 12 июля, 2011 · Жалоба Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи. Итак, пишется реализация частотомера по алгоритму, приведённому здесь Входные импульсы считаются таймером Т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 входной импульс, причем это происходит совершенно хаотично, никакой закономерности я уловить не смог. Уже довольно долго бьюсь с отладкой, но всё мимо. Будут ли какие-нибудь идеи о причинах такого поведения? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
slavka012 0 12 июля, 2011 Опубликовано 12 июля, 2011 · Жалоба Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи. А почему такой извращенный подход? четыре прерывания используются! Зачем опорную частоту надо заводить на пин и считать, когда от нее можно просто затактировать сам процессор? Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
shkal 0 13 июля, 2011 Опубликовано 13 июля, 2011 · Жалоба Зачем опорную частоту надо заводить на пин и считать, когда от нее можно просто затактировать сам процессор? Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения. Опора на внешний пин не заводится, Т3 считает внутренний клок. Вы ссылку на алгоритм посмотрели, которую я дал? Там извращенность подхода тщательно обсуждается. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rx3apf 0 13 июля, 2011 Опубликовано 13 июля, 2011 · Жалоба А при каких порядках входной частоты наблюдается проблема ? Всегда или только в области больших величин ? PIC24 я не знаю, а вот что до реализации на AVR из темы по ссылке - там не все так просто, хотя решение есть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
shkal 0 13 июля, 2011 Опубликовано 13 июля, 2011 · Жалоба При любых. Даже очень низких, хоть 1 Гц. Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rx3apf 0 13 июля, 2011 Опубликовано 13 июля, 2011 · Жалоба При любых. Даже очень низких, хоть 1 Гц. Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой. Тогда надо внимательно разглядывать, как устроена синхронизация периферии у этого семейства. А исследовать можно, заведя в качестве счетного какой-нибудь собственный сигнал (OC, PWM) через регулируемую линию задержки (интегрирующую цепочку с формирователем). Тогда можно удостовериться, что проблема в гонках. А вот можно ли обойти - надо подумать... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
shkal 0 13 июля, 2011 Опубликовано 13 июля, 2011 (изменено) · Жалоба Написал простейшую тестовую программу (инициализацию опускаю). 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 от общего числа циклов. Я в ах.... прострации Изменено 13 июля, 2011 пользователем shkal Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rx3apf 0 13 июля, 2011 Опубликовано 13 июля, 2011 · Жалоба Вдумчиво изучать работу схемы синхронизации. Муторно, противно, но придется... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
shkal 0 13 июля, 2011 Опубликовано 13 июля, 2011 · Жалоба Так изучай-не изучай, повлиять на работу ея я всё равно не могу, отключить - тоже. С таймером всё более-менее понятно и оговорено в даташите - время от входного фронта до изменения состояния 0.75-1.75 цикла. 0-1 цикл - задержка синхронизации, то есть обычный Д-триггер, 0.75 цикла - сам таймер. А вот с input capture тайминги не оговариваюся. есть старый аппнот 96 года по семейству pic17, appnote где на стр.8 нарисованы тайминги работы, так как я их себе представлял (но не так, как на самом деле в pic24) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rx3apf 0 13 июля, 2011 Опубликовано 13 июля, 2011 (изменено) · Жалоба Я бы: 1) разобрался с синхронизацией IC (в том числе для экспериментов используя регулируемую задержку импульса от какого-нибудь собственного выхода); 2) попробовал бы общую внешнюю синхронизацию, отдельной схемой. Но тут надо чесать репу, я PIC24 не знаю совершенно... Изменено 13 июля, 2011 пользователем rx3apf Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
shkal 0 14 июля, 2011 Опубликовано 14 июля, 2011 · Жалоба Нашёл я ответ в документации на pic24f Note1 просто убило. -Таймер, ты сколько импульсов сосчитал? -Ну, сэм-восэм, не больше. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rx3apf 0 14 июля, 2011 Опубликовано 14 июля, 2011 · Жалоба Ну, может быть, не все так страшно, если сделать внешнюю схему синхронизации IC (половинка 74). Вот только поэкспериментировать с фазировкой. Проверять надо, конечно... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
shkal 0 15 июля, 2011 Опубликовано 15 июля, 2011 · Жалоба Отказался в итоге от захвата таймера, считающего входную частоту. Захватываю только таймер, считающий опору, а в прерывании от IC1 просто читаю входной таймер первой командой. При высокой входной частоте этот таймер, конечно, успевает сосчитать несколько фронтов за время входа в прерывание, но, поскольку это время однинаково в начале и конце цикла измерения, то ошибка не возникает. Ессно, при такой схеме требуется , что приоритет прерывания IC1 был наивысшим. Там есть один тонкий момент, связаный с возможностью совпадения прерывания от IC1 и прерывания от переполнения таймеров, но это вроде решаемо, тут где-то была ветка на эту тему. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
my504 2 18 июля, 2011 Опубликовано 18 июля, 2011 · Жалоба Совершенно странное решение, прямо следующее из лени ТС прочитать даташит и референсный мануал на PIC24H. Первое. Нет необходимости дважды входить в прерывание по IC. Для этого есть стек FIFO у этого модуля на 4 захвата. Ставим прерывание по 2 событиям захвата и дважды читаем буфер. Из второго чтения вычитаем первое. Второе. Метод в том виде, в котором его применил ТС абсурден. Он имеет смысл лишь при ВНЕШНЕЙ опоре. Т.е. в этом случае частота МК не имеет значения, поскольку метод ратиометрический - одним и тем же интервалом меряется и опора и измеряемый сигнал. Если опора - это системная частота, то результат ее захвата будет КОНСТАНТОЙ. Ну какого ... ее мерять??? Третье. Модуль захвата. НИКОГДА захват не может быть асинхронным. ВСЕГДА захват происходит не фронтом сигнала, а ближайшим предыдущим (или последующим, зависит от схемотехники модуля) фронтом системной частоты. Иначе можно защелкнуть гонки фронтов таймера/счетчика. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
shkal 0 20 июля, 2011 Опубликовано 20 июля, 2011 · Жалоба Уважаемый, читайте внимательно алгоритм по ссылке из первого поста, вы его нифига не поняли. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться