Introduction
I have recently discovered the Arduino board system. Although it has been out for quite some time I didn't know about this very interesting system. The Raspberry Pi appears to be much more popular, but the Arduino really deserves to be known.
Its specification are way less impressive than the Raspberry PI, but its purpose is totally different. Those who studied electronics like I did more than 25 years ago should find it very familiar and extremely interesting. The Arduino system is a micro-controller prototyping board that can be extended with the only limit of your imagination.
For $38 you can buy online a starter kit that contains many interesting components and solder-less prototyping boards to build many experiments. When I was a student I learned to program a Motorola CPU with I/O circuits on board called Motorola Kit D5. This CPU board the size of a modern laptop was powered by a 6802 with few Kbytes of RAM and primitive operating system that would let you enter assembly instruction in hexadecimal with small 25 keys keyboard using a 6 digits LED display.
Some specs can be found there: http://www.old-computers.com/museum/computer.asp?c=504&st=1
The Arduino is a modern version of this Kit D5 and includes a lot of interesting on-board features:
- 12 digital configurable I/O Pin, 6 of them configurable as PWM output
- 6 10 bits ADC input I/O that can be configured as digital output as well
- 2 in-board timers
- 32K of NVRAM
- 1K of SRAM
- 2K of EEPROM
- RISC instruction set
Of course it doesn't look that impressive if you compare it with the Raspberry PI but the purpose of such a board is not the same as PI. The main purpose of that board is to interface with the outside world using the I/O pins.
The board can be programmed with a simple IDE connected via a USB port to a PC. Once the program installed in the board memory it runs as an infinite loop. Libraries can be written in C++ and used to build more complex applications.
Controlling a Stepper Motor
In this article I'll show you how to write a simple class to control a stepper motor, the one provided with the starter kit I bought, its reference is 28BYJ-48. The Arduino comes with an installed Stepper
class in its libraries but this class doesn't work properly with this motor. There are two problems with this class when it drives the 28BYJ-48:
- It takes 2048 steps to do a 360deg revolution while the specs of the 28BYJ-48 states that it would take 4096 steps
- When sending a negative value to the
step()
method the motor continues to turn in the same direction!
It took me quite some time to understand what was wrong and find the correct specs for the 28BYJ-48 and its control board, then I could understand why it was not working properly. You can find the correct specs there: Geetec Stepper Motor
The trick is the sequence that has to be followed when writing the input commands to the pins of the board. The Stepper
class of the Arduino library follows a sequence of 4 values of 4 bits but the 28BYJ-48 requires a sequence of 8 values of 4 bits. One step of the motor correspond to 1 value of the sequence. Those 8 values must be repeated in this order to turn the motor in one direction and in the reverse order to turn the motor in the other direction.
This is what the code of the StepperMotor
class does. This class is intended to work with this motor, it must be modified to work with a different motor that would have a different control sequence.
The sequence of quartets to send to the 28BYJ-48 is the following:
In 1 | In 2 | In 3 | In 4 |
0 | 0 | 0 | 1 |
0 | 0 | 1 | 1 |
0 | 0 | 1 | 0 |
0 | 1 | 1 | 0 |
0 | 1 | 0 | 0 |
1 | 1 | 0 | 0 |
1 | 0 | 0 | 0 |
1 | 0 | 0 | 1 |
This sequence will have the motor turn in counter clockwise direction, if you run this sequence in reverse order the motor will turn in clockwise direction.
Another parameter is the speed of rotation. It is not clear from the specs what the capability of the motor is and its board, but after a few experiments I found that operated fine with a 1 ms period. This means that you can change the state of the input pins every ms. Above that frequency the board won't respond properly and the motor won't move.
class StepperMotor
{
public:
StepperMotor(byte motorPin1, byte motorPin2, byte motorPin3, byte motorPin4);
void setPeriod(int period);
void move(int numberOfSteps);
void stop(bool interrupt = false);
void reset();
}
This simple class provides two main methods setPeriod
and move
. The method setPeriod
is used to set the time in milliseconds between two pulses sent to the motor to make in move in one or the other direction. The method move is used to move the motor of a given number of steps in a direction given by the sign of the parameter.
The methods stop
and reset
can be used to stop and restart the motor from an interruption serving routine. The stop
method has a parameter to enable its use from a ISR, when true the method will stop the motor asynchronously using an internal flag.
The code of the StepperMotor
is an Arduino library and need to be installed as .zip file from the Arduino IDE.
The following is an extract of the code of the class StepperMotor
that shows how it works.
void StepperMotor::stop(bool stop)
{
if (stop)
{
interrupt = true;
}
for (byte idx = 0; idx < PHASE_NUMBER; ++idx)
{
digitalWrite(motorPins[idx], LOW);
}
}
void StepperMotor::moveOneStep(byte seqIdx)
{
for (byte idx = 0; idx < PHASE_NUMBER; ++idx)
{
digitalWrite(motorPins[idx], stepSequence[seqIdx][idx]);
}
delay(period);
}
bool StepperMotor::moveOneStepForward()
{
if (!interrupt)
{
stepIdx = incrementStep(stepIdx);
moveOneStep(stepIdx);
}
return interrupt;
}
bool StepperMotor::moveStepsForward(int steps)
{
for(int n = 0; n < steps; n++)
{
if (moveOneStepForward())
{
break;
}
}
return interrupt;
}
The behavior of the stop
method is very simple, if called while the method moveStepsForward
is running it interrupts it as moveOneStepForward
returns true and breaks the loop, stopping the motor movement.
In the following examples the connections are the following: pins 1, 2, 3, 4 are connected to the pins 19, 18, 17 and 16 of the Arduino board. The button is connected to the pin 2 of the Arduino which is set to INPUT_PULLUP so there is no need for a resistor.
Demo application: Reversing the rotation every time a button is pressed
The first demo application is using a button to start the rotation of the motor for a given number of steps, then a second click on the button starts the motor in the opposite direction and so on.
#include <StepperMotor.h>
#define _PIN1 19
#define _PIN2 18
#define _PIN3 17
#define _PIN4 16
#define ITR_PIN 2
#define ITR_NB 0
volatile boolean forward = false;
volatile boolean start = false;
volatile boolean first = true;
StepperMotor stepper(_PIN1, _PIN2, _PIN3, _PIN4);
void buttonPressed()
{
if (!first)
{
if (!start)
{
forward = !forward;
start = true;
}
}
else
{
first = false;
}
}
void setup()
{
cli();
stepper.setPeriod(5);
pinMode(ITR_PIN, INPUT_PULLUP);
attachInterrupt(0, buttonPressed, FALL);
sei();
}
void loop()
{
if (start)
{
if (forward)
{
stepper.move(2048);
stepper.stop();
}
else
{
stepper.move(-2048);
stepper.stop();
}
start = false;
}
}
The ISR method is called when the button is pressed on the falling edge of the pulse. However it appears that when started the program always detects an interruption even if the button is not pressed. In order to avoid processing this interruption the flag first
is used to discard this interruption. There is another issue with catching interruptions on a button pressed and it's the bouncing of the contact itself. In this simple example the flag start
is used to avoid any bouncing pulse from the button.
Demo application: Stopping or starting the motor on a button pressed interruption
The second example is demonstrating the ability to stop the motor while the method move is running. Every time the button is pressed it stops or starts the motor depending on its current state. The interruption routine is suffering some time of the bouncing of the button which causes one press of the button to generate several falling edges that are caught by the interruption line of the chip.
I couldn't find a way to control that by software but a simple capacitor and some resistor would do the trick to avoid them. Because of that the current code may some times have an erratic behavior when the button is pressed.
#include <StepperMotor.h>
#define DELAY 2000
#define _PIN1 19
#define _PIN2 18
#define _PIN3 17
#define _PIN4 16
#define ITR_PIN 2
#define ITR_NB 0
volatile bool first = true;
volatile bool moveInterrupted = false;
StepperMotor stepper(_PIN1, _PIN2, _PIN3, _PIN4);
void buttonPressed()
{
static volatile boolean toggle = false;
if (!first)
{
if (toggle)
{
stepper.reset();
}
else
{
stepper.stop(true);
}
toggle = !toggle;
}
else
{
first = false;
}
}
void setup()
{
cli();
stepper.setPeriod(10);
pinMode(ITR_PIN, INPUT_PULLUP);
attachInterrupt(0, buttonPressed, FALLING);
sei();
}
void loop()
{
moveInterrupted = stepper.move(2048);
}
Conclusion and Point of Interest
Those little experiments with the Arduino took me back 25 years ago a bit like a time machine! There is plenty of code and electronic circuits out there that use the Arduino boards, and have barely scratched the surface of this amazing little circuit. I have always preferred to use a computer to interact with the external world, unfortunately my job has been more on the software side although I have never forgotten what I originally studied, CPU boards connected to electronic components. I hope this simple article will plant the seed of learning more on how to interface a computer with the real world!