non-blocking Arduino stepper motor example - Page 1
Forum section: electronics / general

author: FrankBuss
author_rank: Unknown
timestamp: March 13, 2017, 08:05:54 pm
content: First time I used a stepper motor:Unlike the Arduino stepper library, moving the stepper motor doesn't block your loop. This is useful if you want to control more than one motor at the same time. And the high interrupt rate of 10 kHz, in combination with the accumulator, allows a high resolution speed control.Parts used:- Pololu Stepper Motor NEMA 14 Bipolar 200 Steps/Rev 35×28mm 10V 0.5 A/Phase- Pololu A4988 StepStick Stepper Motortreiber Carrier 1182- Arduino Mega 2560- 10k potentiometer- breadboard and cables- 10 V power supply for the motor voltageArduino sketch:Code:// pin numbersconst int analogPin = A0;const int enablePin = 40;const int ms1Pin = 41;const int ms2Pin = 42;const int ms3Pin = 43;const int resetPin = 44;const int sleepPin = 45;const int stepPin = 46;const int dirPin = 47;// interrupt frequencyconst float samplerate = 10000.0f;void setup(){// init pinspinMode(enablePin, OUTPUT);pinMode(ms1Pin, OUTPUT);pinMode(ms2Pin, OUTPUT);pinMode(ms3Pin, OUTPUT);pinMode(resetPin, OUTPUT);pinMode(sleepPin, OUTPUT);pinMode(stepPin, OUTPUT);pinMode(dirPin, OUTPUT);digitalWrite(enablePin, LOW);digitalWrite(ms1Pin, LOW);digitalWrite(ms2Pin, LOW);digitalWrite(ms3Pin, LOW);digitalWrite(resetPin, LOW);digitalWrite(sleepPin, HIGH);digitalWrite(stepPin, HIGH);digitalWrite(dirPin, HIGH);delay(100);digitalWrite(resetPin, HIGH);// initialize timer1noInterrupts(); // disable all interruptsTCCR1A = 0;TCCR1B = 0;TCNT1 = 0;OCR1A = 16000000.0f / samplerate; // compare match register for IRQ with selected samplerateTCCR1B |= (1 << WGM12); // CTC modeTCCR1B |= (1 << CS10); // no prescalerTIMSK1 |= (1 << OCIE1A); // enable timer compare interruptinterrupts(); // enable all interrupts}uint32_t wait = 0;int spd = 0;int clk = 0;// timer 1 interruptISR(TIMER1_COMPA_vect){// generate the next clock pulse on accumulator overflowwait += spd;if (wait > 0xffff) {if (clk) {digitalWrite(stepPin, HIGH);} else {digitalWrite(stepPin, LOW);}wait -= 0x10000;clk = !clk;}}void loop(){// read potentiometer and set directionint poti = analogRead(analogPin);if (poti < 512) {digitalWrite(dirPin, LOW);poti = 512 - poti;} else {digitalWrite(dirPin, HIGH);poti -= 512;}// update the speed, which increments the accumulator in the interruptnoInterrupts();spd = poti * 10;interrupts();}

author: Mechatrommer
author_rank: Super Contributor
timestamp: March 14, 2017, 02:35:29 am
content: A4988 stepper driver has its own mcu to advance the motor and check for spikes etc. you may send pulses from arduino and then forget about it.

author: FrankBuss
author_rank: Unknown
timestamp: March 14, 2017, 09:18:12 am
content: Quote from: Mechatrommer on March 14, 2017, 02:35:29 amYes, this is what I'm doing, it is simpler than controlling the coils individually with something like an ULN2003. But my idea can be used for both (just power the right transistors with a state machine in a sequence on each accumulator overflow in the interrupt, if you are using a ULN2003) and is better than other methods, if you don't want to block the code flow when moving a motor.

author: obiwanjacobi
author_rank: Super Contributor
timestamp: March 14, 2017, 11:52:28 am
content: digitalWrite is very slow (due to a pin conversion lookup) and makes your ISR take longer than needed.I'm sure it's not a problem in this prototype but as the project gets more crowded time pressure increases.I think there are functions to perform the lookup once and then use the resulting MCU pin in subsequent write calls - I remember seeing that in code somewhere. I don't have the details at hand unfortunately.Just something to keep in mind for a real project.

author: FrankBuss
author_rank: Unknown
timestamp: March 14, 2017, 12:12:25 pm
content: Right, you can use the port registers:https://www.arduino.cc/en/Reference/PortManipulation

author: arduinew
author_rank: Newbie
timestamp: April 07, 2020, 07:10:12 am
content: Quote from: FrankBuss on March 13, 2017, 08:05:54 pm

author: FrankBuss
author_rank: Unknown
timestamp: April 10, 2020, 10:43:13 am
content: If you need higher frequencies, but don't need to count the individual steps, you could try the Arduino tone function, or programming the ATmega timer registers directly to avoid the gaps between consecutive tone calls. This will also result in no jitter, which is caused by the interrupt latency and the software phase accumulator implementation. But the resolution wouldn't be that fine anymore (the higher the frequency, the larger the Hz steps, because it divides the main clock by an integer).An alternative would be the AD9833, if you don't need to count the steps. It can generate a clock with up to 12.5 MHz, adjustable in 0.1 Hz steps. Internally it uses the same phase accumulator concept, but with a much higher clock rate, so jitter would be very low, especially at frequencies far away from the max frequency. The datasheet has all the details. There are cheap boards at eBay which you can just connect to an Arduino, and there are lots of demo projects for it on the internet.If you want to count the steps as well, it gets complicated. Maybe a faster microcontroller could do it, like a STM32, which runs with more than 100 MHz depending on the type. Or a DSP. But in this case I would probably use a FPGA. I've done a few projects with FPGA devboards, e.g.this 4 channel ADC with 8 bit and 10 MHz samplerate. You could implement a custom frequency generator with step count etc. with VHDL or Verilog, and combine it with a soft CPU or hard CPU on the FPGA for the firmware, depending on the FPGA.

author: glatocha
author_rank: Regular Contributor
timestamp: April 14, 2020, 02:34:03 am
content: What about using DMA to push the preprogrammed sequence to the outputs? In that case you should only need to update the DMA speed when you change speed. In theory this can work totally in the background.I read a little about DMA in MSP430 and PICs, brief googling is saying the Arduino Due should have DMA controller.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: April 23, 2020, 12:11:39 pm
content: If you want to go further then "arduino", then have a look at grbl.Grbl can be used in an "arduino" environment if you wish, but it's a "normal" C program, independent of "arduino" libs and it controlls 3 syncrhonized stepper motors.It's used in3D printers and small CNC machines.At the moment I'm experimenting with a 6 axis verion on an STM32 "Blue-Pill".

author: Johannsen
author_rank: Regular Contributor
timestamp: February 07, 2021, 10:22:20 pm
content: How can the sketch improved with port manipulation? Is it possible to change the steps/rotation?I would like to set the steps in the loop eg move 360° or move 20turns at a speed of 50RPM

author: phil from seattle
author_rank: Super Contributor
timestamp: February 08, 2021, 01:18:18 am
content: Quote from: Doctorandus_P on April 23, 2020, 12:11:39 pmI second that. It has very high quality synchronization between multiple steppers. grbl has been tuned to the Atmel 386p. Supports up to a 30K step rate. If you want to use an ARM chip, I recommend grblHAL which is a 32 bit version grbl and is easily portable to most 32-bit processors. Currently runs on 13 different processors including most of the popular ones. STM32F1xx (Blue Pill), STM32F3xx, STM32F4xx (Black Pill), ESP32, LPC, iMXRT106x (teensy 4.x), Pico (in progress), and others. The Teensy port has been tested to a 400kHz step rate and can be built in the Arduino environment.