Работа с PulseIn() и Millis().

Я пытаюсь использовать pulseIn с истекшим временем, используя millis(), я не хочу использовать прерывания вместо pulseIn (Мне нужен pulseIn, потому что мне нужно точное время импульса), затраченное время не должно быть очень точным, поэтому для расчета затраченного времени я добавил к затраченному времени время, в течение которого Arduino заблокировано. Проблема в том, что рассчитанное прошедшее время очень неточно. Я подозреваю, что иногда оно никогда не входит в if(elapsed > 1000*60*2). Почему? Как я могу это исправить?

Спасибо.

Код

unsigned long startTime = 0;
unsigned long elapsed = 0;
unsigned int val = 0;
uint32_t time_wait_pulse = 0;

void setup(){
  startTime = millis();
}

void f(){
  uint32_t time = 0;
  time += pulseIn(pin, LOW);
  if(time > 0) {
   ...
   time_wait_pulse += time;
  } else {
   time_wait_pulse += 1000000; // тайм-аут импульсного входа по умолчанию (микрокс)
  }
}

void loop(){
    elapsed = millis() - startTime + (unsigned long)(time_wait_pulse/1000);
    if(elapsed > 1000*60*2){
      val++;
      startTime = millis();
      time_wait_pulse = 0;
    }

     noInterrupts();
     f();
     interrupts();
}

, 👍1


2 ответа


3

millis() использует прерывания для работы. Деактивируя прерывания для вашей функции f(), вы предотвращаете возможность изменения значения, возвращаемого функцией millis(), за это время.

Поэтому, если прерывание, связанное с millis(), произойдет во время выполнения f(), вы потеряете время при измерении, и оно будет неточным. Чем больше времени код проводит с выключенными прерываниями, тем хуже будет. micros() по-прежнему будет обновляться без прерываний (поскольку он напрямую считывает регистр Timer0), но он все равно потеряет время примерно через 500 мс. , поскольку тогда он не сможет компенсировать множественные переполнения за этот период времени.

Путь дальнейшего развития зависит от ваших требований. Действительно ли необходимо отключить прерывания для измерения pulseIn()? Какую точность вы получаете в обоих случаях (включение и выключение прерываний) и какая точность вам нужна? Если вам абсолютно необходимо отключить прерывания, делайте это только на самое короткое время (только в районе pulseIn()). Вы можете создать свой собственный аналог millis() для учета времени, используя 16-битный таймер (вы не указали, какой Arduino вы используете), что даст вам гораздо больше времени, которое микроконтроллер может потратить без прерывания перед потерей времени при измерении. Вы также можете использовать другой прескалер, чтобы еще больше увеличить это время, жертвуя при этом точностью.

,

1

Вы можете рассчитать достаточно длинные интервалы, используя Таймер 2 (на Atmega328), как описано в моя страница о таймерах.

Пример такой:

void startTimer1 ()
  {
  // сброс Таймера 1
  TCCR1A = 0;
  TCCR1B = 0;
  // обнуляем его
  TCNT1 = 0; 
  TIFR1 |= bit (TOV1);  // очищаем флаг переполнения
  // запускаем Таймер 1
  TCCR1B =  bit (CS10) | bit (CS12);  // прескалер 1024
  }  // конец startTimer1
 
unsigned long getTimer1Reading ()
  {
  unsigned long elapsed = TCNT1;
  if (TIFR1 &  bit (TOV1))
     elapsed += 65536;
  return elapsed;   
  }  // конец getTimer1Reading
 
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  }  // конец настройки

void loop ()
  {
  Serial.println ("Starting ...");
  Serial.flush ();

  startTimer1 ();
 
  delay (7560);
  float secs = (1.0 / F_CPU * 1024) * getTimer1Reading ();

  Serial.print ("Time taken = ");
  Serial.print (secs);
  Serial.println (" seconds.");

  } // конец цикла

Приведенный выше код использует Таймер 1 для измерения интервала до 8,388 секунды. Он использует прескалер 1024, что означает, что каждый "счет" таймера 1 составляет 1/16e6 * 1024 секунды (0,000064 секунды или 64 мкс). Поскольку он может считать до 65536, мы можем рассчитать время 0,000064 * 65536 = 4,194304 секунды. Затем таймер переполняется, но мы можем проверить «флаг переполнения». и узнать, что произошло переполнение. Это позволяет нам увеличить время до 8,388608 секунды, прежде чем мы упустим тот факт, что произошло второе переполнение.

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

Вывод на моем Uno:

Starting ...
Time taken = 7.56 seconds.
,