как считывать частоту посредством ардуино.

akz
Offline
Зарегистрирован: 08.11.2011

 можно ли считывать частоту посредством ардуино и если да то,как?

polk
Offline
Зарегистрирован: 13.03.2011

 Постмотрите в этой теме

http://arduino.ru/forum/programmirovanie/kak-izmerit-chastotu

akz
Offline
Зарегистрирован: 08.11.2011

как я понял частоту можно измерять двумя способами функцией pulseIn и с помощью библиотеки FreqCounter.Я хочу измерять частоту и чтобы значение появлялось в serial.Вот код, как думаете написан правильно?

  1. int val = 0;
    void setup(){
      pinMode(7,OUTPUT);
    Serial.begin(9600);
    }
    void loop(){
    val = pulseIn(7,HIGH)/1000000;переводим микросекунды в секунды
    Serial.println(1/val);val это период поделив его на один мы получаем частоту
    }
step962
Offline
Зарегистрирован: 23.05.2011

akz пишет:

val это период поделив его на один мы получаем частоту

val это не период, а длина импульса. А между импульсами есть еще паузы, когда уровень сигнала LOW.

Кроме того, поделив значение, возвращенное pulseIn, на миллион, вы получите нулик. Почему? Потому шта... Потому что переменная val у вас определена как целое, а значит деление будет выполнено по правилам целочисленной арифметики.

Опять таки, кроме того: измерять частоту на базе одного периода - плохая идея. Ибо точность у вас получится вполне себе "метеорологическая". Плюс-минус два лаптя...

akz
Offline
Зарегистрирован: 08.11.2011

 так ввел изменения в код, что думаете?

int val = 0;
void setup(){
   pinMode(7,OUTPUT);
   Serial.begin(9600);
}
void loop(){
  val = pulseIn(7,HIGH);
  Serial.print("h");
  Serial.print(1000000/val);//так мы получаем частоту т.к 1/(val/1000000) = 1000000/val
  Serial.println("HZ");
  val2 = pulseIn(7,LOW);
  Serial.print("l");
  Serial.print(1000000/val2);
  Serial.println("HZ");
}

От модератора: Я подредактировал. Пользуйтесь кнопкой вставки кода пожалуйста. Ну ведь симпатичнее и легче читается с форматированием-то.

 

step962
Offline
Зарегистрирован: 23.05.2011

 так не получится.

Если вы делаете вызов pulseIn(x,HIGH), то в функции сначала ожидается приход возрастающего фронта, запускается таймер и отсчитывается время до прихода ниспадающего фронта. В вызове pulseIn(x,LOW) все наоборот. Н ведь ниспадающий фронт уже прошел, поэтому функция довольно долго - почти период - стоять в ожидании нового нисъодящего фронта. Более-менее приемлемая точность будет достигаться только на достаточно низких и стабильных частотах.

Это первое.

Второе:

Полный период вашего переменного сигнала складывается из длины пульса ("HIGH") и паузы между пульсами ("LOW"). Это val+val2. Соответственно, для вычисления (приблизительной) частоты деление следует производить на эту сумму.

akz
Offline
Зарегистрирован: 08.11.2011

понятно спасибо за ответ буду пробывать.2 модератор не получается пользоваться кнопкой code

akz
Offline
Зарегистрирован: 08.11.2011

ввел изменения.
int val = 0;
void setup(){
   pinMode(7,INPUT);
Serial.begin(9600);
}
void loop(){
  val = pulseIn(7,HIGH) + pulseIn(7,LOW);
  Serial.print(1000000/val);//так мы получаем частоту т.к 1/(val/1000000) = 1000000/val
  Serial.println("HZ");
}

SergeySB
Offline
Зарегистрирован: 11.10.2015

Всем доброй ночи! Не хотел плодить новую тему, решил написать здесь. Вопрос все тот же - измерение частоты (плата arduino pro mini) . Начал разбираться с таймерами счетчиками и попробовал разобраться в найденных здесь на форуме в том числе примерах.

Вот код:

unsigned long time = 0;        //Время срабатывания датчика
unsigned long time_old = 0;        //Предыдущее время
 
void setup() {
	  attachInterrupt(0, impuls, FALLING);   //Прерывание по нарастающему фронту на D2
          Serial.begin(115200);
	}
	 
	void loop(){
        Serial.println(time);
        delay(10);
        }
	 
	void impuls(){
	 time = (1000000.0/(micros()-time_old)); // измеряем частоту
         time_old = micros();
	}

И второй:

    unsigned long tachBuf;  
     unsigned long tachValue;  


    void setup()      
    {    
       pinMode(8, INPUT);  //Вход для импульсов спидометра    
       Serial.begin(9600);  
       TIM_Init();//инициализация таймера  
      _delay_ms(300);  
    }    
    
    void loop()      
    {  
      cli();    
      tachValue = 16000000/tachBuf;   
      Serial.println(tachValue); 
      tachBuf=1;  
      sei();  
      delay(20);
    } 
    

    void TIM_Init(void)  
    {  
       TIMSK1=(1<<ICIE1);   
       TCCR1A=(0<<COM1A1)|(0<<COM1A0)|(0<<WGM11)|(0<<WGM10);    
       TCCR1B=(1<<ICNC1)|(1<<ICES1)|(0<<WGM13)|(0<<WGM12)|(0<<CS12)|(0<<CS11)|(1<<CS10);  
       TCNT1 = 0;                                             
    } 
    
    ISR(TIMER1_CAPT_vect)  
    {   
        TCNT1 = 0;    
        tachBuf = ICR1;   
    }  

Как быть с точностью в измерениях? Чем большую частоту подаем на вход, тем больше погрешность. Результаты могу сообщить в понедельник, т.к. под рукой нет нормального генератора. Хотелось бы измерять сигналы до 1-5 МГц с достаточной точностью, а не +/- два лаптя.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

SergeySB, в принципе есть готовые библы. FreqMeasure оптимизирован для измерения частот от 0.1 Hz до  1 kHz.  И FreqCount для диапазона от 1 kHz до 8 MHz. Точность да, обратно пропорциональна частоте.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

SergeySB
Offline
Зарегистрирован: 11.10.2015

Спасибо за комментарии!! По поводу библиотек-  гуглил их, прежде чем сюда написать. Хотел пока без них разобраться что и как. Вообще у меня стоит основная задача следующая. Имеется сигнал, частота которого постоянно меняется (от единиц килогерц до 30-40 МГц). И необходимо, например измерять длительность каждого положительного импульса, и выводить все это в порт. Понятно, что моей ардуины для такой задачи не хватит, потому что тактовая частота маленькая, скорость обмена по Serial низкая, и тогда просто данные не будут успевать уходить... Но хотелось бы все-таки пока попытаться реализовать задачу на ардуине, снизив частоту исходного сигнала мегагерц до 5. Может попытаться применить счетчик на входе ардуины для деления входного сигнала. Пока мысли такие - грубо: считывать одну из ног порта, куда подается изменяющийся сигнал. Если была "1",  запустить таймер по переполнению, и остановить его, когда пришел "0". Результат выдать в порт. Возможна ли такая реализация без использования аппаратных средств МК? (типа вызов прерывания по спаду, фронту, изменению итп).

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

SergeySB пишет:

сигнала. Пока мысли такие - грубо: считывать одну из ног порта, куда подается изменяющийся сигнал. Если была "1",  запустить таймер по переполнению, и остановить его, когда пришел "0". Результат выдать в порт. Возможна ли такая реализация без использования аппаратных средств МК? (типа вызов прерывания по спаду, фронту, изменению итп).

Этот способ для низких частот, для высоких нужно накапливать число срабатываний за большой отрезок времени, например за секунду. К тому-же на меге328 нельзя давать на таймер частоту выше тактовой/2 . А кстати на микрочиповских PICах -можно, на них хоть 100МГц можно будет измерить.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

На частоте 5 МГц один цикл - это 3 такта процессора. Для сравнения digitelRead выполняется около 80 тактов. Можно, конечно, читать порт низкоуовневыми команндами, но все равно Вы вряд ли уложитесь в 3 такта.

SergeySB
Offline
Зарегистрирован: 11.10.2015

Хорошо. Тогда попробую поиграться с измерительным интервалом. Для начала попробую так: задаем "измерительный" интервал с помощью ТС с прерыванием по переполнению. В момент измерения, всякий раз в прерывании по таймеру в режиме захвата (с ноги ICP) увеличиваем некую переменную. . Правильно? Если несложно напишите как по-человечески это должно выглядеть...

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

SergeySB, я ж вам написал, что этот способ для измерения низких частот, максимум десятки килогерц. Если выше -начнёт страшно врать. Высокие измеряют по-другому. Сигнал подают на вход T1 таймера, соответссно Clock Select настраивают от входа Т1. Настраивают прерывание по переполнению (timer1_ovf_vect), в прерывании инкреминируется переменная, говорящая о том, сколько раз оттактировано по  65536 раз.  Ещё один таймер настраивают на отсчёт точных промежутков времени, обычно секунда. Каждую секунду создаётся прерывание. В нём подсчитывается сколько раз было натикано по 65536 раз + прибавляется содержание переменной TCNT1 . Из полючившегося кол-ва тиков в секунду зная время одного тика вычисляется частота или длительность одного периода. Потом всё обнуляется и процесс повторяется.

SergeySB
Offline
Зарегистрирован: 11.10.2015

dimax, спасибо! Я приблизительно тоже самое хотел написать, просто очень неккоректно выразился. Еще раз спасибо, пошел разбираться. После удачных экспериментов по-возможности отпишусь.

nevkon
Offline
Зарегистрирован: 20.01.2015

Подпишусь пожалуй, мне скоро надо будет частоту считать.

std
Offline
Зарегистрирован: 05.01.2012

Братишки, а по типу стрелочного частотомера ни у кого не получалось? Нахимичить типа переходник, зарядную цепь (какие требования к конденсатору?), и в АЦП это. Просто гуляла схема частотомера для прибора на 100 мкА, но сейчас купить такую вещь проблематично. Да и громоздко это, плюс нельзя ронять/пинать. Интересует диапазон 5 Гц - 50 кГц.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Вот набросал свой код. Как и писал входным сигналом тактируется таймер1, второй таймер засекает время 1сек. Сколько раз успеет натикать за секунду -такая и частота.  В сериал выводит количество  целых периодов в секунду (т.е. Герц :)  Для проверки подал прецизионные 2048 герц, частотометр показал 2051, думаю это нормальная погрешность для дешевых кварцов на ардуинах.

Т.к. 16-битный таймер только один, а нужно 2шт, то пришлось немного извратнуться с таймером2. Он считает отрезками по 8мс, и на 125 раз  (8мс x 125=1сек) запускает подсчёт частоты.

volatile unsigned int int_tic=0; 
volatile unsigned long tic; 

void setup(){
Serial.begin(9600);
pinMode (5,INPUT); // вход сигнала T1 (only для atmega328)
TCCR2A=1<<WGM21; //CTC mode
TIMSK2=1<<OCIE2A; OCR2A=124 ;//прерывание каждые 8мс
TCCR2B=(1<<CS22)|(1<<CS21)|(1<<CS20); //делитель 1024
TCCR1A=0; TIMSK1 = 1<<TOIE1; //прерывание по переполнению
TCCR1B = (1<<CS10)|(1<<CS11)|(1<<CS12);//тактировани от входа Т1
}

ISR (TIMER1_OVF_vect){ int_tic++; }

ISR (TIMER2_COMPA_vect){
  static byte n=1;
  if (n==125){
           tic= ((uint32_t)int_tic<<16) | TCNT1; //сложить что натикало
           int_tic=0;
           TCNT1 = 0; n=0;
           }
      n++; 
}

void loop(){
delay(500);
Serial.println(tic);
}

 

SergeySB
Offline
Зарегистрирован: 11.10.2015

dimax, еще раз благодарю!! А мне учиться, учиться и еще раз...

SergeySB
Offline
Зарегистрирован: 11.10.2015

Сегодня дошли руки проверить частотомер со скетчем от dimax. Максимально что далось намерить- сигнал с лабораторного генератора частотой 3 МГц. Дальше в сериал выводится всякая ерунда.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

SergeySB, это нормально, я упростил кое-что.  В той библе посерьёзней подошли к делу. Но в любом случае 8МГц будет максимум.

SergeySB
Offline
Зарегистрирован: 11.10.2015

Да, dimax, я понял. Про 8 МГц тоже понятно ) . Придется попытаться на PICe реализовать. Все равно спасибо!

UFO 007
UFO 007 аватар
Offline
Зарегистрирован: 11.01.2018

dimax пишет:

 


TCCR2B=(1<<CS22)|(1<<CS21)|(1<<CS20); //делитель 1024

 

Я, конечно, извиняюсь, но не подскажете ли новичку-чайнику как понимать эту клинопись: это что - что-то низкоуровневое типа asm? Что, например, обозначает абревиатура "...TCCR2B..." и все эти стрелочки-скобочки и т. д. Будь ласка - хотя бы ссылочку, где можно почитать про это хотя бы в среднем объёме. Спс

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

UFO 007 пишет:
 Будь ласка - хотя бы ссылочку, где можно почитать про это хотя бы в среднем объёме. Спс
А гугл на что? http://avrprog.blogspot.com/2013/03/t2-8.html

  • TCCR2B - регистр управления B
  • 111 - CLK/1024
Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

UFO 007 пишет:

это что - что-то низкоуровневое

Да, это низкоуровневая настройка переферии микроконтроллера (в данном случае аппаратного таймера).

UFO 007 пишет:

 типа asm? 

Нет, это Си.

UFO 007 пишет:

Что, например, обозначает абревиатура "...TCCR2B..."

Это название одного из регистров для настройки аппаратного таймера. Откройте даташит на микроконтроллер, там расписаны все регистры.

UFO 007 пишет:

и все эти стрелочки-скобочки и т. д.

Это битовые операции в языке Си. Чтобы не писать "магические числа" в регистр, типа TCCR2B |= 00000111, там указаны имена битов в регистре. Все названия и описание регистров есть в даташите. Нужно только подучить битовые операции в языке Си. И научиться читать даташит.

sany_sch
sany_sch аватар
Offline
Зарегистрирован: 19.01.2016

dimax пишет:

Вот набросал свой код. Как и писал входным сигналом тактируется таймер1, второй таймер засекает время 1сек. Сколько раз успеет натикать за секунду -такая и частота.  В сериал выводит количество  целых периодов в секунду (т.е. Герц :)  Для проверки подал прецизионные 2048 герц, частотометр показал 2051, думаю это нормальная погрешность для дешевых кварцов на ардуинах.

Спасибо. Вы как всегда помогаете. проверил частоту ШИМ в прошивках для лазерных ЧПУ на ардуино

grbl_v0_9j даёт частоту 7807 герц

grbl_v1.1f даёт частоту 976 герц

Но задачу нужно усложнить. нужно считывать не просто частоту, а сигнал который идёт. Нашел такую статью: http://pyatilistnik.info/chtenie-signala-pwm-ispolzuya-apparatnyie-prery...

Но там всё очень не точно. Если с меньшей частотой ещё как то работает, то с большой вообще что попало.

Возможно сделать это вашим способом? например заводим сигнал сразу на два пина. Первое прерывание обнуляет счётчик а по второму останавливает подсчёт.    И привести полученные значения обратно в числа от 0 до 255.