Pages: 1 [2] 3   Go Down
Author Topic: Arduino chip as Stepper Controller  (Read 12209 times)
0 Members and 1 Guest are viewing this topic.
South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay - Now I've done it smiley-grin...

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:
/*
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);
  ctr=0;
  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-- ;
  }
  else
  {
    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)
}
Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Dallas
Offline Offline
Sr. Member
****
Karma: 3
Posts: 337
nephew as a kittens
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: January 19, 2012, 04:00:42 pm by BetterSense » Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Got to playing with assembly language - took my code size from 1200 bytes to 86 bytes.

Out of necesity, Arduino adds a lot of overhead to interrupt routines. Saves all the registers and such, but it would take a lot more power to analyze the code and reduce that overhead. Writing in assembler means I had to do the optimizing and I am responsible for what can go wrong...

I am using AVRStudio 4 to write and test the code (has a simulator built in)(even has a disassembler that will load the hex files and list them )
I recommend AVR Studio 4 over 5 as 5 is just to S-L-O-W...

Here's what the code looks like in assembler.
Code:
.NoList
.INCLUDE "tn2313Adef.inc" ; Header for ATTINY2313A
.List
; ============================================
;   R E G I S T E R   D E F I N I T I O N S
; ============================================
.DEF Conf = R16 ; Multipurpose register
.DEF StepCt = R17
.DEF StepMask = R18
.DEF Direction = R19
.DEF PortBHold = R20
.DEF PortDHold = R21
.DEF PortDMask = R22
.DEF SeqBase = R23
; ============================================
;   R E S E T   A N D   I N T   V E C T O R S
; ============================================
.CSEG
.ORG $0000
rjmp Main ; Reset vector
rjmp Step ; Int vector 1
rjmp Dir ; Int vector 2
reti ; Int vector 3
reti ; Int vector 4
reti ; Int vector 5
reti ; Int vector 6
reti ; Int vector 7
; ============================================
;     I N T E R R U P T   S E R V I C E S
; ============================================
Step: add StepCt,Direction
      and StepCt,StepMask
      mov ZL,SeqBase
 add ZL,StepCt
 Lpm PortBHold,Z
 out PORTB,PortBHold
 reti
Dir:  ldi PortDMask,8
      in  PortDHold,PIND
      and PortDHold,PortDMask
      breq SinNeg
 ldi Direction ,1
      rjmp DirEx
SinNeg:  ldi Direction ,-1
DirEx:reti
; ============================================
;     M A I N    P R O G R A M    I N I T
; ============================================
Main:
; Init stack
ldi Conf, LOW(RAMEND) ; Init LSB stack
out SPL,Conf
; Init Port A
ldi Conf,0 ; Direction Port A
out DDRA,Conf
; Init Port B
ldi Conf,(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3) ; Direction Port B
out DDRB,Conf
; [Add all other init routines here]
ldi Conf,(1<<SE)|(0<<ISC00)|(1<<ISC01)|(1<<ISC10)|(0<<ISC11) ; enable sleep
out MCUCR,Conf
ldi StepCt,0
ldi StepMask,7
ldi Direction,1
ldi SeqBase, StepSeq
add SeqBase,SeqBase
  sei
; ============================================
;         P R O G R A M    L O O P
; ============================================
Loop:
rjmp loop ; go back to loop

StepSeq: .DB 1,3,2,6,4,12,8,9,0,0
; End of source code
« Last Edit: June 01, 2012, 11:18:38 am by kf2qd » Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Finally can say I have an ATMel chip - the ATtiny2313 functioning as a stepper driver.
I got it working in Arduino, the assembler version still has some strange behavious (and the assembler version of the code is hareder to troubleshoot in hardware. In the Simulator it works just fine, but in hardware.....

Here's the Arduino version.
By my count it will run in excess of 140RPM  running in Half Step mode when driven by a simple Arduino program.

Code:
/*
Step & Direction Stepper Driver
 Pins 8, 9, 10, 11 are tied to transistors
 for each of the motor phases.
 Pins 2 is used as interruptfor the steps
 and 3 is the 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 = 5;
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);
  ctr=0;
  dir = 0;
  attachInterrupt(0, Step, FALLING);  // Depending on the application FALLING might be a better choice.

}

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

void Step()
{
  if (digitalRead(5))
  {
    ctr++ ;
  }
  else
  {
    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


And I was testing it with code that looks like this in my Arduino-
Code:
int steps;

void setup() {               
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(2, OUTPUT);     
  pinMode(3, OUTPUT);   
  digitalWrite(3,HIGH); 
}

void loop() {
  for (steps=0;steps<400;steps++)
  {
    digitalWrite(2, HIGH);   // set the LED on
    digitalWrite(2, LOW);    // set the LED off
    delay(1);              // wait for a second
  }
}

Still want to crack the assembly code...
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi, I am experimenting with the code you posted ( Reply #15 on: January 15, 2012, 05:19:57 PM ) with 4 IRF530's, an Arduino Uno, a small unipolar stepper and solder-less board and It works really well with EMC2 linux.  My goal is to drive a homebrew CNC router table with the above components.  I have very little (NO) experience with port manipulation and am somewhat apprehensive about adding another axis to the code, as I recall reading that I could bugger up the TX RX ports?  does the UNO have enough ports available to control 3 axes? I would still be happy with 2!  In any case, great work on this elegant code so far!
Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am using the ATtiny2313 for this application. It has a few extra pins, It turns out I need 1 interrupt pin, 1 input and 4 outputs. I would also like to add 1 more input as an enable  and possibly a Ready output. The problem with trying to run more than one stepper in this application is the Interrupt inputs. Because of priority of interrupts the second interrupt would get missed if it occurred at the time as the first.

Look into some ATtiny2313 chips and use the Arduino as the programmer. That circuit is abut as inexpensive and simple as an Arduino circuit can be. If yuwant more info to use that approach I can point you to the few pieces you need to put together to do that.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That would be great.  I was wondering about the ability to program with the arduino and swap the IC out to a PCB, so that sounds like it could work for me.  Any resources you can point me to would be greatly appreciated.
Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

To program a chip I use Arduino-0.22 to load the ArduinoISP sketch on my Arduino Uno board. I made a small board that has 2 sockets, a 20 pin that could be used to program 20 and 8 pin chips and a 28 pin socket that I use to program 328's (like the Uno).  Search for Arduino as ISP as there is a lot of info out there that exlains the process and the wiring. My little board has 6 wires between the boards to program the chip and a 120 Ohm resistor between the Reset and +5 on the Arduino Uno. To program an Arduino sketch using the ArduinoISP there is a selection on the ArduinoIDE File menu to write the sketch usin Arduino as ISP.


I use the Arduino-0.22 IDE as there was a change in something in Arduino-1.0 and the ArduinoISP only works at 9600 baud instead of 19200 which causes a problem.

Here's a picture (poor) of my ISP board to use with the Uno.


* ArduinoISP1.jpg (652.85 KB, 2048x1536 - viewed 92 times.)
Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I got it working -

Figured out what was lacking in my assembly code and now it works.

Code:
.NoList
.INCLUDE "tn2313Adef.inc" ; Header for ATTINY2313A
.List
; ============================================
;   R E G I S T E R   D E F I N I T I O N S
; ============================================
.DEF Conf = R16 ; Multipurpose register
.DEF StepCt = R17
.DEF StepMask = R18
.DEF Direction = R19
.DEF PortBHold = R20
.DEF PortDHold = R21
.DEF PortDMask8 = R22
.DEF SeqBase = R24
; ============================================
;       S R A M   D E F I N I T I O N S
; ============================================
.DSEG
.ORG  0X0060
; ============================================
;   R E S E T   A N D   I N T   V E C T O R S
; ============================================
.CSEG
.ORG $0000
rjmp Main ; Reset vector
rjmp Step ; Int vector 1
reti ; Int vector 2
reti ; Int vector 3
reti ; Int vector 4
reti ; Int vector 5
reti ; Int vector 6
reti ; Int vector 7
; ============================================
;     I N T E R R U P T   S E R V I C E S
; ============================================
Step:   in  PortDHold,PIND
; look at only PD#, the Direction input
        and PortDHold,PortDMask8
; if PD3 High, Add Direct to StepCt, if PD3 Low Subtract Direction
        breq SinNeg
        add StepCt,Direction
        rjmp Next
SinNeg: sub StepCt,Direction
; limit StepCt to range from 0 to 7 (-1 becomes 7)
Next:   and StepCt,StepMask
; use Z as a pointer to the StepSequence table
        mov ZL,SeqBase
; StepCt is teh offset into the table
add ZL,StepCt
; transfer the next bit pattern from ProgMem to PortBHold, R20
Lpm PortBHold,Z
out PortB,PortBHold
reti
; ============================================
;     M A I N    P R O G R A M    I N I T
; ============================================
Main:
; Init stack
ldi Conf, LOW(RAMEND) ; Init LSB stack
out SPL,Conf
; Init Port A
ldi Conf,0 ; Direction Port A
out DDRA,Conf
; Init Port B
ldi Conf,(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3) ; Direction Port B
out DDRB,Conf
; Set ISC00 to 0 and ISC01 to 1, Falling Edge INT0 Interrupt
ldi Conf,(0<<ISC00)|(1<<ISC01)
out MCUCR,Conf
; Set INTO , Bit 6 of GIMSK to Enable INT0
ldi Conf,(1<<int0)
out GIMSK, Conf
; Initialize StepCt, R17
ldi StepCt,0
; Initailize StepMask, R18
ldi StepMask,7
;Initialize Direction, R19
ldi Direction,1
; Initialize SeqBase to point to 8 bit address of StepSeq
ldi SeqBase, StepSeq
add SeqBase,SeqBase
; Initialize PortDMask8, R22, to mask only PD3 of port D
        ldi PortDMask8,8
        ldi ZH,0   
  sei
; ============================================
;         P R O G R A M    L O O P
; ============================================
Loop:  rjmp loop ; go back to loop
; bit patterns for output to the Stepper Drive Bits.
StepSeq: .DB 1,3,2,6,4,12,8,9,0,0
; End of source code
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This was one of the best reads on Arduino stepper logic thata I have seen.  You are to be commended and I thank you very much. 

It's a pity that it seemed to drop so abruptly,

Do you have any final specs on the speed of your assembly code version?

Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Got into a few other projects so I have not paid much attention to it for a while. I was able to get hold of a Hardinge UM milling machine and that took some time to get cleaned and moved into my shop. I have a Harbor Freight Mini Mill that I want to convert to CNC along with a 10:1 gearbox that I want to use as an indexer on that mill. Living down here in South Texas my year is backwards from folks that live farther north. Here it get too hot to work in an un-airconditioned garage so most of my projects out there take place from September to May whereas when I lived up in Indiana the projects took place from April to October when it was warm enough to work out in the shop.

I have mounted 2 2313 on a perf board along with the 8 MOSFETs and actually had it running - ~140 rpm using my Arduino as the step generator. As that was written in the Arduino IDE I suspect that I can get quite a bit more speed using MACH3 or GRBL.

What's the timing for 12 instructions and an interrupt on an ATtiny running at 8Mhz? According to how I interpret the info from AVRStudio the interrupt shold process in ~2.25mSec.
« Last Edit: June 28, 2012, 05:33:03 pm by kf2qd » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 1
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Now i am confused. Do you use the dedicated chip to drive the stepper or the Arduino.
If you use the PC to send Direction and Step commands what does the Arduino do?
yeah very interesting...
Logged

i love blogs... smiley

Pages: 1 [2] 3   Go Up
Jump to: