Notifications
Clear all

Use hardware interrupt to read a 5 button switch (DAOKI 5 button board)

27 Posts
5 Users
15 Likes
1,341 Views
(@eliza)
Member
Joined: 2 years ago
Posts: 81
Topic starter  

I use a 5 key board attached to A5 and get values from zero to around 1000 from an analog pin. I use simple if statements to determine which button was pressed, like if(input > 160 && input < 170) return keyRight

I'd like to use an interrupt to catch the keydown event and read the pin inside the ISR like:

volatile int rawData; // global


volatile int globalStateVariable;


void myISR(){

rawData = analogRead(2); // catch interrupt with CHANGE but read the value here

globalStateVariable  = MapValue(rawData);

}

-------------------

now I would have a switch statement in the Arduino main loop to handle the key press detected

Is it possible to read analog in an ISR or do I only get "button pressed is true"

Thanks!!

This topic was modified 2 years ago by Eliza

   
Inq reacted
Quote
(@codeslinger)
Member
Joined: 4 years ago
Posts: 30
 

Not a good idea to do any I/O inside the ISR.  This can cause other interrupt driven events to be delayed or missed entirely.  The mantra of writing interrupt service routines is get the information, pass it on, get out.  DO NOT do anything that will cause the ISR to wait for something else to happen.  I/O usually requires at least some waiting even if just for the data to transfer at a speed much slower than memory accesses.


   
Inq, Ron and ron bentley reacted
ReplyQuote
(@eliza)
Member
Joined: 2 years ago
Posts: 81
Topic starter  

@codeslinger Bill's video shows reading binary switches with both regular ISR (uno pins 1 & 2) and also digital read of pin 7. I'm looking to do a similar thing but capture from among 5 values. So get info, pass it on and get out is exactly what I want to do. I was able to set up a pin change interrupt method but the only value I can capture is zero.


   
ron bentley reacted
ReplyQuote
(@codeslinger)
Member
Joined: 4 years ago
Posts: 30
 

No, my point is that the read should not be in th ISR.  In this particular case, the only info to pass on is that the interrupt happened.  Set a volatile flag that tells the main thread about it and get out.  The main thread will then check the flag and reset it. If the flag was set, the main processing loop and NOT the ISR will do what you have ind your ISR.  Even reading a single pin can cause the thread doing the read to wait.  An ISR MUST NOT be made to wait while handling the interrupt. 


   
Ron and ron bentley reacted
ReplyQuote
ron bentley
(@ronbentley1)
Member
Joined: 2 years ago
Posts: 385
 
Posted by: @eliza

I use a 5 key board attached to A5 and get values from zero to around 1000 from an analog pin. I use simple if statements to determine which button was pressed, like if(input > 160 && input < 170) return keyRight

I'd like to use an interrupt to catch the keydown event and read the pin inside the ISR like:

volatile int rawData; // global


volatile int globalStateVariable;


void myISR(){

rawData = analogRead(2); // catch interrupt with CHANGE but read the value here

globalStateVariable  = MapValue(rawData);

}

 

-------------------

now I would have a switch statement in the Arduino main loop to handle the key press detected

Is it possible to read analog in an ISR or do I only get "button pressed is true"

Thanks!!

@eliza,

Hi Eliza,

Some sound advice given thus far in this thread, against which I cant argue.

However, depending on  the criticality of your application you may find you find no issues doing what you suggest.

So, try it out.

Let's know how you do.

Ron B

 

 

Ron Bentley
Creativity is an input to innovation and change is the output from innovation. Braden Kelley
A computer is a machine for constructing mappings from input to output. Michael Kirby
Through great input you get great output. RZA
Gauss is great but Euler rocks!!


   
Inq reacted
ReplyQuote
Inq
 Inq
(@inq)
Member
Joined: 2 years ago
Posts: 1900
 

@eliza 

I too agree with @codeslinger and @ronbentley1.  But it is a matter of degrees and judgment.  @codeslinger view is anything (of use) inside the ISR is bad.  And that is the common opinion you'll find on the Internet.  I tend toward @ronbentley1 viewpoint - try it, abuse it and see if bad behavior erupts. 

The off chance you don't see any bad behavior while abusing it, but crops-up somewhere down the road is still a possibility.  You have to decide for yourself how critical your application is and whether that one in ten-thousand key presses causes an exception is something you can live with.

I think @codeslinger's concern stems from knowing how painfully slow an analogRead() is.  I do quite a bit of timing sensitive coding.  It's the Three Bears thing - Spend too long in an ISR, it doesn't work.  Spend too little and you get nothing done.  Spend just right and all are happy in the world.  I have a little test loop I run to time various snippets of code.

and here are some results for an ESP8266.  YMMV drastically longer on an Arduino, so you'll have to do your own tests, but...

image

... as you can see on an ESP8266 an analogRead() takes over 4 milli-seconds while a digitalRead() only takes 1.2 micro-seconds.  From my experience 4 milli-seconds would certainly go belly up on an ESP8266 that is also acting as a web server, serving web content and websocket communications.  On a single purpose Arduino that has nothing else to do but read the button presses, you might be able to get away with it.

As an example of pushing that envelope, I'm currently working on a two-wheel robot motor driver with accelerations and even has gear change capability.  It's a work in progress, but below is the current version that can cause one digitalRead() and up to three digitalWrite() calls within the ISR and a whole bunch of logic.  It also includes two calls to a quadratic equation solver that has a square-root call in it.  This all works while running the robot, serving web pages and supporting websocket telemetry.  The point being, I do a whole lot more than setting a flag, but I believe the worst case through it is still less than 100 micro-seconds.  I haven't tested for that lately.

ICACHE_RAM_ATTR void Stepper::setPulseOnTime()
{
    // Sets the _intrpt time for this stepper motor.
    
    // Increment the steps done AS IF no gear change is involved.
    _stepsDone += sgn(_segments->_stepsToDo) * _gear;   
    
    float t;
    if (_segments->_accel > 0)
    {
        // ACCELERATING
        t = stepTime();        
        
        // Do we need to change gear?
        if ((_gear == 1) &&         // Are we in low gear?
            (_v > GEAR_CHANGE) &&   // Are we over the gear change speed?
            // Since high gear skips micro-steps AND we want the last one
            // at _stepsDone to get a pulse, we need to make sure we're
            // MICRO_STEPPING steps away from the end one.  Are we?
            ((_segments->_stepsToDo - _stepsDone + 1) % MICRO_STEPPING == 0))
        {
            // Change gear
            _gear = MICRO_STEPPING;
            digitalWrite(_pinGear, LOW);       // TOP GEAR
            // Re-get new time to interrupt
            _stepsDone += MICRO_STEPPING - 1;
            t = stepTime();
        #ifdef BUGIT
            bugit("Up-shift ");
        #endif
        }
        #ifdef BUGIT
        bugit("s=%d  i=%u  v=%f", _stepsDone, _intrpt, _v);
        #endif
    }
    else if (_segments->_accel < 0)
    {
        // DECELERATING
        t = stepTime();              
        
        // Do we need to change gear?
        if ((_gear == MICRO_STEPPING) && // Are we in high gear?
            (_v < GEAR_CHANGE))         // Are we under the gear change speed?
        {
            // Change gear
            _gear = 1;
            digitalWrite(_pinGear, HIGH);       // FIRST GEAR
            // Re-get new time to interrupt
            _stepsDone -= MICRO_STEPPING - 1;
            t = stepTime();
        #ifdef BUGIT
            bugit("Down-shift ");
        #endif
        }
        #ifdef BUGIT
        bugit("s=%d  i=%u  v=%f", _stepsDone, _intrpt, _v);
        #endif
    }
    else
        // CONSTANT SPEED
        t = _stepsDone / _v;

    // Set _intrpt to the time desired for pulsing this motor.
    _intrpt = _start + (u32)(t * 1000000);    
    
    if (_stepsDone == _segments->_stepsToDo)
    {
        // We're finished with this segment.
        // Update the initial velocity for the next segment.
        _v0 = _v;
        bugit("v=%f   %f mph\n", _v, _v > 0 ? _v / 4553.512 : 0);
        // Get the next segment and free old one.
        Segment* tmp = _segments;
        _segments = _segments->_next;
        free(tmp);    
        
        if (_segments)
        {
            _start = _intrpt;            
            _stepsDone = 0;
        }
        else
        {
            //DRV->log("%c: End of segments and Vf = %f\n", _side, _v0);
            _v0 = 0;
            _stepsDone = 0;
            if (!_other->_segments)  
            {
                DRV->log("\nMotors disabled\n");
                digitalWrite(DRV->_pinEnable, HIGH); // Disable them.
                NXT = NULL;
            }
        }
    }
}

3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide


   
ron bentley reacted
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6864
 

@eliza Besides all the good advice you have already received, you might consider using semaphores for data synchronization. 

I haven't checked but I assume this high level style of interrupt handler is automatically placed in fast ram.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
ron bentley
(@ronbentley1)
Member
Joined: 2 years ago
Posts: 385
 

@inq , @eliza,

It is always a balance isn't it - theory v.  Pragmatism. 

Ron B

PS

I like the timing table, very instructive, and revealing.

 

 

Ron Bentley
Creativity is an input to innovation and change is the output from innovation. Braden Kelley
A computer is a machine for constructing mappings from input to output. Michael Kirby
Through great input you get great output. RZA
Gauss is great but Euler rocks!!


   
Inq reacted
ReplyQuote
Inq
 Inq
(@inq)
Member
Joined: 2 years ago
Posts: 1900
 
Posted by: @zander

@eliza Besides all the good advice you have already received, you might consider using semaphores for data synchronization. 

I haven't checked but I assume this high level style of interrupt handler is automatically placed in fast ram.

Professionally, I used semaphores when writing Windows multi-threaded code regularly.  I wasn't aware there is an equivalent in the Arduino world.  Can you (or someone) give an example or point me to some reference?   In this case, my ignorance was only bliss up until I read this. 🤣 

VBR,

Inq

3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6864
 

@eliza There is quite a bit of supporting code needed to get that pin change interrupt to work. Not only is it not shown, but the ISR code shown is likely incorrect because the ISR's have specific names for the 'any pin' 'pin change' ISR to work. Also as Bill states, you get 2 interrupts per keypress, and it's up to your code to handle that as well as decode the meaning of the interrupt.

Perhaps posting all the relevant code will go a long way to helping us understand what you are doing and if it needs to be changed.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6864
 

@inq Here is one example https://iotespresso.com/button-external-interrupts-with-esp32/

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6864
 

@inq EDIT It is an ESP32 only mechanism it appears.

I just tried to compile for the ESP8266 and UNO and sadly the MUX feature does not appear to be supported.

EDIT2 Keep in mind that there is an arduino function nointerrupts() and interrupts() that can be used.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
Inq
 Inq
(@inq)
Member
Joined: 2 years ago
Posts: 1900
 
Posted by: @zander

@inq Here is one example https://iotespresso.com/button-external-interrupts-with-esp32/

  1. The word "sema" does not occur in that article.
  2. A semaphore and a critical section are two different things.
  3. It appears that is an ESP32 only capability - Nice!  I like it.  Might have to do more ESP32 work.
  4. @eliza hasn't mentioned, what MPU he is using.  It may be a ESP32 or an Arduino, but it certainly isn't an ESP8266 since he referenced an A5 pin... Doesn't exist on an ESP8266.
  5. I don't see where a critical section will help in this situation.  The issue is not synchronization, but the length of time it takes for the analogRead to run.  Doing this in an ISR may starve other things going on and cause a fatal exception or just bad behavior.  

VBR,

Inq

3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6864
 

@inq Darn, I went down the rabbit hole chasing a sema, but took a turn into critsec somewhere. Both are used and now I can't find the pesky sema but will keep looking.

EDIT, I did find one ref to sema, but it's actually a critsec. I didn't notice the change in nomenclature, my apologies for the confusion. See pic attached.

Screen Shot 2022 09 08 at 06.52.46

 

Agreed the analogRead is the bigger issue.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
Inq reacted
ReplyQuote
Inq
 Inq
(@inq)
Member
Joined: 2 years ago
Posts: 1900
 
Posted by: @zander

@inq Darn, I went down the rabbit hole chasing a sema, but took a turn into critsec somewhere. Both are used and now I can't find the pesky sema but will keep looking.

EDIT, I did find one ref to sema, but it's actually a critsec. I didn't notice the change in nomenclature, my apologies for the confusion. See pic attached.

Screen Shot 2022 09 08 at 06.52.46

 

Agreed the analogRead is the bigger issue.

I'd be curious, but don't go to too much trouble.  Semaphores are a little bigger beast to implement than a critical section.  As you show... a critical section often can be avoided by just using a volatile (until you have to synchronize more than one variable - transaction style).  I'm guessing, a semaphore may be special to ESP32's when they had those dual core versions.  Since they went back to a single core implementation, they may have axed it.  

3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide


   
ReplyQuote
Page 1 / 2