Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: LC: multiple interrupts on one pin

  1. #1
    Junior Member
    Join Date
    Apr 2019
    Location
    Cologne (DE)
    Posts
    10

    LC: multiple interrupts on one pin

    Hello,
    I have a soft-off/emergency switch that, when triggered, should pause a blocking routine (multistepper) immediately. That said, I might miss some basic understanding about interrupts, but here's what I did:

    In setup():
    Code:
    attachInterrupt(SafeOffPin, offRising, RISING);
    attachInterrupt(SafeOffPin, offHigh, HIGH);
    attachInterrupt(SafeOffPin, offFalling, FALLING);
    then
    Code:
    void offRising(){
       Serial.println("offRising");
    }
    void offHigh(){
       Serial.println("offHigh");
    }
    void offFalling(){
       Serial.println("offFalling");
    }
    When I do that, only offFalling() is executed. But it would be nice if there was some way to wrap some definite start and end around the pause, basically to display a message and release some enable pins on motor drivers. Any ideas?

    best, Philip

  2. #2
    Senior Member
    Join Date
    Feb 2015
    Location
    Finland
    Posts
    133
    What I would do, is extend multistepper to support a global volatile flag, that is initialized to zero in the user init() function, and set in the interrupt function.

    Necessary changes to MultiStepper.cpp include replacing the last two functions with
    Code:
    external volatile unsigned char  emergency_stopped;
    
    // Returns true if any motor is still running to the target position.
    boolean MultiStepper::run()
    {
        uint8_t i;
        boolean ret = false;
        for (i = 0; i < _num_steppers; i++)
        {
    	if ( _steppers[i]->distanceToGo() != 0)
    	{
    	    if (!emergency_stopped)
    	    {
    		_steppers[i]->runSpeed();
    	    }
    	    ret = true;
    	}
        }
        return ret;
    }
    
    // Blocks until all steppers reach their target position and are stopped
    void    MultiStepper::runSpeedToPosition()
    { 
        while (run() && !emergency_stopped)
    	;
    }
    
    // Blocks until all steppers reach their target position and are stopped
    void    MultiStepper::runSpeedToPosition()
    { 
        while (!emergency_stopped && run())
    	;
    }
    and probably two functions in AccelStepper.cpp ,
    Code:
    external volatile unsigned char  emergency_stopped;
    
    // Implements steps according to the current step interval
    // You must call this at least once per step
    // returns true if a step occurred
    boolean AccelStepper::runSpeed()
    {
        // Do nothing if emergency stopped
        if (emergency_stopped)
    	return false;
    
        // Dont do anything unless we actually have a step interval
        if (!_stepInterval)
    	return false;
    
        unsigned long time = micros();   
        if (time - _lastStepTime >= _stepInterval)
        {
    	if (_direction == DIRECTION_CW)
    	{
    	    // Clockwise
    	    _currentPos += 1;
    	}
    	else
    	{
    	    // Anticlockwise  
    	    _currentPos -= 1;
    	}
    	step(_currentPos);
    
    	_lastStepTime = time; // Caution: does not account for costs in step()
    
    	return true;
        }
        else
        {
    	return false;
        }
    }
    and
    Code:
    // Blocks until the target position is reached and stopped
    void AccelStepper::runToPosition()
    {
        while (!emergency_stopped && run())
    	;
    }
    In your own code, you declare the flag,
    Code:
    volatile unsigned char  emergency_stopped;
    clear it in your init() function using
    Code:
    emergency_stopped = 0;
    before initializing the steppers; you can also do that whenever the emergency stop is canceled (so it acts more like an immediate "pause" than stop),
    and in your interrupt function that gets called whenever the emergency stop is enabled, add
    Code:
    emergency_stopped = 1;
    It only uses one additional byte of RAM, and a tiny bit of Flash/ROM.

    The idea is that the flag acts like a big red "DON'T DO IT!" to the multistepper library. It is marked extern in the MultiStepper files, because it is not declared there (it is declared in your own code); and it is also marked volatile to tell the compiler that its value can change at any time, so must not be cached. "Caching" in the compiler context means that if it sees it used in a while loop condition, means that even if it sees that the loop body does not change the value, it cannot just check it once before the loop; it must check it every loop iteration.

    Normally, such flags are made internal to the class. Here, I deliberately made it part of your own code, accessed "externally" by the modifications in the multistepper library, as it is a kinda "global" feature.

    It would not be difficult to think about this for a bit, then submit a patch to the maintainer (Mike McCauley at AirSpayce), to implement it more properly. Probably, the AccelStepper class should have a public volatile flag, say stopped , that the above functions access; cleared in the constructor, and set in the user emergency stop function.

    However, I do not use AccelStepper/MultiStepper myself, and haven't even bothered to check if the above code works: I'm too lazy, sorry.

  3. #3
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,176
    only offFalling() is executed
    That's because attachinterrupt can't set multiple tests for the state of one pin. Each call to attachInterrupt for a particular pin replaces whatever was previously set. Your last call was to test for FALLING which replaces what was there before and only a FALLING state on the pin will trigger an interrupt.
    You can, however, use CHANGE which will cause an interrupt on a FALLING or RISING state. The interrupt routine can then examine the state of the pin to determine whether it was a rising or falling state which caused the change. The interrupt routine can also keep track of whether the pin is LOW or HIGH.

    Pete

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,813
    Using CHANGE is probably the best way.

    You could also connect the same signal to multiple pins and use attachInterrupt on each pin. However, if you use HIGH and your function returns while the signal is still high, you can expect that interrupt to run again immediately, very likely preventing any others from running.

    If this feature is needed in emergency situations where lives may be at risk, you probably should have a physical switch which is certain to disconnect power even if the software has crashed with interrupts disabled.

  5. #5
    Junior Member
    Join Date
    Apr 2019
    Location
    Cologne (DE)
    Posts
    10
    Wow, thank you so much for your thorough answers!
    @Nominal Animal: Your answer goes a little beyond the scope of my little problem, but I'll definitively keep it in mind for later polishing of the project-- I want to simply be able to disable the stepper drivers at any time. When I do it while the steppers are in motion, they'll loose their position and that is where your approach would make an excellent solution.

    I was not aware that there is only one interrupt per pin, but the use of CHANGE might just cover everything needed. And I'll make shure not to have the fate of mankind be dependent solely on my code, I swear =)

    So, here's what works for me:
    somewhere early:
    Code:
    #define SafeOffPin 10       // Int
    volatile bool safeOffState; // Store the SafeOff Btn state
    In setup()
    Code:
    pinMode(SafeOffPin, INPUT_PULLUP);
    attachInterrupt(SafeOffPin, offChange, CHANGE);
    somewhere else:
    Code:
    void offChange(){
       if(debug) Serial.println("offChange");
    
       if(!digitalRead(SafeOffPin)){                // [Machine ON] (knob right)
          if(safeOffState){                         // Was [OFF] before
             if(debug) Serial.println("Machine ON");
             digitalWrite(ALL_EN_PIN, LOW);         // Enable stepper drivers
             mcp.digitalWrite(powerLED, HIGH);
             mcp.digitalWrite(InOutLED, HIGH);
             updateLcd(0,true);                     // draw all items
             safeOffState = false;
          }
       }
       else{                                        // [Machine OFF] (knob left)
          if(!safeOffState){                        // Was [ON] before
             if(debug) Serial.println("Machine SoftOFF");
             digitalWrite(ALL_EN_PIN, HIGH);        // Disable stepper drivers
             mcp.digitalWrite(powerLED, LOW);
             mcp.digitalWrite(InOutLED, LOW);
             updateLcd(24, true);                   // Msg: "save to cut power now"
             safeOffState = true;
          }
       }   
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •