ARDUINO Определение входящей частоты

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
По мотивам ЭТОЙ темы
Написал простенький код. В двух словах. Вешаем на 2-ой пин прерываение , счиатем кол-во переходов от 0 к 1. при этом запоминаем первое время и последнее время.
Далее если последнее время не меняется больше секунды вычисляем частоту и выводим результат.
Частоту задаем при помощи tone на 8-ой пин.

И вот получаю несколько результатов за 1 тон... не могу понять почему. Вот такой результат:
Код:
3838-3585 Кол-во:1557 Частота:434.31
6142-5889 Кол-во:999 Частота:433.97
6653-6400 Кол-во:221 Частота:434.18
14961-15962 Кол-во:3715 Частота:434.10
Не могу понять почему условие
if ((millis() - lasttime) > 1000)
не выполняется корректно..
Если у кого будут идеи - сообщите. Голова что то отказывается сейчас что то придумать.

ДА! Время всегда одно и то же для одной частоты. При изменении частоты время сброса меняется. Видимо как то с работой таймера и прерываений тона зависит. От прерывания не зависит. что 0, что 1.

C++:
uint32_t Cntr = 0;
uint32_t lasttime = 0;
uint32_t firstime = 0;
bool Start = false;

void intCounter()
{
  if (!Start)
  {
    firstime = millis();
    lasttime = firstime;
    Start = true;
  }
  else
  {
    Cntr++;
    lasttime = millis();
  }
}

void setup() {
  // put your setup code here, to run once:
  pinMode(8, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(0, intCounter, RISING);
  Serial.begin(115200);

  Cntr = 0;
  tone(8, 433, 15000);
}

void loop() {
  if ((millis() - lasttime) > 1000)
  {
    if (Start)
    {
      Serial.print(lasttime); Serial.print("-"); Serial.print(millis());
      Serial.print(" Кол-во:"); Serial.print(Cntr);
      Serial.print(" Частота:"); Serial.println(1000.0 * (float)Cntr / (float)(lasttime - firstime));
      Start = false;
      Cntr = 0;
    }
  }
}
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,409
976
58
Марий-Эл
Я не стал бы для этой задачи использовать ядро ардуины. Тем более функцию millis().
Частотомеры используют для этого таймер.
Все делители таймера настраиваются на генерацию определённой частоты. Зависит от того, какой диапазон нужно мерять и с какой точностью.
При первом фронте входящего сигнала таймер запускается, при следующем останавливается. Считывается счётчик таймера и, при известной частоте таймера, вычисляется частота входящего сигнала. Если точность нужно повысить, то таймер может запускаться при первом фронте, останавливаться при десятом.

Есть другой метод.
Тоже с таймером.
Опять же программируем таймер, что бы он генерил импульсы определённой длительности. При фронте этого импульса, начинает считать счётчик приходящие импульсы, при спаде счётчик останавливается. Количество посчитанных импульсов даёт основу для расчёта частоты.

При чём все эти действия, на медленных контроллерах, должны быть написаны на ассемблере. На С можно такое вытворять только на быстродействующих МК. Да и то, если измеряемая частота не велика.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
На самом деле подумав решил проблему.
Дело было в возникновении прерывания при проверке условия. Изменил код loop вот так и проблема ушла
millis брал для простоты, вообще брать надо miocros.

C++:
uint32_t dt=0;
void loop() {
  noInterrupts();
  dt=millis() - lasttime;
  interrupts();
  if ( dt> 1000)
  {
    if (Start)
    {
      Serial.print(" Кол-во:"); Serial.print(Cntr);
      Serial.print(" Частота:"); Serial.println(1000.0 * (float)Cntr / (float)(lasttime - firstime));
      Start = false;
      Cntr = 0;
    }
  }
}
 

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
Дело было в возникновении прерывания при проверке условия
Так отож, я потому и делал в коде в той теме attach/detachinterupt до и после проверки условий в loop(). Кстати, в обработчике прерывания millis() замирает.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
честно не смотрел твой код. В принципе логика изначально была ясна , а написать программу уже плевое дело, не бином Ньютона

а от миллис в прерывании (знаю, что замирает. давно наэксперементировал) надо только время знать для понимания конца меандра, что бы вывести в монитор результаты и сбросить данные. Хотел еще по кнопке запилить новый тон с рандомом, но пока разбирался с проблемой время ушло. Точность не ахти, но пойдет видимо от дельты времени надо какую то составляющую вычесть, поправку.