Unipolar Stepper Motor

Summary

Driver a stepper motor back and forth.

Code

#define MHVLIB_NEED_PURE_VIRTUAL

// Bring in the MHV IO header
#include <MHV_io.h>

// Bring in the Stepper driver
#include <MHV_StepperMotorUnipolar.h>

// Bring in the realtime clock driver
#include <MHV_RTC.h>

// Bring in the power management header
#include <avr/power.h>
#include <avr/sleep.h>

// Bring in the timer header
#include <MHV_Timer8.h>

// Program space header, saves RAM by storing constants in flash
#include <avr/pgmspace.h>

#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
#define ATTINY
// A timer we will use to tick the RTC
MHV_Timer8 tickTimer(MHV_TIMER8_0);
MHV_TIMER_ASSIGN_1INTERRUPT(tickTimer, MHV_TIMER0_INTERRUPTS);

#else
// A timer we will use to tick the RTC
MHV_Timer8 tickTimer(MHV_TIMER8_2);
MHV_TIMER_ASSIGN_1INTERRUPT(tickTimer, MHV_TIMER2_INTERRUPTS);
#endif

#define ALARM_COUNT	4
// The RTC object we will use
MHV_RTC_CREATE (rtc, ALARM_COUNT);

// A timer trigger that will tick the RTC
void rtcTrigger(void *data) {
	rtc.tick1ms();
}


/* We have a hard limit of 1000 steps/second due to the 1ms resolution of the RTC class
 * The motor & control circuitry have a lower limit due to their electrical & mechanical properties
 */
#define MAX_SPEED 500
#define MIN_SPEED 100

/* The steps per rotation
 * For the 28BYJ48 geared stepper, this is 32 steps multiplied by a gear ratio of 64,
 * multiplied by 2 since we are half stepping
 */
#define STEPS_PER_ROTATION	(2*32*64)

/* The stepper driver
 * Available modes are WAVE, FULL and HALF
 */
MHV_StepperMotorUnipolar stepper(rtc, MHV_STEPPER_MODE_HALF, MHV_PIN_B0);


/* A class that tells the stepper what to do next
 * Note that moveComplete() is called every time the stepper driver has completed a move
 */
class StepperInstructions : public MHV_StepperListener {
private:
	bool	_forward;		// The direction we are currently rotating
	float	_speed;			// The rotation speed (steps/second)
	bool	_speedUp;		// True if we are increasing speed, false otherwise

public:
	StepperInstructions();
	void moveComplete(int32_t position);
};

StepperInstructions::StepperInstructions() :
	_forward(true),
	_speed(1),
	_speedUp(true) {}

void StepperInstructions::moveComplete(int32_t position) {
	_forward = !_forward;
	_speed += (_speedUp) ? 100 : -100;

	if (_speed > MAX_SPEED) {
		_speed = MAX_SPEED;
		_speedUp = false;
	} else if (_speed < MIN_SPEED) {
		_speed = MIN_SPEED;
		_speedUp = true;
	}


	int32_t newPosition = (_forward) ? 1 * STEPS_PER_ROTATION : 0;
	stepper.rotate(_forward, _speed, newPosition);
}

StepperInstructions stepperInstructions;


int NORETURN main(void) {
	// Disable all peripherals and enable just what we need
	power_all_disable();
#ifdef ATTINY
	power_timer0_enable();
#define PRESCALER	MHV_TIMER_PRESCALER_5_64
#else
	power_timer2_enable();
#define PRESCALER	MHV_TIMER_PRESCALER_7_64
#endif

	set_sleep_mode(SLEEP_MODE_IDLE);

	// Register the listener with the stepper driver to be notified when moves are complete
	stepper.registerListener(stepperInstructions);

	// Configure the tick timer to tick every 1ms (at 16MHz)
	tickTimer.setPeriods(PRESCALER, 249, 0);
	tickTimer.setTriggers(rtcTrigger, 0, 0, 0);
	tickTimer.enable();

	sei();

	/* Start with a forward rotation of 1 revolution at 100 steps/second
	 * Further instructions will be triggered from StepperInstructions::moveComplete
	 */
	stepper.rotate(true, 100, 1 * STEPS_PER_ROTATION);

	for (;;) {
		/* All the interesting things happen in events
		 */
		rtc.handleEvents();

		// Sleep until an interrupt occurs
		sleep_mode();
	}

	UNREACHABLE;
}

