1

I have the following really simple code. On every iteration it'll read the first pin, and if it's high - do some serial IO. Then do the same with the second pin. Nice and simple, works during my tests.

My concern is that (in production), the digital pulse I'm receiving (and trying to mimic during testing) would be too quick for the arduino to read. So, for example, the race condition could be:

  1. First pin is low Read first pin - result is low.
  2. First pin is now high Read second pin - result is low.
  3. Fist pin now low again Read first pin - result is low again (missed the 'high')

How much of an 'edge case' is this? Am I worrying about something that's likely to be a non-issue here? In production, each single pulse would represent a significant amount of product (I multiply it elsewhere) - so even skipping one could throw my data off.

boolean count1High = false;
boolean count2High = false;
int count1 = 7;
int count2 = 8;
int a = 1;
void setup() {
  pinMode(count1, INPUT);
  pinMode(count2, INPUT);
  Serial.begin(9600);
}

void loop()
{
  if (digitalRead(count1) == HIGH)
  {
    if (!count1High)
    {
      count1High = true;
      a++;
      Serial.println(a); 
    }
  }
  else 
  {
    count1High = false;
  }

  if (digitalRead(count2) == HIGH)
  {
    if (!count2High)
    {
      count2High = true;
      a++;
      Serial.println(a); 
    }
  }
  else 
  {
    count2High = false;
  }
}

EDIT:

I know best approach here might be to attach an interrupt but I've been advised to avoid serial IO from within interrupts, I don't see an alternative here.

0

This is exactly the reason interrupts were invented. They can respond a lot faster than a simple digital read. By "a lot" I mean orders of magnitude.

When an interrupt is triggered your interrupt handler for that interrupt executes. If another interrupt occurs whilst your interrupt handler is running the interrupt controller remembers it and the interrupt handler for that interrupt will execute as soon as the previous one is finished.

So you won't miss that pulse while the other pulse is processing.

However note that you shouldn't use Serial in interrupts, since Serial itself uses an interrupt internally to control the transmission, so transmission can't happen properly if your interrupt is already running. So instead your interrupt routine should just set a flag which you then check for in your main loop. You then benefit from the ability to notice a pulse while processing another pulse and still be able to use Serial. In fact, if you arrange it right, you can recognise and remember a pulse on the same input as the one you're currently processing.

0

It depends where the signal comes from and on other code in your sketch.

Typically, a sketch is rather way too fast for a mechanical switch, and you have to debounce it.

If your fear to lose events is realistic, interrupts are the way to go. But an ISR is some very special code: You won't do more than collect the information that there was an event. And you have to assert your main sketch won't interfere with the ISR.

0

As explained by Majenko's answer, you should detect the pulses from an interrupt and print the result from the regular code. However, instead of just setting a flag in the interrupt handler, I suggest you instead count the pulses within the handler. This will make the handler a tiny bit slower, which is often something you want to avoid. However, the extra overhead is really tiny, and you gain the ability to count not only pulses that are short but also pulses that are closely spaced.

There is, however, something to be aware of when sharing a variable between interrupt code and regular code. If the variable cannot be read in a single CPU instruction (which is the case of an int on an AVR-based board), there is some risk that the value gets changed in the middle of you reading it. To void this problem, you have to disable interrupts when reading the variable from regular code.

Here is an implementation of this strategy highlighting this point:

const uint8_t intPin1 = 2;
const uint8_t intPin2 = 3;

// This is volatile because it is shared between interrupt context
// and regular code.
volatile unsigned int sharedCount = 0;

// Interrupt handler.
void countPulse() { sharedCount++; }

// A variable shared with interrupt context should be accessed from
// regular code with interrupts disabled, in order to avoid race
// conditions.
unsigned int getCount() {
    noInterrupts();
    unsigned int countCopy = sharedCount;
    interrupts();
    return countCopy;
}

void setup() {
    pinMode(intPin1, INPUT);
    pinMode(intPin2, INPUT);
    attachInterrupt(digitalPinToInterrupt(intPin1), countPulse, RISING);
    attachInterrupt(digitalPinToInterrupt(intPin2), countPulse, RISING);
    Serial.begin(9600);
}

void loop() {
    static unsigned int previousCount = 0;
    unsigned int count = getCount();
    if (count != previousCount) {
        Serial.println(count);
        previousCount = count;
    }
}

Note that if the pulses come very fast, you may miss printing the value, but you will not miss counting it (up to some limit, of course). Thus you may see non-consecutive numbers being printed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.