Arduino as Pulse Divider

Hello guys,

i would like to use Arduino as a pulse divider by 6 for counting a turbine RPM that has 6 blades and a pulse coming per blade.
meaning for each 6 pulse inputs i need to output 1 pulse.
this can be done by HW but i have arduinos available and they are cheap enough :slight_smile:
the input signal is about 690,000 per minute, or 11,500 per second, or 11.5Khz. not extremely fast for software i believe.

i was thinking about an ISR reading the rise and fall edge of the pin and then in the main loop when we reach 6 run the pulse and init the counter.
any other ideas?
and code examples / implementations available?

Thank you.

Are the pulses "clean" - or will they need debouncing?

pulses are clean yes

Like this:

    +-+        +-+        +-+        +-+        +-+        +-+    
____| |________| |________| |________| |________| |________| |____

    +--------------------------------------------------------+
____|                                                        |____

Or Like this:

    +-+        +-+        +-+        +-+        +-+        +-+    
____| |________| |________| |________| |________| |________| |____

    +-+
____| |___________________________________________________________

Or other?

like this:
image

its possible that both methods may work, but i think the above is better.

That will work but you only need to read the rising edge.
Connect the itput to pin 2

i think i need to read the falling edge too, in order to run the output to low on time.

How wide is the pulse?

sorry but i havent measured pulse width as of yet. i can estimate it to be around 50-100us

If it's a known fixed width, then there is no need to measure it in the code.
Servicing interrupts that occur within 50usec of each other may be a little tricky just using arduino functions.

2 Likes

the width is not permenant, its a spinning turbine so width is RPM dependant.

Actually, lets do some calculations together:
turbine max RPM = 120K RPM. and the turbine has 6 blades. each blase in front of the sensor creates an ON signal and no blade = OFF signal.

With 6 blades we have 720K pulses per minute, and 12,000 pulses per second.
Also, a full turbine revolution takes ~500us.
Assuming the width of the 6 blades "cover" around 10% of the turbine diameter therefore all 6 blades (ON state time) is about 50us for all 6, or each lets round to around 8us.
that would mean the signal will look like this in one full revolution:
8us ON
75us OFF
8us ON
75us OFF
8us ON
75us OFF
8us ON
75us OFF
8us ON
75us OFF
8us ON
75us OFF

So the width of the pulse really isn't important?

at 120,000 rpm or 12,000 pulse /sec, there's 83 usec between pulses, probably enough to do sufficient processing within an interrupt

look this over
the digitalWrite() can be replaced by directly setting the output register (e,g. PORTA?)

const byte PinIntrpt = 2;
const byte PinOut = 4;

byte sensorLast;

byte  cnt;
byte  state;

// -----------------------------------------------------------------------------
void
isr (void)
{
    if (6 <= ++cnt) {
        cnt = 0;
        state = !state;
        digitalWrite (PinOut, state);
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    pinMode      (PinIntrpt, INPUT_PULLUP);
    pinMode      (PinOut,     OUTPUT);

    attachInterrupt(digitalPinToInterrupt(PinIntrpt), isr, RISING);
}

void loop ()
{
}

Your numbers are correct but that 8usec will be a problem.
Timing estimates for just doing a digitalWrite range from 3.5us to 10us
Interrupt latency could be 3 to 5usec.

If you write the code using direct register manipulation, i.e. writing directly to the 328 regisers and not using any Arduino functions it would be possible. How much experience do you have in this regard?

does the pulse width need to be measured, or the # of edges / revolution?

Arduino - Port Manipulation describes how to identify the port register for a specific pin

PORTB ^= 0x20; works (toggle pin 13)

Here is something you can try:

Connect the input to both interrupt pins 2 and 3 on an UNO.

Set one ISR for Rising edge to count the pulses and one for Falling just to know when the pulse ends.
So on the sixth Rising edge set the output High and when the Falling edge ISR occurs set the output Low

You will have to experiment a little.

It is even possible to do it without using interrupts.

I've modified the "StateChangeDetection" example in the IDE, by removing the serial.print(), and a delay(), and using modulo 6 instead of modulo 4.

The results using an Arduino Uno R3 were OK, until we came up with the latest 12kHz/8ยตs pulse width.

However an Arduino Uno R4 minima will cope with pulse widths down to about 5ยตs.

const int  inputPin = 8;
const int outputPin = 12;
int pulseCount = 0;
int inputState = 0;
int lastInputState = 0;

void setup() {
  pinMode(inputPin, INPUT);
  pinMode(outputPin, OUTPUT);
}

void loop() {
  inputState = digitalRead(inputPin);
  if (inputState != lastInputState) {
    if (inputState == HIGH) {
      pulseCount++;
    }
  }
  lastInputState = inputState;
  if (pulseCount % 6 == 0) {
    digitalWrite(outputPin, HIGH);
  } else {
    digitalWrite(outputPin, LOW);
  }
}

gcjr's sketch in post #13 works on an Arduino Uno R3, but needs a slight modification.

Because it toggles the output every 6 pulses, it actually divides by 12.

Easily changed: replace if (6 <= ++cnt), with if (3 <= ++cnt).

sorry for replying late.

  1. actual pulse width isnt important.
  2. i need to utilize it on a small lightweight board such as pro mini as its going into a small RC jet. i think UNO and pro mini both use tbe same atmel 328p?
  3. i have no knowledge of writing to registers. happy to get any help in that regard.

If pulse width isn't important, then you could use one of the 8-pin TINY devices to generate a nice square wave. I use TINY85's to generate baud rate clocks by dividing down an external system clock.

From memory it's Timer0 on a TINY85. You can feed in the external clock and use the hardware counter of Timer0 to toggle the associated output compare pin. This can all be done in hardware and once configured requires no further software involvement - i.e. no interrupt handlers or pin toggling needed.

Here's my complete code for my 8085 micro where the TINY85 handles the processor reset and divides the system clock down to create the baud rate clock for my UART:

// 8085 System reset & baud rate clock generator / divider
//
// PB0 : Timer / Counter 0 compare match A output - i.e. baud rate clock
// PB2 : Timer / Counter 0 clock source from 8085 system clock
// PB4 : drives the 8085 RESET line LOW for 100mS on TINY85 RESET.

#define BAUD_CLOCK  PIN_PB0  //OC0A
#define SYS_CLK_85  PIN_PB2  // T0 CLK IN
#define RESET_85    PIN_PB4

void setup() {
  Serial.begin(9600);
  // set all the pins as outputs
  pinMode( SYS_CLK_85, INPUT );
  pinMode( BAUD_CLOCK, OUTPUT );
  pinMode( RESET_85,   OUTPUT );

  // drive the 8085 RESET lines LOW
  digitalWrite( RESET_85, LOW );

  // delay for 100mS
  delay( 100 );

  // drive the 8085 RESET lines HIGH
  digitalWrite( RESET_85, HIGH );
  
  // TIMER / COUNTER 0 is repurposed here so the millis() and delay()
  // functions that rely on Timer 0 no longer work.

  TIMSK = 0;     // disable any timer interrupts
  TCCR0B = 0;     // stop Timer 0

  // Configure Timer 0 to count clock pulses on the T0 pin (i.e. PB2)
  // in CTC mode toggling OC0A on a Compare Match.
  TCCR0A = B01000010;
  TCCR0B = B00000110;

  // Seems that setting the TNCT0 initial value and the
  // OCR0A should be done last otherwise strange things happen!!!
  TCNT0 = 0;      // initialise Timer 0 to 0 - counts up

  // NOTE: 1 complete cycle of OC0A is 2 compare matches of OCR0A
  // For an 8085 crystal of 3.6864MHz, the CPU CLK OUT is 1.8432MHz (i.e half)
  // To get 9600Hz out, the timer has to count 00..95 twice.
  OCR0A = 95;

  GTCCR = B00000001;
}

void loop() {
  // nothing to do - hurrah!
}