Interrupts

Hello, I've been playing around with an Arduino Mega and intend to use it in conjunction with flowmeters to actuate valves. I have everything working (or so I thought) until I came to test with a physical flowmeter and found that the Mega counts way in excess of the number of pulses it should.

Even when taking all the complication out and simply setting the most basic interrupt with the pullup resistor then grounding it by hand once, causes anything up to 30/40 counts!

Basic code below, I can't understand why it won't count. I've tried either using internal pullup resistors or with 10k external ones.

If I create a simple debounce script, it minimises the impact, but still double or triple counts.

Any suggestions please?

volatile unsigned long count;
unsigned long lastUpdateMillis=0;

void setup(){
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), countRoutine, RISING);
}

void loop(){
  if((unsigned long)(millis()-lastUpdateMillis)>=500){
    Serial.print("Count: ");
    Serial.println(count);
    lastUpdateMillis=millis();
  }
}

void countRoutine(){
  count++;
}

The ISR itself must ignore any transitions occurring too soon since the last transition.

volatile unsigned long last_transition = 0L;

void isr ()
{
  unsigned long transition = micros();
  if (transition - last_transition < THRESHOLD)
    return ;
  last_transistion = transition ;
  count ++; 
}

Also its important to read any multi-byte volatile variables in a critical section so that you
always see a consistent state.

unsigned long read_count ()
{
  noInterrupts() ;  // enter critical section
  unsigned long copy = count ;    // 4 bytes have to be copied while consistent
  interrupts() ;  // leave critical section
  return copy ;
}

Thanks for your message. I've updated my code as follows

volatile unsigned long count;
unsigned long lastUpdateMillis=0;

void setup(){
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), countRoutine, RISING);
}

void loop(){
  if((unsigned long)(millis()-lastUpdateMillis)>=500){
    Serial.print("Count: ");
    Serial.println(count);
    lastUpdateMillis=millis();
  }
}

void countRoutine(){
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if(interrupt_time-last_interrupt_time>25){
    last_interrupt_time = interrupt_time;
    noInterrupts();
    count++;
    interrupts();
 }
}

It still does not seem to function correctly, with counts registering after the flowmeter has clearly stopped spinning. Is there something else that I am doing wrong?

scolland:
Thanks for your message. I've updated my code as follows

void countRoutine(){

static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if(interrupt_time-last_interrupt_time>25){
    last_interrupt_time = interrupt_time;
    noInterrupts();
    count++;
    interrupts();
}
}



Is there something else that I am doing wrong?

Could be. Could be.

If the interrupt routine is working on a thing do and another interrupt comes in the interrupt handler may get confused and just poo the bed.

Just for kibbles and bits have you reduced your interrupt code to a simple counts++; to check if there are too many things going on in the interrupt routine?

In the main loop put a print count statement to see if the count is going up.

The interrupt code should be as minimal as possible.

If the count is increasing as the loop code runs, the interrupt is working fine and needs to have less stuff to do.

If you move the code into another function that gets called by the interrupt you may run into some collisions.

For instance if a interrupt happens and a function is called, but, whiles the function is doing the thing, another interrupt comes in to call the function something in the chain is going to poo the bed. As may be obvious a calling function from an interrupt may need some traffic control.

You may want to look into a small / light weight task scheduler that has mutex or semaphore functions. Using a mutex or semaphonre will prevent the collisions and keep the interrupt handler from pooping the bed.

I use uMT for the DUE, which I think would tax the Mega, in my opinion. There ae other lightweight task schedulers with mutex and semaphonre capibilities. Do a search using your favorite search engine on "arduino using mutex."

For sure, start with thinning out the interrupt handler and see how well it works with minimal thing do's. The code does not have to do what you want, you are just troubleshooting, and reducing operations to their simpliest components.

If I remember correctly the MEGA is a 8 bit machine and asking a 8 bit machine to do this kind of math

static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if(interrupt_time-last_interrupt_time>25){
    last_interrupt_time = interrupt_time;

in an interrupt routine is asking for a lot.

Idahowalker:
If the interrupt routine is working on a thing do and another interrupt comes in the interrupt handler may get confused and just poo the bed.

Not at all, it never gets confused, this sort of thing happens all the time in real systems and a microcontroller
with broken interrupt handling would never pass muster.

On the ATmega there is a 1-bit queue per interrupt vector, at most one pending interrupt of each
sort is queued and when interrupts are re-enabled after one ISR the highest priority pending
interrupt, if any, is then triggered.

The thing that's obviously wrong is this in the ISR:

    noInterrupts();
    count++;
    interrupts();

The whole ISR is a critical section, it cannot be interrupted, so trying to call noInterrupts() or
interrupts() in it is bogus.

Critical sections in the main part of the program need guarding with noInterrupts/interrupts, not in
an ISR. I thought I explained that?

The problem with false triggering is probably due to relying on internal pull-ups which are too weak
for use with some sensor on the end of a cable.

Add a 1k pull up resistor on the pin, should kill false triggers when the flow sensor is stationary.

What kind of pulse generator is in the flowmeter? Mechanical reed switch, Hall sensor, opto electronic? Cable length?