Go Down

Topic: Arduino chip as Stepper Controller (Read 12 times) previous topic - next topic


Okay - Now I've done it :D...

Got looking for Direct Port I/O and found a part of the Arduino documentation that doesn't seem to have a direct link.
Here's the link - http://arduino.cc/en/Reference/PortManipulation
It explains some of the problems and some of the uses. In a general Arduino project you might not want to use this approach as there are a few pitfalls that the Arduino IDE hides from you, and that is not always a bad thing. As i intend to use this in a dedicated chip it should cause me no problems.

Port B on the Arduino/ATMege328 is pins 8, 9, 10, 11, 12, 13, with the 2 remaining pins used by the crystal/resonator
DDRB is the Data Direction Register. Assigning 1 to a bit makes it an output, 0 makes it an input.
PORTB is the predefined variable that lets you read or write port B.
PORTB = B0001 turns on pin 8 and turns off pin 9, 10 & 11.

I also used pre-initialized arrays to set up the step patterns.

Down to 1232 bytes.

Here's the last version of the code -
Code: [Select]

Step & Direction Stepper Driver
Pins 8, 9, 10, 11 are tied to transistors
for each of the motor phases.
Pins 2 & 3 are used as interrupts,
Pin 2 as Step and 3 as Direction.
// the following 3 arrays contain the bit patterns to drive the transistors to in turn drive each of the phases of the stepper
int patSimple[] = {B0011,B0010, B0100, B1000, B0001, B0010, B0100, B1000};
int patWave[] = {B0011, B0110, B1100, B1001, B0011, B0110, B1100, B1001};
int patHalf[] = {B0001, B0011, B0010, B0110, B0100, B1100, B1000, B1001};
int pinDir = 3;
volatile int ctr;
volatile int dir;
void setup()
  DDRB = B1111 ;  // this enables Port B Bits 0 - 3, Arduino I/O 8, 9, 10, 11 as outputs
  pinMode(pinDir, INPUT);
  dir = 0;
  attachInterrupt(0, Step, RISING);  // Depending on the application FALLING might be a better choice.
  attachInterrupt(1, Direction, CHANGE);

void loop()
// Nothing to see here... 

void Step()
  if (dir)
    ctr-- ;
    ctr++ ;
  ctr = ctr & 7;
// 2 of the following 3 lines must be commented.
//  PORTB = patsimple[ctr];  // Simple Stepping
//  PORTB = patWave[ctr];    // Wave Stepping
     PORTB = patHalf[ctr];      // Half Stepping

void Direction()
    dir = -digitalRead(pinDir);    // Direction is 0 (zero) or -1 (minus one)


Got my chips and parts on Monday and Tuesday night I put together a board for programming ATMega328 and ATTiny2313 chips. I didn't order any 120Ohm resistors so I had to wait til yesterday to test it out.

Used AVRDude to program the chips and had to add a section to the avrdude.conf file for the ATMega328 as it only had the ATMEga328P. I could have added the -F argument to the command string, but it was just as easy to add a section for the 328. Copied the whole 328P section and made 3 changes - in the 2 locations where 328P appeared I erased the P and I changed the System Descriptor bytes (avrdude reports what it finds if it doesn't agree) from 1E 95 0F to 1E 95 14 and everything worked. Took the chip out of the programming socket and swapped it for the chip in my Arduino board and loaded it with Blink.

I also put one of my ATTiny2313 chips in the and used the Arduino IDE to load the code to the 2313 and it seems to have loaded just fine. Have to hook up a circuit and test out the chip. Will probably be Sunday night before I get the chance as I have too much in my schedule between now and then.

I will post a picture of my programmer board and a schematic the first of the week.


Jan 19, 2012, 09:58 pm Last Edit: Jan 19, 2012, 10:00 pm by BetterSense Reason: 1
Just so you know, you are on the right track. I drive a unipolar stepper using 1/16 step microstepping driving 4 TIP110s's from PORTB and C of my ATMEGA168 using 8-bit fast PWM. You can either store the sine wave values in an array or in a table/switch statement.

IMPORTANT: this works for me because 1. I use ~5kHz PWM, not the standard arduino 500Hz PWM and 2. I only need to rotate slowly.

To microstep using PWM you have to use a PWM frequency several times higher than the fastest microstepping rate you anticipate. This means that at the standard Arduino PWM rate of ~500Hz you will only be able to meaningfully microstep at about 100Hz--even that is only 5 PWM cycles per microstep--which at full step for a 1.8* motor is only 30RPM and for 1/8 microstepping (1600steps/rev) you will only be able to meaningfully microstep at 1/4RPM. Which might be fine if you are making a rotisserie; I only need 10RPM myself.

To be able to output small microsteps at high microstepping rates, you have to crank up the PWM frequency; this is basic sampling theory.  There is no problem in principle; commercial microstepping drivers run their PWM at about 30kHz, but I assume we would run into trouble with our discrete TIPs and have to write very tight code (maybe assembly) to use PWM rates that high. I don't know, but I do know that my 5kHz is going fine.


Well it works. (almost didn't, hooked the chip up upside down and generated a little heat...) plugged the chip in and hooked up to my led drivers and it works.

Got a few things to take care of before I can play with it too much so ... The first of the week i should have a stepper driver working.

So - It is really rather easy to hook up an ATTiny2313 to an Arduino programmed as an ISP and load the code into the 2313. I did have to use version 0.22 of the Arduino Environment to have teh ArduinoISP work properly, but I stumbled across that little piece of info somewhere along the way.


Worked on the board for my first driver. Got it mostly soldered up and got to thinking that I would have to change the bleeder resistor as 56Ohms wold draw too much current from the 328, so I will have to pull them and get some resistors in the 120 to 220 range. Oh well, minor setback.

Go Up