Using MotorShield v2.3 with an ISR routine set up

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
plus1etal
 
Posts: 3
Joined: Sat Feb 11, 2017 2:35 am

Using MotorShield v2.3 with an ISR routine set up

Post by plus1etal »

Hi,
I've set up an ISR routine on my Arduino for consistent control, and I've found that any time I try to set motor direction or speed, the ISR stops running. I have a vague idea that the MotorShield must be using one of the timers I set up for the ISR and that's what's causing the issue. Any chance I could get some help with this? I've attached code of my timer setup. I'm using code developed by one of my Professors, so I'm not super sure how the timers work. I'm more familiar with ISR setup on TI Launchpad products, to be honest.

Code: Select all

  //  Configure TIMER1 to drive the Interrupt Service Routine
  TCCR1A = 0;                  // initialize entire TCCR1A register to zero (upper half of a 2-byte register)
  TCCR1B = 0;                  // initialize entire TCCR1B register to zero (lower half of a 2-byte register)
  TCNT1  = 0;                  // reset the initial Timer1 CouNT value
  if (FS >= 10)  {  // configure for higher sample rates
    OCR1A = (int)(62500 / FS); // define the total counts in the Output Control Register to achieve FS Hz
    TCCR1B |= (1 << WGM12);    // set the WGM12 bit to enable Clear Timer on Compare (CTC) mode
    TCCR1B |= (1 << CS12);     // set the CS12 bit
    //  the CS12 bit creates a prescaled 16MHz/256=62.5kHz base clock
  } else {          // cofigure for lower sample rates
    OCR1A = (int)(15625 / FS); // define the total counts in the Output Control Register to achieve FS Hz
    TCCR1B |= (1 << WGM12);    // set the WGM12 bit to enable Clear Timer on Compare (CTC) mode
    TCCR1B |= (1 << CS12);     // set the CS12 bit
    TCCR1B |= (1 << CS10);     // set the CS10 bit
    //  the CS12 and CS10 bits create a prescaled 16MHz/1024=15.625kHz base clock
  }
  TIMSK1 |= (1 << OCIE1A);     // enable the timer Output Compare Interrupt bit OCIE1A


  //  Configure TIMER2 to drive the PWM's (for generating analog outputs in pins 9 and 10 on the Mega)
  TCCR2B &= 0b11111000;        // set bits 7, 6, 5, 4, and 3 to define the PWM waveform generator mode
  TCCR2B |= (1 << CS11);       // set the CS11 bit
  //  the CS11 bit defines a divisor of 8 to create a prescaled 16MHz/510/8 = 3921.57 Hz base PWM frequency

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using MotorShield v2.3 with an ISR routine set up

Post by adafruit_support_bill »

The V2 motor shield library does not use timers. Please post your complete code.

User avatar
plus1etal
 
Posts: 3
Joined: Sat Feb 11, 2017 2:35 am

Re: Using MotorShield v2.3 with an ISR routine set up

Post by plus1etal »

My full code is very long, but I will post the relevant parts (setup, ISR). I'm using Serial printing for debug, and I've found that when the motorshield commands are used (setspeed, run) the ISR is terminated and stops running. When I comment those lines out it runs as expected.

Code: Select all

//Motorshield objects
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *pumpMotor = AFMS.getMotor(1);


/*  ----------setup----------
 *
 *    This is the setup function which is automatically run one time during startup,
 *    which happens at powerup or reset.  This function should include all 
 *    initialization tasks.
 *
 *    Do NOT change the name of this function!
 */

void setup()
{
  //  Copyright  (c)   2016  Steve Southward <[email protected]>

  //  Declare variables local to the setup function


  //  Initialize the serial monitor (required if you are printing to the Serial Monitor)
  //  the entered number is called the "baud rate" which means "bits per second"
  Serial.begin(9600);  // can choose {4800, 9600, 19200, 38400, 57600, 115200, 230400, or 250000}


  //  Configure the digital I/O pins we want to use
  pinMode(switchRockPin, INPUT);
  pinMode(controlRockPin, INPUT);
  pinMode(LEDvPin, OUTPUT);
  pinMode(LEDpPin, OUTPUT);
  pinMode(LEDboardPin, OUTPUT);
  pinMode(lim1Pin, INPUT);
  pinMode(lim2Pin, INPUT);
  pinMode(motPWMPin, OUTPUT);
  pinMode(motDIRPin, OUTPUT);


  //  Initialize and configure the low-pass filter for operation
  digital_lpf(0, true);


  //  Configure the TIMERS
  /*  see the following links for more details on configuring PWM Timers on the Arduino platform
   *  <http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/>
   *  <https://arduino-info.wikispaces.com/Arduino-PWM-Frequency>
   *  <https://arduinodiy.BANNED.com/2012/02/28/timer-interrupts/>
   *  <https://arduino-info.wikispaces.com/Timers-Arduino>
   *  <http://playground.arduino.cc/Main/TimerPWMCheatsheet>
   */

  noInterrupts();              // first disable all global processor interrupts

  //  Configure TIMER1 to drive the Interrupt Service Routine
  TCCR1A = 0;                  // initialize entire TCCR1A register to zero (upper half of a 2-byte register)
  TCCR1B = 0;                  // initialize entire TCCR1B register to zero (lower half of a 2-byte register)
  TCNT1  = 0;                  // reset the initial Timer1 CouNT value
  if (FS >= 10)  {  // configure for higher sample rates
    OCR1A = (int)(62500 / FS); // define the total counts in the Output Control Register to achieve FS Hz
    TCCR1B |= (1 << WGM12);    // set the WGM12 bit to enable Clear Timer on Compare (CTC) mode
    TCCR1B |= (1 << CS12);     // set the CS12 bit
    //  the CS12 bit creates a prescaled 16MHz/256=62.5kHz base clock
  } else {          // cofigure for lower sample rates
    OCR1A = (int)(15625 / FS); // define the total counts in the Output Control Register to achieve FS Hz
    TCCR1B |= (1 << WGM12);    // set the WGM12 bit to enable Clear Timer on Compare (CTC) mode
    TCCR1B |= (1 << CS12);     // set the CS12 bit
    TCCR1B |= (1 << CS10);     // set the CS10 bit
    //  the CS12 and CS10 bits create a prescaled 16MHz/1024=15.625kHz base clock
  }
  TIMSK1 |= (1 << OCIE1A);     // enable the timer Output Compare Interrupt bit OCIE1A


  //  Configure TIMER2 to drive the PWM's (for generating analog outputs in pins 9 and 10 on the Mega)
  TCCR2B &= 0b11111000;        // set bits 7, 6, 5, 4, and 3 to define the PWM waveform generator mode
  TCCR2B |= (1 << CS11);       // set the CS11 bit
  //  the CS11 bit defines a divisor of 8 to create a prescaled 16MHz/510/8 = 3921.57 Hz base PWM frequency

  interrupts();                // now it is ok to enable all global processor interrupts

  //Start the motorshield
  AFMS.begin();
  pumpMotor->setSpeed(0);
}




/*  ----------ISR----------
 *
 *   This is the Interrupt Service Routine function.  This function will be called at
 *   the fixed uniform sample rate of FS Hz.  All of the real-time functional tasks
 *   that must run at a fixed clock frequency should be implemented inside this function.
 *
 *   Do NOT change the name of this function!
 */

