Частотомер на LGT8F328P (Arduino)

На базе микроконтроллера LGT8F328P можно сделать частотомер, с достаточно большой точностью и стабильностью измерений. Показания частоты выводятся на 8-и разрядный семисегментный индикатор на базе микросхемы max7219.

Микросхема MAX7219 – это компактный драйвер индикатора, который позволяет управлять 7-сегментными индикаторами разрядностью до 8 цифр или 64 отдельными светодиодами. Драйвер управляется по последовательному интерфейсу SPI.

Микроконтроллер LGT8F328P содержит 4 таймера, два из которых 8-и разрядные и два 16-и разрядные. На основе двух 16-и битный таймеров будет осуществлено измерение частоты.

Схема частотомера очень простая:

На вход D5 подается сигнал уровня TTL, а на выходе D11 присутствует импульсный сигнала (меандр) для тестирования работы частотомера.

Частотомер работает следующим образом — на вход Т1 Timer_1 подается входной сигнал (TTL), Timer_1 сконфигурирован на работу с внешним тактовыми импульсами источником которого является измеряемый сигнал.

 TCCR1A = 0;TCCR1B = 0;TCNT1=0;
  TCCR1B = (1 << CS12) | (1 << CS11) | (1 << CS10);
  TIMSK1 = (1 << TOIE1);
  TCKCSR = (1 << F2XEN) | (1 << TC2XS1);

Биты CS12 CS11 CS10 регистра TCCR1B настроены на внешний режим тактирования:

Бит TOIE1 регистра TIMSK1 переводит таймер в режим — сброс при переполнении (прерывание).

Биты F2XEN TC2XS1 регистра TCKCSR запускаю высокоскоростной режим синхронизации.

Если на вход Т1 таймера поступают тактовые импульсы, счетчик TCNT1 начинает отсчет импульсов до максимального значения 65535 (при условии что входной сигнал имеет частоту выше 65535 Гц) далее он обнуляется и происходит прерывание. В обработчике прерывания указан следующий код:

ISR(TIMER1_OVF_vect) {tic++;}

Этот код просто подсчитывает кол-во прерываний.

Timer_3 имеет разрешение 16 бит, его задача состоит в точном отсчете времени измерения в 1 секунду.

 TCCR3B = (1 << CS32) | (1 << WGM32);
  TIMSK3 = (1 << OCIE3A);
  OCR3A = CAL;

Бит CS32 регистра TCCR3B устанавливает предделитель на 256, бит WGM32 активирует режим СТС (сброс при совпадении счетного регистра TCNT1 с регистром сравнения OCR3A).

Регистр OCR3A содержит значение CAL отмеряющее 1 секунду.

При совпадении счетного регистра TCNT1 с регистром сравнения OCR3A происходит прерывание:

ISR(TIMER3_vect) {
  if (TIFR3 & (1 << OCF3A)){
      TIFR3 = 1 << OCF3A;
      freq = tic*0xFFFF + TCNT1;
      tic=0;TCNT1=0;
      w=1;
  }
}

В обработчике прерывания Timer_3 происходит подсчет частоты:

freq = tic*0xFFFF + TCNT1;

то есть кол-во прерываний Timer_1 умножается на 65535, так как одно прерывание содержит 65535 отсчетов счетного регистра TCNT1 и далее к полученному значению прибавляется остаток счета в регистре TCNT1.

После чего кол-во прерываний Timer_1 и показания счетного регистра обнуляются, а в переменной freq содержится значение измеренной частоты, которые выводится на восьми разрядный семисегментный индикатор.

#define DIN PD4
#define CLK PD2
#define CS  PD3
#define CAL 62502; //62499 = 1 sec 
 
static unsigned long tic,freq;
bool w=1;
byte fq[8],pd,x;
 