Hardware

To complete this tutorial, you need a unipolar stepper motor and 4 N channel MOSFETs with internal bypass diodes.

The following hardware was used to write this tutorial

The hardware should be wired up to your MHVBoard as shown in the schematic below.

The gate of each MOSFET are connected to the MHVBoard pins B0, B1, B2 and B3. Optionally, you may also connect an LED from these pins to ground to show the activity of each channel. Note that the LEDs will prevent the MOSFETs from turning on fully. This isn't a problem for the specified motor, as it is low current, but if you are using a motor that requires more current, you may need to remove the LEDs to get theMOSFETs to turn on fully.

The source of each MOSFET are connected to ground.

The drain of each MOSFET is connected to the respective coil of the stepper motor, B0 to Coil A, B1 to Coil B, and so forth.

The common wire of the stepper motor is connected to +5V.

28BYJ Motor Wiring

Wire Colour

Connection
Blue Coil A
Pink Coil B
Yellow Coil C
Orange Coil D
Red +5V

 

 What it Does

The Stepper motor driver is dependent on the realtime clock class MHV_RTC in order to schedule events in the future (in this case, moving the motor).

When the stepper driver completes a move, it can notify a listener that the move is complete. In this tutorial, we use this to issue another move command to the motor driver.

The main loop consists of checking for events, and then putting the microcontroller to sleep. The microcontroller will be woken again when the timer ticks, and execution will commences from the next line after the sleep (looping back to check for new events to process).

Lines 30-31

This creates a timer instance, bound to the hardware time 2, and assigns the class as the interrupt handler for it.

Line 36

Here we create the realtime clock instance. It can handle a queue of events (alarms) to trigger at a certain time.

Line 39

The rtcTrigger() function will be passed to the timer as a callback, to be called once per millisecond. It ticks the realtime clock class so it can keep track of time.

Line 59

Here we create an instance of the stepper driver, telling it which realtime clock to interact with, the mode (in this case, we are half-stepping for greater resolution), and the starting pin for the 4 output channels. The driver will automatically consume the 4 consecutive channels on that port, so you can only specify pins 0-3 here and get defined behaviour. In this case, we want to output to pins B0, B1, B2 & B3, so we specify pin B0 here.

Line 65

Here we define a class to act as a listener to be notified when the stepper driver completes a move.

Line 81

This method will be called when the stepper driver completes a move. It reverses direction, and ramps the speed up and down after each move.

Line 95

This call instructs the stepper driver to move to a new position. The direction, speed and final destination (in steps) are provided. Note that you may call this again before a move is complete. In this case, the driver discards the old move and commences the new one.

Line 108

Since we are dependant on the timer, we re-enable it (after disabling all peripherals to save power).

Line 115

Here we tell the stepper driver what to notify when a move is complete.

Lines 118-120

These lines configure the timer hardware, set the callback functions to be executed and enable the timer.

In this situation, we configure the timer in clocks. You can also configure the timer in microseconds, however, this call uses more program space so is not recommended.

Line 122

This function comes from AVR LibC and enables hardware interrupts on  the microcontroller.

Line 127

Since motor move commands are issued upon completion of previous commands, we need an initial command to kick things off. Here we ask the driver to rotate forwards at 100 steps/second for 1 revolution.

Lines 129-136

This infinite loop asks the realtime clock class to execute any pending events (alarms). Once done, the microcontroller is put to sleep to conserve power. It will be woken up to reiterate the loop on the next timer tick, where it will again check for pending events.

Line 112

Here we configure a sleep mode that allows the timer to wake the microcontroller up, while saving as much power as possible.