Go Down

Topic: LCD interrupts stepper motor (Read 428 times) previous topic - next topic

snewpers

Aug 10, 2018, 09:22 pm Last Edit: Aug 10, 2018, 09:23 pm by snewpers
Hi


I have a stepper that runs at x speed when a button is pressed. On the LCD I have a countdown using millis() subtracting seconds from the total runtime.

The problem is that every time the LCD updates (every second) the motor has an interrupt, it stutters every millis interval().


How do I update the LCD every second without interrupting the stepper?

I am using accelstepper lib for the motor.

Robin2

You have not posted your program so I can only guess ...

I suspect that when you update the LCD it takes longer than the required interval between steps so the Accelstepper .run() function is not being called often enough. It should really be called several times for every step so that it can make the step at the right moment.
 
If this is the problem try breaking down the update into a few smaller parts and only do one part in each iteration of loop().

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

snewpers

Thanks Robin,

My code is packed with comments, that's why I didn't include it. Will do if needed of course.

The only time it runs smooth is when I remove the LCD update completely. I have 'stepper1.runSpeed();' in most loops. Putting it in void loop without any conditions will just make it run, obviously.

I will start over with the LCD stuff until I get this sorted out.

Thanks!

DKWatson

Any reliable pulse generation for stepper motors needs to be interrupt-driven with a higher priority interrupt than any other request in your code. Millis() uses the timer0 overflow interrupt which sits 17th in the vector table. If your pulse generation function is not higher (lower number in the vector table) or worse - polled - you will suffer that interruption every 1.024ms. If you are running at high-speed, where the pulses need to be maintained above the pull-in torque threshold, you also run the risk of stalling the motor. All non-pulse activity running concurrently needs to be carefully timed and sliced to take place in the gaps. Unfortunately, updating displays and/or serial communication must sometimes be disabled entirely. As a guide, at full step with a 1.8 degree motor, pulses need to be generated every (300,000/rpm) microseconds. This is your window of opportunity. Any event taking longer than that is going to disrupt the motor unless it can be interrupted by the pulse generator.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

Robin2

Any reliable pulse generation for stepper motors needs to be interrupt-driven with a higher priority interrupt than any other request in your code.
That is only true if the step interval needs to be smaller than can be accomplished using polling. My motors work fine with about 1000 to 1500 steps per second using polling.

I suspect the OP's problem is simply due to a long-running LCD update function that could be re-written so that it behaves nicely with the stepper code.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

snewpers

When a button is pressed, this gets called (along with the stepper code). The interval2=1000ms.

Code: [Select]
void displayStuff() {

  //schermupdater
  unsigned long currentMillis2 = millis();
  if (currentMillis2 - previousMillis2 >= interval2) {
    previousMillis2 = currentMillis2;
    stepper1.runSpeed();
    countdown = countdown - 1;
    if (counterget != 0) {
      stepper1.runSpeed();
      Serial.println(counterget);
      //Serial.println(countdown);
      lcd.setCursor (0, 0); lcd.print(F("TIJD RESTEREND: "));
      lcd.setCursor (0, 1); lcd.print(F("   ")); // minutenveld leegmaken
      lcd.setCursor (9, 1); lcd.print(F("  ")); // seconden leegmaken

      if ( counterget < 100 && counterget >= 10) {
        lcd.setCursor (0, 1); lcd.print(F("0")); lcd.setCursor (1, 1); lcd.print(counterget); lcd.setCursor (3, 1);  lcd.print(F(" Min. ")); // tijd
      } else if ( counterget < 10) {
        lcd.setCursor (0, 1); lcd.print(F("00")); lcd.setCursor (2, 1); lcd.print(counterget); lcd.setCursor (3, 1);  lcd.print(F(" Min. ")); // tijd
      } else {
        lcd.setCursor (0, 1); lcd.print(counterget); lcd.setCursor (3, 1);  lcd.print(F(" Min. "));
      }
      lcd.setCursor (9, 1); lcd.print(countdown); lcd.setCursor (11, 1); lcd.print(F(" S.      "));
    }
  }
}



I don't think it's the amount of code that causes the interrupt? If I remove everything and put in a static thing like 'lcd.print(F(" Min. "))' it will still interrupt.

I need the motor to run at 13 steps a second (varies) so I thought that might cause a problem. But if I speed it up it still stutters every second. Also, it *is* the interval causing it because when I set it to 6000ms it happens there and not 1.024ms.

Robin2

#6
Aug 11, 2018, 10:30 am Last Edit: Aug 11, 2018, 10:31 am by Robin2
You need to post  a complete working program that demonstrates the problem. Code snippets don't tell us anything.

13 steps per second is very slow and should be readily achieved.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

sterretje

No idea of steppers, but you have two runSpeed statements in succession if counter is not equal to zero.
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

snewpers

No idea of steppers, but you have two runSpeed statements in succession if counter is not equal to zero.
Correct! I was testing to see if there were not enough calls for runspeed.


But calling for the motor to step has to be done on more than one location. If I'm correct, there is no problem in calling runspeed more than once in this loop, it won't affect the outcome.


Robin2

But calling for the motor to step has to be done on more than one location. If I'm correct, there is no problem in calling runspeed more than once in this loop, it won't affect the outcome.
When are you going to get around to posting the complete program so we can begin to figure out how to help you?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

snewpers

It's larger than 9000 characters so I post it in 2 parts:

Code: [Select]
include <Wire.h>
#include <ClickEncoder.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>

// init the LCD
LiquidCrystal_I2C  lcd(0x3F, 2, 1, 0, 4, 5, 6, 7);

/*******Interrupt-based Rotary Encoder Menu Sketch*******
   by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt and Steve Spence, and code from Nick Gammon
   3,638 bytes with debugging on UNO, 1,604 bytes without debugging
*/
// Rotary encoder declarations
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
// Button reading, including debounce without delay function declarations
const byte buttonPin = 4; // this is the Arduino pin we are connecting the push button to
byte oldButtonState = HIGH;  // assume switch open because of pull-up resistor
const unsigned long debounceTime = 10;  // milliseconds
unsigned long buttonPressTime;  // when the switch last changed state
boolean buttonPressed = 0; // a flag variable
// Menu and submenu/setting declarations
byte Mode = 0;   // This is which menu mode we are in at any given time (top level or one of the submenus)
const byte modeMax = 5; // This is the number of submenus/settings you want
byte setting1 = 30;  // snelheid M
byte setting2 = 180;  // rotatie Graden
byte setting3 = 100;  // lengte CM
byte setting4 = 0;  // 0 = Links - Rechts
byte setting5 = 0;  // 0 = clock = 1 counter clock
//PAUZE
//byte setting6 = 24; //FPS
//byte setting7 = 0; //pauze modus? 0 is nee / 1 is ja
//byte setting8 = 10; //pauzetijd? tussen iedee opname in seconden
/* Note: you may wish to change settingN etc to int, float or boolean to suit your application.
  Remember to change "void setAdmin(byte name,*BYTE* setting)" to match and probably add some
  "modeMax"-type overflow code in the "if(Mode == N && buttonPressed)" section*/

int StartMotion = 0; // actie met steppers



// ##############################################################################################################
// STEPPER in full step = 200 steps per rotatie, met een 16 tands sprocket is dat 40 steps per mm.
// Step formule is afstand in mm / tijd in seconde -> in full step is dat 40 steps per mm.
// voorbeeld   1000 / 3600 = 0.27 mm per seconde = 0,27 x 40 steps = 11 steps per seconde.
// ##############################################################################################################


// STEPPER 1 LINEAIR
AccelStepper stepper1(1, 9, 10);                   // stepper 1 pin setup 9 step - 10 dir
byte steps = 50; // 200 steps: 16 tanden keer 2 pitch = 32mm per rev @ full. 8x micro = 32mm in 1600 steps. 1600 / 32 = 50 steps per mm.
double steppersnelheid =  double(setting3 * 10) / double(setting1 * 60) * steps; // lengte gedeeld door tijd keer steps per mm is aantal steps per seconde
//double steppersnelheid = 2100;
long int fakeDist = 1000000L; // miljoen steppen voor MoveTo... gebruiken we dus alleen om de stepper te bewegen


