Чтение частоты ввода в цифровых выводах

Это несколько теоретический вопрос: Допустим, у меня есть датчик, который посылает цифровые сигналы на один из цифровых портов Arduino. Я не знаю, на какой частоте он посылает эти сигналы. Например, частота выходного сигнала датчика может быть 100 Гц, поэтому каждую 1/100 секунды он изменяет напряжение (или сохраняет старое напряжение, если следующий сигнал такой же, как предыдущий), но с тем же успехом он может быть 200 Гц. Сможет ли Arduino точно считывать цифровой сигнал независимо от его частоты? Или есть обычная частота, которую цифровые устройства обычно используют для вывода? Не будет ли так, что если Arduino ожидает сигнал 100 Гц, но сигнал 200 Гц, он пропустит половину входов? Для: (1 0 1 0 0 1...) не увидит ли он: (1 1 0) - то есть пропустить все остальные входные данные, потому что частота дискретизации будет слишком низкой вдвое? Большое спасибо за вашу помощь..

, 👍-1

Обсуждение

Что вы подразумеваете под цифровым сигналом? Какой-то протокол синхронной последовательной передачи? СПИ может? Или I2C? Или, может быть, асинхронный, как UART логического уровня? Или это индивидуальный протокол? Ваш вопрос начнет иметь смысл только после того, как вы укажете, как данные должны синхронизироваться в вашем протоколе., @Edgar Bonet

*поэтому каждые 1/100 секунды он изменяет напряжение (или сохраняет старое напряжение, если следующий сигнал такой же, как предыдущий),* - вы спрашиваете об измерении напряжения или частоты?, @Nick Gammon

@Ник, давайте предположим, что напряжение 1 достаточно высокое (но не слишком высокое), чтобы Arduino зарегистрировал 1, а напряжение 0 также находится в пределах порогового значения, чтобы Arduino зарегистрировал ноль (скажем, 5 В для ВЫСОКОГО и 0,5 В для НИЗКИЙ ). Меня интересует частота сигнала., @Daniel

@ Эдгар, я имею в виду обычный цифровой датчик, скажем, тот, который измеряет температуру и отправляет цифровой сигнал на Arduino на какой-то неизвестной частоте., @Daniel

«Общий» цифровой датчик ничего не значит. Цифровые датчики обычно поставляются с интерфейсами SPI или I2C. Иногда асинхронный сериал. Все это хорошо определенные протоколы связи. Существуют _правила_, определяющие, как отправитель должен уведомлять получателя о том, _когда_ именно строка данных должна быть выбрана. Arduino имеет аппаратную поддержку этих протоколов, но другие протоколы должны быть реализованы программно (например, один провод). Так что забудьте о своем бессмысленном «универсальном» и сообщите нам, какой последовательный протокол вы собираетесь использовать., @Edgar Bonet


2 ответа


Лучший ответ:

2

100 Гц — это довольно тривиально. См. мою страницу о таймерах.

Этот скетч может обнаруживать частоты примерно до 5 МГц:

// Пример таймера и счетчика
// Автор: Ник Гэммон
// Дата: 17 января 2012 г.

// Вход: контакт D5

// они проверяются в основной программе
volatile unsigned long timerCounts;
volatile boolean counterReady;

// внутри подпрограммы подсчета
unsigned long overflowCount;
unsigned int timerTicks;
unsigned int timerPeriod;

