3
votes

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.

3

3 Answers

3
votes

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.
3
votes

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).

2
votes

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