From 0572687cb262e9b01af2bb72d664fd42a4d255b7 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Wed, 8 Sep 2010 15:53:37 +0200 Subject: [PATCH 1/9] dda_create(): clear _all_ flags before proceeding. --- dda.c | 3 +-- dda.h | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/dda.c b/dda.c index 9fdf6d3..80e5816 100644 --- a/dda.c +++ b/dda.c @@ -119,8 +119,7 @@ void dda_create(DDA *dda, TARGET *target) { uint32_t distance; // initialise DDA to a known state - dda->live = 0; - dda->waitfor_temp = 0; + dda->allflags = 0; if (debug_flags & DEBUG_DDA) serial_writestr_P(PSTR("\n{DDA_CREATE: [")); diff --git a/dda.h b/dda.h index d775b70..2248f10 100644 --- a/dda.h +++ b/dda.h @@ -24,19 +24,24 @@ typedef struct { // this is where we should finish TARGET endpoint; - // status fields - uint8_t nullmove :1; - uint8_t live :1; - uint8_t accel :1; + union { + struct { + // status fields + uint8_t nullmove :1; + uint8_t live :1; + uint8_t accel :1; - // wait for temperature to stabilise flag - uint8_t waitfor_temp :1; + // wait for temperature to stabilise flag + uint8_t waitfor_temp :1; - // directions - uint8_t x_direction :1; - uint8_t y_direction :1; - uint8_t z_direction :1; - uint8_t e_direction :1; + // directions + uint8_t x_direction :1; + uint8_t y_direction :1; + uint8_t z_direction :1; + uint8_t e_direction :1; + }; + uint8_t allflags; // used for clearing all flags + }; // distances uint32_t x_delta; From 54f9598ef16c933fcfd0cddfa46fa3b7fa968113 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Wed, 8 Sep 2010 16:20:58 +0200 Subject: [PATCH 2/9] Turn off the DEBUG flag by default for making the default build fit onto a '168. There's a commented line for turning it on easily. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f9b9e88..47ceb0c 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ OBJCOPY = $(ARCH)objcopy DEFS = -DF_CPU=$(F_CPU) # DEFS += "-DDEBUG=1" -OPTIMIZE = -Os -ffunction-sections -finline-functions-called-once -DDEBUG +OPTIMIZE = -Os -ffunction-sections -finline-functions-called-once # OPTIMIZE = -O0 CFLAGS = -g -Wall -Wstrict-prototypes $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -save-temps LDFLAGS = -Wl,--as-needed -Wl,--gc-sections From 446b2223cc4faf4690e464aa32cc8a4d9215e7f9 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Wed, 8 Sep 2010 16:27:02 +0200 Subject: [PATCH 3/9] Makefile: add a variable for avrdude.conf, as the location of this file depends on the environment. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 47ceb0c..313d1b2 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,7 @@ CFLAGS = -g -Wall -Wstrict-prototypes $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) -s LDFLAGS = -Wl,--as-needed -Wl,--gc-sections AVRDUDE = avrdude +AVRDUDECONF = /etc/avrdude.conf ############################################################################## # # @@ -68,7 +69,7 @@ program: $(PROGRAM).hex stty $(PROGBAUD) raw ignbrk hup < $(PROGPORT) @sleep 0.1 @stty $(PROGBAUD) raw ignbrk hup < $(PROGPORT) - $(AVRDUDE) -cstk500v1 -b$(PROGBAUD) -p$(MCU_TARGET) -P$(PROGPORT) -C/etc/avrdude.conf -U flash:w:$^ + $(AVRDUDE) -cstk500v1 -b$(PROGBAUD) -p$(MCU_TARGET) -P$(PROGPORT) -C$(AVRDUDECONF) -U flash:w:$^ stty 115200 raw ignbrk -hup -echo ixon < $(PROGPORT) clean: From 7f57634e8c99dae040cf0cf68e6dc1b8d28377d1 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Sun, 5 Sep 2010 17:20:55 +0200 Subject: [PATCH 4/9] Allow comments in parentheses. Some GCode generators and machine controller implementations prefer this. --- gcode.c | 15 ++++++++++----- gcode.h | 5 +++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/gcode.c b/gcode.c index 73ebe1f..ac03a53 100644 --- a/gcode.c +++ b/gcode.c @@ -210,7 +210,7 @@ void scan_char(uint8_t c) { } // skip comments - if (next_target.seen_comment == 0) { + if (next_target.seen_semi_comment == 0 && next_target.seen_parens_comment == 0) { // new field? if ((c >= 'A' && c <= 'Z') || c == '*') { last_field = c; @@ -262,9 +262,12 @@ void scan_char(uint8_t c) { // comments case ';': - next_target.seen_comment = 1; + next_target.seen_semi_comment = 1; // option_bitfield |= OPTION_COMMENT; break; + case '(': + next_target.seen_parens_comment = 1; + break; // now for some numeracy case '-': @@ -288,7 +291,9 @@ void scan_char(uint8_t c) { } // everything else is ignored } - } + } else if ( next_target.seen_parens_comment == 1 && c == ')') + next_target.seen_parens_comment = 0; // recognize stuff after a (comment) + #ifndef ASTERISK_IN_CHECKSUM_INCLUDED if (next_target.seen_checksum == 0) @@ -348,8 +353,8 @@ void scan_char(uint8_t c) { next_target.seen_E = next_target.seen_F = next_target.seen_G = \ next_target.seen_S = next_target.seen_P = next_target.seen_N = \ next_target.seen_M = next_target.seen_checksum = \ - next_target.seen_comment = next_target.checksum_read = \ - next_target.checksum_calculated = 0; + next_target.seen_semi_comment = next_target.seen_parens_comment = \ + next_target.checksum_read = next_target.checksum_calculated = 0; last_field = 0; read_digit.sign = read_digit.mantissa = read_digit.exponent = 0; } diff --git a/gcode.h b/gcode.h index 48db7ac..d825e1d 100644 --- a/gcode.h +++ b/gcode.h @@ -36,8 +36,9 @@ typedef struct { uint8_t seen_P :1; uint8_t seen_N :1; - uint8_t seen_checksum :1; - uint8_t seen_comment :1; + uint8_t seen_checksum :1; + uint8_t seen_semi_comment :1; + uint8_t seen_parens_comment :1; uint8_t option_relative :1; uint8_t option_inches :1; From 548b79f3d691f9685a43755cd12fb1aada535836 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Tue, 7 Sep 2010 23:04:13 +0200 Subject: [PATCH 5/9] dda.c: replaced can_step() with a more simple solution. This saves a whopping 270 bytes in interrupt context. --- dda.c | 60 +++++++++++++---------------------------------------------- 1 file changed, 13 insertions(+), 47 deletions(-) diff --git a/dda.c b/dda.c index 80e5816..0733c3e 100644 --- a/dda.c +++ b/dda.c @@ -314,56 +314,20 @@ void dda_start(DDA *dda) { setTimer(dda->c >> 8); } -/* - CAN STEP -*/ - -uint8_t can_step(uint8_t min, uint8_t max, int32_t current, int32_t target, uint8_t dir) { - if (dir) { - // forwards/positive - if (max) - return 0; - if (current >= target) - return 0; - } - else { - // backwards/negative - if (min) - return 0; - if (target >= current) - return 0; - } - - return 255; -} - /* STEP */ void dda_step(DDA *dda) { // called from interrupt context! keep it as simple as possible - uint8_t step_option = 0; -#define X_CAN_STEP 1 -#define Y_CAN_STEP 2 -#define Z_CAN_STEP 4 -#define E_CAN_STEP 8 -#define DID_STEP 128 + uint8_t did_step = 0; -// step_option |= can_step(x_min(), x_max(), current_position.X, dda->endpoint.X, dda->x_direction) & X_CAN_STEP; - step_option |= can_step(0 , 0 , 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(0 , 0 , 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(0 , 0 , current_position.Z, dda->endpoint.Z, dda->z_direction) & Z_CAN_STEP; - step_option |= can_step(0 , 0 , current_position.E, dda->endpoint.E, dda->e_direction) & E_CAN_STEP; -// step_option |= can_step(0 , 0 , current_position.F, dda->endpoint.F, dda->f_direction) & F_CAN_STEP; - - if (step_option & X_CAN_STEP) { + if (current_position.X != dda->endpoint.X /* && + x_max() != dda->x_direction && x_min() == dda->x_direction */) { dda->x_counter -= dda->x_delta; if (dda->x_counter < 0) { x_step(); - step_option |= DID_STEP; + did_step = 1; if (dda->x_direction) current_position.X++; else @@ -373,11 +337,12 @@ void dda_step(DDA *dda) { } } - if (step_option & Y_CAN_STEP) { + if (current_position.Y != dda->endpoint.Y /* && + y_max() != dda->y_direction && y_min() == dda->y_direction */) { dda->y_counter -= dda->y_delta; if (dda->y_counter < 0) { y_step(); - step_option |= DID_STEP; + did_step = 1; if (dda->y_direction) current_position.Y++; else @@ -387,11 +352,12 @@ void dda_step(DDA *dda) { } } - if (step_option & Z_CAN_STEP) { + if (current_position.Z != dda->endpoint.Z /* && + z_max() != dda->z_direction && z_min() == dda->z_direction */) { dda->z_counter -= dda->z_delta; if (dda->z_counter < 0) { z_step(); - step_option |= DID_STEP; + did_step = 1; if (dda->z_direction) current_position.Z++; else @@ -401,11 +367,11 @@ void dda_step(DDA *dda) { } } - if (step_option & E_CAN_STEP) { + if (current_position.E != dda->endpoint.E) { dda->e_counter -= dda->e_delta; if (dda->e_counter < 0) { e_step(); - step_option |= DID_STEP; + did_step = 1; if (dda->e_direction) current_position.E++; else @@ -440,7 +406,7 @@ void dda_step(DDA *dda) { // else we are already at target speed } - if (step_option) { + if (did_step) { // we stepped, reset timeout steptimeout = 0; From b2e1cfd8b52b6615900eadb55880a3e8789db9ad Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Mon, 6 Sep 2010 17:26:43 +0200 Subject: [PATCH 6/9] Make XON/XOFF flow control compile. --- dda_queue.c | 17 +++++++++-------- gcode.c | 4 ++-- machine.h | 4 +++- serial.c | 47 +++++++++++++++++++++++++---------------------- serial.h | 8 +++++--- 5 files changed, 44 insertions(+), 36 deletions(-) diff --git a/dda_queue.c b/dda_queue.c index 84a68e2..e5a0cc5 100644 --- a/dda_queue.c +++ b/dda_queue.c @@ -3,6 +3,7 @@ #include #include +#include "machine.h" // for XONXOFF #include "timer.h" #include "serial.h" #include "sermsg.h" @@ -70,9 +71,9 @@ void enqueue(TARGET *t) { mb_head = h; #ifdef XONXOFF - // if queue is full, stop transmition - if (queue_full()) - xoff(); + // if queue is full, stop transmission + if (queue_full()) + xoff(); #endif // fire up in case we're not running yet @@ -104,9 +105,9 @@ void enqueue_temp_wait() { mb_head = h; #ifdef XONXOFF - // if queue is full, stop transmition - if (queue_full()) - xoff(); + // if queue is full, stop transmission + if (queue_full()) + xoff(); #endif // fire up in case we're not running yet @@ -126,8 +127,8 @@ void next_move() { } #ifdef XONXOFF - // restart transmission - xon(); + // restart transmission + xon(); #endif } diff --git a/gcode.c b/gcode.c index ac03a53..a4770a3 100644 --- a/gcode.c +++ b/gcode.c @@ -414,7 +414,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { // G4 - Dwell case 4: #ifdef XONXOFF - xoff(); + xoff(); #endif // wait for all moves to complete for (;queue_empty() == 0;) @@ -422,7 +422,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { // delay delay_ms(gcmd->P); #ifdef XONXOFF - xon(); + xon(); #endif break; diff --git a/machine.h b/machine.h index 13ff441..033ec7c 100644 --- a/machine.h +++ b/machine.h @@ -60,7 +60,9 @@ // this should help immensely with dropped serial characters, but may also make debugging infuriating due to the complexities arising from nested interrupts #define STEP_INTERRUPT_INTERRUPTIBLE 1 -// Xon/Xoff flow control. Should be redundant +// Xon/Xoff flow control. Redundant when using RepRap Host for sending GCode, +// but mandatory when sending GCode files with a plain terminal emulator, +// like GtkTerm (Linux), CoolTerm (Mac) or HyperTerminal (Windows). // #define XONXOFF /* diff --git a/serial.c b/serial.c index 63d66e3..d094ee1 100644 --- a/serial.c +++ b/serial.c @@ -45,11 +45,14 @@ volatile uint8_t txbuf[BUFSIZE]; data = buf[tail++]; tail &= (BUFSIZE - 1); */ -volatile uint8_t flowflags = 0; -#define FLOWFLAG_SEND_XOFF 1 -#define FLOWFLAG_SEND_XON 2 -#define FLOWFLAG_SENT_XOFF 4 -#define FLOWFLAG_SENT_XON 8 +#ifdef XONXOFF +#define FLOWFLAG_SEND_XON 1 +#define FLOWFLAG_SEND_XOFF 2 +#define FLOWFLAG_SENT_XON 4 +#define FLOWFLAG_SENT_XOFF 8 +// initially, send an XON +volatile uint8_t flowflags = FLOWFLAG_SEND_XON; +#endif void serial_init() { @@ -82,15 +85,15 @@ ISR(USART_RX_vect) ISR(USART_UDRE_vect) { - #if XONXOFF - if (flowflags & FLOWFLAG_SEND_XOFF) { - UDR0 = ASCII_XOFF; - flowflags = (flowflags & ~FLOWFLAG_SEND_XOFF) | FLOWFLAG_SENT_XOFF; - } - else if (flowflags & FLOWFLAG_SEND_XON) { + #ifdef XONXOFF + if (flowflags & FLOWFLAG_SEND_XON) { UDR0 = ASCII_XON; flowflags = (flowflags & ~FLOWFLAG_SEND_XON) | FLOWFLAG_SENT_XON; } + else if (flowflags & FLOWFLAG_SEND_XOFF) { + UDR0 = ASCII_XOFF; + flowflags = (flowflags & ~FLOWFLAG_SEND_XOFF) | FLOWFLAG_SENT_XOFF; + } else #endif if (buf_canread(tx)) @@ -189,16 +192,16 @@ void serial_writestr_P(PGM_P data) } #ifdef XONXOFF - void xon() { - if (flowflags & FLOWFLAG_SENT_XOFF) - flowflags = FLOWFLAG_SEND_XON; - // enable TX interrupt so we can send this character - UCSR0B |= MASK(UDRIE0); - } +void xon() { + if (flowflags & FLOWFLAG_SENT_XOFF) + flowflags = FLOWFLAG_SEND_XON; + // enable TX interrupt so we can send this character + UCSR0B |= MASK(UDRIE0); +} - void xoff() { - flowflags = FLOWFLAG_SEND_XOFF; - // enable TX interrupt so we can send this character - UCSR0B |= MASK(UDRIE0); - } +void xoff() { + flowflags = FLOWFLAG_SEND_XOFF; + // enable TX interrupt so we can send this character + UCSR0B |= MASK(UDRIE0); +} #endif diff --git a/serial.h b/serial.h index 73a7b18..6deccf3 100644 --- a/serial.h +++ b/serial.h @@ -5,6 +5,8 @@ #include #include +#include "machine.h" // for XONXOFF + // initialise serial subsystem void serial_init(void); @@ -28,9 +30,9 @@ void serial_writeblock_P(PGM_P data, int datalen); void serial_writestr_P(PGM_P data); #ifdef XONXOFF - // XON/XOFF flow control - void xoff(void); - void xon(void); +// XON/XOFF flow control +void xon(void); +void xoff(void); #endif #endif /* _SERIAL_H */ From 8b5e21be51159ef466ff4a54f4096b0d69cf62b7 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Wed, 8 Sep 2010 19:10:54 +0200 Subject: [PATCH 7/9] Make XON/XOFF flow control actually work. Still disabled by default. --- dda_queue.c | 12 ++++++++---- serial.c | 24 +++++++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/dda_queue.c b/dda_queue.c index e5a0cc5..b6a77e7 100644 --- a/dda_queue.c +++ b/dda_queue.c @@ -21,6 +21,10 @@ uint8_t queue_empty() { return ((mb_tail == mb_head) && (movebuffer[mb_tail].live == 0))?255:0; } +// ------------------------------------------------------- +// This is the one function called by the timer interrupt. +// It calls a few other functions, though. +// ------------------------------------------------------- void queue_step() { disableTimerInterrupt(); @@ -71,8 +75,9 @@ void enqueue(TARGET *t) { mb_head = h; #ifdef XONXOFF - // if queue is full, stop transmission - if (queue_full()) + // If the queue has only two slots remaining, stop transmission. More + // characters might come in until the stop takes effect. + if (((mb_tail - mb_head - 1) & (MOVEBUFFER_SIZE - 1)) < (MOVEBUFFER_SIZE - 2)) xoff(); #endif @@ -105,8 +110,7 @@ void enqueue_temp_wait() { mb_head = h; #ifdef XONXOFF - // if queue is full, stop transmission - if (queue_full()) + if (((mb_tail - mb_head - 1) & (MOVEBUFFER_SIZE - 1)) < (MOVEBUFFER_SIZE - 2)) xoff(); #endif diff --git a/serial.c b/serial.c index d094ee1..4141dfb 100644 --- a/serial.c +++ b/serial.c @@ -48,8 +48,7 @@ volatile uint8_t txbuf[BUFSIZE]; #ifdef XONXOFF #define FLOWFLAG_SEND_XON 1 #define FLOWFLAG_SEND_XOFF 2 -#define FLOWFLAG_SENT_XON 4 -#define FLOWFLAG_SENT_XOFF 8 +#define FLOWFLAG_STATE_XON 4 // initially, send an XON volatile uint8_t flowflags = FLOWFLAG_SEND_XON; #endif @@ -88,11 +87,11 @@ ISR(USART_UDRE_vect) #ifdef XONXOFF if (flowflags & FLOWFLAG_SEND_XON) { UDR0 = ASCII_XON; - flowflags = (flowflags & ~FLOWFLAG_SEND_XON) | FLOWFLAG_SENT_XON; + flowflags = FLOWFLAG_STATE_XON; } else if (flowflags & FLOWFLAG_SEND_XOFF) { UDR0 = ASCII_XOFF; - flowflags = (flowflags & ~FLOWFLAG_SEND_XOFF) | FLOWFLAG_SENT_XOFF; + flowflags = 0; } else #endif @@ -193,15 +192,26 @@ void serial_writestr_P(PGM_P data) #ifdef XONXOFF void xon() { - if (flowflags & FLOWFLAG_SENT_XOFF) + // disable TX interrupt + UCSR0B &= ~MASK(UDRIE0); + + if ((flowflags & FLOWFLAG_STATE_XON) == 0) flowflags = FLOWFLAG_SEND_XON; + else + flowflags = FLOWFLAG_STATE_XON; // purge a possible FLOWFLAG_SEND_XOFF + // enable TX interrupt so we can send this character UCSR0B |= MASK(UDRIE0); } void xoff() { - flowflags = FLOWFLAG_SEND_XOFF; - // enable TX interrupt so we can send this character + UCSR0B &= ~MASK(UDRIE0); + + if (flowflags & FLOWFLAG_STATE_XON) + flowflags = FLOWFLAG_SEND_XOFF | FLOWFLAG_STATE_XON; + else + flowflags = 0; + UCSR0B |= MASK(UDRIE0); } #endif From 639f5237bebbacb730e68dc3fbab4fd5f7207f26 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Wed, 8 Sep 2010 22:33:20 +0200 Subject: [PATCH 8/9] Make acceleration, RepRap-style, disable-able. This is also in preparation for introducing acceleration ramping. --- dda.c | 6 ++++++ dda.h | 4 ++++ gcode.c | 4 ++++ machine.h | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/dda.c b/dda.c index 0733c3e..0b098d7 100644 --- a/dda.c +++ b/dda.c @@ -209,6 +209,7 @@ void dda_create(DDA *dda, TARGET *target) { // distance * 2400 .. * F_CPU / 40000 so we can move a distance of up to 1800mm without overflowing uint32_t move_duration = ((distance * 2400) / dda->total_steps) * (F_CPU / 40000); + #ifdef ACCELERATION_REPRAP // c is initial step time in IOclk ticks dda->c = (move_duration / startpoint.F) << 8; @@ -269,6 +270,9 @@ void dda_create(DDA *dda, TARGET *target) { } else dda->accel = 0; + #else + dda->c = (move_duration / target->F) << 8; + #endif } if (debug_flags & DEBUG_DDA) @@ -389,6 +393,7 @@ void dda_step(DDA *dda) { sei(); #endif + #ifdef ACCELERATION_REPRAP // linear acceleration magic, courtesy of http://www.embedded.com/columns/technicalinsights/56800129?printable=true if (dda->accel) { if ( @@ -405,6 +410,7 @@ void dda_step(DDA *dda) { } // else we are already at target speed } + #endif if (did_step) { // we stepped, reset timeout diff --git a/dda.h b/dda.h index 2248f10..78863ce 100644 --- a/dda.h +++ b/dda.h @@ -29,7 +29,9 @@ typedef struct { // status fields uint8_t nullmove :1; uint8_t live :1; + #ifdef ACCELERATION_REPRAP uint8_t accel :1; + #endif // wait for temperature to stabilise flag uint8_t waitfor_temp :1; @@ -60,8 +62,10 @@ typedef struct { // linear acceleration variables: c and end_c are 24.8 fixed point timer values, n is the tracking variable uint32_t c; + #ifdef ACCELERATION_REPRAP uint32_t end_c; int32_t n; + #endif } DDA; /* diff --git a/gcode.c b/gcode.c index a4770a3..b3063d7 100644 --- a/gcode.c +++ b/gcode.c @@ -693,7 +693,11 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { serial_writestr_P(PSTR(",F:")); serwrite_int32(movebuffer[mb_tail].endpoint.F); serial_writestr_P(PSTR(",c:")); + #ifdef ACCELERATION_REPRAP serwrite_uint32(movebuffer[mb_tail].end_c); + #else + serwrite_uint32(movebuffer[mb_tail].c); + #endif serial_writestr_P(PSTR("}\n")); print_queue(); diff --git a/machine.h b/machine.h index 033ec7c..49a981a 100644 --- a/machine.h +++ b/machine.h @@ -36,6 +36,11 @@ #define TEMP_HYSTERESIS 20 #define TEMP_RESIDENCY_TIME 60 +// acceleration, reprap style. Each movement starts at the speed of +// the previous command and accelerates or decelerates linearly +// to reach target speed at the end of the movement. +#define ACCELERATION_REPRAP + // -------------------------------------------------------------------------- // you shouldn't need to edit something below this line From 2178ff4ac1542b6194cbf6bc9e9e3bc4306640ea Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Fri, 10 Sep 2010 02:07:50 +0200 Subject: [PATCH 9/9] Implement acceleration ramping. Enjoy always smooth rides! --- dda.c | 40 ++++++++++++++++++++++++++++++++++++++++ dda.h | 21 +++++++++++++++++++++ machine.h | 16 ++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/dda.c b/dda.c index 0b098d7..6d409f5 100644 --- a/dda.c +++ b/dda.c @@ -270,9 +270,19 @@ void dda_create(DDA *dda, TARGET *target) { } else dda->accel = 0; + #else // #elifdef isn't valid for gcc + #ifdef ACCELERATION_RAMPING + dda->ramp_steps = dda->total_steps / 2; + dda->step_no = 0; + // c is initial step time in IOclk ticks + dda->c = ACCELERATION_STEEPNESS << 8; + dda->c_min = (move_duration / target->F) << 8; + dda->n = 1; + dda->ramp_state = RAMP_UP; #else dda->c = (move_duration / target->F) << 8; #endif + #endif } if (debug_flags & DEBUG_DDA) @@ -411,6 +421,36 @@ void dda_step(DDA *dda) { // else we are already at target speed } #endif + #ifdef ACCELERATION_RAMPING + // - algorithm courtesy of http://www.embedded.com/columns/technicalinsights/56800129?printable=true + // - for simplicity, taking even/uneven number of steps into account dropped + // - number of steps moved is always accurate, speed might be one step off + switch (dda->ramp_state) { + case RAMP_UP: + case RAMP_MAX: + if (dda->step_no >= dda->ramp_steps) { + // RAMP_UP: time to decelerate before reaching maximum speed + // RAMP_MAX: time to decelerate + dda->ramp_state = RAMP_DOWN; + dda->n = -((int32_t)2) - dda->n; + } + if (dda->ramp_state == RAMP_MAX) + break; + case RAMP_DOWN: + dda->n += 4; + // be careful of signedness! + dda->c = (int32_t)dda->c - ((int32_t)(dda->c * 2) / dda->n); + if (dda->c <= dda->c_min) { + // maximum speed reached + dda->c = dda->c_min; + dda->ramp_state = RAMP_MAX; + dda->ramp_steps = dda->total_steps - dda->step_no; + } + setTimer(dda->c >> 8); + break; + } + dda->step_no++; + #endif if (did_step) { // we stepped, reset timeout diff --git a/dda.h b/dda.h index 78863ce..2f84951 100644 --- a/dda.h +++ b/dda.h @@ -6,6 +6,16 @@ #include "pinout.h" #include "machine.h" +/* + enums +*/ +// wether we accelerate, run at full speed, break down, etc. +typedef enum { + RAMP_UP, + RAMP_MAX, + RAMP_DOWN +} ramp_state_t; + /* types */ @@ -66,6 +76,17 @@ typedef struct { uint32_t end_c; int32_t n; #endif + #ifdef ACCELERATION_RAMPING + // start of down-ramp, intitalized with total_steps / 2 + uint32_t ramp_steps; + // counts actual steps done + uint32_t step_no; + // 24.8 fixed point timer value, maximum speed + uint32_t c_min; + // tracking variable + int32_t n; + ramp_state_t ramp_state; + #endif } DDA; /* diff --git a/machine.h b/machine.h index 49a981a..ab6a1e3 100644 --- a/machine.h +++ b/machine.h @@ -41,6 +41,22 @@ // to reach target speed at the end of the movement. #define ACCELERATION_REPRAP +// acceleration and deceleration ramping. Each movement starts at +// (almost) no speed, linearly accelerates to target speed and decelerates +// just in time to smoothly stop at the target. +// alternative to ACCELERATION_REPRAP +//#define ACCELERATION_RAMPING +// how fast to accelerate when using ACCELERATION_RAMPING +// smaller values give quicker acceleration +// valid range = 1 to 8,000,000; 500,000 is a good starting point +#define ACCELERATION_STEEPNESS 500000 + +#ifdef ACCELERATION_REPRAP +#ifdef ACCELERATION_RAMPING +#error Cant use ACCELERATION_REPRAP and ACCELERATION_RAMPING together. +#endif +#endif + // -------------------------------------------------------------------------- // you shouldn't need to edit something below this line