3

I'm trying to count number of HB100 microwave sensor pulses in 200ms time quanta.

Here is the code:

#include <SoftwareSerial.h>
#include <elapsedMillis.h>
elapsedMillis ElapsedTime;

#define Sensor A0
#define TimeQuanta 200

int Counter = 0;
boolean LastState;

void setup()
{
  Serial.begin(250000);
  pinMode(Sensor, INPUT);  
}

void loop()
{
  Counter = 0;
  ElapsedTime = 0;
  while (ElapsedTime < TimeQuanta ){
    LastState = digitalRead(Sensor);
    if (LastState == LOW && digitalRead(Sensor) == HIGH ){
      Counter += 1;  //Compare Last state with current state 
    }
  }
  Serial.print(digitalRead(Sensor));
  Serial.print("\t");
  Serial.println(Counter);
}

I need to know the digital read cycles. I'm comparing the last state of the sensor with current state and if a change is made (LOW TO HIGH) the counter is incremented. However, my counter is always 0!

  • is the code correct (the if condition)?
  • do I need some delays?
  • is it possible to count these pulses?

Here is the Logic Analyzer output of Microwave Sensor:

enter image description here


Edit: if I add delay(1); before if then the counter is not 0 anymore.

Patrick Trentin
  • 7,126
  • 3
  • 23
  • 40
Mehran
  • 307
  • 1
  • 3
  • 15

3 Answers3

3

As these pulses seems to be even less than microsecond short, you just can't use digitalRead function that takes about 80 machine cycles (about 5us @16MHz).

Not to mention printing so many data in every single loop iteration!!!!

So you have frequencies over 1MHz and your code might be able to count about 10kHz (at best, maybe less)

Anyway, you should use HW Timer/Counter 1 with clock source on T1 input. This one is able to count pulses at the half of main clock rate (50% pulse width)

And using Timer/Counter 1 is pretty simple:

TCCR1A = 0; // default mode, no output compare modes
TCCR1B = _BV(CS10) | _BV(CS11) | _BV(CS12); // clock select mode 7 - External clock source on T1 pin. Clock on rising edge.

And every 200ms just read TCNT1 and eventually reset to 0 or just remember last value and make difference (don't forget it's just 16b number).

KIIV
  • 3,534
  • 2
  • 18
  • 23
  • Thanks for your answer, where can I find a document for CPU cycles for any operation? The printings are out of while loop now. – Mehran Feb 06 '17 at 08:32
  • Hardly, I was using my own "benchmark". But it's possible to toggle some output and measure the frequency. Maybe there are some articles on this topic somewhere. – KIIV Feb 06 '17 at 08:43
3

You can use timer1 to calculate the elapsed time.

// Set Timer1 without prescaler at CPU frequency
TCCR1A = 0; // TCCRx - Timer/Counter Control Register. The pre-scaler can be configured here. 
TCCR1B = 1;

noInterrupts ();  // Disable interrupts.

uint16_t  StartTime = TCNT1;  // TCNTx - Timer/Counter Register. The actual timer value is stored here.
digitalRead(pin); // your code.
uint16_t EndTime = TCNT1
uint16_t ElapsedTime = EndTime - StartTime;

interrupts (); //Enable interrupts.

As a second solution you can set and unset a pin and calculate the time with your Logic Analyzer.

DDRD = DDRD | B10000000; // Set digital pin 7 as output.
PORTD = PORTD | B10000000; // Set digital pin 7.
digitalRead(pin); // your code.
PORTD = PORTD & B01111111; // Unset digital pin 7.
Soroush
  • 81
  • 2
2

KIIV explained very well why your code doesn't work, here I would like to suggest an alternative approach that achieves the same goal.


Code:

const byte interruptPin = 2;
volatile unsigned long counter = 0;                // overflow after 2^32-1 pulses

void setup() {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), count, RISING);
  Serial.begin(19200);
}

void loop() {
  Serial.print("No. pulses: ");
  Serial.println(counter);
  delay(1000);
}

void count() {
  counter++;                                       // never use print() in an ISR!
}

Please note that I changed the input pin, because there are some restrictions on the pins that can be used with this technique which depend on the board you use:

Board                                Digital Pins Usable For Interrupts
Uno, Nano, Mini, other               2, 3
Mega, Mega2560, MegaADK              2, 3, 18, 19, 20, 21
Micro, Leonardo, other 32u4-based    0, 1, 2, 3, 7
Zero                                 all digital pins, except 4
MKR1000 Rev.1                        0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Due, 101                             all digital pins
Patrick Trentin
  • 7,126
  • 3
  • 23
  • 40
  • Thanks I'll read the mentioned document and test the code. I need to count pulses continuously in 200ms so Ithink delay(1000) is not appropriate. – Mehran Feb 06 '17 at 08:44
  • 1
    The delay is over the **print** function, the pulses are counted **continuosly** using the **interrupt service routine** `count()`. You can reduce the *delay*, but only up to a certain extent. Modern *Arduino* implementation uses *interrupts* to deal with `print()`, and this means that if you abuse of this instruction then you may miss *some pulses* or have other weird behaviour going on. Given the high frequency of your pulses, you certainly don't want to use `print()` after each and every pulse. Perhaps *every 100/1000 pulses* might be a better idea. – Patrick Trentin Feb 06 '17 at 08:45
  • Your code is working. I Haven't used ISRs before, I'll should read more, thanks for your suggestion. – Mehran Feb 06 '17 at 13:27