void setup(){
  PORTD |= (1 << PD5); // подтягивающий резистор на PD5 (вход T1)
  DDRB |= (1 << PB3); // D11 OUTPUT 8 MHz
  _delay_ms(2);
  DDRD |= (1 << DIN) | (1 << CS) | (1 << CLK);
  PORTD |= (1 << CS);
  WriteBit16(0x0F, 0);// тест выкл.
  WriteBit16(0x0C, 1);// вкл. индик.
  WriteBit16(0x0A, 0);// яркость
  WriteBit16(0x09, 0xFF);// дешифраторы вкл.
  WriteBit16(0x0B, 7);// кол-во разрядов
  cl();
  _delay_ms(2);
  noInterrupts();
// TIMER_1 INPUT T1
  TCCR1A = 0;TCCR1B = 0;TCNT1=0;
  TCCR1B = (1 << CS12) | (1 << CS11) | (1 << CS10);
  TIMSK1 = (1 << TOIE1);
  TCKCSR = (1 << F2XEN) | (1 << TC2XS1);
// TIMER_3 1 SEC
  TCCR3A = 0;
  TCCR3B = 0;
  TCNT3=0;
  TCCR3B = (1 << CS32) | (1 << WGM32);
  TIMSK3 = (1 << OCIE3A);
  OCR3A = CAL;
// TIMER_2 OUTPUT 8 MHz
TCCR2A = 0;TCCR2B = 0;TCNT2=0;
  TCCR2A = 1 << COM2A0 |1 << WGM21;
  TCCR2B = 1 << CS20;
  OCR2A = 0;
  interrupts();
}
 
void loop(){
 if(w==1){w=0;
  if(freq>=10000000){WriteBit16(0x0B, 7);x=8;}
  if(freq<10000000&&freq>=1000000){WriteBit16(0x0B, 6);x=7;}
  if(freq<1000000&&freq>=100000){WriteBit16(0x0B, 5);x=6;}
  if(freq<100000&&freq>=10000){WriteBit16(0x0B, 4);x=5;}
  if(freq<10000&&freq>=1000){WriteBit16(0x0B, 3);x=4;}
  if(freq<1000&&freq>=100){WriteBit16(0x0B, 2);x=3;}
  if(freq<100&&freq>=10){WriteBit16(0x0B, 1);x=2;}
  if(freq<10){WriteBit16(0x0B, 0);x=1;}
 
  fq[7]= freq/10000000%10;
  fq[6]= freq/1000000%10|0b10000000;
  fq[5]= freq/100000%10;
  fq[4]= freq/10000%10;
  fq[3]= freq/1000%10|0b10000000;
  fq[2]= freq/100%10;
  fq[1]= freq/10%10;
  fq[0]= freq%10%10;
 
  for(int i=0;i<8;i++){WriteBit16(i+1, fq[i]);}
  }
}
 
ISR(TIMER1_OVF_vect) {tic++;}
 
ISR(TIMER3_vect) {
  if (TIFR3 & (1 << OCF3A)){
      TIFR3 = 1 << OCF3A;
      freq = tic*0xFFFF + TCNT1;
      tic=0;TCNT1=0;
      w=1;
  }
}
void cl(){for(char i=1;i<=8;i++){WriteBit16(i, 0);}}
void WriteBit16(byte reg, byte data){  
     PORTD &= ~(1 << CLK);PORTD &= ~(1 << CS);
     for(int i = 7; i >= 0; i--){
        if(((reg >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);}
        PORTD |=(1 << CLK);PORTD &= ~(1 << CLK);
        }
     for(int i = 7; i >= 0; i--){
        if(((data >> i) & 1) == 1){PORTD |= (1 << DIN);}else{PORTD &= ~(1 << DIN);}
        PORTD |=(1 << CLK);PORTD &= ~(1 << CLK);
        }
     PORTD &= ~(1 << CLK);PORTD &= ~(1 << DIN);PORTD |=(1 << CS);
  }

При наличии очень точного импульсного генератора можно откалибровать частотомер при помощи значения счетного регистра:

#define CAL 62502; //62499 = 1 sec

При прошивке микроконтроллера рекомендую ознакомится со следующей статьей —  LGT8F328P-LQFP32 MiniEVB в Arduino IDE , версия платы не ниже 2.0.0

Так же при конфигурировании платы в Arduino IDE руководствуйтесь этим скриншотом:

Форум — http://forum.rcl-radio.ru/viewtopic.php?id=535

Максимальная частота измерения мне не известна, так нет в наличии генератора с выходной частотой более 10 МГц.

Добавить комментарий

Войти с помощью: