|
Реализация частотомера обратного счёта на PIC24H, Нужна помощь |
|
|
|
Jul 12 2011, 12:13
|
Местный
  
Группа: Свой
Сообщений: 281
Регистрация: 29-04-08
Из: Москва
Пользователь №: 37 149

|
Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи. Итак, пишется реализация частотомера по алгоритму, приведённому здесьВходные импульсы считаются таймером Т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 входной импульс, причем это происходит совершенно хаотично, никакой закономерности я уловить не смог. Уже довольно долго бьюсь с отладкой, но всё мимо. Будут ли какие-нибудь идеи о причинах такого поведения?
|
|
|
|
|
Jul 12 2011, 21:43
|
self made
   
Группа: Свой
Сообщений: 855
Регистрация: 7-03-09
Из: Toronto, Canada
Пользователь №: 45 795

|
Цитата(shkal @ Jul 12 2011, 08:13)  Добрый день всем. Я попал в некоторый затык и не могу выбраться из него без посторонней помощи. А почему такой извращенный подход? четыре прерывания используются! Зачем опорную частоту надо заводить на пин и считать, когда от нее можно просто затактировать сам процессор? Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения.
|
|
|
|
|
Jul 13 2011, 06:48
|
Местный
  
Группа: Свой
Сообщений: 281
Регистрация: 29-04-08
Из: Москва
Пользователь №: 37 149

|
Цитата(ar__systems @ Jul 13 2011, 00:43)  Зачем опорную частоту надо заводить на пин и считать, когда от нее можно просто затактировать сам процессор? Измеряемую частоту заведите на счетчик, и одно прерывание по Т1 - истечение периода усреднения. Опора на внешний пин не заводится, Т3 считает внутренний клок. Вы ссылку на алгоритм посмотрели, которую я дал? Там извращенность подхода тщательно обсуждается.
|
|
|
|
|
Jul 13 2011, 08:37
|
Местный
  
Группа: Свой
Сообщений: 281
Регистрация: 29-04-08
Из: Москва
Пользователь №: 37 149

|
При любых. Даже очень низких, хоть 1 Гц. Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой.
|
|
|
|
|
Jul 13 2011, 10:09
|
Гуру
     
Группа: Участник
Сообщений: 3 834
Регистрация: 14-06-06
Из: Moscow, Russia
Пользователь №: 18 047

|
Цитата(shkal @ Jul 13 2011, 12:37)  При любых. Даже очень низких, хоть 1 Гц. Тут, похоже, дело не в алгоритме. У меня есть такое впечатление, что, когда один и тот же фронт приходит и на IC1, и на таймер, который она захватывает, то иногда сначала срабатывает таймер, а потом IC, а иногда наоборот, и зависит это от фазы входного фронта по отношению к фронту тактовой. Тогда надо внимательно разглядывать, как устроена синхронизация периферии у этого семейства. А исследовать можно, заведя в качестве счетного какой-нибудь собственный сигнал (OC, PWM) через регулируемую линию задержки (интегрирующую цепочку с формирователем). Тогда можно удостовериться, что проблема в гонках. А вот можно ли обойти - надо подумать...
|
|
|
|
|
Jul 13 2011, 14:10
|
Местный
  
Группа: Свой
Сообщений: 281
Регистрация: 29-04-08
Из: Москва
Пользователь №: 37 149

|
Написал простейшую тестовую программу (инициализацию опускаю). 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 от общего числа циклов. Я в ах.... прострации
Сообщение отредактировал shkal - Jul 13 2011, 14:44
|
|
|
|
|
Jul 13 2011, 21:23
|
Местный
  
Группа: Свой
Сообщений: 281
Регистрация: 29-04-08
Из: Москва
Пользователь №: 37 149

|
Так изучай-не изучай, повлиять на работу ея я всё равно не могу, отключить - тоже. С таймером всё более-менее понятно и оговорено в даташите - время от входного фронта до изменения состояния 0.75-1.75 цикла. 0-1 цикл - задержка синхронизации, то есть обычный Д-триггер, 0.75 цикла - сам таймер. А вот с input capture тайминги не оговариваюся. есть старый аппнот 96 года по семейству pic17, appnote где на стр.8 нарисованы тайминги работы, так как я их себе представлял (но не так, как на самом деле в pic24)
|
|
|
|
|
Jul 14 2011, 11:43
|
Местный
  
Группа: Свой
Сообщений: 281
Регистрация: 29-04-08
Из: Москва
Пользователь №: 37 149

|
Нашёл я ответ в документации на pic24f
Note1 просто убило. -Таймер, ты сколько импульсов сосчитал? -Ну, сэм-восэм, не больше.
|
|
|
|
|
Jul 15 2011, 13:00
|
Местный
  
Группа: Свой
Сообщений: 281
Регистрация: 29-04-08
Из: Москва
Пользователь №: 37 149

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