Firmware/FiveD on Arduino

From RepRap
Revision as of 20:47, 6 July 2010 by Traumflug (talk | contribs) (Implemented start/stop ramping.)
Jump to: navigation, search

Rewrite of FiveD firmware for the atmega168-based Arduino by Triffid Hunter

This firmware is loosely based on the official FiveD Firmware, however it uses 100% integer math, and works very hard to minimize/eliminate long math operations in interrupt context.

Constant Acceleration

The official FiveD firmware changes speed by a fixed amount each step, however this also alters the step time. acceleration = dv/dt, so with a fixed dv but a changing dt, acceleration changes during a move. This makes high speeds quite unattainable as the acceleration quickly outstrips the motor's ability to keep up.

FiveD on Arduino contains a constant acceleration implementation.

Transmission of Multiple Commands

FiveD on Arduino has the capability to receive additional commands while working on the first one. This avoids short stops between commands and will allow for even more smooth transitions from one movement to the next in the future.

Patches, Enhancements

As Triffid Hunter is apparently out of town currently (2010-07-03), the Git Repository doesn't allow to upload patches and I don't want to do yet another fork, I'll upload patches here.

To apply those patches, type git apply in your terminal, inside your local copy of git repo, then copy the text given her into the terminal and hit ctrl-d. Well, I hope it works that way.

If you work on FiveD on Arduino as well, please do commits in small pieces, one topic/bug at a time. This helps a lot in reviewing patches. That done, you can create a set of patches easily:

git format-patch --keep-subject -o out origin

You'll find each of your patches in the out/ directory, nicely equipped with a mail header and a proper file name.

Make sources buildable with the Arduino IDE.

---
 mendel/mendel.pde |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 mendel/mendel.pde

diff --git a/mendel/mendel.pde b/mendel/mendel.pde
new file mode 100644
index 0000000..1c3f190
--- /dev/null
+++ b/mendel/mendel.pde
@@ -0,0 +1,4 @@
+
+/*
+	The existence of this file makes sources buildable with the Arduino IDE.
+*/
--

Achieve C89 compatibility, Arduino IDE's default.

---
 mendel/serial.c |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/mendel/serial.c b/mendel/serial.c
index bce947b..767bdeb 100644
--- a/mendel/serial.c
+++ b/mendel/serial.c
@@ -146,7 +146,9 @@ void serial_writechar(uint8_t data)
 
 void serial_writeblock(void *data, int datalen)
 {
-	for (int i = 0; i < datalen; i++)
+	int i;
+
+	for (i = 0; i < datalen; i++)
 		serial_writechar(((uint8_t *) data)[i]);
 }
 
@@ -170,15 +172,17 @@ void serial_writechar_P(PGM_P data)
 
 void serial_writeblock_P(PGM_P data, int datalen)
 {
-	for (int i = 0; i < datalen; i++)
+	int i;
+
+	for (i = 0; i < datalen; i++)
 		serial_writechar_P(&data[i]);
 }
 
 void serial_writestr_P(PGM_P data)
 {
-	uint8_t i = 0;
+	uint8_t r, i = 0;
 	// yes, this is *supposed* to be assignment rather than comparison, so we break when r is assigned zero
-	for (uint8_t r; (r = pgm_read_byte(&data[i])); i++)
+	for (r; (r = pgm_read_byte(&data[i])); i++)
 		serial_writechar(r);
 }

--

RepRap host software expects a lowercase "ok" for confirmation.

---
 mendel/gcode.c |    4 ++++
 mendel/gcode.h |    3 +++
 2 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index 6d06e08..04a7e71 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -312,7 +312,11 @@ void scan_char(uint8_t c) {
 					) {
 					process_gcode_command(&next_target);
 
+					#ifdef	LOWERCASE_OK
+					serial_writestr_P(PSTR("ok\n"));
+					#else
 					serial_writestr_P(PSTR("OK\n"));
+					#endif
 
 					// expect next line number
 					next_target.N_expected++;
diff --git a/mendel/gcode.h b/mendel/gcode.h
index bfc9061..4805501 100644
--- a/mendel/gcode.h
+++ b/mendel/gcode.h
@@ -5,6 +5,9 @@
 
 #include	"dda.h"
 
+// host software expectations
+#define	LOWERCASE_OK
+
 // this is a very crude decimal-based floating point structure. a real floating point would at least have signed exponent
 typedef struct {
 	uint32_t	sign			:1;
--

Moved REQUIRE_LINENUMBER and REQUIRE_CHECKSUM #defines to gcode.h as well.

---
 mendel/Makefile |    4 +---
 mendel/gcode.h  |    2 ++
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/mendel/Makefile b/mendel/Makefile
index fcc78f6..3dfd4b6 100644
--- a/mendel/Makefile
+++ b/mendel/Makefile
@@ -38,8 +38,6 @@ OBJCOPY = $(ARCH)objcopy
 
 DEFS = -DF_CPU=$(F_CPU)
 # DEFS += "-DDEBUG=1"
-# DEFS += "-DREQUIRE_LINENUMBER"
-# DEFS += "-DREQUIRE_CHECKSUM"
 
 OPTIMIZE = -Os -ffunction-sections -finline-functions-called-once -DDEBUG
 # OPTIMIZE = -O0
@@ -103,4 +101,4 @@ size: $(PROGRAM).hex
 
 %.sym: %.elf
 	@echo "  SYM       $@"
-	@$(OBJDUMP) -t $< | perl -ne 'BEGIN { printf "  ADDR  NAME                  SIZE\n"; } /([0-9a-f]+)\s+(\w+)\s+O\s+\.(bss|data)\s+([0-9a-f]+)\s+(\w+)/ && printf "0x%04x  %-20s +%d\n", eval("0x$$1") & 0xFFFF, $$5, eval("0x$$4")' | sort -k1 > $@
\ No newline at end of file
+	@$(OBJDUMP) -t $< | perl -ne 'BEGIN { printf "  ADDR  NAME                  SIZE\n"; } /([0-9a-f]+)\s+(\w+)\s+O\s+\.(bss|data)\s+([0-9a-f]+)\s+(\w+)/ && printf "0x%04x  %-20s +%d\n", eval("0x$$1") & 0xFFFF, $$5, eval("0x$$4")' | sort -k1 > $@
diff --git a/mendel/gcode.h b/mendel/gcode.h
index 4805501..9fd1795 100644
--- a/mendel/gcode.h
+++ b/mendel/gcode.h
@@ -7,6 +7,8 @@
 
 // host software expectations
 #define	LOWERCASE_OK
+//#define	REQUIRE_LINENUMBER
+//#define	REQUIRE_CHECKSUM
 
 // this is a very crude decimal-based floating point structure. a real floating point would at least have signed exponent
 typedef struct {
--

An M-code stuck when receiving G commands and vice-versa.

Testcase:

M105
G1 F100

--> Temperatures would be sent the second time as well.

---
 mendel/gcode.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index 04a7e71..c05c598 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -335,7 +335,12 @@ void scan_char(uint8_t c) {
 		}
 
 		// reset variables
-		next_target.seen_X = next_target.seen_Y = next_target.seen_Z = next_target.seen_E = next_target.seen_F = next_target.seen_S = next_target.seen_P = next_target.seen_N = next_target.seen_checksum = next_target.seen_comment = next_target.checksum_read = next_target.checksum_calculated = 0;
+		next_target.seen_X = next_target.seen_Y = next_target.seen_Z = \
+			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;
 		last_field = 0;
 		read_digit.sign = 0;
 		read_digit.mantissa = 0;
--

If line numbers aren't required, don't bother about their actual value either.

Failing case was RepRap host software, sending

N0 T8 *18
N0 G21 *58

on each build start.

Also saves a whopping 124 bytes of memory on ignoring line numbers. next_target.N_expected is never used and it's handling should be optimized out.

---
 mendel/gcode.c |   10 +++++-----
 1 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index c05c598..86259b0 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -297,11 +297,11 @@ void scan_char(uint8_t c) {
 		// process
 		if (next_target.seen_G || next_target.seen_M) {
 			if (
-					#ifdef	REQUIRE_LINENUMBER
-					((next_target.N_expected == next_target.N) && (next_target.seen_N == 1))
-					#else
-					((next_target.N_expected == next_target.N) || (next_target.seen_N == 0))
-					#endif
+				#ifdef	REQUIRE_LINENUMBER
+				((next_target.N_expected == next_target.N) && (next_target.seen_N == 1))
+				#else
+				1
+				#endif
 				) {
 				if (
 					#ifdef	REQUIRE_CHECKSUM
--

Allow non-consecutive line numbers.

There isn't much point in insisting on consecutive line numbers. Some CNC softwares even do steps of 5 or 10 intentionally.

---
 mendel/gcode.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index 86259b0..a9cb139 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -298,7 +298,7 @@ void scan_char(uint8_t c) {
 		if (next_target.seen_G || next_target.seen_M) {
 			if (
 				#ifdef	REQUIRE_LINENUMBER
-				((next_target.N_expected == next_target.N) && (next_target.seen_N == 1))
+				((next_target.N >= next_target.N_expected) && (next_target.seen_N == 1))
 				#else
 				1
 				#endif
@@ -319,7 +319,7 @@ void scan_char(uint8_t c) {
 					#endif
 
 					// expect next line number
-					next_target.N_expected++;
+					next_target.N_expected = next_target.N  + 1;
 				}
 				else {
 					serial_writestr_P(PSTR("RESEND: BAD CHECKSUM: EXPECTED "));
--

Count only line numbers with N seen.

This allows to issue manual commands in midst of a (paused) build.

---
 mendel/gcode.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index a9cb139..1622b51 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -319,7 +319,8 @@ void scan_char(uint8_t c) {
 					#endif
 
 					// expect next line number
-					next_target.N_expected = next_target.N  + 1;
+					if (next_target.seen_N == 1)
+						next_target.N_expected = next_target.N + 1;
 				}
 				else {
 					serial_writestr_P(PSTR("RESEND: BAD CHECKSUM: EXPECTED "));
--

Don't fail on unknown commands.

For example, RepRap host sends T commands, which are not (yet) known.

Previously, the firmware would fail silently, which was even worse.

---
 mendel/gcode.c |   54 +++++++++++++++++++++++++++++++-----------------------
 1 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index 1622b51..d75e18e 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -221,7 +221,7 @@ void scan_char(uint8_t c) {
 
 		// process character
 		switch (c) {
-			// each command is either G or M, so preserve previous G/M unless a new one has appeared
+			// each currently known command is either G or M, so preserve previous G/M unless a new one has appeared
 			case 'G':
 				next_target.seen_G = 1;
 				next_target.seen_M = 0;
@@ -294,46 +294,54 @@ void scan_char(uint8_t c) {
 	if ((c == 10) || (c == 13)) {
 		if (debug_flags & DEBUG_ECHO)
 			serial_writechar(c);
-		// process
-		if (next_target.seen_G || next_target.seen_M) {
+
+		if (
+			#ifdef	REQUIRE_LINENUMBER
+			((next_target.N >= next_target.N_expected) && (next_target.seen_N == 1))
+			#else
+			1
+			#endif
+			) {
 			if (
-				#ifdef	REQUIRE_LINENUMBER
-				((next_target.N >= next_target.N_expected) && (next_target.seen_N == 1))
+				#ifdef	REQUIRE_CHECKSUM
+				((next_target.checksum_calculated == next_target.checksum_read) && (next_target.seen_checksum == 1))
 				#else
-				1
+				((next_target.checksum_calculated == next_target.checksum_read) || (next_target.seen_checksum == 0))
 				#endif
 				) {
-				if (
-					#ifdef	REQUIRE_CHECKSUM
-					((next_target.checksum_calculated == next_target.checksum_read) && (next_target.seen_checksum == 1))
-					#else
-					((next_target.checksum_calculated == next_target.checksum_read) || (next_target.seen_checksum == 0))
-					#endif
-					) {
+				// process
+				if (next_target.seen_G || next_target.seen_M) {
 					process_gcode_command(&next_target);
-
 					#ifdef	LOWERCASE_OK
 					serial_writestr_P(PSTR("ok\n"));
 					#else
 					serial_writestr_P(PSTR("OK\n"));
 					#endif
-
-					// expect next line number
-					if (next_target.seen_N == 1)
-						next_target.N_expected = next_target.N + 1;
 				}
 				else {
-					serial_writestr_P(PSTR("RESEND: BAD CHECKSUM: EXPECTED "));
-					serwrite_uint8(next_target.checksum_calculated);
-					serial_writestr_P(PSTR("\n"));
+					// write "OK" for invalid/unknown commands as well
+					#ifdef	LOWERCASE_OK
+					serial_writestr_P(PSTR("ok huh?\n"));
+					#else
+					serial_writestr_P(PSTR("OK Huh?\n"));
+					#endif
 				}
+
+				// expect next line number
+				if (next_target.seen_N == 1)
+					next_target.N_expected = next_target.N  + 1;
 			}
 			else {
-				serial_writestr_P(PSTR("RESEND: BAD LINE NUMBER: EXPECTED "));
-				serwrite_uint32(next_target.N_expected);
+				serial_writestr_P(PSTR("RESEND: BAD CHECKSUM: EXPECTED "));
+				serwrite_uint8(next_target.checksum_calculated);
 				serial_writestr_P(PSTR("\n"));
 			}
 		}
+		else {
+			serial_writestr_P(PSTR("RESEND: BAD LINE NUMBER: EXPECTED "));
+			serwrite_uint32(next_target.N_expected);
+			serial_writestr_P(PSTR("\n"));
+		}
 
 		// reset variables
 		next_target.seen_X = next_target.seen_Y = next_target.seen_Z = \
--

Make resend requests RepRap host compatible, too.

Elaborate on the various #defines related to host expectations in gcode.h

---
 mendel/gcode.c |   25 +++++++++++++++++++++++--
 mendel/gcode.h |   12 ++++++++++++
 2 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index d75e18e..8460737 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -332,15 +332,17 @@ void scan_char(uint8_t c) {
 					next_target.N_expected = next_target.N + 1;
 			}
 			else {
-				serial_writestr_P(PSTR("RESEND: BAD CHECKSUM: EXPECTED "));
+				serial_writestr_P(PSTR("Expected checksum "));
 				serwrite_uint8(next_target.checksum_calculated);
 				serial_writestr_P(PSTR("\n"));
+				request_resend();
 			}
 		}
 		else {
-			serial_writestr_P(PSTR("RESEND: BAD LINE NUMBER: EXPECTED "));
+			serial_writestr_P(PSTR("Expected line number "));
 			serwrite_uint32(next_target.N_expected);
 			serial_writestr_P(PSTR("\n"));
+			request_resend();
 		}
 
 		// reset variables
@@ -725,3 +727,22 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 		}
 	}
 }
+
+/****************************************************************************
+*                                                                           *
+* Request a resend of the current line - used from various places.          *
+*                                                                           *
+* Relies on the global variable next_target.N being valid.                  *
+*                                                                           *
+****************************************************************************/
+
+void request_resend() {
+	#ifdef	LOWERCASE_OK
+	serial_writestr_P(PSTR("Resend:"));
+	#else
+	serial_writestr_P(PSTR("RESEND: "));
+	#endif
+	serwrite_uint8(next_target.N);
+	serial_writestr_P(PSTR("\n"));
+}
+
diff --git a/mendel/gcode.h b/mendel/gcode.h
index 9fd1795..8b37e46 100644
--- a/mendel/gcode.h
+++ b/mendel/gcode.h
@@ -6,10 +6,19 @@
 #include	"dda.h"
 
 // host software expectations
+
+// wether host software expects all caps or (mostly) lowercase responses
+// keeping it defined makes us matching RepRap host software's expectations
 #define	LOWERCASE_OK
+
+// wether to insist on N line numbers
+// if not defined, N's are completely ignored
 //#define	REQUIRE_LINENUMBER
+
+// wether to insist on a checksum
 //#define	REQUIRE_CHECKSUM
 
+
 // this is a very crude decimal-based floating point structure. a real floating point would at least have signed exponent
 typedef struct {
 	uint32_t	sign			:1;
@@ -68,4 +77,7 @@ void scan_char(uint8_t c);
 // when we have a whole line, feed it to this
 void process_gcode_command(GCODE_COMMAND *gcmd);
 
+// uses the global variable next_target.N
+void request_resend();
+
 #endif	/* _GCODE_H */
--

Add the option to not include the asterisk in checksum calculation.

RepRap host software does it this way.

---
 mendel/gcode.c |   11 +++++++++--
 mendel/gcode.h |    4 ++++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index 8460737..c1065f1 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -17,6 +17,8 @@ uint8_t last_field = 0;
 
 const char alphabet[] = "GMXYZEFSPN*";
 
+#define crc(a, b)		(a ^ b)
+
decfloat read_digit					__attribute__ ((__section__ (".bss")));
  
GCODE_COMMAND next_target		__attribute__ ((__section__ (".bss")));
@@ -103,10 +105,10 @@ void SpecialMoveE(int32_t e, uint32_t f) {
 ****************************************************************************/
 
 void scan_char(uint8_t c) {
-	// move this below switch(c) if the asterisk isn't included in the checksum
-	#define crc(a, b)		(a ^ b)
+	#ifdef ASTERISK_IN_CHECKSUM_INCLUDED
 	if (next_target.seen_checksum == 0)
 		next_target.checksum_calculated = crc(next_target.checksum_calculated, c);
+	#endif
 
 	// uppercase
 	if (c >= 'a' && c <= 'z')
@@ -290,6 +292,11 @@ void scan_char(uint8_t c) {
 		}
 	}
 
+	#ifndef ASTERISK_IN_CHECKSUM_INCLUDED
+	if (next_target.seen_checksum == 0)
+		next_target.checksum_calculated = crc(next_target.checksum_calculated, c);
+	#endif
+
 	// end of line
 	if ((c == 10) || (c == 13)) {
 		if (debug_flags & DEBUG_ECHO)
diff --git a/mendel/gcode.h b/mendel/gcode.h
index 8b37e46..c4bc440 100644
--- a/mendel/gcode.h
+++ b/mendel/gcode.h
@@ -11,6 +11,10 @@
 // keeping it defined makes us matching RepRap host software's expectations
 #define	LOWERCASE_OK
 
+// wether the asterisk (checksum-command) is included for checksum calculation
+// undefined for RepRap host software
+//#define ASTERISK_IN_CHECKSUM_INCLUDED
+
 // wether to insist on N line numbers
 // if not defined, N's are completely ignored
 //#define	REQUIRE_LINENUMBER
--

Every character, plus *, starts a new field.

Previously, unknown commands would mess up decimals handling. Failing testcase was "N11 T22", which was interpreted as "N1122".

The new code now also compiles to 56 bytes less.

---
 mendel/gcode.c |   16 ++++++----------
 1 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index c1065f1..ed0db29 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -15,8 +15,6 @@
 
 uint8_t last_field = 0;
 
-const char alphabet[] = "GMXYZEFSPN*";
-
 #define crc(a, b)		(a ^ b)
 
 decfloat read_digit					__attribute__ ((__section__ (".bss")));
@@ -117,7 +115,8 @@ void scan_char(uint8_t c) {
 	// process previous field
 	if (last_field) {
 		// check if we're seeing a new field or end of line
-		if ((indexof(c, alphabet) >= 0) || (c == 10) || (c ==13)) {
+		// any character will start a new field, even invalid/unknown ones
+		if ((c >= 'A' && c <= 'Z') || c == '*' || (c == 10) || (c == 13)) {
 			switch (last_field) {
 				case 'G':
 					next_target.G = read_digit.mantissa;
@@ -206,16 +205,14 @@ void scan_char(uint8_t c) {
 			}
 			// reset for next field
 			last_field = 0;
-			read_digit.sign = 0;
-			read_digit.mantissa = 0;
-			read_digit.exponent = 0;
+			read_digit.sign = read_digit.mantissa = read_digit.exponent = 0;
 		}
 	}
 
 	// skip comments
 	if (next_target.seen_comment == 0) {
 		// new field?
-		if (indexof(c, alphabet) >= 0) {
+		if ((c >= 'A' && c <= 'Z') || c == '*') {
 			last_field = c;
 			if (debug_flags & DEBUG_ECHO)
 				serial_writechar(c);
@@ -289,6 +286,7 @@ void scan_char(uint8_t c) {
 					if (read_digit.exponent)
 						read_digit.exponent++;
 				}
+				// everything else is ignored
 		}
 	}
 
@@ -360,9 +358,7 @@ void scan_char(uint8_t c) {
 			next_target.seen_comment = next_target.checksum_read = \
 			next_target.checksum_calculated = 0;
 		last_field = 0;
-		read_digit.sign = 0;
-		read_digit.mantissa = 0;
-		read_digit.exponent = 0;
+		read_digit.sign = read_digit.mantissa = read_digit.exponent = 0;
 	}
 }
 
--

Achieve RepRap Host compatibility for temperature readouts as well.

---
 mendel/temp.c |    4 ++++
 mendel/temp.h |    3 +++
 2 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/mendel/temp.c b/mendel/temp.c
index 913b91c..c1310b9 100644
--- a/mendel/temp.c
+++ b/mendel/temp.c
@@ -159,7 +159,11 @@ void temp_print() {
 
 		c = (current_temp & 3) * 25;
 		t = (target_temp & 3) * 25;
+		#ifdef REPRAP_HOST_COMPATIBILITY
+		sersendf_P(PSTR("T: %u.%u\n"), current_temp >> 2, c);
+		#else
 		sersendf_P(PSTR("T: %u.%u/%u.%u :%u\n"), current_temp >> 2, c, target_temp >> 2, t, temp_residency);
+		#endif
 	}
 }
 
diff --git a/mendel/temp.h b/mendel/temp.h
index 71902fd..44c3acd 100644
--- a/mendel/temp.h
+++ b/mendel/temp.h
@@ -3,6 +3,9 @@
 
 #include	<stdint.h>
 
+// RepRap host software isn't exactly tolerant on what it ready back.
+#define REPRAP_HOST_COMPATIBILITY
+
 // extruder heater PID factors
 // google "PID without a PHD" if you don't understand this PID stuff
 extern int32_t p_factor;
--

Major overhaul of machine.h.

All those calculations were confusingly complex and made for one type of machine, only.

---
 mendel/gcode.c   |   32 ++++++++--------
 mendel/machine.h |  108 +++++++++++++++++++++--------------------------------
 mendel/mendel.c  |    2 +-
 3 files changed, 60 insertions(+), 82 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index ed0db29..49687b4 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -398,7 +398,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 			// 	G0 - rapid, unsynchronised motion
 			// since it would be a major hassle to force the dda to not synchronise, just provide a fast feedrate and hope it's close enough to what host expects
 			case 0:
-				gcmd->target.F = FEEDRATE_FAST_XY;
+				gcmd->target.F = MAXIMUM_FEEDRATE_X;
 				enqueue(&gcmd->target);
 				break;
 
@@ -449,15 +449,15 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 					Home XY first
 				*/
 				// hit endstops, no acceleration- we don't care about skipped steps
-				startpoint.F = FEEDRATE_FAST_XY;
-				SpecialMoveXY(-250L * STEPS_PER_MM_X, -250L * STEPS_PER_MM_Y, FEEDRATE_FAST_XY);
+				startpoint.F = MAXIMUM_FEEDRATE_X;
+				SpecialMoveXY(-250L * STEPS_PER_MM_X, -250L * STEPS_PER_MM_Y, MAXIMUM_FEEDRATE_X);
 				startpoint.X = startpoint.Y = 0;
 
 				// move forward a bit
-				SpecialMoveXY(5 * STEPS_PER_MM_X, 5 * STEPS_PER_MM_Y, FEEDRATE_SLOW_XY);
+				SpecialMoveXY(5 * STEPS_PER_MM_X, 5 * STEPS_PER_MM_Y, SEARCH_FEEDRATE_X);
 
 				// move back in to endstops slowly
-				SpecialMoveXY(-20 * STEPS_PER_MM_X, -20 * STEPS_PER_MM_Y, FEEDRATE_SLOW_XY);
+				SpecialMoveXY(-20 * STEPS_PER_MM_X, -20 * STEPS_PER_MM_Y, SEARCH_FEEDRATE_X);
 
 				// wait for queue to complete
 				for (;!queue_empty(););
@@ -469,15 +469,15 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 					Home Z
 				*/
 				// hit endstop, no acceleration- we don't care about skipped steps
-				startpoint.F = FEEDRATE_FAST_Z;
-				SpecialMoveZ(-250L * STEPS_PER_MM_Z, FEEDRATE_FAST_Z);
+				startpoint.F = MAXIMUM_FEEDRATE_Z;
+				SpecialMoveZ(-250L * STEPS_PER_MM_Z, MAXIMUM_FEEDRATE_Z);
 				startpoint.Z = 0;
 
 				// move forward a bit
-				SpecialMoveZ(5 * STEPS_PER_MM_Z, FEEDRATE_SLOW_Z);
+				SpecialMoveZ(5 * STEPS_PER_MM_Z, SEARCH_FEEDRATE_Z);
 
 				// move back into endstop slowly
-				SpecialMoveZ(-20L * STEPS_PER_MM_Z, FEEDRATE_SLOW_Z);
+				SpecialMoveZ(-20L * STEPS_PER_MM_Z, SEARCH_FEEDRATE_Z);
 
 				// wait for queue to complete
 				for (;queue_empty(););
@@ -495,9 +495,9 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 					Home F
 				*/
 
-				// F has been left at FEEDRATE_SLOW_Z by the last move, this is a usable "home"
+				// F has been left at SEARCH_FEEDRATE_Z by the last move, this is a usable "home"
 				// uncomment the following or substitute if you prefer a different default feedrate
-				// startpoint.F = FEEDRATE_SLOW_XY
+				// startpoint.F = SEARCH_FEEDRATE_Z
 
 				break;
 
@@ -516,7 +516,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 				startpoint.X = startpoint.Y = startpoint.Z = startpoint.E =
 				current_position.X = current_position.Y = current_position.Z = current_position.E = 0;
 				startpoint.F =
-				current_position.F = FEEDRATE_SLOW_Z;
+				current_position.F = SEARCH_FEEDRATE_Z;
 				break;
 
 			// unknown gcode: spit an error
@@ -545,8 +545,8 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 				do {
 					// backup feedrate, move E very quickly then restore feedrate
 					uint32_t	f = startpoint.F;
-					startpoint.F = FEEDRATE_FAST_E;
-					SpecialMoveE(E_STARTSTOP_STEPS, FEEDRATE_FAST_E);
+					startpoint.F = MAXIMUM_FEEDRATE_E;
+					SpecialMoveE(E_STARTSTOP_STEPS, MAXIMUM_FEEDRATE_E);
 					startpoint.F = f;
 				} while (0);
 				break;
@@ -558,8 +558,8 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 				do {
 					// backup feedrate, move E very quickly then restore feedrate
 					uint32_t	f = startpoint.F;
-					startpoint.F = FEEDRATE_FAST_E;
-					SpecialMoveE(-E_STARTSTOP_STEPS, FEEDRATE_FAST_E);
+					startpoint.F = MAXIMUM_FEEDRATE_E;
+					SpecialMoveE(-E_STARTSTOP_STEPS, MAXIMUM_FEEDRATE_E);
 					startpoint.F = f;
 				} while (0);
 				break;
diff --git a/mendel/machine.h b/mendel/machine.h
index 2c91c6c..067391e 100644
--- a/mendel/machine.h
+++ b/mendel/machine.h
@@ -1,85 +1,56 @@
 #ifndef	_MACHINE_H
 #define	_MACHINE_H
 
-/*
-	move buffer size, in number of moves
-		note that each move takes a fair chunk of ram (69 bytes as of this writing) so don't make the buffer too big - a bigger serial readbuffer may help more than increasing this unless your gcodes are more than 70 characters long on average.
-		however, a larger movebuffer will probably help with lots of short consecutive moves, as each move takes a bunch of math (hence time) to set up
-*/
-#define	MOVEBUFFER_SIZE	8
-
-/*
-	axis calculations, adjust as necessary
-*/
-
-// XY can have lots of precision and still move speedily, so microstepping is helpful
-#define	X_STEPS_PER_REV						3200.0
-#define	Y_STEPS_PER_REV						X_STEPS_PER_REV
-
-// we need far more speed than precision on Z due to the threaded rod drive, maybe just half stepping
-#define	Z_STEPS_PER_REV						400.0
-
-#define	X_COG_CIRCUMFERENCE				(4.77 * 16.0)
-#define	Y_COG_CIRCUMFERENCE				X_COG_CIRCUMFERENCE
-// also try:
-// #define	XY_COG_RADIUS			9.5
-// #define	XY_COG_CIRCUMFERENCE	(XY_COG_RADIUS * PI * 2)
-
-// this is the ratio between number of teeth on the Z motor gear, and teeth on the Z leadscrew base gear.
-#define	Z_GEAR_RATIO							1.0
-
-// we need more torque and smoothness at very low speeds on E, maximum microstepping
-#define	E_STEPS_PER_REV						3200.0
-#define	EXTRUDER_SHAFT_RADIUS			5.0
-#define	EXTRUDER_INLET_DIAMETER		3.0
-#define	EXTRUDER_NOZZLE_DIAMETER	0.8
+// --------------------------------------------------------------------------
+// values reflecting the gearing of your machine
+// all numbers are integers, so no decimals, please :-)
 
-// these feedrates are used during homing and G0 rapid moves
-#define	FEEDRATE_FAST_XY					6000
-#define	FEEDRATE_SLOW_XY					300
+// calculate these values appropriate for your machine
+#define	STEPS_PER_MM_X		320
+#define	STEPS_PER_MM_Y		320
+#define	STEPS_PER_MM_Z		320
 
-#define	FEEDRATE_FAST_Z						6000
-#define	FEEDRATE_SLOW_Z						300
-
-#define	FEEDRATE_FAST_E						1200
+// http://blog.arcol.hu/?p=157 may help with this next one
+#define	STEPS_PER_MM_E		320
 
 // this is how many steps to suck back the filament by when we stop
-#define	E_STARTSTOP_STEPS					20
+#define	E_STARTSTOP_STEPS	20
 
-// extruder settings
-#define	TEMP_HYSTERESIS						20
-#define	TEMP_RESIDENCY_TIME				60
+// --------------------------------------------------------------------------
+// values depending on the capabilities of your stepper motors and other mechanics
+// again, all numbers are integers
 
-/*
-	calculated values - you shouldn't need to touch these
-	however feel free to put in your own values if they can be more precise than the calculated approximations, remembering that they must end up being integers- floating point by preprocessor only thanks!
-*/
+// used for G0 rapid moves and as a cap for all other feedrates
+#define	MAXIMUM_FEEDRATE_X	200
+#define	MAXIMUM_FEEDRATE_Y	200
+#define	MAXIMUM_FEEDRATE_Z	200
+#define	MAXIMUM_FEEDRATE_E	200
 
-#define	STEPS_PER_MM_X						((uint32_t) ((X_STEPS_PER_REV / X_COG_CIRCUMFERENCE) + 0.5))
-#define	STEPS_PER_MM_Y						((uint32_t) ((Y_STEPS_PER_REV / Y_COG_CIRCUMFERENCE) + 0.5))
-#define	STEPS_PER_MM_Z						((uint32_t) ((Z_STEPS_PER_REV * Z_GEAR_RATIO) + 0.5))
+// used when searching endstops and similar
+#define	SEARCH_FEEDRATE_X	50
+#define	SEARCH_FEEDRATE_Y	50
+#define	SEARCH_FEEDRATE_Z	50
+#define	SEARCH_FEEDRATE_E	50
 
-// http://blog.arcol.hu/?p=157 may help with this next one
-// I haven't tuned this at all- it's just a placeholder until I read the above carefully enough
-// does this refer to filament or extrudate? extrudate depends on XY distance vs E distance.. hm lets go with filament
-// #define	STEPS_PER_MM_E						((uint32_t) ((E_STEPS_PER_REV / (EXTRUDER_SHAFT_RADIUS * PI * EXTRUDER_INLET_DIAMETER / EXTRUDER_NOZZLE_DIAMETER)) + 0.5))
+// extruder settings
+#define	TEMP_HYSTERESIS		20
+#define	TEMP_RESIDENCY_TIME	60
 
-#define	STEPS_PER_MM_E						((uint32_t) ((E_STEPS_PER_REV * EXTRUDER_NOZZLE_DIAMETER / EXTRUDER_SHAFT_RADIUS / PI / EXTRUDER_INLET_DIAMETER) + 0.5))
+// --------------------------------------------------------------------------
+// you shouldn't need to edit something below this line
 
 // same as above with 25.4 scale factor
-#define	STEPS_PER_IN_X						((uint32_t) ((25.4 * X_STEPS_PER_REV / X_COG_CIRCUMFERENCE) + 0.5))
-#define	STEPS_PER_IN_Y						((uint32_t) ((25.4 * Y_STEPS_PER_REV / Y_COG_CIRCUMFERENCE) + 0.5))
-#define	STEPS_PER_IN_Z						((uint32_t) ((25.4 * Z_STEPS_PER_REV * Z_GEAR_RATIO) + 0.5))
-#define	STEPS_PER_IN_E						((uint32_t) ((25.4 * E_STEPS_PER_REV * EXTRUDER_NOZZLE_DIAMETER / (EXTRUDER_SHAFT_RADIUS * PI * EXTRUDER_INLET_DIAMETER)) + 0.5))
+#define	STEPS_PER_IN_X		((uint32_t) ((25.4 * STEPS_PER_MM_X) + 0.5))
+#define	STEPS_PER_IN_Y		((uint32_t) ((25.4 * STEPS_PER_MM_Y) + 0.5))
+#define	STEPS_PER_IN_Z		((uint32_t) ((25.4 * STEPS_PER_MM_Z) + 0.5))
+#define	STEPS_PER_IN_E		((uint32_t) ((25.4 * STEPS_PER_MM_E) + 0.5))
 
 // inverse, used in distance calculation during DDA setup
-#define	UM_PER_STEP_X			((uint32_t) ((1000.0 / STEPS_PER_MM_X) + 0.5))
-#define	UM_PER_STEP_Y			((uint32_t) ((1000.0 / STEPS_PER_MM_Y) + 0.5))
-#define	UM_PER_STEP_Z			((uint32_t) ((1000.0 / STEPS_PER_MM_Z) + 0.5))
-#define	UM_PER_STEP_E			((uint32_t) ((1000.0 / STEPS_PER_MM_E) + 0.5))
+#define	UM_PER_STEP_X		((uint32_t) ((1000.0 / STEPS_PER_MM_X) + 0.5))
+#define	UM_PER_STEP_Y		((uint32_t) ((1000.0 / STEPS_PER_MM_Y) + 0.5))
+#define	UM_PER_STEP_Z		((uint32_t) ((1000.0 / STEPS_PER_MM_Z) + 0.5))
+#define	UM_PER_STEP_E		((uint32_t) ((1000.0 / STEPS_PER_MM_E) + 0.5))
 
-// should be the same for all machines! ;)
-#define	PI	3.1415926535
 
 /*
 	firmware build options
@@ -92,4 +63,11 @@
 // Xon/Xoff flow control. Should be redundant
 // #define	XONXOFF
 
+/*
+	move buffer size, in number of moves
+		note that each move takes a fair chunk of ram (69 bytes as of this writing) so don't make the buffer too big - a bigger serial readbuffer may help more than increasing this unless your gcodes are more than 70 characters long on average.
+		however, a larger movebuffer will probably help with lots of short consecutive moves, as each move takes a bunch of math (hence time) to set up
+*/
+#define	MOVEBUFFER_SIZE	8
+
 #endif	/* _MACHINE_H */
diff --git a/mendel/mendel.c b/mendel/mendel.c
index 37128b8..f2b8b60 100644
--- a/mendel/mendel.c
+++ b/mendel/mendel.c
@@ -89,7 +89,7 @@ void init(void) {
 	temp_init();
 
 	// set up default feedrate
-	current_position.F = startpoint.F = next_target.target.F = FEEDRATE_SLOW_Z;
+	current_position.F = startpoint.F = next_target.target.F = SEARCH_FEEDRATE_Z;
 
 	// enable interrupts
 	sei();
--

Eliminate serial_writechar_P()

-> serial_writechar() perfoms better, uses less memory. Also, reviewed all code for usage of single-character strings.

---
 mendel/gcode.c  |    6 +++---
 mendel/serial.c |   14 ++++++++------
 mendel/serial.h |    1 -
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/mendel/gcode.c b/mendel/gcode.c
index 49687b4..5030ccb 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -339,14 +339,14 @@ void scan_char(uint8_t c) {
 			else {
 				serial_writestr_P(PSTR("Expected checksum "));
 				serwrite_uint8(next_target.checksum_calculated);
-				serial_writestr_P(PSTR("\n"));
+				serial_writechar('\n');
 				request_resend();
 			}
 		}
 		else {
 			serial_writestr_P(PSTR("Expected line number "));
 			serwrite_uint32(next_target.N_expected);
-			serial_writestr_P(PSTR("\n"));
+			serial_writechar('\n');
 			request_resend();
 		}
 
@@ -746,6 +746,6 @@ void request_resend() {
 	serial_writestr_P(PSTR("RESEND: "));
 	#endif
 	serwrite_uint8(next_target.N);
-	serial_writestr_P(PSTR("\n"));
+	serial_writechar('\n');
 }
 
diff --git a/mendel/serial.c b/mendel/serial.c
index 767bdeb..13703c2 100644
--- a/mendel/serial.c
+++ b/mendel/serial.c
@@ -163,19 +163,21 @@ void serial_writestr(uint8_t *data)
 
 /*
 	Write from FLASH
-*/
 
-void serial_writechar_P(PGM_P data)
-{
-	serial_writechar(pgm_read_byte(data));
-}
+	Extensions to output flash memory pointers. This prevents the data to
+	become part of the .data segment instead of the .code segment. That means
+	less memory is consumed for multi-character writes.
+
+	For single character writes (i.e. '\n' instead of "\n"), using
+	serial_writechar() directly is the better choice.
+*/
 
 void serial_writeblock_P(PGM_P data, int datalen)
 {
 	int i;
 
 	for (i = 0; i < datalen; i++)
-		serial_writechar_P(&data[i]);
+		serial_writechar(pgm_read_byte(&data[i]));
 }
 
 void serial_writestr_P(PGM_P data)
diff --git a/mendel/serial.h b/mendel/serial.h
index 1f30c2c..73a7b18 100644
--- a/mendel/serial.h
+++ b/mendel/serial.h
@@ -24,7 +24,6 @@ void serial_writeblock(void *data, int datalen);
 void serial_writestr(uint8_t *data);
 
 // write from flash
-void serial_writechar_P(PGM_P data);
 void serial_writeblock_P(PGM_P data, int datalen);
 void serial_writestr_P(PGM_P data);
 
--

dda.c: eliminated abs32() in favour of the ABS() macro.

Saves 50 bytes.

---
 mendel/dda.c |   18 +++++-------------
 1 files changed, 5 insertions(+), 13 deletions(-)

diff --git a/mendel/dda.c b/mendel/dda.c
index fe0ef27..9fdf6d3 100644
--- a/mendel/dda.c
+++ b/mendel/dda.c
@@ -92,12 +92,6 @@ uint32_t approx_distance_3( uint32_t dx, uint32_t dy, uint32_t dz )
 	return (( approx + 512 ) >> 10 );
 }
 
-uint32_t abs32(int32_t v) {
-	if (v < 0)
-		return (uint32_t) (-v);
-	return (uint32_t) (v);
-}
-
 uint32_t delta32(uint32_t v1, uint32_t v2) {
 	if (v1 >= v2)
 		return v1 - v2;
@@ -126,7 +120,6 @@ void dda_create(DDA *dda, TARGET *target) {
 
 	// initialise DDA to a known state
 	dda->live = 0;
-// 	dda->total_steps = 0;
 	dda->waitfor_temp = 0;
 
 	if (debug_flags & DEBUG_DDA)
@@ -135,10 +128,10 @@ void dda_create(DDA *dda, TARGET *target) {
 	// we end at the passed target
 	memcpy(&(dda->endpoint), target, sizeof(TARGET));
 
-	dda->x_delta = abs32(target->X - startpoint.X);
-	dda->y_delta = abs32(target->Y - startpoint.Y);
-	dda->z_delta = abs32(target->Z - startpoint.Z);
-	dda->e_delta = abs32(target->E - startpoint.E);
+	dda->x_delta = ABS(target->X - startpoint.X);
+	dda->y_delta = ABS(target->Y - startpoint.Y);
+	dda->z_delta = ABS(target->Z - startpoint.Z);
+	dda->e_delta = ABS(target->E - startpoint.E);
 
 	dda->x_direction = (target->X >= startpoint.X)?1:0;
 	dda->y_direction = (target->Y >= startpoint.Y)?1:0;
@@ -162,8 +155,7 @@ void dda_create(DDA *dda, TARGET *target) {
 		serial_writestr_P(PSTR("] ["));
 	}
 
-// 	if (dda->x_delta > dda->total_steps)
-		dda->total_steps = dda->x_delta;
+	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)
--

Implement start/stop ramping.

This accounts for the fact real-world machines can't gain full speed instantly. In tests with my setup it also allowed more than the doubling target speed - over 1000 rpm with an ordinary NEMA-23 stepper!

The code allowing slow acceleration over the whole move had to be dropped. To be honest, I see no use in this code other than a hack in the wrong place (i.e. the G-code producing application) to achieve what's now done by firmware.

A fact of the current implementation is, it stops after each move. This means slow average speeds in case of many short moves.

It'll be possible to improve this situation by taking the next move into account when ramping down. A not-so-trivial task, predestined for a hacking week sometimes in the future.

---
 mendel/dda.c     |  122 ++++++++++++++++-------------------------------------
 mendel/dda.h     |   38 ++++++++++++----
 mendel/gcode.c   |    2 -
 mendel/machine.h |    8 ++++
 4 files changed, 73 insertions(+), 97 deletions(-)

diff --git a/mendel/dda.c b/mendel/dda.c
index 9fdf6d3..bffbfa3 100644
--- a/mendel/dda.c
+++ b/mendel/dda.c
@@ -98,19 +98,6 @@ uint32_t delta32(uint32_t v1, uint32_t v2) {
 	return v2 - v1;
 }
 
-// this is an ultra-crude pseudo-logarithm routine, such that:
-// 2 ^ msbloc(v) >= v
-const uint8_t	msbloc (uint32_t v) {
-	uint8_t i;
-	uint32_t c;
-	for (i = 31, c = 0x80000000; i; i--) {
-		if (v & c)
-			return i;
-		c >>= 1;
-	}
-	return 0;
-}
-
 /*
 	CREATE a dda given current_position and a target, save to passed location so we can write directly into the queue
 */
@@ -210,66 +197,17 @@ 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_RAMPING
+		dda->ramp_steps = dda->total_steps / 2;
+		dda->step_no = 0;
 		// c is initial step time in IOclk ticks
-		dda->c = (move_duration / startpoint.F) << 8;
-
-		if (debug_flags & DEBUG_DDA) {
-			serial_writestr_P(PSTR(",md:")); serwrite_uint32(move_duration);
-			serial_writestr_P(PSTR(",c:")); serwrite_uint32(dda->c >> 8);
-		}
-
-		if (startpoint.F != target->F) {
-			uint32_t stF = startpoint.F / 4;
-			uint32_t enF = target->F / 4;
-			// now some constant acceleration stuff, courtesy of http://www.embedded.com/columns/technicalinsights/56800129?printable=true
-			uint32_t ssq = (stF * stF);
-			uint32_t esq = (enF * enF);
-			int32_t dsq = (int32_t) (esq - ssq) / 4;
-
-			uint8_t msb_ssq = msbloc(ssq);
-			uint8_t msb_tot = msbloc(dda->total_steps);
-
-			dda->end_c = (move_duration / target->F) << 8;
-			// the raw equation WILL overflow at high step rates, but 64 bit math routines take waay too much space
-			// at 65536 mm/min (1092mm/s), ssq/esq overflows, and dsq is also close to overflowing if esq/ssq is small
-			// but if ssq-esq is small, ssq/dsq is only a few bits
-			// we'll have to do it a few different ways depending on the msb locations of each
-			if ((msb_tot + msb_ssq) <= 30) {
-				// we have room to do all the multiplies first
-				if (debug_flags & DEBUG_DDA)
-					serial_writechar('A');
-				dda->n = ((int32_t) (dda->total_steps * ssq) / dsq) + 1;
-			}
-			else if (msb_tot >= msb_ssq) {
-				// total steps has more precision
-				if (debug_flags & DEBUG_DDA)
-					serial_writechar('B');
-				dda->n = (((int32_t) dda->total_steps / dsq) * (int32_t) ssq) + 1;
-			}
-			else {
-				// otherwise
-				if (debug_flags & DEBUG_DDA)
-					serial_writechar('C');
-				dda->n = (((int32_t) ssq / dsq) * (int32_t) dda->total_steps) + 1;
-			}
-
-			if (debug_flags & DEBUG_DDA) {
-// 				serial_writestr_P(PSTR("\n{DDA:CA end_c:")); serwrite_uint32(dda->end_c >> 8);
-// 				serial_writestr_P(PSTR(", n:")); serwrite_int32(dda->n);
-// 				serial_writestr_P(PSTR(", md:")); serwrite_uint32(move_duration);
-// 				serial_writestr_P(PSTR(", ssq:")); serwrite_uint32(ssq);
-// 				serial_writestr_P(PSTR(", esq:")); serwrite_uint32(esq);
-// 				serial_writestr_P(PSTR(", dsq:")); serwrite_int32(dsq);
-// 				serial_writestr_P(PSTR(", msbssq:")); serwrite_uint8(msb_ssq);
-// 				serial_writestr_P(PSTR(", msbtot:")); serwrite_uint8(msb_tot);
-// 				serial_writestr_P(PSTR("}\n"));
-				sersendf_P(PSTR("\n{DDA:CA end_c:%lu, n:%ld, md:%lu, ssq:%lu, esq:%lu, dsq:%lu, msbssq:%u, msbtot:%u}\n"), dda->end_c >> 8, dda->n, move_duration, ssq, esq, dsq, msb_ssq, msb_tot);
-			}
-
-			dda->accel = 1;
-		}
-		else
-			dda->accel = 0;
+		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
 	}
 
 	if (debug_flags & DEBUG_DDA)
@@ -424,22 +362,36 @@ void dda_step(DDA *dda) {
 		sei();
 	#endif
 
-	// linear acceleration magic, courtesy of http://www.embedded.com/columns/technicalinsights/56800129?printable=true
-	if (dda->accel) {
-		if (
-				((dda->n > 0) && (dda->c > dda->end_c)) ||
-				((dda->n < 0) && (dda->c < dda->end_c))
-			 ) {
-			dda->c = (int32_t) dda->c - ((int32_t) (dda->c * 2) / dda->n);
-			dda->n += 4;
-			setTimer(dda->c >> 8);
+	#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;
 		}
-		else if (dda->c != dda->end_c) {
-			dda->c = dda->end_c;
-			setTimer(dda->c >> 8);
+		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;
 		}
-		// else we are already at target speed
+ 		setTimer(dda->c >> 8);
+		break;
 	}
+	dda->step_no++;
+	#endif
 
 	if (step_option) {
 		// we stepped, reset timeout
diff --git a/mendel/dda.h b/mendel/dda.h
index d775b70..9c250ed 100644
--- a/mendel/dda.h
+++ b/mendel/dda.h
@@ -7,6 +7,16 @@
 #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
 */
 
@@ -25,18 +35,17 @@ typedef struct {
 	TARGET						endpoint;
 
 	// status fields
-	uint8_t						nullmove			:1;
-	uint8_t						live					:1;
-	uint8_t						accel					:1;
+	uint8_t						nullmove	:1;
+	uint8_t						live		: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;
+	uint8_t						x_direction	:1;
+	uint8_t						y_direction	:1;
+	uint8_t						z_direction	:1;
+	uint8_t						e_direction	:1;
 
 	// distances
 	uint32_t					x_delta;
@@ -52,11 +61,20 @@ typedef struct {
 
 	// total number of steps: set to max(x_delta, y_delta, z_delta, e_delta)
 	uint32_t					total_steps;
-
-	// linear acceleration variables: c and end_c are 24.8 fixed point timer values, n is the tracking variable
+	// 24.8 fixed point timer value, initial speed
 	uint32_t					c;
-	uint32_t					end_c;
+
+	#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/mendel/gcode.c b/mendel/gcode.c
index 5030ccb..5258ec2 100644
--- a/mendel/gcode.c
+++ b/mendel/gcode.c
@@ -694,8 +694,6 @@ void process_gcode_command(GCODE_COMMAND *gcmd) {
 				serwrite_int32(movebuffer[mb_tail].endpoint.E);
 				serial_writestr_P(PSTR(",F:"));
 				serwrite_int32(movebuffer[mb_tail].endpoint.F);
-				serial_writestr_P(PSTR(",c:"));
-				serwrite_uint32(movebuffer[mb_tail].end_c);
 				serial_writestr_P(PSTR("}\n"));
 
 				print_queue();
diff --git a/mendel/machine.h b/mendel/machine.h
index 067391e..b4d38d9 100644
--- a/mendel/machine.h
+++ b/mendel/machine.h
@@ -32,6 +32,14 @@
 #define	SEARCH_FEEDRATE_Z	50
 #define	SEARCH_FEEDRATE_E	50
 
+// acceleration and deceleration ramping
+// undefine this for disabling the feature
+#define ACCELERATION_RAMPING
+// how fast to accelerate
+// smaller values give quicker acceleration
+// valid range = 1 to 8,000,000; 500,000 is a good starting point
+#define ACCELERATION_STEEPNESS	500000
+
 // extruder settings
 #define	TEMP_HYSTERESIS		20
 #define	TEMP_RESIDENCY_TIME	60
--