diff --git a/mendel/Makefile b/mendel/Makefile index 1daeffe..b05a7e3 100644 --- a/mendel/Makefile +++ b/mendel/Makefile @@ -14,7 +14,7 @@ PROGRAM = mendel -SOURCES = $(PROGRAM).c ringbuffer.c serial.c dda.c gcode.c pinout.c +SOURCES = $(PROGRAM).c ringbuffer.c serial.c dda.c gcode.c timer.c ############################################################################## # # @@ -46,10 +46,10 @@ PROGBAUD = 19200 OBJ = $(patsubst %.c,%.o,${SOURCES}) -.PHONY: all program clean +.PHONY: all program clean size .PRECIOUS: %.o %.elf -all: $(PROGRAM).hex $(PROGRAM).lst +all: $(PROGRAM).hex $(PROGRAM).lst size program: $(PROGRAM).hex stty $(PROGBAUD) raw ignbrk hup < $(PROGPORT) @@ -60,6 +60,9 @@ program: $(PROGRAM).hex clean: rm -rf *.o *.elf *.lst *.map *.sym *.lss *.eep *.srec *.bin *.hex *.al *.i *.s *~ +size: $(PROGRAM).hex + @objdump -h mendel.elf | perl -ne '/.(data|text)\s+([0-9a-f]+)/ && do { $$a += eval "0x$$2" }; END { printf "%d bytes (%d%% of %dkb)\n", $$a, $$a * 100 / 16384, 16 }' + %.o: %.c $(CC) -c $(CFLAGS) -Wa,-adhlns=$(<:.c=.al) -o $@ $^ diff --git a/mendel/dda.c b/mendel/dda.c index 53692f3..df9c053 100644 --- a/mendel/dda.c +++ b/mendel/dda.c @@ -2,6 +2,8 @@ #include +#include "timer.h" + extern struct { volatile int32_t X; volatile int32_t Y; @@ -10,10 +12,53 @@ extern struct { volatile int32_t F; } current_position; -// courtesy of http://www.flipcode.com/archives/Fast_Approximate_Distance_Functions.shtml +uint8_t mb_head = 0; +uint8_t mb_tail = 0; +DDA movebuffer[MOVEBUFFER_SIZE]; + +uint8_t queue_full() { + if (mb_tail == 0) + return mb_head == (MOVEBUFFER_SIZE - 1); + else + return mb_head == (mb_tail - 1); +} + +inline uint8_t queue_empty() { + return (mb_tail == mb_head) && !movebuffer[tail].live; +} + +void enqueue(TARGET *t) { + while (queue_full()) + delay(WAITING_DELAY); + + uint8_t h = mb_head; + h++; + if (h == MOVEBUFFER_SIZE) + h = 0; + mb_head = h; + dda_create(t, &movebuffer[h]); +} + +void next_move() { + if ((mb_tail == mb_head) && (!movebuffer[mb_tail].live)) { + // queue is empty + disable_steppers(); + setTimer(DEFAULT_TICK); + } + else { + uint8_t t = mb_tail; + t++; + if (t == MOVEBUFFER_SIZE) + t = 0; + mb_tail = t; + dda_start(&movebuffer[t]); + } +} + +// courtesy of http://www.oroboro.com/rafael/docserv.php/index/programming/article/distance uint32_t approx_distance( int32_t dx, int32_t dy ) { - uint32_t min, max; + uint32_t min, max, approx; if ( dx < 0 ) dx = -dx; if ( dy < 0 ) dy = -dy; @@ -27,32 +72,80 @@ uint32_t approx_distance( int32_t dx, int32_t dy ) max = dx; } - // coefficients equivalent to ( 123/128 * max ) and ( 51/128 * min ) - return ((( max << 8 ) + ( max << 3 ) - ( max << 4 ) - ( max << 1 ) + - ( min << 7 ) - ( min << 5 ) + ( min << 3 ) - ( min << 1 )) >> 8 ); + approx = ( max * 1007 ) + ( min * 441 ); + if ( max < ( min << 4 )) + approx -= ( max * 40 ); + + // add 512 for proper rounding + return (( approx + 512 ) >> 10 ); +} + +// courtesy of http://www.oroboro.com/rafael/docserv.php/index/programming/article/distance +uint32_t approx_distance_3( int32_t dx, int32_t dy, int32_t dz ) +{ + uint32_t min, med, max, approx; + + if ( dx < 0 ) dx = -dx; + if ( dy < 0 ) dy = -dy; + if ( dz < 0 ) dz = -dz; + + if ( dx < dy ) + { + min = dy; + med = dx; + } else { + min = dx; + med = dy; + } + + if ( dz < (int32_t)min ) + { + max = med; + med = min; + min = dz; + } else if ( dz < (int32_t)med ) { + max = med; + med = dz; + } else { + max = dz; + } + + approx = ( max * 860 ) + ( med * 851 ) + ( min * 520 ); + if ( max < ( med << 1 )) approx -= ( max * 294 ); + if ( max < ( min << 2 )) approx -= ( max * 113 ); + if ( med < ( min << 2 )) approx -= ( med * 40 ); + + // add 512 for proper rounding + return (( approx + 512 ) >> 10 ); } /* CREATE */ -void dda_create(GCODE_COMMAND *cmd, DDA *dda) { +void dda_create(TARGET *target, DDA *dda) { static TARGET startpoint = { 0, 0, 0, 0, 0 }; // we start at the previous endpoint memcpy(&dda->currentpoint, &startpoint, sizeof(TARGET)); // we end at the passed command's endpoint - memcpy(&dda->endpoint, &cmd->target, sizeof(TARGET)); + memcpy(&dda->endpoint, target, sizeof(TARGET)); dda->x_delta = dda->endpoint.X - startpoint.X; dda->y_delta = dda->endpoint.Y - startpoint.Y; + dda->z_delta = dda->endpoint.Z - startpoint.Z; // always relative dda->e_delta = dda->endpoint.E; // always absolute dda->f_delta = dda->endpoint.F - startpoint.F; - - dda->distance = approx_distance(dda->x_delta, dda->y_delta); + // since it's unusual to combine X, Y and Z changes in a single move on reprap, check if we can use simpler approximations before trying the full 3d approximation. + if (dda->z_delta == 0) + dda->distance = approx_distance(dda->x_delta, dda->y_delta); + else if (dda->x_delta == 0 && dda->y_delta == 0) + dda->distance = dda->z_delta; + else + dda->distance = approx_distance_3(dda->x_delta, dda->y_delta, dda->z_delta); if (dda->distance < 2) dda->distance = dda->e_delta; @@ -62,6 +155,9 @@ void dda_create(GCODE_COMMAND *cmd, DDA *dda) { dda->total_steps = dda->x_delta; if (dda->y_delta > dda->total_steps) dda->total_steps = dda->y_delta; + if (dda->z_delta > dda->total_steps) + dda->total_steps = dda->z_delta; + if (dda->e_delta > dda->total_steps) dda->total_steps = dda->e_delta; if (dda->f_delta > dda->total_steps) @@ -79,7 +175,6 @@ void dda_create(GCODE_COMMAND *cmd, DDA *dda) { // dda->endpoint.F = distance / total_steps; if (dda->f_delta > dda->total_steps) { - // TODO: rescale F dda->f_scale = dda->f_delta / dda->total_steps; if (dda->f_scale > 3) { dda->f_delta /= dda->f_scale; @@ -92,14 +187,22 @@ void dda_create(GCODE_COMMAND *cmd, DDA *dda) { dda->x_direction = (dda->endpoint.X > startpoint.X)?1:0; dda->y_direction = (dda->endpoint.Y > startpoint.Y)?1:0; + dda->z_direction = (dda->endpoint.Z > startpoint.Z)?1:0; dda->e_direction = (dda->endpoint.E > startpoint.E)?1:0; dda->f_direction = (dda->endpoint.F > startpoint.F)?1:0; - dda->x_counter = dda->y_counter = dda->e_counter = dda->f_counter + dda->x_counter = dda->y_counter = dda->z_counter = dda->e_counter = dda->f_counter = -(dda->total_steps >> 1); + // pre-calculate move speed in millimeter microseconds per step minute for less math in interrupt context + // mm (distance) * 60000000 us/min / step (total_steps) = mm.us per step.min + // mm.us per step.min / mm/min (F) = us per step + dda->move_duration = dda->distance * 60000000 / dda->total_steps; + // next dda starts where we finish memcpy(&startpoint, &dda->endpoint, sizeof(TARGET)); + + dda->live = 0; } /* @@ -107,10 +210,17 @@ void dda_create(GCODE_COMMAND *cmd, DDA *dda) { */ void dda_start(DDA *dda) { + // called from interrupt context: keep it simple! + if (dda->nullmove) + return; + x_direction(dda->x_direction); y_direction(dda->y_direction); z_direction(dda->z_direction); e_direction(dda->e_direction); + + enable_steppers(); + dda->live = 1; } /* @@ -147,16 +257,17 @@ void dda_step(DDA *dda) { step_option |= can_step(x_min(), x_max(), current_position.X, dda->endpoint.X, dda->x_direction) & X_CAN_STEP; step_option |= can_step(y_min(), y_max(), current_position.Y, dda->endpoint.Y, dda->y_direction) & Y_CAN_STEP; step_option |= can_step(z_min(), z_max(), current_position.Z, dda->endpoint.Z, dda->z_direction) & Z_CAN_STEP; - step_option |= can_step(-1, -1, current_position.E, dda->endpoint.E, dda->e_direction) & E_CAN_STEP; - step_option |= can_step(-1, -1, current_position.F, dda->endpoint.F, dda->f_direction) & F_CAN_STEP; + step_option |= can_step(-1 , -1 , current_position.E, dda->endpoint.E, dda->e_direction) & E_CAN_STEP; + step_option |= can_step(-1 , -1 , current_position.F, dda->endpoint.F, dda->f_direction) & F_CAN_STEP; if (step_option & X_CAN_STEP) { - dda->x_counter += dda->x_delta; - if (dda->x_counter > 0) { + dda->x_counter -= dda->x_delta; + if (dda->x_counter < 0) { step_option |= REAL_MOVE; - // do X step - dda->x_counter -= dda->total_steps; + x_step(); + + dda->x_counter += dda->total_steps; if (dda->x_direction) current_position.X++; @@ -166,12 +277,13 @@ void dda_step(DDA *dda) { } if (step_option & Y_CAN_STEP) { - dda->y_counter += dda->y_delta; - if (dda->y_counter > 0) { + dda->y_counter -= dda->y_delta; + if (dda->y_counter < 0) { step_option |= REAL_MOVE; - // do Y step - dda->y_counter -= dda->total_steps; + y_step(); + + dda->y_counter += dda->total_steps; if (dda->y_direction) current_position.Y++; @@ -181,12 +293,13 @@ void dda_step(DDA *dda) { } if (step_option & Z_CAN_STEP) { - dda->z_counter += dda->z_delta; - if (dda->z_counter > 0) { + dda->z_counter -= dda->z_delta; + if (dda->z_counter < 0) { step_option |= REAL_MOVE; - // do Z step - dda->z_counter -= dda->total_steps; + z_step(); + + dda->z_counter += dda->total_steps; if (dda->z_direction) current_position.Z++; @@ -196,12 +309,13 @@ void dda_step(DDA *dda) { } if (step_option & E_CAN_STEP) { - dda->e_counter += dda->e_delta; - if (dda->e_counter > 0) { + dda->e_counter -= dda->e_delta; + if (dda->e_counter < 0) { step_option |= REAL_MOVE; - // do E step - dda->e_counter -= dda->total_steps; + e_step(); + + dda->e_counter += dda->total_steps; if (dda->e_direction) current_position.E++; @@ -211,10 +325,10 @@ void dda_step(DDA *dda) { } if (step_option & F_CAN_STEP) { - dda->f_counter += dda->f_delta; - if (dda->f_counter > 0) { - // do F step - dda->f_counter -= dda->total_steps; + dda->f_counter -= dda->f_delta; + if (dda->f_counter < 0) { + + dda->f_counter += dda->total_steps; if (dda->f_direction) current_position.F += dda->f_scale; @@ -222,9 +336,14 @@ void dda_step(DDA *dda) { current_position.F -= dda->f_scale; } } - } while (((step_option & REAL_MOVE) == 0) && (step_option & F_CAN_STEP)); + } while ( ((step_option & REAL_MOVE ) == 0) && + ((step_option & F_CAN_STEP) != 0) ); + + unstep(); if (step_option & REAL_MOVE) { - setTimer(dda->distance * (60000000 / current_position.F / dda->total_steps)); + setTimer(dda->move_duration / current_position.F); } + + dda->live = (step_option & (X_CAN_STEP | Y_CAN_STEP | Z_CAN_STEP | E_CAN_STEP | F_CAN_STEP)); } diff --git a/mendel/dda.h b/mendel/dda.h index b8eed7c..f1a6d8e 100644 --- a/mendel/dda.h +++ b/mendel/dda.h @@ -6,25 +6,25 @@ #include "target.h" #include "pinout.h" #include "gcode.h" +#include "machine.h" typedef struct { TARGET currentpoint; TARGET endpoint; - uint8_t steep :1; - uint8_t swap :1; uint8_t x_direction :1; uint8_t y_direction :1; uint8_t z_direction :1; uint8_t e_direction :1; uint8_t f_direction :1; uint8_t nullmove :1; + uint8_t live :1; - int8_t x_delta; - int8_t y_delta; - int8_t z_delta; - int8_t e_delta; - int8_t f_delta; + int16_t x_delta; + int16_t y_delta; + int16_t z_delta; + int16_t e_delta; + int16_t f_delta; int32_t x_counter; int32_t y_counter; @@ -36,8 +36,13 @@ typedef struct { uint16_t f_scale; uint32_t distance; + uint32_t move_duration; } DDA; +extern uint8_t mb_head; +extern uint8_t mb_tail; +extern DDA movebuffer[MOVEBUFFER_SIZE]; + uint32_t approx_distance( int32_t dx, int32_t dy ); void dda_create(GCODE_COMMAND *cmd, DDA *dda); diff --git a/mendel/gcode.c b/mendel/gcode.c index 2fe091a..b46b868 100644 --- a/mendel/gcode.c +++ b/mendel/gcode.c @@ -5,9 +5,6 @@ #include "machine.h" #include "dda.h" -extern uint8_t mb_head; -extern uint8_t mb_tail; -extern DDA movebuffer[16]; extern uint8_t option_bitfield; #define PI 3.1415926535 @@ -135,7 +132,7 @@ void scan_char(uint8_t c) { else if ((c == '.') && ((exp & 0x7F) == 0)) exp |= 1; else if (c >= '0' && c <= '9') { - mantissa = (mantissa * 10) + (c - '0'); + mantissa = ((mantissa << 3) + (mantissa << 1)) + (c - '0'); if (exp & 0x7F) exp++; } @@ -189,6 +186,6 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { } if (do_move) { - dda_create(gcmd, movebuffer); + dda_create(&gcmd->target, movebuffer); } } diff --git a/mendel/machine.h b/mendel/machine.h index b70514f..c4804d9 100644 --- a/mendel/machine.h +++ b/mendel/machine.h @@ -4,9 +4,7 @@ /* machine variables */ -#define AXIS_COUNT 5 -#define AXIS_HOMING_COUNT 3 - +#define MOVEBUFFER_SIZE 8 /* axis calculations, adjust as necessary */ diff --git a/mendel/mendel.c b/mendel/mendel.c index 130b427..be45727 100644 --- a/mendel/mendel.c +++ b/mendel/mendel.c @@ -9,13 +9,10 @@ #include "dda.h" #include "gcode.h" +#include "timer.h" #include "machine.h" -uint8_t mb_head = 0; -uint8_t mb_tail = 0; -DDA movebuffer[16]; - uint8_t option_bitfield; struct { @@ -31,6 +28,9 @@ int main (void) // set up serial serial_init(); + // set up timers + setupTimerInterrupt(); + // enable interrupts sei(); diff --git a/mendel/pinout.c b/mendel/pinout.c deleted file mode 100644 index 8515b10..0000000 --- a/mendel/pinout.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "pinout.h" - -void delayMicrosecondsInterruptible(uint16_t us) -{ - // for a one-microsecond delay, simply return. the overhead - // of the function call yields a delay of approximately 1 1/8 us. - if (--us == 0) - return; - - // the following loop takes a quarter of a microsecond (4 cycles) - // per iteration, so execute it four times for each microsecond of - // delay requested. - us <<= 2; - - // account for the time taken in the preceeding commands. - us -= 2; - - // busy wait - __asm__ __volatile__ ("1: sbiw %0,1" "\n\t" // 2 cycles -"brne 1b" : - "=w" (us) : - "0" (us) // 2 cycles - ); -} - -void x_step() { - WRITE(DIO0, 1); - delayMicrosecondsInterruptible(5); - WRITE(DIO0, 0); -} - -void y_step() { - WRITE(DIO4, 1); - delayMicrosecondsInterruptible(5); - WRITE(DIO4, 0); -} - -void z_step() { - WRITE(DIO8, 1); - delayMicrosecondsInterruptible(5); - WRITE(DIO8, 0); -} - -void e_step() { - WRITE(DIO12, 1); - delayMicrosecondsInterruptible(5); - WRITE(DIO12, 0); -} diff --git a/mendel/pinout.h b/mendel/pinout.h index d5f8811..a2226de 100644 --- a/mendel/pinout.h +++ b/mendel/pinout.h @@ -7,74 +7,59 @@ #define MASK(PIN) (1 << PIN) #endif -#define _PIN(P) #P - #define READ(IO) (RPORT_ ## IO & MASK(PIN_ ## IO)) #define WRITE(IO, v) if (v) { WPORT_ ## IO |= MASK(PIN_ ## IO); } else { WPORT_ ## IO &= ~MASK(PIN_ ## IO); } + #define SET_INPUT(IO) (DDR_ ## IO |= MASK(PIN_ ## IO)) #define SET_OUTPUT(IO) (DDR_ ## IO &= ~MASK(PIN ## IO)) -// #define X_STEP_PIN DIO0 -// #define X_DIR_PIN DIO1 -// #define X_MIN_MIN DIO2 -// #define X_MAX_PIN DIO3 +// void x_step(void); +// void y_step(void); +// void z_step(void); +// void e_step(void); -// #define Y_STEP_PIN DIO4 -// #define Y_DIR_PIN DIO5 -// #define Y_MIN_MIN DIO6 -// #define Y_MAX_PIN DIO7 +#define _x_step(st) WRITE(AIO0, st) +#define x_step() _x_step(1); +#define x_direction(dir) WRITE(AIO1, dir) +#define x_min() READ(AIO2) +#ifdef MAX_ENDSTOPS +#define x_max() READ(AIO3) +#else +#define x_max() (0) +#endif -// #define Z_STEP_PIN DIO8 -// #define Z_DIR_PIN DIO9 -// #define Z_MIN_MIN DIO10 -// #define Z_MAX_PIN DIO11 +#define _y_step(st) WRITE(DIO2, st) +#define y_step() _y_step(1); +#define y_direction(dir) WRITE(DIO3, dir) +#define y_min() READ(DIO4) +#ifdef MAX_ENDSTOPS +#define y_max() READ(DIO5) +#else +#define y_max() (0) +#endif -// #define E_STEP_PIN DIO12 -// #define E_DIR_PIN DIO13 +#define _z_step(st) WRITE(DIO6, st) +#define z_step() _z_step(1); +#define z_direction(dir) WRITE(DIO7, dir) +#define z_min() READ(DIO8) +#ifdef MAX_ENDSTOPS +#define z_max() READ(DIO9) +#else +#define z_max() (0) +#endif -void x_step(void); -void y_step(void); -void z_step(void); -void e_step(void); +#define _e_step(st) WRITE(AIO4, st) +#define e_step() _e_step(1); +#define e_direction(dir) WRITE(AIO5, dir) -inline void x_direction(uint8_t dir) { - WRITE(DIO1, dir); -} +#define enable_steppers() WRITE(DIO10, 1) +#define disable_steppers() WRITE(DIO10, 0) -inline uint8_t x_min(void) { - return READ(DIO2); -} - -inline uint8_t x_max(void) { - return READ(DIO3); -} - -inline void y_direction(uint8_t dir) { - WRITE(DIO5, dir); -} - -inline uint8_t y_min(void) { - return READ(DIO6); -} - -inline uint8_t y_max(void) { - return READ(DIO7); -} - -inline void z_direction(uint8_t dir) { - WRITE(DIO9, dir); -} - -inline uint8_t z_min(void) { - return READ(DIO10); -} - -inline uint8_t z_max(void) { - return READ(DIO11); -} - -inline void e_direction(uint8_t dir) { - WRITE(DIO13, dir); +inline void unstep(void) { + _x_step(0); + _y_step(0); + _z_step(0); + _e_step(0); } #endif /* _PINOUT_H */ diff --git a/mendel/timer.c b/mendel/timer.c new file mode 100644 index 0000000..ea43835 --- /dev/null +++ b/mendel/timer.c @@ -0,0 +1,158 @@ +#include "timer.h" + +#include + +#include "pinout.h" +#include "dda.h" + +ISR(TIMER1_COMPA_vect) { +// static interruptBlink = 0; +// +// interruptBlink++; +// if (interruptBlink == 0x80) { +// blink(); +// interruptBlink = 0; +// } + + if(movebuffer[mb_tail].live) + dda_step(&movebuffer[mb_tail]); + else + next_move(); +} + +void setupTimerInterrupt() +{ + //clear the registers + TCCR1A = 0; + TCCR1B = 0; + TCCR1C = 0; + TIMSK1 = 0; + + //waveform generation = 0100 = CTC + TCCR1B &= ~(1<> 3) & 0xffff); + // our slowest speed at our medium resolution ( (2^16-1) * 4 usecs = 262140 usecs) + else if (delay <= 4194240L) + return ((delay >> 6) & 0xffff); + // our slowest speed at our medium-low resolution ( (2^16-1) * 16 usecs = 1048560 usecs) + else if (delay <= 16776960L) + return ((delay >> 8) & 0xffff); + // our slowest speed at our lowest resolution ((2^16-1) * 64 usecs = 4194240 usecs) + else if (delay <= 67107840L) + return ((delay >> 10) & 0xffff); + //its really slow... hopefully we can just get by with super slow. + else + return 65535; +} + + +// Depending on how much work the interrupt function has to do, this is +// pretty accurate between 10 us and 0.1 s. At fast speeds, the time +// taken in the interrupt function becomes significant, of course. + +// Note - it is up to the user to call enableTimerInterrupt() after a call +// to this function. + +void setTimer(uint32_t delay) +{ + // delay is the delay between steps in microsecond ticks. + // + // we break it into 5 different resolutions based on the delay. + // then we set the resolution based on the size of the delay. + // we also then calculate the timer ceiling required. (ie what the counter counts to) + // the result is the timer counts up to the appropriate time and then fires an interrupt. + + // Actual ticks are 0.0625 us, so multiply delay by 16 + + // convert to ticks + delay = delay US; + + setTimerCeiling(getTimerCeiling(delay)); + setTimerResolution(getTimerResolution(delay)); +} + +// from reprap project 5D firmware +void delayMicrosecondsInterruptible(uint16_t us) +{ + // for a one-microsecond delay, simply return. the overhead + // of the function call yields a delay of approximately 1 1/8 us. + if (--us == 0) + return; + + // the following loop takes a quarter of a microsecond (4 cycles) + // per iteration, so execute it four times for each microsecond of + // delay requested. + us <<= 2; + + // account for the time taken in the preceeding commands. + us -= 2; + + // busy wait + __asm__ __volatile__ ("1: sbiw %0,1" "\n\t" // 2 cycles +"brne 1b" : + "=w" (us) : + "0" (us) // 2 cycles + ); +} diff --git a/mendel/timer.h b/mendel/timer.h new file mode 100644 index 0000000..ee73127 --- /dev/null +++ b/mendel/timer.h @@ -0,0 +1,37 @@ +#ifndef _TIMER_H +#define _TIMER_H + +#include +#include + +// time-related constants +#define US * (F_CPU / 1000000) +#define MS * (F_CPU / 1000) + +#define DEFAULT_TICK (100 US) + +void setupTimerInterrupt(void); +uint8_t getTimerResolution(const uint32_t delay); +void setTimerResolution(uint8_t r); +uint16_t getTimerCeiling(const uint32_t delay); + +void setTimer(uint32_t delay); + +void delayMicrosecondsInterruptible(unsigned int us); + +inline void enableTimerInterrupt(void) +{ + TIMSK1 |= (1<