Forcing Interrupt Service Routine only iterate once in a loop

I'm writing code that needs to detect rising edges on two digital signals. I've chosen to do this using interrupts. The way I have it set up right now the interrupt constantly occurs, saving the value of the timer into two variables AInt and BInt. The resolution of how often these interrupts are occurring is giving me bad values for each variable. If I could have a way to detect both the B signal edge and A signal edge exactly only once in a loop and save each timer value to compare, it would be perfect. So not just the ISR, but the whole interrupt. I thought about using input capture, but there is not enough available hardware on the board I am using, the Spark fun Qwik Pro Micro (Atmega32U4).

Here is my code:

int Asignal = 2;
int Bsignal = 3;
unsigned int AInt;
unsigned int BInt;
void setup() {
  Serial.begin(9600);
  pinMode(Asignal, INPUT_PULLUP);
  pinMode(Bsignal, INPUT_PULLUP);
  //attachInterrupt(digitalPinToInterrupt(Asignal),intRT1,RISING);
  //attachInterrupt(digitalPinToInterrupt(Bsignal),intRT2,RISING);
}

void loop() {
 
  attachInterrupt(digitalPinToInterrupt(Asignal),intRT1,RISING);
  attachInterrupt(digitalPinToInterrupt(Bsignal),intRT2,RISING);
  
  if (AInt > BInt) {
    Serial.println("Scrolling A direction");
  }
  if (BInt > AInt) {
    Serial.println("Scrolling B direction");
  }
  Serial.println(AInt);
  Serial.println(BInt);
  delay(1000);

}

void intRT1() {
  //Serial.println("A interrupt");
   AInt=TCNT1;
}
void intRT2() {
  //Serial.println("B interrupt");
   BInt=TCNT1;
}

Once again, the solution to my problem would be a way for the whole interrupt to only iterate once per loop.

Your Aint and Bint variables should be volatile; you will also need to disable interrupts before you read them in loop() and enable the interrupts again after that.

2 Likes

re-enable after I read the interrupts or at the end of the loop?

Post the complete sketch that contains the codes for Timer/Counter 1 (TCNT1).

there is no external code for TCNT1. I just take the value of TCNT1 and use it as a "timestamp" for each rising edge.

Post the timing diagrams of the two external interrupting signals. Are you trying to measure the time difference between the two rising edges? There should be codes to intialize the TC1 (Timer/Counter 1) (mode of operation, frequency of the driving clock).

  1. do some stuff that is useful
  2. disable interrupts
  3. do something with Aint and Bint
  4. enable interrupts
  5. do whatever else needs to happen



Depending on which way I spin my encoder, the leading signal will tell me which direction Im rotating the encoder. I need to know which time value is less than the other so I can detect my direction. if the yellow signa is leading, im spinning "forward" for example.

my current solution is to make two flags that are set if I have a value for AInt or BInt. If these are 1, then I dont save a new value for every interrupt. It sort of locks the value for the compare.

Are you using the followig encoder to decide the direction of movement? If so, then why do you need TCNT1?
image

Since you are trying to program a rotary encoder, you should immediately abandon your current path and study A Proper Rotary Encoder Algorithm.

2 Likes

The encoder I am using is that of one that would be in a computer mouse:

image

If I Can correctly get the time stamps of these interrupts I should be able to detect what I am looking for.
There is three pins, A, B and GND. Like I said before depending on direction one signal is leading the other.

Like any other encoder.

You don't need to know which signal changes first. Trying to measure that makes everything far more difficult. You just need to check if A is HIGH or LOW when B changes and vice-versa. This is how pretty much all encoder libraries work.

3 Likes

I tried this approach, and I'm still getting an incorrect result. The code just looks to read one of the signals after it detects a rising edge, and then prints the direction based on an evaluation. Not sure what else could be wrong here other than resolution of how many times the interrupt samples the data.

 int Asignal = 2;
  int Bsignal = 3;
  volatile unsigned int AInt;
  volatile unsigned int BInt;
  volatile unsigned int Dir;
  
void setup() {
  Serial.begin(9600);
  pinMode(Asignal, INPUT);
  pinMode(Bsignal, INPUT);
}

void loop() {
  attachInterrupt(digitalPinToInterrupt(Asignal),IntRT1,RISING);
  //delay(100);
  if (Dir == 1) {
    Serial.println("Scrolling in B direction");
  }
  if (Dir == 0) {
    Serial.println("Scrolling in A Direction");
  }
  delay(1000);
}

void IntRT1 () {
  volatile unsigned int BStatus = digitalRead(Bsignal);
  if (BStatus == 1) {
    Dir = 1;
  }
  if (BStatus == 0) {
    Dir = 0;
  }
}

Like I said this still isn't giving the right input its like its getting random values to compare. Im wondering if maybe its a hardware problem with the micro.

that fixed the issue, works like a charm. Thanks

Why are you calling attachInterrupt() on every pass through loop?

The algorithm I linked in Post #10 automatically handles contact bounce with no extra hardware required. Here's an Interrupt-Driven Library that I wrote based on that algorithm.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.