[SOLVED] Reading PWM with interrupts while outputting PWM with Timer/Counter

I’m trying to use an Arduino Nano to control a micropump motor. The motor takes a 20-100Hz PWM input and has a tachometer output. I’d like to output a control signal while reading the RPM.

I’m using an ISR to read the PWM tachometer signal on D2 and Timer/Counter2 to output a ~30Hz on D9 (OC2A). I had to use the prescaler on TCCR2 to get to the lower frequency.

I can read the tachometer signal and get an RPM reading just fine, but when I add the PWM output signal the tachometer reading goes crazy.

I’m wondering if the PWM signal from TCCR2 is messing with the interrupts, but I wasn’t able to find anything about interference between the internal TCCR2 interrupts (which I’m not using) and the external interrupt pins.

I used the ATMega328 datasheet for register values (esp. section 31.11) https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf

The code I’m working on:

#include  <avr/io.h>

unsigned long now = 0; //current millis reading
unsigned long prev = 0; //previous millis reading
unsigned long deltaT = 500; //millis interval

//D11 reserved for PWM output

int tachPin = 2; //requires interrupt pins D2 or D3, but D3 also runs on clock/timer2

volatile int revCount = 0; //by the ISR revInc()


void setup() {
  Serial.begin(9600);
  Serial.println("Started serial output");
  
  pinMode(tachPin, INPUT);
  pinMode(11, OUTPUT);

  prev = millis();

  attachInterrupt(digitalPinToInterrupt(tachPin), revInc, RISING); //attach revInc()

  //motor uses PWM from 20-100 Hz
  //using prescaler = 1024, TOP = 255, Phase Correct PWM
  //16MHz / (2 * 1024 * 255) = ~30.5Hz
  
  Serial.println(TCCR2A, BIN);
  TCCR2A |= (1<<COM2A1); //non-inverted OC2A (D11) for phase correct PWM
  TCCR2A |= (1<<WGM20); //WGM20:2 sets WGM to phase correct PWM with TOP = OCR2A
  TCCR2A &= ~(1<<WGM21);
  TCCR2B &= ~(1<<WGM22);
  Serial.println(TCCR2A, BIN); //check that it reads 10000001
  Serial.println(TCCR2B, BIN);
  TCCR2B &= ~((1<<FOC2A)|(1<<FOC2B));
  TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20); //CS20:22 sets prescaler = 1024
  Serial.println(TCCR2B, BIN); //check that it reads 111
  OCR2A = 50; //duty cycle out of 255
  
}

void loop() {


 //adds up the revCounts over deltaT
  now = millis();
  if(now - prev >= deltaT){
    Serial.println(revCount * 3 * deltaT / 100 ); //calculate and output RPM
    revCount = 0;
    prev = now;
  }
  

}




//counts tach pulses
void revInc(){
  revCount++;
}

You shouldn't be diddling with the timer with interrupts enabled. You shouldn't be Serial.print()ing while diddling with the timer.

You should not be accessing or assigning a value to a multi-byte variable with interrupts that can affect that value enabled.

You should fix those issues, and test your code again.

I can read the tachometer signal and get an RPM reading just fine, but when I add the PWM output signal the tachometer reading goes crazy.

Your code worked reliably for me when I connected pin 11 to pin 2 of the Uno I was using. Count was a stable 15 or 16 for the 500ms. rpm was 225 or 240, but not "crazy. I did not see an interaction between the pwm and interrupt reading.

I'd focus on the hardware side of what you are doing. Are the grounds connected?

Can you provide a wiring diagram of the pump, pump controller, power supply, arduino, etc.

Here's how to make a protected copy of the data in the interrupt. copy_revCount declared as the same type as revCount, but not volatile

if (now - prev >= deltaT) {
    noInterrupts();
    copy_revCount = revCount;
    revCount = 0;
    interrupts();
    Serial.println(copy_revCount);
    Serial.println(copy_revCount * 3 * deltaT / 100 ); //calculate and output RPM

Thanks @cattledog for asking about wiring, I totally forgot to ground reference the pump power supply to the Arduino's!

Also thanks @PaulS for the tip about disabling interrupts/serial, I should have been more careful before "diddling" with the timer.