Since I am mostly a software engineer, I tackled the drivers first. I found some old floppy stepper motor pulls from Futurlec, looked like the old 5-1/4" type so I grabbed 4 of them. They were within my budget :-). Unfortunately they seem to be out of them now, so I am glad I got 4.
There were no docs for these, even from the manufacturer's site, but there is enough information on steppers out on the net that figuring out what to do was reasonably painless.
I chose to implement on my favorite microcontrollers using the AVR-GCC tools under gentoo linux.
The first attempt was using an ATMega16, which has plenty of resources for development.
Final version uses a ATTiny2313 for each axis. I'll get the code presentable (as sits, following pix) :-) and in here as one of the projects to do with this. I've still got to do PCBs for this, but the mechanical side is going to be were I am struggling along, so that can stay on the protoboards for now.
I had really expected this low budget approach to discourage me, but that failed. The test jig with the 1/4-20 rod, nut and paper clip (to keep the nut from spinning :-) worked out surprisingly well.Not shown on the test jig are the labels, pencil marks thereon and the pointer in the center of the wing of the paper clip. I had assumed (heh) that as soon as I went to repeat a move to and back, that there would be no allignment to where it should be. Strangely, if it is misalligned it is within the pencil mark width. I am taking this as encouraging :-).
Driving this using EMC (Enhanced Machine Controller) from LinuxCNC.org worked out well also after some initial thrashing around dispelling (some of :-) my ignorance and some help in doing so from the fine folks on the #emc IRC channel on irc.freenode.net. I've still got adjustments to make to the EMC config file, but I have a fine start.
Please ignore the date on the pix, the camera got bumped somehow and turned on the date stamp which I don't usually use.
Below are pictures of the development board, test jigs, adapter plugs for the Atmel STK-500 to protoboard and some general details.
Please click on thumbnails for a larger image






# Makefile for Stepper motor driver V1. # cvw - 10/22/05 # cvw - 6/15/04 CC=/usr/local/src/avr-toolchain-2005.4/bin/avr-gcc # CC=/usr/local/avr/bin/avr-gcc OBJCOPY=avr-objcopy OBJDUMP=avr-objdump CFLAGS= -g -Os -mmcu=attiny2313 -Wall BURNER= -q -P /dev/ttyS1 -c stk500v2 -U flash:w:vtst.hex -p t2313 VERIFY= -q -P /dev/ttyS1 -c stk500v2 -U flash:v:vtst.hex -p t2313 MKPROTOS=util/mkprotos2 all: make protos make vtst.hex make vburn # make vverify #################################################################### DEPS_LIB = interrupts.c initiall.c main.c protos: (cd util ; make) $(MKPROTOS) $(DEPS_LIB) > protos.p #################################################################### OBJS = interrupts.o initall.o main.o vtst.hex: vtst.elf $(OBJCOPY) -O ihex vtst.elf vtst.hex $(OBJDUMP) -S vtst.elf > vtst.listing vtst.elf: $(OBJS) $(CC) $(CFLAGS) -o vtst.elf -Wl,-Map,vtst.map $(OBJS) %.o: %.c *.h $(CC) $(CFLAGS) -o $@ -c $< #################################################################### clean: rm -fv *.o *.out *.map *.hex *.p *.listing *.elf *.c~ *.bak (cd util ; make clean) #################################################################### # Program into chip. #################################################################### vburn: /usr/local/bin/avrdude $(BURNER) vverify: /usr/local/bin/avrdude $(VERIFY)
/***************************************************
* ADrive CNC Driver development code.
* cvw 10/22/05
***************************************************/
#include "vdefs.h"
extern void initialize_all(void);
PUBLIC void vmsdelay(unsigned int delay);
PUBLIC void vusdelay(unsigned int delay);
PUBLIC void step8t_fwd(void);
PUBLIC void step8t_rev(void);
// PUBLIC void step8ti_fwd(void);
// PUBLIC void step8ti_rev(void);
PUBLIC const unsigned char step_tab[8] = { 0x08, 0x0a, 0x02, 0x06, 0x04, 0x05, 0x01, 0x09 };
PUBLIC volatile unsigned char step_work, step_hold;
int main(void)
{
initialize_all();
wdt_disable(); // turn off the watchdog
sei(); // Globally enable interrupts.
while (1)
{
if ( bit_is_clear( PIND,5)) // Jog reverse.
{
while (bit_is_clear(PIND,5))
{
step8t_fwd();
}
}
if ( bit_is_clear(PIND,4)) // jog forward
{
while (bit_is_clear( PIND,4))
{
step8t_rev();
}
}
}
return 0;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Functions follow
//++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Trying for about a mS here...
//
void vmsdelay(unsigned int delay)
{
unsigned int i, j;
for ( i = 0 ; i < delay ; i++ )
{
for ( j = 0 ; j < 1000 ; j++) // 1mS~?~
{
micro_sleep(); // by the uS.
}
}
}
// Trying for about a uS here...
//
void vusdelay(unsigned int delay)
{
unsigned int i;
for ( i = 0 ; i < delay ; i++ )
{
micro_sleep(); // by the uS.
}
}
/*************** Moved to interrupt routine *********************
void step8ti_fwd(void)
{
extern unsigned char step_tab[];
if (step_hold == 7) // 8)
step_work = 0;
else
step_work++;
PORTB = step_tab[step_work];
step_hold = step_work;
// PORTB = 0x00; //Do not hold position for now.
}
void step8ti_rev(void)
{
extern unsigned char step_tab[];
if (step_hold == 0)
step_work = 7;
else
step_work--;
PORTB = step_tab[step_work];
step_hold = step_work;
// PORTB = 0x00; //Do not hold position for now.
}
**************************************************************/
void step8t_fwd(void)
{
unsigned char i;
extern unsigned char step_tab[];
for ( i = 0 ; i < 8 ; i++)
{
PORTB = step_tab[i];
vusdelay(STEPDELAY);
}
PORTB = 0x00; //Do not hold position for now.
}
void step8t_rev(void)
{
unsigned char i;
extern unsigned char step_tab[];
for ( i = 7 ; i > 0 ; i--)
{
PORTB = step_tab[i];
vusdelay(STEPDELAY);
}
PORTB = 0x00; //Do not hold position for now.
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* initall.c - All initialization takes place in this file
* CVW - 07/15/04
*
* PUBLIC void initialize_all(void)
* PUBLIC void Initialize_PortB(void)
* PUBLIC void Initialize_PortD(void)
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "vdefs.h"
PUBLIC void Initialize_PortB(void);
PUBLIC void Initialize_PortD(void);
PUBLIC void initialize_all(void)
{
Initialize_PortB(); // Setup Port B.
Initialize_PortD(); // Setup Port D.
// And on with the show.
// uart_init(9600, 8, 0, 1); // init usart to 9600 8n1.
wdt_disable(); // turn off the watchdog
int0_init(); // Setup external interrupt.
int1_init(); // Setup external interrupt.
t0_init(); // setup timer0
//
// Initalize stepper position so we know where we are.
//
PORTB = step_tab[0];
step_hold = 0;
// uart_fp = fdevopen(uart_putchar, uart_getchar, 0);
// if(uart_fp == NULL)
// for(;;);
// {
//#ifdef DEBUG
// vdot(2); vdash(1); vspace(1); // Morse "U" says uart open failed.
//#endif
// }
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Port B assignments and initializations.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PUBLIC void Initialize_PortB(void)
{
// +++++++++++++ Port B assignments +++++++++++++++++++++++
// =========================================================
// PB0: Stepper
// PB1: Stepper
// PB2: Stepper
// PB3: Stepper
// PB4: ESTOP LED.
// PB5:
// PB6:
// PB7:
// =========================================================
// All outputs and low to begin with.
DDRB = 0xff;
PORTB = 0x00;
// sbi(DDRB, 0); // PB0 -
// cbi(PORTB,0);
// sbi(DDRB, 1); // PB1 -
// cbi(PORTB,1);
// cbi(DDRB, 2); // PB2 -
// sbi(PORTB,2);
// cbi(DDRB, 3); // PB3 -
// sbi(PORTB,3);
sbi(DDRB, 4); // PB4 - ESTOP LED.
cbi(PORTB,4);
// cbi(DDRB, 5); // PB5 -
// sbi(PORTB,5);
// cbi(DDRB, 6); // PB6 -
// sbi(PORTB,6);
// cbi(DDRB, 7); // PB7 -
// sbi(PORTB,7);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Port D assignments and initializations.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PUBLIC void Initialize_PortD(void)
{
// +++++++++++++ Port D assignments +++++++++++++++++++++++
// =========================================================
// PD0: Direction (RXD).
// PD1: Not used (TXD).
// PD2: ESTOP (INT0).
// PD3: Step/clock (INT1).
// PD4: Jog rev switch.
// PD5: Jog fwd switch.
// PD6: ESTOP LED.
// PD7: N/A on the 2313.
// =========================================================
// All outputs and high to begin with.
DDRD = 0xff; //ff;
PORTD = 0xff; //ff;
// sbi(DDRD, 0); // PD0 - Direction
// cbi(PORTD,0);
// sbi(DDRD, 1); // PD1 -
// cbi(PORTD,1);
cbi(DDRD, 2); // PD2 - ESTOP input.
sbi(PORTD,2);
cbi(DDRD, 3); // PD3 - Step / clock.
sbi(PORTD,3);
// cbi(DDRD, 4); // PD4 -
// cbi(PORTD,4);
// cbi(DDRD, 5); // PD5 -
// cbi(PORTD,5);
sbi(DDRD, 6); // PD6 - Heartbeat LED
cbi(PORTD,6);
// cbi(DDRD, 7); // PD7 -
// sbi(PORTD,7);
}
/************************************************************************
* Interrupt routines.
*
************************************************************************/
#include "vdefs.h"
/************************************************************************
* This is where we point things we want to do nothing.
************************************************************************/
PUBLIC void do_nothing(void)
{
}
/************************************************************************
* External Interrupt 0 processing - Process emergency stop input.
************************************************************************/
PUBLIC void (*sig_interrupt0_int)(void) = do_nothing;
PUBLIC void int0_init(void) // Setup INT0
{
MCUCR &= ( (1 << ISC01) | (1 << ISC00) ); // set INT0 active low.
GIMSK |= ( 1 << INT0); // enable INT0
}
SIGNAL(SIG_INTERRUPT0) // Emergency stop.
{
cli(); // stop interrupts and force reset to continue.
PORTB = 0x00; // disengage steppers.
while (1) // just flash the ESTOP LED until reset.
{
sbi(PORTB,4);
vmsdelay(10);
cbi(PORTB,4);
vmsdelay(10);
}
}
/************************************************************************
* External Interrupt 1 - processes controller step/clock input.
************************************************************************/
PUBLIC void (*sig_interrupt1_int)(void) = do_nothing;
PUBLIC void int1_init(void) // Setup INT1
{
MCUCR &= ( (1 << ISC11) | (1 << ISC10) ); // set INT1 active low.
GIMSK |= ( 1 << INT1); // enable INT1
}
SIGNAL(SIG_INTERRUPT1) // process step/clock commands from controller.
{
GIMSK &= ( 1 << INT1); // disable INT1
TIMSK &= (1 << TOIE0); // Disable timer0
if ( bit_is_clear(PIND,0) ) // if dir is low (fwd)
{
if (step_hold == 7)
step_work = 0;
else
step_work++;
PORTB = step_tab[step_work];
step_hold = step_work;
}
else
{
// step8ti_rev();
// vusdelay(STEPDELAY);
if (step_hold == 0)
step_work = 7;
else
step_work--;
PORTB = step_tab[step_work];
step_hold = step_work;
}
TIMSK |= (1 << TOIE0); // Enable timer0
GIMSK |= ( 1 << INT1); // enable INT1
}
/************************************************************************
* Timer 0 overflow interrupt - Drives heartbeat LED.
************************************************************************/
PUBLIC void (*sig_overflow0_int)(void);
PUBLIC void t0_init(void)
{
TCCR0B = 2; // attiny2313: 2=clk/8.
TCNT0 = 6; // (6) preset count.
TIMSK |= (1 << TOIE0); // Enable timer0
}
SIGNAL(SIG_OVERFLOW0) // heartbeat LED.
{
static volatile unsigned char t0_wk1;
t0_wk1++;
if (t0_wk1 > 50 ) // prescale.
{
PORTD ^= 0x40; // toggle bit 6 of PORT D
t0_wk1 = 0;
}
TCNT0 = 6; // (6)restart count. No need to disable it, re-able it,
// this resets it here.
}
/************************************************************************
*
************************************************************************/
PUBLIC void (*sig_input_capture1_int)(void) = do_nothing;
SIGNAL(SIG_INPUT_CAPTURE1)
{
sig_input_capture1_int();
}
/************************************************************************
*
************************************************************************/
PUBLIC void (*sig_overflow1_int)(void) = do_nothing;
SIGNAL(SIG_OVERFLOW1)
{
sig_overflow1_int();
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++ * Kitty Box Controller, Version 41 * Copyright (C) 2005 By Victoria Welch * * defs.h - Primary #defines and global variables. ============================================================*/ #define REVISION "V1.0" // This Version 1.0 #include// #include #include #include "protos.p" #define PRIVATE static #define PUBLIC #define STEPDELAY 900 #define XTAL_FREQ 8000000 #define _NOP() __asm__ __volatile__ ("nop") #define _SLEEP() __asm__ __volatile__ ("sleep") /* * this is pretty much 8 cycles = 1us */ /* defines for backward compatibility */ #define micro_sleep() {_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();} #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif
[Need to get protos stuff in here, archive it all up when cleaned up, since that has a binary in it.]
Home