void startCounting (unsigned int ms) 
  {
  counterReady = false;         // время еще не истекло
  timerPeriod = ms;             // сколько 1 мс отсчитывает сделать
  timerTicks = 0;               // сброс счетчика прерываний
  overflowCount = 0;            // пока нет переполнения

  // сбросить Таймер 1 и Таймер 2
  TCCR1A = 0;             
  TCCR1B = 0;              
  TCCR2A = 0;
  TCCR2B = 0;

  // Таймер 1 - считает события на выводе D5
  TIMSK1 = bit (TOIE1);   // прерывание при переполнении Таймера 1

  // Таймер 2 - дает нам интервал счета в 1 мс
  // Тактовая частота 16 МГц (62,5 нс на тик) — предварительно масштабирована на 128
  // счетчик увеличивается каждые 8 мкс.
  // Итак, мы насчитали 125 из них, что дает ровно 1000 мкс (1 мс)
  TCCR2A = bit (WGM21) ;   // режим СТС
  OCR2A  = 124;            // считать до 125 (относительно нуля!!!!)

  // Таймер 2 - прерывание при совпадении (т.е. каждые 1 мс)
  TIMSK2 = bit (OCIE2A);   // включить прерывание от Таймера 2

  TCNT1 = 0;      // Оба счетчика обнулены
  TCNT2 = 0;     

  // Сброс предделителей
  GTCCR = bit (PSRASY);        // сбросить предварительный делитель сейчас
  // запускаем Таймер 2
  TCCR2B =  bit (CS20) | bit (CS22) ;  // прескалер 128
  // запускаем Таймер 1
  // Внешний источник синхронизации на выводе T1 (D5). Часы на переднем фронте.
  TCCR1B =  bit (CS10) | bit (CS11) | bit (CS12);
  }  // конец startCounting

ISR (TIMER1_OVF_vect)
  {
  ++overflowCount;               // подсчитываем количество переполнений Counter1
  }  // конец TIMER1_OVF_vect


//*************************************************** ******************
// Служба прерывания Таймера 2 вызывается аппаратным Таймером 2 каждые 1 мс = 1000 Гц
// 16 МГц / 128 / 125 = 1000 Гц

ISR (TIMER2_COMPA_vect) 
  {
  // получить значение счетчика до того, как оно изменится
  unsigned int timer1CounterValue;
  timer1CounterValue = TCNT1;  // см. техническое описание, стр. 117 (доступ к 16-битным регистрам)
  unsigned long overflowCopy = overflowCount;

  // смотрим, достигли ли мы периода времени
  if (++timerTicks < timerPeriod) 
    return;  // еще нет

  // если только что пропустили переполнение
  if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256)
    overflowCopy++;

  // конец времени стробирования, измерение готово

  TCCR1A = 0;    // остановить таймер 1
  TCCR1B = 0;    

  TCCR2A = 0;    // остановить таймер 2
  TCCR2B = 0;    

  TIMSK1 = 0;    // отключить прерывание от Таймера 1
  TIMSK2 = 0;    // отключить прерывание от Таймера 2

  // подсчитываем общее количество
  timerCounts = (overflowCopy << 16) + timer1CounterValue;  // каждое переполнение на 65536 больше
  counterReady = true;              // устанавливаем глобальный флаг для окончания периода счета
  }  // конец TIMER2_COMPA_vect

void setup () 
  {
  Serial.begin(115200);       
  Serial.println("Frequency Counter");
  } // конец настройки

void loop () 
  {
  // останавливаем прерывания Таймера 0 от сброса отсчета
  byte oldTCCR0A = TCCR0A;
  byte oldTCCR0B = TCCR0B;
  TCCR0A = 0;    // остановить таймер 0
  TCCR0B = 0;    

  startCounting (500);  // сколько мс считать

  while (!counterReady) 
     { }  // цикл до окончания счета

  // настроить счетчики по интервалу счета, чтобы получить частоту в Гц
  float frq = (timerCounts *  1000.0) / timerPeriod;

  Serial.print ("Frequency: ");
  Serial.print ((unsigned long) frq);
  Serial.println (" Hz.");

  // перезапустить таймер 0
  TCCR0A = oldTCCR0A;
  TCCR0B = oldTCCR0B;

  // давайте закончим работу с последовательным интерфейсом
  delay(200);
  }   // конец цикла

Или существует обычная частота, которую цифровые устройства обычно используют для вывода?

Нет

Не будет ли так, что если Arduino ожидает сигнал частотой 100 Гц, а сигнал имеет частоту 200 Гц, половина входных данных будет пропущена? Для: (1 0 1 0 0 1...)

Нет

Почему он "ожидает" частоту? Это не человек, ожидающий шоколадного торта на свой день рождения.


Таким образом, на какой бы частоте датчик ни отправлял свои данные — будь то 100 Гц или 5 МГц, — Arduino достаточно умен, чтобы точно считывать все сигналы напряжения?

Я не совсем понимаю, что вы подразумеваете под "прочитать все сигналы напряжения", что, по-видимому, подразумевает, что вы хотите измерить напряжение.

поэтому каждые 1/100 секунды он изменяет напряжение (или сохраняет старое напряжение, если следующий сигнал такой же, как предыдущий)

Но потом вы говорите:

предположим, что напряжение 1 достаточно высокое (но не слишком высокое) для того, чтобы Arduino зарегистрировала 1, а напряжение 0 также находится в пределах порогового значения, при котором Arduino может зарегистрировать ноль (скажем, 5 В для ВЫСОКОГО и 0,5 В для НИЗКОГО). ). Меня интересует частота сигнала.

Уровни напряжения для 0 и 1 указаны в техническом описании. Для Atmega328P для большинства контактов они следующие:

Низкий и высокий уровни напряжения

Вы можете видеть, что если Vcc равно 2,4 В или выше, то LOW будет от -0,5 В до 0,3 * Vcc, а если Vcc равно 5 В, то LOW будет от -0,5 до 1,5 В.

Аналогичным образом, HIGH равен 0,6 В * Vcc до Vcc + 0,5, поэтому, если Vcc равен 5 В, тогда HIGH равен от 3 В до 5,5 В.

При напряжении от 1,5 В до 3 В цифровой статус будет неопределенным.

Итак, если ваши LOW и HIGH находятся в этих диапазонах напряжения, да, вы можете определить частоту сигнала.

Я не уверен в том, что вы сказали "сохраняет старое напряжение, если следующий сигнал такой же, как предыдущий". Похоже, это означает, что если сигналы остаются неизменными (например, в течение секунды), то напряжение вообще не изменится, и, следовательно, частота будет равна нулю.

,

Спасибо @Ник. Итак, на какой бы частоте датчик ни отправлял свои данные — будь то 100 Гц или 5 МГц — Arduino достаточно умен, чтобы точно считывать все сигналы напряжения? Если это так, то это обнадеживающая новость.. Еще один вопрос, пожалуйста: можете ли вы определить частоту датчика по коду - т.е. определить, является ли датчик 100 Гц, 200 Гц и т. д., @Daniel

Смотрите измененный ответ., @Nick Gammon

спасибо @nick за всю информацию. О последнем абзаце: не будет ли так, что если есть поток 1 с, а не 0 с в течение целой секунды, то график напряжения будет выглядеть как горизонтальная линия 5 В (в основном, как постоянный ток 5 В) для этой 1 секунды .)?, @Daniel

Да, конечно., @Nick Gammon


0

Еще один метод, если он будет полезен миру:

Используя вдохновение из https://forum.arduino.cc/ t/как-читать-частоту-с-arduino/528143/2

Я просто измеряю секунды с помощью micros() между каждой верхней или нижней частью сигнала, умножая время на 2, чтобы получить полный период (при условии, что рабочий цикл равен 50%). А затем инвертируем полный период для расчета частоты.

Для этого я использовал Teensy 4.1.

// ЧИТАЙТЕ ЧАСТОТУ ИЗ СКЕТЧА ЦИФРОВОГО ВХОДА

// это нельзя использовать, если используется набор инструментов FreqMeasure.h, если он установлен на подростковом уровне.
// https://www.pjrc.com/teensy/td_libs_FreqMeasure.html

// определяем ваш цифровой входной контакт
#define inputPin 22 

// инициализируем переменные для функции чтения частоты
boolean inputState = false; boolean lastInputState = false;
long lastTime; long period; double freq;

// функция чтения частоты
double readFrequency(){
  inputState = digitalRead(inputPin);

  if (inputState != lastInputState) {
    period = micros() - lastTime; // в микросекундах
    freq = 1/(2*period*pow(10,-6)); // преобразование в Гц

    lastInputState = inputState;
    lastTime = micros(); 
  }
  return freq; // всегда возвращаем частоту, даже если она не изменила состояние...
}

// помечаем булавку
void setup() {
  // Serial.begin(115200); // раскомментируем & приспособьтесь к Arduino — я использовал Teensy.
  pinMode(inputPin, INPUT);
}

// вызываем функцию и печатаем результат на полной скорости
void loop() {
  Serial.print("Frequency:"); Serial.println(readFrequency());
  // работает, даже если я вставлю сюда задержку (например, задержку(1) миллисекунду)
}

Выход моего последовательного монитора

,