Stepper motor with potentiometer + display

Hi all,

I'm working on a project where I have to control stepper motor with potentiometer and would like to monitor motor's speed with 4-digit 7 segment led display screen.

I'm using TMC2100 driver and NEMA17 stepper with 200 steps/rev. I connected 2 buttons for start and stop and Hall effect sensor for stopping the motor at the exact position. I've managed to figure out how to control everything and it's working fine accept monitoring the speed on a monitor. The screen is pulsating and the motor is spinning very slow, I can't even control the speed any more.

My question is, how to implement the screen into the code not to have that strange loop delay?

I hope I explained my situation well, so maybe someone can help me.

#include <Arduino.h>
#include <TM1637Display.h>

// defines pins numbers
#define DIR_PIN     9
#define STEP_PIN    8
#define EN_PIN      7
#define POT_PIN     A0
#define On          12
#define Off         13
#define SENSOR      2
#define CLK         3
#define DIO         4

int pot;
int motorSpeed;
int state = 0, Loadstate = 0;

TM1637Display display(CLK, DIO);

void setup() {
  Serial.begin(9600);
  pinMode(DIR_PIN,OUTPUT);
  pinMode(STEP_PIN,OUTPUT);
  pinMode(EN_PIN,OUTPUT);
  pinMode(On, INPUT);
  pinMode(Off, INPUT);
  pinMode(SENSOR, INPUT);
  
  digitalWrite(EN_PIN,LOW);
  digitalWrite(DIR_PIN,HIGH); // Enables the motor to move in a particular direction
  digitalWrite(SENSOR, HIGH); //Hall effect sensor read
}

void loop() {
  motorSpeed = potValue();
  motorStep(motorSpeed);

  if (state == 0 && digitalRead(Off) == HIGH && digitalRead(SENSOR) == LOW) {
    state = 1;
    Loadstate = !Loadstate;
  }
  if (state == 1 && digitalRead(On) == HIGH) {   
    state = 0;
    Loadstate = !Loadstate;
  }
  if (Loadstate == HIGH) {
    digitalWrite(EN_PIN, HIGH);
  }
  if (Loadstate == LOW) {
    digitalWrite(EN_PIN, LOW);
  }
}

void motorStep(int STEP){
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(STEP);
  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(STEP);
}

int potValue()
{
  int val = analogRead(POT_PIN);
  int Speed = map(val,0,1023,100,500);
  return Speed;
}

and the part for the display

  uint8_t data[] = { 0x0, 0x0, 0x0, 0x0 };
  display.setSegments(data);
  display.showNumberDec(motorSpeed, false, 3,1);

You really need to post your entire sketch. How are you controlling your display? My guess is that you are updating the display every time through loop() which is wasteful.

I guess you make a common mistake when using button, see Button FAQ: common mistake - button does NOT work as expected. - Introductory Tutorials - Arduino Forum

If you want a responsive program you must get rid of delay() everywhere and also any long delayMicroseconds().

Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

And see Using millis() for timing. A beginners guide if you need more explanation.

Also have a look at the second (non-blocking) example in this Simple Stepper Code

...R

Hi,
Welcome to the forum.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks for using code tags. Tom.... :slight_smile:

Thank you for the welcome. I'm relatively new to Arduino, so I'm trying to figure things out and to learn how does things work in Arduino. I'll post circuit sketch and full code when I'll come home at the evening. In a meantime I'll check the links posted above.

Thanks

P.S. Is it possible to enlarge the code window for better viewing or that's it?

wip369:
P.S. Is it possible to enlarge the code window for better viewing or that's it?

I don't think so.

I just select the code and copy it to my text editor which has lots of helpful features.

...R

Hi,

Finally I managed ti find spare time to make a sketch for my circuit.

Here is my code with display part.

#include <Arduino.h>
#include <TM1637Display.h>

// defines pins numbers
#define DIR_PIN     9
#define STEP_PIN    8
#define EN_PIN      7
#define POT_PIN     A0
#define On          12
#define Off         13
#define SENSOR      2
#define CLK         3
#define DIO         4

int pot;
int motorSpeed;
int state = 0, Loadstate = 0;

TM1637Display display(CLK, DIO);

void setup() {
  Serial.begin(9600);
  pinMode(DIR_PIN,OUTPUT);
  pinMode(STEP_PIN,OUTPUT);
  pinMode(EN_PIN,OUTPUT);
  pinMode(On, INPUT);
  pinMode(Off, INPUT);
  pinMode(SENSOR, INPUT);
  
  digitalWrite(EN_PIN,LOW);
  digitalWrite(DIR_PIN,HIGH); // Enables the motor to move in a particular direction
  digitalWrite(SENSOR, HIGH); //Hall effect sensor read


}

void loop() {
  motorSpeed = potValue();
  motorStep(motorSpeed);
  display.setBrightness(0x0f);
  uint8_t data[] = { 0x0, 0x0, 0x0, 0x0 };
  display.setSegments(data);
  display.showNumberDec(motorSpeed, false, 3,1);

  if (state == 0 && digitalRead(Off) == HIGH && digitalRead(SENSOR) == LOW) {
    state = 1;
    Loadstate = !Loadstate;
  }
  if (state == 1 && digitalRead(On) == HIGH) {   
    state = 0;
    Loadstate = !Loadstate;
  }
  if (Loadstate == HIGH) {
    digitalWrite(EN_PIN, HIGH);
  }
  if (Loadstate == LOW) {
    digitalWrite(EN_PIN, LOW);
  }
}

void motorStep(int STEP){
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(STEP);
  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(STEP);
}

int potValue()
{
  int val = analogRead(POT_PIN);
  int Speed = map(val,0,1023,10,300);
  return Speed;
}

I assume that putting display read in loop() is making this problem, but I don't know how to make it right. Should I put it in separate function and just call result in loop() or should I make it totally different? I still didn't managed to try the millis() function for stepper function, do you think that is the problem?

Thanks for the help.

Image from Reply #7 so we don't have to download it. See this Simple Image Guide

...R

Sorry, for the bad news but its much too easy to misunderstand a Fritzing diagram and it is impossible to read the pin names. A photo of a simple pencil drawing with everything clearly labelled will be much better. Don't worry if you consider yourself bad at drawing.

...R

I've been working on my existing code and studied provided links, so I changed the code quite a bit. I've changed the buttons and the sensor with debounce function (it works without any problems), I've changed potentiometer with encoder for more accuracy and finally changed the display library. My initial problem with the display flickering was my display code implementation, as I've put everything directly into the loop (I'm still learning the loop functionality) so I've fixed that, but the second problem was motor delay when I was changing the motor speed with encoder. Every time I made a step with encoder, the motor stopped for few milliseconds. The problem was in TM1637 library from Avishay Orpaz, as it uses some delays for displaying data to display. Later I've tried with different library from AKJ and it worked flawlessly. The only thing I don't like with later library is that it shows 0s instead blank when numbers are not displayed and don't know how to change that.
The last thing I was not able to figure out is millis() function or micros() for the stepper driver. The commented code worked, but it was not consistent with speed. So if anyone has an idea how to correctly use the micros() with TMC2100 (TMC2130) drivers I'd appreciate any feedback.
I've documented my code with comments for every pin and function, so I hope it will be sufficiently transparent now.

Thanks

/*******Interrupt-based Rotary Encoder Sketch*******
by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence
*/
#include <Bounce2.h>
#include <TM1637.h>

#define DIR_PIN     9 // stepper dir
#define STEP_PIN    8 // stepper step
#define EN_PIN      7 // stepper enable
#define ON          12 // On button
#define OFF         11 // Off button
#define SENSOR      4 // Hall sensor input
#define CLK         5 // display CLK
#define DIO         6 // display DIO

TM1637 display(CLK, DIO);

int motorSpeed;
int state = 0, Loadstate = 0;
//unsigned long previousMillis = 0;
//unsigned long curMillis;

static int pinA = 2; // Encoder's signal A
static int pinB = 3; // Encoder's signal B
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 int encoderPos = 148; //this variable stores our current value of encoder position.
volatile byte oldEncPos = 0; //stores the last encoder position value
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

// Instantiate On button bounce object
Bounce debouncer1 = Bounce(); 

// Instantiate Off button bounce object
Bounce debouncer2 = Bounce(); 

// Instantiate Hall sensor bounce object
Bounce debouncer3 = Bounce(); 

void setup() {
  // Setup the stepper driver
  pinMode(DIR_PIN,OUTPUT);
  pinMode(STEP_PIN,OUTPUT);
  pinMode(EN_PIN,OUTPUT);
  
  pinMode(ON,INPUT_PULLUP); // set the On button with an internal pull-up
  debouncer1.attach(ON); // After setting up the button, setup the Bounce instance
  debouncer1.interval(5); // interval in ms
  
  pinMode(OFF,INPUT_PULLUP); // set the Off button with an internal pull-up
  debouncer2.attach(OFF); // After setting up the button, setup the Bounce instance
  debouncer2.interval(5); // interval in ms

  pinMode(SENSOR,INPUT_PULLUP); // set the Hall sensor with an internal pull-up
  debouncer3.attach(SENSOR); // After setting up the button, setup the Bounce instance
  debouncer3.interval(5); // interval in ms
  
  pinMode(pinA, INPUT_PULLUP); // set Encoder's pinA as an input
  pinMode(pinB, INPUT_PULLUP); // set Encoder's pinB as an input
  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)
  Serial.begin(115200);

  // Write to stepper driver
  digitalWrite(EN_PIN,LOW);
  digitalWrite(DIR_PIN,HIGH);
  digitalWrite(SENSOR, HIGH);

  // Setup the display
  display.init();
  display.setBrightness(5);
}

void PinA(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
  if(reading == B00001100 && aFlag && encoderPos > 48) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos = encoderPos - 4; //decrement the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void PinB(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag && encoderPos < 248) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos = encoderPos + 4; //increment the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void loop(){
  if(oldEncPos != encoderPos) {
    display.dispNumber((248-encoderPos)/2);
    oldEncPos = encoderPos;  
  }
  motorStep(encoderPos);

  // Update the Bounce instances :
  debouncer1.update();
  debouncer2.update();
  debouncer3.update();

  // Get the updated value :
  int OnValue = debouncer1.read();
  int OffValue = debouncer2.read();
  int sensorValue = debouncer3.read();

  if (state == 0 && OnValue == LOW) {
    state = 1;
    Loadstate = !Loadstate;
  }
  if (state == 1 && OffValue == LOW) {   
    state = 0;
    Loadstate = !Loadstate;
  }
  if (Loadstate == HIGH && sensorValue == LOW) {
    digitalWrite(EN_PIN, HIGH);
  }
  if (Loadstate == LOW) {
    digitalWrite(EN_PIN, LOW);
  }
}

void motorStep(unsigned long STEP) {
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(STEP);
  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(STEP);

  /*curMillis = micros();

  if (curMillis - previousMillis >= STEP) {
    previousMillis = curMillis;
    digitalWrite(STEP_PIN, HIGH);
    digitalWrite(STEP_PIN, LOW);
  }*/
}

wip369:
P.S. Is it possible to enlarge the code window for better viewing or that's it?

Grab the bottom corner of the code window with your mouse, pull it down. Can't make it wider; can make it longer (and narrower).

wvmarle:
Grab the bottom corner of the code window with your mouse, pull it down.

Thanks for that. I had not been aware of it.

...R

wvmarle:
Grab the bottom corner of the code window with your mouse, pull it down. Can't make it wider; can make it longer (and narrower).

Thanks for the info.

Any other thoughts for the code and micros() function?

For the code, a few things.

  • for reading your encoder, you can use the encoder library. Works great, very simple to use.
  • a hall effect sensor does not need debounce. It doesn't bounce.

So does it work properly now? If not, what's wrong with it?

Thanks for the suggestions. I'll try the hall effect without bounce, to see, how it works. For the encoder library I believe I had problems with accuracy, it would stall if rotated quickly. I've tried many different codes (don't even remember all I've tried) for the encoder and this works the best for me.
My question about micros() and the stepper was, will it work better than what I have now and how to code it, if it works better. The small issue I still have is, every time I make a step with the encoder it stops the motor for microseconds. I can't see it, but I definitely can hear it if I listen carefully. With small gears on pulley it doesn't make problems, but when I'll change them with bigger pulleys it would make a problem.
So, would micros() make any difference or not?

Thanks.