ISR(TIMER1_COMPA_vect)
{
  //  Copyright  (c)   2016  Steve Southward <[email protected]>
  Serial.println(7);
  //If Active Control is not enabled
  if(manualControlEnable)
  {
    Serial.println(9);
    //Get current pressure
    sensorVoltage = .0049*analogRead(sensorPin);

    actualPressure = (sensorVoltage-0.5)/(.04);
    
    if(positivePressure)
    {
      //Get command pressure
      potValue = analogRead(potpPin);
      //Normalize
      commandPressure = (85.5/1024)*potValue + 14.5;
    }
    else
    {
      //Get command pressure
      potValue = analogRead(potvPin);
      //Normalize
      commandPressure = 14.5 - (14.5/1024)*potValue;
    }

    error = commandPressure - actualPressure;
    if(error < 0) //If command pressure is less than actual pressure
    {
      //Backwards
      motDir = 0;
      error = error*-1;
      //digitalWrite(motDIRPin, LOW);
      //SHIELD
      pumpMotor->run(BACKWARD);
    }
    else
    {
      //Forwards
      motDir = 1;
      //digitalWrite(motDIRPin, HIGH);
      //SHIELD
      pumpMotor->run(FORWARD);
    }

    // compute the control signal
    motPWM = (int)(error / 4);    

    //  Note:  the value of control_out MUST be an 8-bit unsigned integer in the range of 0 to 255
    //  because the PWM output is only 8-bit, so it can only accept values between 0 and 255
    motPWM = constrain(motPWM, 0, 255); //  saturate the control_out value between 0 and 255

    //  Output the control_out signal as a PWM output
    //  for a PWM output:  0 = 0% duty cycle, 255 = 100% duty cycle, operation duration: ~16.6us
    //analogWrite(motPWMPin, (int)motPWM);
    //SHIELD
    pumpMotor->setSpeed(motPWM);
    
    //Check Rocker Switch (vac to pos)
    if (digitalRead(switchRockPin) == LOW)
    {
      rockerSelect = 0;
      if (prevRockerSelect == 1)
      {
        rockerTransition = 1;
        positivePressure = false;
      }
      prevRockerSelect = 0;
    }
    else if (digitalRead(switchRockPin) == HIGH)
    {
      rockerSelect = 1;
      if (prevRockerSelect == 0)
      {
        rockerTransition = 1;
        positivePressure = true;
      }
      prevRockerSelect = 1;
    }

    //Check Rocker Switch (Control on/off)
    if (digitalRead(controlRockPin) == LOW)
    {
      controlSelect = 0;
      if (prevControlSelect == 1)
      {
        rockerTransition = 1;
        controlEnable = false;
        manualControlEnable = true;
      }
      prevControlSelect = 0;
    }
    else if (digitalRead(controlRockPin) == HIGH)
    {
      controlSelect = 1;
      if (prevControlSelect == 0)
      {
        rockerTransition = 1;
        controlEnable = true;
        manualControlEnable = false;
      }
      prevControlSelect = 1;
    }  

    if(rockerTransition == 1)
    {
      Serial.println(1000);
      Serial.println(rockerSelect);
      Serial.println(controlSelect);
      rockerTransition = 0;
    }

    //Print for plotting
    Serial.println(actualPressure);
    Serial.println(elapsedTime);
    Serial.println(commandPressure);
    if(Serial.available() > 0)
    {
      if(Serial.parseInt() == 2)
      {
        state = false;      
      }
    }
  }

  if(controlEnable)
  {

    //Get current pressure
    sensorVoltage = .0049*analogRead(sensorPin);
    //  Digitally filter the analog input signal
    sensorVoltageFilter = digital_lpf((float)sensorVoltage, false);
    
    actualPressure = (sensorVoltage-0.5)/(.04);

    error = activeCommandPressure - actualPressure;
    if(error < 0)
    {
      //Backwards
      motDir = 0;
      error = error*-1;
      //digitalWrite(motDIRPin, LOW);
      //SHIELD
      pumpMotor->run(BACKWARD);
    }
    else
    {
      //Forwards
      motDir = 1;
      //digitalWrite(motDIRPin, HIGH);
      //SHIELD
      pumpMotor->run(FORWARD);
    }

    // compute the control signal
    motPWM = (int)(error / 4);     // replace this simple example with your desired control function

    //  Note:  the value of control_out MUST be an 8-bit unsigned integer in the range of 0 to 255
    //  because the PWM output is only 8-bit, so it can only accept values between 0 and 255
    motPWM = constrain(motPWM, 0, 255); //  saturate the control_out value between 0 and 255

    //  Output the control_out signal as a PWM output
    //  for a PWM output:  0 = 0% duty cycle, 255 = 100% duty cycle, operation duration: ~16.6us
    //analogWrite(motPWMPin, (int)motPWM);
    //SHIELD
    pumpMotor->setSpeed(motPWM);
    
    //Check Rocker Switch (vac to pos)
    if (digitalRead(switchRockPin) == LOW)
    {
      rockerSelect = 0;
      if (prevRockerSelect == 1)
      {
        rockerTransition = 1;
        positivePressure = false;
      }
      prevRockerSelect = 0;
    }
    else if (digitalRead(switchRockPin) == HIGH)
    {
      rockerSelect = 1;
      if (prevRockerSelect == 0)
      {
        rockerTransition = 1;
        positivePressure = true;
      }
      prevRockerSelect = 1;
    }

    //Check Rocker Switch (Control on/off)
    if (digitalRead(controlRockPin) == LOW)
    {
      controlSelect = 0;
      if (prevControlSelect == 1)
      {
        rockerTransition = 1;
        controlEnable = false;
        manualControlEnable = true;
      }
      prevControlSelect = 0;
    }
    else if (digitalRead(controlRockPin) == HIGH)
    {
      controlSelect = 1;
      if (prevControlSelect == 0)
      {
        rockerTransition = 1;
        controlEnable = true;
        manualControlEnable = false;
      }
      prevControlSelect = 1;
    }  

    if(rockerTransition == 1)
    {
      Serial.println(1000);
      Serial.println(rockerSelect);
      Serial.println(controlSelect);
      rockerTransition = 0;
    }
    Serial.println("alive");
    //Print for plotting
    Serial.println(actualPressure);
    Serial.println(elapsedTime);
    Serial.println(activeCommandPressure);
    if(Serial.available() > 0)
    {
      if(Serial.parseInt() == 2)
      {
        state = false;      
      }
    }
  }
  
  //If control is happening, increase the time stamp
  if(timing)
  {
    elapsedTime = elapsedTime + 1/FS;
  }

}
Thank you for your help!

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using MotorShield v2.3 with an ISR routine set up

Post by adafruit_support_bill »

You are calling the motor control library functions inside the ISR. Communication with the shield is via i2c and i2c communication requires interrupts to be enabled. By default, interrupts are disabled upon entry to the ISR, so that will cause it to hang.

You could re-enable interrupts inside the ISR - a strategy not without risk.

Or you could collect all the relevant state information in the ISR and act on it on the next pass through the main loop.

User avatar
plus1etal
 
Posts: 3
Joined: Sat Feb 11, 2017 2:35 am

Re: Using MotorShield v2.3 with an ISR routine set up

Post by plus1etal »

Ah, that makes sense. Would enabling interrupts in the ISR exit the ISR when the motor functions are called? Or would the interrupt requests "queue", as in it would wait until after a run of the ISR before updating the motors?

I'm thinking I might just push the motor communications to the end of the ISR and enable interrupts again right before the communication

Thanks again.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using MotorShield v2.3 with an ISR routine set up

Post by adafruit_support_bill »

The Arduino has only one level of interrupts, so there is no queueing. If you enable interrupts in the ISR and get another interrupt before you are done, you run the risk of overflowing the stack.

Generally it is a good idea to keep the ISR short & sweet. Do only the work that has to be done at interrupt level and defer the rest to your loop.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”