Stepper Control on an Arduino using only interrupts.     Latest change 2015-10-22


In brief:   Nederlands
I've seen people running a steppermotor with an Arduino, using typical "Arduino" statements like delay() etc. to let the motor run on different speeds and have it accellerated and decellerated. Mostly they were not happy with the result. The motor did run, but irregularly and noisy.
Also, other tasks in the application were difficult to implement and interacted sometimes in strange and hard to trace ways with the motor behaviour.
I think that the disapointing results came from "sub optimal" programming practises. The Arduino community is not really focused on advocating "good programming practises" and that translates sometimes in bad code and bad behaviour of the system, in particular with time-critcal things like stepper control.
Mind that delay() brings everything in your application to a standstill until delay() has done. Only interrupt driven routines will continue to do their job.
 
In this example I present a way to control a stepper exclusively with interrupt driven code. This results in that the other tasks in the application are hardly influenced by the stepper activity.   

Download the .zip packed demo project, unzip it in a suitable directory and run it in your Arduino environment.
It contains 3 files, StepperTestA4983.ino, Stepper.cpp and globals.h

The most interesting stuff is in Stepper.cpp.
Concentrate on the sometimes delicate interactions between the functions StartTraject(...),  EvalStepper ( ),  and  ISR(TIMER1_COMPA_vect).

StartTraject() sets a number of parameters and starts the interrupts for Timer1 with the interrupt handler ISR(TIMER1_COMPA_vect).

That handler does the actual stepping, but also keeps track of the progress. In particular, as long as the accelleration phase lasts, a decelleration value grows, such that when the end of the trajectory approaches, a neat decelleration can take place to arrive exactly at the intended position.

The function EvalStepper() is called at a fixed rate, 100 Hz usually does well.
It calculates the reload value for Timer1 to do the accelleration and decelleration.
In even more time-critical applications the time-consuming division can be eliminated by using a look-up table. This is not demonstrated in his project.

This demo project has an Arduino NANO as target. It will run in the same way on other Arduinos with the same chip -ATmega328.
For other Arduinos / chips you may want or need to change Pins / Ports / Timers
The "DIAGpinxxx" statements are used to examine the behaviour with an oscilloscope. For other Arduinos you should define another set of diagnostic pins. When everything runs as intended you may outcomment or delete these statements, and also all Serial() statements..  


Nederlands:  Het besturen van een stappenmotor met een Arduino onder gebruikmaking van interrupts.
In het kort:
Ik heb mensen bezig gezien om een stappenmotor te besturen met een Arduino, met gebruik van typische "Arduino" instructies als "delay()" enz. om de motor op verschillende snelheden te laten lopen en te laten versnellen en vertragen. Vaak waren deze mensen niet erg gelukkig met het resultaat. De motor draaide weliswwaar,  maar onregelmatig en luidruchtig. Ook waren andere taken in de toepassing soms lastig te realiseren en/of interfereerden met het gedrag van de motor.
Ik denk dat de teleurstellende resultaten veroorzaakt werden door "niet optimale" programmeer praktijken. De Arduino gemeenschap is niet bijster gefocusseerd op het aanleren van "goede programmeer praktijken" en dat vertaalt zich nog wel eens in slechte code en slecht gedrag van het systeem, in het bijzonder bij tijd-kritische dingen als het besturen van stappenmotoren.
Bedenk dat de instructie delay() alle activiteiten in je applicatie doet stilstaan totdat delay() klaar is. Alleen routines die op interrupts lopen blijven hun taak uitvoeren.

In dit voorbeeld presenteer ik een manier om een stepper geheel via interrupts te laten werken. Het resultaat is dat andere taken in de applicatie hoegenaamd niet beinvloed worden door de stepper activiteit.

Download het .zip packed demo project, unzip het in een map naar je keuze en run het in je arduino omgeving.

Er zijn 3 bestanden: StepperTestA4983.ino, Stepper.cpp en globals.h

Het interessantste zit in Stepper.cpp.
Concentreer op de soms soms delicate wisselwerking tussen de functies   StartTraject(...),  EvalStepper ( ),  en  ISR(TIMER1_COMPA_vect).

StartTraject() zet een aantal parameters en start de interrupts van Timer1 met de interrupt service routine ISR(TIMER1_COMPA_vect).
Die service routine doet het eigenlijke stappen, maar houdt ook de voortgang bij. In het bijzonder, zolang het versnellen (acceleration) duurt groeit er een vertragingsgetal, zo dat als het einde van het traject nadert er een nette vertraging kan plaatsvinden en de motor precies op de voorgenomen positie stopt.

De functie  EvalStepper() wordt in een vast tempo aangeroepen, 100 Hz voldoet meestal. Deze functie berekent de herlaad-waarde voor Timer1 om de juiste versnelling en vertraging te doen. In nog meer tijdkritische toepassingen, als de motor veel sneller moet kunnen, kan de tijdrovende deelsom geëlimineerd worden door het gebruik van een opzoek-tabel (look-up-table) Dat wordt hier niet gedemonstreerd.

Dit demoproject heeft een Arduino Nano als doelprocessor. Het werkt net zo op andere Arduino's met de 328 processor.
Voor Arduino's met een andere processor bijv. de Mega2560 zul je wellicht andere poorten en timers willen gebruiken.
De "DIAGpinxxx" instructies worden gebruikt om het gedrag met een oscilloscoop te kunnen bestuderen. Bij andere Arduino's moet dit wellicht gewijzigd worden, en als alles goed draait kun je ze uitcommentaren of weglaten, evenals de Serial() instructies.