Saturday, October 28, 2017

Library for A4988 stepper motor driver using timer interrupt

This library is designed for AVR ATmega328 microcontroller, but with few adjustments can work with any AVR microcontroller with at least 3-4 Kb of flash program memory.
At the moment only one motor is supported.

Features

  • the interrupt can be triggered by Timer0 or Timer1
  • automatic microstepping mode selection
  • can work with constant and very low speeds
  • accelerated speed mode, with separate acceleration and deceleration settings
  • can work with only one pin of the microcontroller if the rest are hardwired

Nema 17 Bipolar Stepper Motor

Characteristics:


Features
Program Memory
Data Memory (SRAM)
All enabled
3552 bytes
40 bytes
Acceleration, no microstepping
3088 bytes
40 bytes
Microstepping, no acceleration
2104 bytes
14 bytes
No microstepping, no acceleration
1724 bytes
12 bytes

Compiled using avr-gcc (WinAVR 20100110) 4.3.3 and -Os optimization level and MATH_LIB = -lm.
If MATH_LIB = -lm is commented out in Make file, the memory with all functions enabled is 6520 bytes program and 304 bytes data memory.So allways uncomment MATH_LIB = -lm.





To know more about the A4988 module, visit this post  How to use A4988 stepper motor driver module tutorial


How to use the A4988-stepperDriver library

The stepper motor library uses a timer which triggers an interrupt every 10us. If the delay between steps is more than 10us the ISR will take only a few nano seconds so the flow of the user's program will not be affected.
Based on the motor speed the microstepping will be selected automatically - the lower the speed the higher the microstepping. That way the motor will be more silent and won't vibrate. Also the movement will be more fluid.

How to use the code 

  • Setting some things up

First open the header file and setup a few things.
I/O
In the section SETUP FOR USER modify the DDR, PORT and PIN defines for each output pin you want to use, according to your setup. Excluding the STEP pin all the rest are optional.

/*************************************************************
 SETUP FOR USER
**************************************************************/
///////////////////    DIGITAL CONTROL PINS SETUP
/* Step Pin */
#define STEP_DDR    DDRD
#define STEP_PORT    PORTD
#define STEP_PIN    PD0

/* Direction Pin (Optional) */
#define USE_DIRECTION_PIN          1
#define DIR_DDR     DDRD
#define DIR_PORT    PORTD
#define DIR_PIN     PD1

/* Enable Pin (Optional) */
#define USE_ENABLE_PIN           1
#define ENABLE_DDR    DDRD
#define ENABLE_PORT    PORTD
#define ENABLE_PIN    PD6

/* Sleep Pin (Optional) */
#define USE_SLEEP_PIN           1
#define SLEEP_DDR    DDRB
#define SLEEP_PORT    PORTB
#define SLEEP_PIN    PB2

/* Reset Pin (Optional) */
#define USE_RESET_PIN           1
#define RESET_DDR    DDRC
#define RESET_PORT    PORTC
#define RESET_PIN    PC5


STEPPER_DEGREES - stepper motor type. How many degrees the motor turns in one full step. Usually 1.8 for 200 steps per rotation or 0.9 for 400 steps per rotation.


USE_TIMER0 and USE_TIMER1 - put 1 to the one you want to use and 0 to the rest.

If you use Arduino to compile this code, choose Timer1 like so: USE_TIMER1  1 because Arduino is using Timer0 for millis and other functions and could interfere with this code.


USE_MICROSTEPPING - if you don't need microstepping set this to 0. Default 1.
MICROSTEPPING_DIVIDE_TIME - if total duration of spinning doesn't matter, set this to 0. This will allow using smaller delays between steps at a higher microstepping resolution. Default 1.

USE_stepperRotateToDegree - the function is descibed bellow. Use 0 if not needed.
IMPROVE_TIME_PRECISION -  sometimes the delay between steps (the motor speed) will result in a fraction. If this is set to 1 it will be corrected. If you only care about the number of steps and not the time taken, set this to 0 to save space.
MICROSTEPPING TIME THRESHOLD - since the code selects the step resolution based on the delay between steps you can tweak the threshold values in microseconds.
- FULL_STEP_THRESHOLD     200 - speeds under 200 microseconds will use full step
- HALF_STEP_THRESHOLD    500 - under 500 us 1/2 step will be used
- QUARTER_STEP_THRESHOLD   800
- EIGHTH_STEP_THRESHOLD       10000
- SIXTEENTH_STEP_THRESHOLD 1000000
USE_ACCELERATION - linear acceleration. If you don't need acceleration, deceleration set this to 0 to save space and ISR time execution.
ACCELERATION and DECELERATION -  in percents from 0 to 100. Both must add up to 100. Lets say you want for the motor to make 1000 steps. By setting these to constants to 20, the first 200 steps will be used to accelerate to the desired speed. Same for the deceleration. Useful when you have a heavy load or you want to reach a certain high speed. The motor is like a car - you can't accelerate from 0 to 120 Km/h in an instant nor decelerate that fast without problems.
ACCELERATION_STARTING_SPEED - in microseconds. The greater the value the slower the starting speed when the acceleration is used.
PERPETUAL_MOTION - if set to 1 the motor will spin forever or until the function A4988_stepperStop is called, or until the battery runs out, or... i'll stop now.

  • The functions

void A4988_stepperSetup(void); 
Run this before any other functions. This will set up the output pins and the timer, but will not start the timer.

void A4988_stepMilliseconds(uint16_t nr_of_steps, uint32_t delay_between_steps_ms);
Make a desired number of steps.
nr_of_steps - how many steps. Maximum value is 65535
delay_between_steps_ms - the delay between each step in miliseconds. This will dictate the speed of rotation 

void A4988_stepMicroseconds(uint16_t nr_of_steps, uint16_t delay_between_steps_us);
Same as the function above but the delay is in microseconds.
For both function the delay will be divided by 2 because each step needs a HIGH and a LOW pulse.

void A4988_stepperDirection(uint8_t direction);
Direction of rotation. The argument can be TURN_RIGHT or TURN_LEFT.

void A4988_isBusy(void);
Since every function that spins the motor will start a timer and trigger the ISR, the next function will be run by the CPU until the ISR triggers and that will mess things up. So after each function that spins the motor, use this function. The function uses a while loop, so use this only if the order of rotations matter.

uint8_t A4988_isNotBusy(void);
To execute other code while the rotation takes place, use this function. It will return 1 if the rotation is complete or 0 if not. See the example bellow on how to use it.




Other functions you might need:

void A4988_stepperRotateNTimes(uint16_t nr_of_rotations, uint16_t total_duration_ms);
Rotate the motor a certain times - full rotation not just a step.
nr_of_rotations - how many rotations to make
total_duration_ms - the total duration time in which the rotations will be made

void A4988_stepperRotateToDegree(float degree, uint16_t delay_between_steps_us);
Rotate the motor to a certain degree. See the video for more details. I don't know where this can be used but i made it just for fun.

void A4988_stepperStop(void); 
When PERPETUAL_MOTION is activated use this function to stop the motor.

void A4988_stepperIncreaseSpeed(uint8_t speed);
void A4988_stepperDecreaseSpeed(uint8_t speed);
 

When PERPETUAL_MOTION is activated use this functions to increase speed or decrease speed. The value can be 1 or greater. One value represent 10us because this is the resolution at which the ISR is triggered. You can have two buttons and on every button press you can execute one of the functions passing 1 as a argument and the speed will increase or decrease with 10us.

Enable function
Enable or disable the motor output. See the pins description above. 
A4988_stepperEnable()
A4988_stepperDisable()

Sleep mode
Enter in sleep mode or exit the sleep mode. Saves more power than disable function.A4988_stepperSleep()
A4988_stepperWakeUp()


  • Example

/*************************************************************
 INCLUDES
**************************************************************/
#include <avr/io.h>
#include "A4988-stepperDriver.h"
#include <util/delay.h>



/*************************************************************
 SETUP
**************************************************************/



/*************************************************************
 MAIN FUNCTION
**************************************************************/
int main(void){
 // A4988 setup
 A4988_stepperSetup();
 
 // Turn left
 A4988_stepperDirection(TURN_LEFT);
 
 // Make 100 steps with 800 microseconds delay between each step
 A4988_stepMicroseconds(100, 800);
 
 // Wait until the rotation ends
 A4988_isBusy();
 
 // Add a small delay when changing the direction of rotation to prevent
 // mechanical shock. The value can be changed depending on the speed
 _delay_ms(100);
 
 // A delay just to see when a rotation stops and the other begins
 _delay_ms(2000);
 
 // Turn right
 A4988_stepperDirection(TURN_RIGHT);  
 
 // Make 50 steps with 800 microseconds delay between each step
 A4988_stepMicroseconds(50, 800);
 
 // Wait until the rotation ends
 A4988_isBusy();
 
 // A delay just to see when a rotation stops and the other begins
 _delay_ms(2000);
 
 while(1){
  // If the rotation is complete (not busy) the function will return 1,
  // else will return 0 and the code inside can run again
  if(A4988_isNotBusy()){
   // Clock mode. Make a full rotation in 60,000 milliseconds = 1 minute
   A4988_stepperRotateNTimes(1, 60000);
  }
  
  // Do other stuff
 }
}

 

Tips:

- If MATH_LIB = -lm is commented out in Make file, the memory with all functions enabled is 6520 bytes program and 304 bytes data memory, compared to 3552 bytes and 40 bytes data memory. So always uncomment MATH_LIB = -lm.

- Disconnecting the stepper motor while power is on, may lead to the A4988 board destruction.

- If you hear squeaky high pitched noises coming from the stepper motor, first check if the motor voltage is not to low in case of using a battery.

- If the motor was working on a certain high speed and now works only on lower speeds, again check the motor voltage if is not low.


Download

A4988-stepperDriver v1.0