// STEPPER 2 ROTATIE
AccelStepper stepper2(1, 16, 17);                   // stepper 4 pin setup 16 step - 17 dir
double rotatiesteps =  2.22; // stepper 2 in 8x microstepping 4*200 steps / 360 graden = 2,2222222
double rotatiesnelheid =  double(setting2) * rotatiesteps / double(setting1 * 60.00); // rotatie in graden
double RotatieAfstand = setting2*rotatiesteps; // aantal steps per graad * het aantal graden

// ##############################################################################################################
// TIMER SETTINGS

unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned long previousMillis2 = 0;        // will store last time LED was updated
const long interval = 60000;           // interval at which to blink (milliseconds)
//const long interval = 6000;              // testen
const long interval2 = 1000;           // interval voor scherm
byte countdown = 60;

int counterget = setting1;           // counterdisplay is de timer, de +1 is omdat hij anderterugtelt vanaf tijd -1
//int counterget2 = setting1 * 60 ;           // looptijd in seconden
//toggle switch spul hier
const int startbuttonPin = 8;     // the number of the pushbutton pin
//const int startledPin =  13;      // the number of the LED pin
byte StartbuttonState = 0;         // variable for reading the pushbutton status


// ##############################################################################################################
// VOID SETUP

void setup() {
  Serial.begin(9600);

  Serial.println("Basic Encoder Test:");
  lcd.begin (16, 2);
  lcd.setBacklightPin(3, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home();

  // Create the custom character.
  lcd.setCursor (0, 0); lcd.print(F("TIMELAPSE THING "));
  lcd.setCursor (0, 1); lcd.print(F("JP HEYMANS  v.12"));
  delay(1000); // not a problem to use delay here, it's executed once
  lcd.setCursor (0, 1); lcd.print(F("                "));


  stepper1.setMaxSpeed(steppersnelheid);
  stepper1.setSpeed(steppersnelheid);





  //Rotary encoder section of setup
  pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  attachInterrupt(0, PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(1, PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
  // button section of setup
  pinMode (buttonPin, INPUT_PULLUP); // setup the button pin
  // DEBUGGING section of setup

  //startbutton
  //pinMode(startledPin, OUTPUT);
  pinMode(startbuttonPin, INPUT_PULLUP);

}

snewpers

part 2...

Code: [Select]
void loop() {
  if (StartMotion == 0) { // alleen menu actief als motoren niet lopen
    rotaryMenu();
  };

  //  // controleer rijriching
  //  if (setting4 >> 0) {
  //    fakeDist * -1;
  //  }


  // #################
  // BUTTON controleer of TL actief is, dan andere functies aanroepen!
  StartbuttonState = digitalRead(startbuttonPin);

  if (StartbuttonState == LOW) { // als knop actief is dan tijd gaan aftellen
    //digitalWrite(startledPin, HIGH);// turn LED on: debugging
    //Serial.println(steppersnelheid); Serial.print(" steppersnelheid");//DEBUGGING
      Serial.println(RotatieAfstand);
      Serial.println(rotatiesteps);
    StartMotion = 1;
    stepperStuff();
    stepper1.runSpeed();
    timerStuff(); // de timer laten lopen, en de steppers ook!
    displayStuff(); //schermweergave
  } else {
    //digitalWrite(startledPin, LOW);// debugging LED uit
    StartMotion = 0;
  }
}



// ##############################################################################################################
// VOID SCREENUPDATE
void displayStuff() {
  stepper1.runSpeedToPosition();
  //schermupdater
  unsigned long currentMillis2 = millis();
  if (currentMillis2 - previousMillis2 >= interval2) {
    previousMillis2 = currentMillis2;

    stepper1.runSpeedToPosition();

    countdown = countdown - 1;
    if (counterget != 0) {
      stepper1.runSpeedToPosition();
      Serial.println(counterget);
      //Serial.println(countdown);
      lcd.setCursor (0, 0); lcd.print(F("TIJD RESTEREND: "));
      lcd.setCursor (0, 1); lcd.print(F("   ")); // minutenveld leegmaken
      lcd.setCursor (9, 1); lcd.print(F("  ")); // seconden leegmaken

      if ( counterget < 100 && counterget >= 10) {
        lcd.setCursor (0, 1); lcd.print(F("0")); lcd.setCursor (1, 1); lcd.print(counterget); lcd.setCursor (3, 1);  lcd.print(F(" Min. ")); // tijd
      } else if ( counterget < 10) {
        lcd.setCursor (0, 1); lcd.print(F("00")); lcd.setCursor (2, 1); lcd.print(counterget); lcd.setCursor (3, 1);  lcd.print(F(" Min. ")); // tijd
      } else {
        lcd.setCursor (0, 1); lcd.print(counterget); lcd.setCursor (3, 1);  lcd.print(F(" Min. "));
      }
      lcd.setCursor (9, 1); lcd.print(countdown); lcd.setCursor (11, 1); lcd.print(F(" S.      "));
    }
  }
}

// ##############################################################################################################
// VOID TIMERSTUFF

void timerStuff() {
  stepper1.runSpeedToPosition();
  //timer voor aftellen
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if (counterget != 0) {
      counterget = counterget - 1;
      countdown = 60;
      Serial.print(counterget);
      stepper1.runSpeedToPosition();
    } else if (counterget == 0) {
      stepper1.runSpeedToPosition();
      StartMotion = 0;
      //      StartMotion = 1; // zo moet je alles herstarten voor een nieuwe timelapse.


      for (int i = 0; i = 5; i++)
      {
        lcd.setCursor (0, 0); lcd.print(F("TIMELAPSE KLAAR,"));
        lcd.setCursor (0, 1); lcd.print(F("SYSTEEM UIT!    "));
        delay(1000);
        lcd.setCursor (0, 1); lcd.print(F("                "));
        delay(500);
      }

    }
  }
  //time

}


// ##############################################################################################################
// VOID STEPPERSTUFF

void stepperStuff() {

  stepper1.setMaxSpeed(steppersnelheid);
  stepper1.setSpeed(steppersnelheid);
  stepper1.moveTo(fakeDist);
  stepper1.runSpeedToPosition();

  stepper2.setMaxSpeed(rotatiesnelheid);
  stepper2.setSpeed(rotatiesnelheid);
  stepper2.moveTo(RotatieAfstand);
  stepper2.runSpeedToPosition();

}

snewpers

Ok this will take a lot of parts, not just 2. I'll attach the file instead.

link to file

snewpers

#13
Aug 12, 2018, 12:04 am Last Edit: Aug 12, 2018, 12:05 am by snewpers
I have another question about my sketch...

I have some simple calculations for the rotation speed etc. From the start they are based on some global values (setting1, setting2 etc).

The menu updates the setting1, setting2 values but does not change the outcome of the calculations that follow.
Serial print confirms the values are changed, but the calculations that uses those values are not. Do they not change or should I not have set them as global calculations?

two examples:
the first 4 values are calculated from the last 4 values. If the last values change, so should the first 4.

Code: [Select]
-steppersnelheid: 27.78
-rotatiesnelheid: 0.22
-loopafstand:     50000
-rotatieafstand:  400.00
-stetting 1:      25
-stetting 2:      180
-stetting 3:      100
-stetting 4:      0


Code: [Select]
-steppersnelheid: 27.78
-rotatiesnelheid: 0.22
-loopafstand:     50000
-rotatieafstand:  400.00
-stetting 1:      30
-stetting 2:      150
-stetting 3:      88
-stetting 4:      0

Robin2

If your code is too long just add your .ino file as an attachment. I am not going to join parts together in case I make a mistake.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Go Up