1

Is there a way to write a custom function which would be the mode/reason to trigger an interrupt. I have an analog joystick connected to my arduino analog pin. It reads a value of 689 on one end and 0 on the other and around 480 in the center. I would like to trigger an interrupt based on these value changes i.e an interrupt when it more then 600 and another when less then 10. None of the pre-defined modes RISING,HIGH,LOW,FALLING,CHANGE are working satisfactorily.

3 Answers 3

1

No. Interrupts are hardware. You cannot "create" interrupts in software.

The closes there is to what you are after is the Analog Comparator (see section 27 of the datasheet).

The analog comparator allows you to trigger an interrupt if a voltage is above (or below) a reference voltage which is up to you to provide.

For anything more complex you would need to use external comparators to trigger on different voltage levels which then trigger external INT pins on the ATMega.

0
1

Of course, you can make software interrupts on Arduino; it just costs a hardware pin. The following is a quote from the Atmel ATmega48A/PA/88A/PA/168A/PA/328/P datasheet:

"13. External Interrupts The External Interrupts are triggered by the INT0 and INT1 pins or any of the PCINT23...0 pins. Observe that, if enabled, the interrupts will trigger even if the INT0 and INT1 or PCINT23...0 pins are configured as outputs. This feature provides a way of generating a software interrupt."

You set up an interrupt on the pin you select for the purpose, you set the pin as output, and then you trig your "software interrupt" by writing a value to the pin.

1
  • This could be combined with @Edgar Bonet's suggestion to use a free-running ADC, which generates an interrupt every 104us. The ADC interrupt does a range check and, on entering or leaving the range, raise a PCINT to carry out whatever actions are required.
    – JavaLatte
    Apr 12, 2022 at 12:42
0

As Majenko already said, you cannot do that. But you can look for alternative ways of achieving a similar end result. The real question is: what are you trying to achieve? Interrupts may not be the best answer.

I assume what you have in mind is a program roughly along these lines:

// Interrut handler called when input > 600.
void handleHigh()
{
    // ...
}

// Interrut handler called when input < 10.
void handleLow()
{
    // ...
}

void setup()
{
    // Register the interrupt handlers.
    attachInterrupt(ANALOG0, handleHigh, MORE_THAN_600);
    attachInterrupt(ANALOG0, handleLow, LESS_THAN_10);
}

void loop()
{
    doSomeStuff();
}

Of course, that syntax for calling attachInterrupt() does not work, I just made it up.

The most obvious alternative is to read the value through an analog input in loop() and act accordingly:

void loop()
{
    // Check the analog input.
    int sample = analogRead(A0);
    if (sample > 600) handleHigh();
    if (sample < 10) handleLow();

    // Do the rest of the job.
    doSomeStuff();
}

No need to use interrupts. The only drawback is that the latency of your response to a change in the joystick position can be as long as the time your program takes to run through loop(). This should normally not be a problem: the joystick is a very slow mechanical device, and your loop should take no more than a few milliseconds. If the loop if really too slow, you are probably doing something wrong, like using delay(). You should then fix that problem rather than trying to work around it with interrupts.

Just for completeness, there is an interrupt-based solution. Assuming you have no other analog input to read, you could configure the ADC to continously read the same input (say, A0) in the so called “free running mode”, and trigger an interrupt on each reading. In the ISR, you can then call one of your interrupt handlers if needed (if the sample you get is outside [10, 600]). Here is how you can do it on an Arduino Uno:

void setup()
{
    // Configure the ADC in "free running mode".
    ADMUX  = _BV(REFS0)  // ref = AVCC
           | 0;          // input channel = A0
    ADCSRB = 0;          // free running mode
    ADCSRA = _BV(ADEN)   // enable
           | _BV(ADSC)   // start conversion
           | _BV(ADATE)  // auto trigger enable
           | _BV(ADIF)   // clear interrupt flag
           | _BV(ADIE)   // interrupt enable
           | 7;          // prescaler = 128
}

// ADC interrupt service routine.
// Called each time a conversion result is available.
ISR(ADC_vect)
{
    // Read the ADC.
    int sample = ADC;

    // Call the handlers if needed.
    if (sample > 600) handleHigh();
    if (sample < 10) handleLow();
}

Edit: With the code above, the ISR runs every 104 µs. Then you should make sure that your handlers take significantly less than that time to complete. Otherwise they will consume 100% of the CPU power whenever the joystick is in the range to trigger one of them. As a small optimization, you could declare the handlers static inline in order to avoid the overhead of a function call.

2
  • Thank you Edgar. I cannot use the loop to check the value since I have other code running there but this input has higher priority. But I guess the second suggestion would work. Thank you
    – Vignesh
    Dec 2, 2016 at 21:41
  • @Vignesh: You wrote: “I cannot use the loop to check the value since I have other code running there”. This makes no sense at all. See the doSomeStuff() call in my example: it's still there when you check the input in the loop. Dec 2, 2016 at 21:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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