diff --git a/attic/input-float/apply-to-fc4bfca06ae6c21b3103b9a721bf19ca4cf33a25.patch b/attic/input-float/apply-to-fc4bfca06ae6c21b3103b9a721bf19ca4cf33a25.patch new file mode 100644 index 0000000..d5a2f75 --- /dev/null +++ b/attic/input-float/apply-to-fc4bfca06ae6c21b3103b9a721bf19ca4cf33a25.patch @@ -0,0 +1,672 @@ +From cb05c9dda6bb8da6dbf6bbbbc64e79b069d8b2a0 Mon Sep 17 00:00:00 2001 +From: Michael Moon +Date: Sat, 5 Feb 2011 18:39:35 +1100 +Subject: big gcode_parse update- wait for whole line to ease processing, use + floats to receive numerical data, plugs into existing gcode_process.c. TODO: + revamp gcode_process to use new gcode_parse data structures + +--- + func.sh | 32 ++++ + gcode_parse.c | 563 +++++++++++++++++++++++----------------------------------- + gcode_parse.h | 4 +- + 3 files changed, 256 insertions(+), 343 deletions(-) + +diff --git a/func.sh b/func.sh +index e59b7a5..18827e0 100755 +--- a/func.sh ++++ b/func.sh +@@ -322,6 +322,38 @@ ENDPERL + } + + # Read status of PID routines. ++mendel_readsym_nexttarget() { ++ local val=$(mendel_readsym next_target) ++ perl - <<'ENDPERL' -- $val ++ $i = -1; ++ @a = qw/flags 2 G 1 M 1 X 4 Y 4 Z 4 E 4 F 4 S 2 P 2 T 1 N 4 eN 4 cr 1 cc 1/; ++ $c = 1234567; ++ while (length $ARGV[1]) { ++ if ($c > ($#a / 2)) { ++ $i++; ++ $c = 0; ++ } ++ if ($a[$c * 2 + 1] & 8) { ++ printf "\n"; ++ } ++ if (($a[$c * 2 + 1] & 7) == 4) { ++ $ARGV[1] =~ s#^(..)(..)(..)(..)##; ++ printf "%s: %d\t", $a[$c * 2], eval "0x$4$3$2$1"; ++ } ++ if (($a[$c * 2 + 1] & 7) == 2) { ++ $ARGV[1] =~ s#^(..)(..)##; ++ printf "%s: %d\t", $a[$c * 2], eval "0x$2$1"; ++ } ++ elsif (($a[$c * 2 + 1] & 7) == 1) { ++ $ARGV[1] =~ s#^(..)##; ++ printf "%s: %d\t", $a[$c * 2], eval "0x$1"; ++ } ++ $c++; ++ } ++ printf "\n"; ++ENDPERL ++} ++ + mendel_heater_pid() { + local P=$(mendel_readsym_int16 heater_p) + local I=$(mendel_readsym_int16 heater_i) +diff --git a/gcode_parse.c b/gcode_parse.c +index 1eb4eb0..626b694 100644 +--- a/gcode_parse.c ++++ b/gcode_parse.c +@@ -4,15 +4,12 @@ + \brief Parse received G-Codes + */ + +-#include ++#include + ++#include "config.h" ++#include "sersendf.h" + #include "serial.h" + #include "sermsg.h" +-#include "dda_queue.h" +-#include "debug.h" +-#include "heater.h" +-#include "sersendf.h" +- + #include "gcode_process.h" + + /// current or previous gcode word +@@ -22,64 +19,232 @@ uint8_t last_field = 0; + /// crude crc macro + #define crc(a, b) (a ^ b) + +-/// crude floating point data storage +-decfloat read_digit __attribute__ ((__section__ (".bss"))); +- +-/// this is where we store all the data for the current command before we work out what to do with it +-GCODE_COMMAND next_target __attribute__ ((__section__ (".bss"))); +- +-/* +- decfloat_to_int() is the weakest subject to variable overflow. For evaluation, we assume a build room of +-1000 mm and STEPS_PER_MM_x between 1.000 and 4096. Accordingly for metric units: +- +- df->mantissa: +-0..1048075 (20 bit - 500 for rounding) +- df->exponent: 0, 2, 3, 4 or 5 (10 bit) +- multiplicand: 1000 (10 bit) +- +- imperial units: +- +- df->mantissa: +-0..32267 (15 bit - 500 for rounding) +- df->exponent: 0, 2, 3, 4 or 5 (10 bit) +- multiplicand: 25400 (15 bit) +-*/ +-// decfloat_to_int() can handle a bit more: +-#define DECFLOAT_EXP_MAX 3 // more is pointless, as 1 um is our presision +-// (2^^32 - 1) / multiplicand - powers[DECFLOAT_EXP_MAX] / 2 = +-// 4294967295 / 1000 - 5000 = +-#define DECFLOAT_MANT_MM_MAX 4289967 // = 4290 mm +-// 4294967295 / 25400 - 5000 = +-#define DECFLOAT_MANT_IN_MAX 164093 // = 164 inches = 4160 mm ++#define GCODE_LINE_BUFFER_LEN 64 ++ ++#define iA 0 ++#define iB 1 ++#define iC 2 ++#define iD 3 ++#define iE 4 ++#define iF 5 ++#define iG 6 ++#define iH 7 ++#define iI 8 ++#define iJ 9 ++#define iK 10 ++#define iL 11 ++#define iM 12 ++#define iN 13 ++#define iO 14 ++#define iP 15 ++#define iQ 16 ++#define iR 17 ++#define iS 18 ++#define iT 19 ++#define iU 20 ++#define iV 21 ++#define iW 22 ++#define iX 23 ++#define iY 24 ++#define iZ 25 ++#define iAsterisk 26 ++ ++GCODE_COMMAND next_target; ++ ++uint8_t gcode_line[GCODE_LINE_BUFFER_LEN]; ++uint8_t gcode_line_pointer = 0; ++ ++float words[32]; ++uint32_t seen_mask = 0; ++ ++#define SEEN(c) (seen_mask & (1L << (c))) ++ ++uint32_t line_number = 0; ++ ++const uint8_t char2index(uint8_t c) __attribute__ ((pure)); ++const uint8_t char2index(uint8_t c) { ++ if (c >= 'a' && c <= 'z') ++ return c - 'a'; ++ if (c >= 'A' && c <= 'Z') ++ return c - 'A'; ++ if (c == '*') ++ return 26; ++ return 255; ++} + +-/* +- utility functions +-*/ +-extern const uint32_t powers[]; // defined in sermsg.c ++void gcode_parse_char(uint8_t c) { ++ if (gcode_line_pointer < (GCODE_LINE_BUFFER_LEN - 1)) ++ gcode_line[gcode_line_pointer++] = c; ++ if ((c == 13) || (c == 10)) { ++ uint8_t i; ++ for (i = gcode_line_pointer; i < GCODE_LINE_BUFFER_LEN; i++) ++ gcode_line[i] = 0; ++ if (gcode_line_pointer > 2) ++ gcode_parse_line(gcode_line); ++ gcode_line_pointer = 0; ++ } ++} + +-/// convert a floating point input value into an integer with appropriate scaling. +-/// \param *df pointer to floating point structure that holds fp value to convert +-/// \param multiplicand multiply by this amount during conversion to integer +-/// +-/// Tested for up to 42'000 mm (accurate), 420'000 mm (precision 10 um) and +-/// 4'200'000 mm (precision 100 um). +-static int32_t decfloat_to_int(decfloat *df, uint16_t multiplicand) { +- uint32_t r = df->mantissa; +- uint8_t e = df->exponent; ++void gcode_parse_line(uint8_t *c) { ++ enum { ++ STATE_FIND_WORD, ++ STATE_FIND_VALUE, ++ STATE_SEMICOLON_COMMENT, ++ STATE_BRACKET_COMMENT, ++ } state = STATE_FIND_WORD; ++ ++ uint8_t i; // string index ++ uint8_t w = 0; // current gcode word ++ uint8_t checksum = 0; ++ ++ seen_mask = 0; ++ ++ // calculate checksum ++ for(i = 0; c[i] != '*' && c[i] != 0; i++) ++ checksum = checksum ^ c[i]; ++ ++ // extract gcode words from line ++ for (i = 0; c[i] != 0 && c[i] != 13 && c[i] != 10; i++) { ++ switch (state) { ++ case STATE_FIND_WORD: ++ // start of word ++ if (char2index(c[i]) < 255) { ++ w = char2index(c[i]); ++ state = STATE_FIND_VALUE; ++ } ++ // comment until end of line ++ if (c[i] == ';') ++ state = STATE_SEMICOLON_COMMENT; ++ // comment until close bracket ++ if (c[i] == '(') ++ state = STATE_BRACKET_COMMENT; ++ break; ++ case STATE_FIND_VALUE: ++ if ((c[i] >= '0' && c[i] <= '9') || c[i] == '-') { ++ uint8_t *ep; ++ float v = strtod((const char *) &c[i], (char **) &ep); ++ state = STATE_FIND_WORD; ++ if (ep > &c[i]) { ++// sersendf_P(PSTR("[seen %c: %lx->"), w + 'A', seen_mask); ++ seen_mask |= (1L << w); ++// sersendf_P(PSTR("%lx]"), seen_mask); ++ words[w] = v; ++ i = ep - c - 1; ++ } ++ } ++ break; ++ case STATE_BRACKET_COMMENT: ++ if (c[i] == ')') ++ state = STATE_FIND_WORD; ++ break; ++ case STATE_SEMICOLON_COMMENT: ++ // dummy entry to suppress compiler warning ++ break; ++ } // switch (state) ++ } // for i=0 .. newline ++ ++ // TODO: process line just read ++ ++ if (SEEN(iAsterisk)) { ++ if (checksum != words[iAsterisk]) { ++ if (seen_mask & iAsterisk) ++ sersendf_P(PSTR("rs %d "), ((uint8_t) words[iAsterisk])); ++ sersendf_P(PSTR("Bad checksum, received %d, expected %d\n"), ((uint8_t) words[iAsterisk]), checksum); ++ seen_mask = 0; ++ return; ++ } ++ } ++ ++ if (SEEN(iN)) { ++ if (((uint32_t) words[iN]) != line_number) { ++ sersendf_P(PSTR("Bad line number, received %ld, expected %ld\n"), ((uint32_t) words[iN]), line_number); ++ seen_mask = 0; ++ return; ++ } ++ line_number++; ++ } ++ ++ serial_writestr_P(PSTR("ok ")); + +- // e=1 means we've seen a decimal point but no digits after it, and e=2 means we've seen a decimal point with one digit so it's too high by one if not zero +- if (e) +- e--; ++ // patch words into next_target struct ++ // TODO: eliminate next_target, update gcode_process to use words[] directly + +- // This raises range for mm by factor 1000 and for inches by factor 100. +- // It's a bit expensive, but we should have the time while parsing. +- while (e && multiplicand % 10 == 0) { +- multiplicand /= 10; +- e--; ++ next_target.flags = 0; ++ if (SEEN(iG)) { ++ next_target.seen_G = 1; ++ next_target.G = words[iG]; ++// sersendf_P(PSTR("G:%d/"), next_target.G); ++ } ++ if (SEEN(iM)) { ++ next_target.seen_M = 1; ++ next_target.M = words[iM]; ++// sersendf_P(PSTR("M:%d/"), next_target.M); ++ } ++ if (SEEN(iX)) { ++ next_target.seen_X = 1; ++ next_target.target.X = words[iX] * STEPS_PER_MM_X; ++// sersendf_P(PSTR("X:%ld/"), next_target.target.X); ++ } ++ if (SEEN(iY)) { ++ next_target.seen_Y = 1; ++ next_target.target.Y = words[iY] * STEPS_PER_MM_Y; ++// sersendf_P(PSTR("Y:%ld/"), next_target.target.Y); ++ } ++ if (SEEN(iZ)) { ++ next_target.seen_Z = 1; ++ next_target.target.Z = words[iZ] * STEPS_PER_MM_Z; ++// sersendf_P(PSTR("Z:%ld/"), next_target.target.Z); ++ } ++ if (SEEN(iE)) { ++ next_target.seen_E = 1; ++ next_target.target.E = words[iE] * STEPS_PER_MM_E; ++// sersendf_P(PSTR("E:%ld/"), next_target.target.E); ++ } ++ if (SEEN(iF)) { ++ next_target.seen_F = 1; ++ next_target.target.F = words[iF]; ++// sersendf_P(PSTR("F:%ld/"), next_target.target.F); ++ } ++ if (SEEN(iS)) { ++ next_target.seen_S = 1; ++ // if this is temperature, multiply by 4 to convert to quarter-degree units ++ // cosmetically this should be done in the temperature section, ++ // but it takes less code, less memory and loses no precision if we do it here instead ++ if ((next_target.M == 104) || (next_target.M == 109)) ++ next_target.S = words[iS] * 4.0; ++ // if this is heater PID stuff, multiply by PID_SCALE because we divide by PID_SCALE later on ++ else if ((next_target.M >= 130) && (next_target.M <= 132)) ++ next_target.S = words[iS] * PID_SCALE; ++ else ++ next_target.S = words[iS]; ++// sersendf_P(PSTR("S:%d/"), next_target.S); ++ } ++ if (SEEN(iP)) { ++ next_target.seen_P = 1; ++ next_target.P = words[iP]; ++// sersendf_P(PSTR("P:%u/"), next_target.P); ++ } ++ if (SEEN(iT)) { ++ next_target.seen_T = 1; ++ next_target.T = words[iT]; ++// sersendf_P(PSTR("T:%d/"), next_target.T); ++ } ++ if (SEEN(iN)) { ++ next_target.seen_N = 1; ++ next_target.N = words[iN]; ++// sersendf_P(PSTR("N:%lu/"), next_target.N); + } ++ next_target.N_expected = line_number; ++ if (SEEN(iAsterisk)) { ++ next_target.seen_checksum = 1; ++ next_target.checksum_read = words[iAsterisk]; ++ } ++ next_target.checksum_calculated = checksum; + +- r *= multiplicand; +- if (e) +- r = (r + powers[e] / 2) / powers[e]; ++ process_gcode_command(); ++ serial_writechar('\n'); + +- return df->sign ? -(int32_t)r : (int32_t)r; ++ seen_mask = 0; + } + + void gcode_init(void) { +@@ -94,290 +259,6 @@ void gcode_init(void) { + #endif + } + +-/// Character Received - add it to our command +-/// \param c the next character to process +-void gcode_parse_char(uint8_t c) { +- uint8_t checksum_char = c; +- +- // uppercase +- if (c >= 'a' && c <= 'z') +- c &= ~32; +- +- // process previous field +- if (last_field) { +- // check if we're seeing a new field or end of line +- // 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; +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint8(next_target.G); +- break; +- case 'M': +- next_target.M = read_digit.mantissa; +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint8(next_target.M); +- break; +- case 'X': +- if (next_target.option_inches) +- next_target.target.X = decfloat_to_int(&read_digit, 25400); +- else +- next_target.target.X = decfloat_to_int(&read_digit, 1000); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_int32(next_target.target.X); +- break; +- case 'Y': +- if (next_target.option_inches) +- next_target.target.Y = decfloat_to_int(&read_digit, 25400); +- else +- next_target.target.Y = decfloat_to_int(&read_digit, 1000); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_int32(next_target.target.Y); +- break; +- case 'Z': +- if (next_target.option_inches) +- next_target.target.Z = decfloat_to_int(&read_digit, 25400); +- else +- next_target.target.Z = decfloat_to_int(&read_digit, 1000); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_int32(next_target.target.Z); +- break; +- case 'E': +- if (next_target.option_inches) +- next_target.target.E = decfloat_to_int(&read_digit, 25400); +- else +- next_target.target.E = decfloat_to_int(&read_digit, 1000); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint32(next_target.target.E); +- break; +- case 'F': +- // just use raw integer, we need move distance and n_steps to convert it to a useful value, so wait until we have those to convert it +- if (next_target.option_inches) +- next_target.target.F = decfloat_to_int(&read_digit, 25400); +- else +- next_target.target.F = decfloat_to_int(&read_digit, 1); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint32(next_target.target.F); +- break; +- case 'S': +- // if this is temperature, multiply by 4 to convert to quarter-degree units +- // cosmetically this should be done in the temperature section, +- // but it takes less code, less memory and loses no precision if we do it here instead +- if ((next_target.M == 104) || (next_target.M == 109) || (next_target.M == 140)) +- next_target.S = decfloat_to_int(&read_digit, 4); +- // if this is heater PID stuff, multiply by PID_SCALE because we divide by PID_SCALE later on +- else if ((next_target.M >= 130) && (next_target.M <= 132)) +- next_target.S = decfloat_to_int(&read_digit, PID_SCALE); +- else +- next_target.S = decfloat_to_int(&read_digit, 1); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint16(next_target.S); +- break; +- case 'P': +- next_target.P = decfloat_to_int(&read_digit, 1); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint16(next_target.P); +- break; +- case 'T': +- next_target.T = read_digit.mantissa; +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint8(next_target.T); +- break; +- case 'N': +- next_target.N = decfloat_to_int(&read_digit, 1); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint32(next_target.N); +- break; +- case '*': +- next_target.checksum_read = decfloat_to_int(&read_digit, 1); +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serwrite_uint8(next_target.checksum_read); +- break; +- } +- // reset for next field +- last_field = 0; +- read_digit.sign = read_digit.mantissa = read_digit.exponent = 0; +- } +- } +- +- // skip comments +- if (next_target.seen_semi_comment == 0 && next_target.seen_parens_comment == 0) { +- // new field? +- if ((c >= 'A' && c <= 'Z') || c == '*') { +- last_field = c; +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serial_writechar(c); +- } +- +- // process character +- // Can't do ranges in switch..case, so process actual digits here. +- // Do it early, as there are many more digits than characters expected. +- if (c >= '0' && c <= '9') { +- if (read_digit.exponent < DECFLOAT_EXP_MAX + 1 && +- ((next_target.option_inches == 0 && +- read_digit.mantissa < DECFLOAT_MANT_MM_MAX) || +- (next_target.option_inches && +- read_digit.mantissa < DECFLOAT_MANT_IN_MAX))) { +- // this is simply mantissa = (mantissa * 10) + atoi(c) in different clothes +- read_digit.mantissa = (read_digit.mantissa << 3) + +- (read_digit.mantissa << 1) + (c - '0'); +- if (read_digit.exponent) +- read_digit.exponent++; +- } +- } +- else { +- switch (c) { +- // Each currently known command is either G or M, so preserve +- // previous G/M unless a new one has appeared. +- // FIXME: same for T command +- case 'G': +- next_target.seen_G = 1; +- next_target.seen_M = 0; +- next_target.M = 0; +- break; +- case 'M': +- next_target.seen_M = 1; +- next_target.seen_G = 0; +- next_target.G = 0; +- break; +- case 'X': +- next_target.seen_X = 1; +- break; +- case 'Y': +- next_target.seen_Y = 1; +- break; +- case 'Z': +- next_target.seen_Z = 1; +- break; +- case 'E': +- next_target.seen_E = 1; +- break; +- case 'F': +- next_target.seen_F = 1; +- break; +- case 'S': +- next_target.seen_S = 1; +- break; +- case 'P': +- next_target.seen_P = 1; +- break; +- case 'T': +- next_target.seen_T = 1; +- break; +- case 'N': +- next_target.seen_N = 1; +- break; +- case '*': +- next_target.seen_checksum = 1; +- break; +- +- // comments +- case ';': +- next_target.seen_semi_comment = 1; +- break; +- case '(': +- next_target.seen_parens_comment = 1; +- break; +- +- // now for some numeracy +- case '-': +- read_digit.sign = 1; +- // force sign to be at start of number, so 1-2 = -2 instead of -12 +- read_digit.exponent = 0; +- read_digit.mantissa = 0; +- break; +- case '.': +- if (read_digit.exponent == 0) +- read_digit.exponent = 1; +- break; +- #ifdef DEBUG +- case ' ': +- case '\t': +- case 10: +- case 13: +- // ignore +- break; +- #endif +- +- default: +- #ifdef DEBUG +- // invalid +- serial_writechar('?'); +- serial_writechar(c); +- serial_writechar('?'); +- #endif +- break; +- } +- } +- } else if ( next_target.seen_parens_comment == 1 && c == ')') +- next_target.seen_parens_comment = 0; // recognize stuff after a (comment) +- +- if (next_target.seen_checksum == 0) +- next_target.checksum_calculated = +- crc(next_target.checksum_calculated, checksum_char); +- +- // end of line +- if ((c == 10) || (c == 13)) { +- if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) +- serial_writechar(c); +- +- if ( +- #ifdef REQUIRE_LINENUMBER +- ((next_target.N >= next_target.N_expected) && (next_target.seen_N == 1)) || +- (next_target.seen_M && (next_target.M == 110)) +- #else +- 1 +- #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 +- serial_writestr_P(PSTR("ok ")); +- process_gcode_command(); +- serial_writechar('\n'); +- +- // expect next line number +- if (next_target.seen_N == 1) +- next_target.N_expected = next_target.N + 1; +- } +- else { +- sersendf_P(PSTR("rs N%ld Expected checksum %d\n"), next_target.N_expected, next_target.checksum_calculated); +-// request_resend(); +- } +- } +- else { +- sersendf_P(PSTR("rs N%ld Expected line number %ld\n"), next_target.N_expected, next_target.N_expected); +-// request_resend(); +- } +- +- // 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_T = next_target.seen_N = \ +- next_target.seen_M = next_target.seen_checksum = next_target.seen_semi_comment = \ +- next_target.seen_parens_comment = next_target.checksum_read = \ +- next_target.checksum_calculated = 0; +- // last_field and read_digit are reset above already +- +- // assume a G1 by default +- next_target.seen_G = 1; +- next_target.G = 1; +- +- if (next_target.option_all_relative) { +- next_target.target.X = next_target.target.Y = next_target.target.Z = 0; +- } +- if (next_target.option_all_relative || next_target.option_e_relative) { +- next_target.target.E = 0; +- } +- } +-} +- + /***************************************************************************\ + * * + * Request a resend of the current line - used from various places. * +diff --git a/gcode_parse.h b/gcode_parse.h +index 825bbbc..73e44c1 100644 +--- a/gcode_parse.h ++++ b/gcode_parse.h +@@ -64,8 +64,8 @@ extern GCODE_COMMAND next_target; + + void gcode_init(void); + +-/// accept the next character and process it +-void gcode_parse_char(uint8_t c); ++// once we have a whole line, process it ++void gcode_parse_line(uint8_t *c); + + // uses the global variable next_target.N + void request_resend(void); +-- +1.8.1.2 + diff --git a/attic/input-float/gcode_parse.c b/attic/input-float/gcode_parse.c new file mode 100644 index 0000000..626b694 --- /dev/null +++ b/attic/input-float/gcode_parse.c @@ -0,0 +1,274 @@ +#include "gcode_parse.h" + +/** \file + \brief Parse received G-Codes +*/ + +#include + +#include "config.h" +#include "sersendf.h" +#include "serial.h" +#include "sermsg.h" +#include "gcode_process.h" + +/// current or previous gcode word +/// for working out what to do with data just received +uint8_t last_field = 0; + +/// crude crc macro +#define crc(a, b) (a ^ b) + +#define GCODE_LINE_BUFFER_LEN 64 + +#define iA 0 +#define iB 1 +#define iC 2 +#define iD 3 +#define iE 4 +#define iF 5 +#define iG 6 +#define iH 7 +#define iI 8 +#define iJ 9 +#define iK 10 +#define iL 11 +#define iM 12 +#define iN 13 +#define iO 14 +#define iP 15 +#define iQ 16 +#define iR 17 +#define iS 18 +#define iT 19 +#define iU 20 +#define iV 21 +#define iW 22 +#define iX 23 +#define iY 24 +#define iZ 25 +#define iAsterisk 26 + +GCODE_COMMAND next_target; + +uint8_t gcode_line[GCODE_LINE_BUFFER_LEN]; +uint8_t gcode_line_pointer = 0; + +float words[32]; +uint32_t seen_mask = 0; + +#define SEEN(c) (seen_mask & (1L << (c))) + +uint32_t line_number = 0; + +const uint8_t char2index(uint8_t c) __attribute__ ((pure)); +const uint8_t char2index(uint8_t c) { + if (c >= 'a' && c <= 'z') + return c - 'a'; + if (c >= 'A' && c <= 'Z') + return c - 'A'; + if (c == '*') + return 26; + return 255; +} + +void gcode_parse_char(uint8_t c) { + if (gcode_line_pointer < (GCODE_LINE_BUFFER_LEN - 1)) + gcode_line[gcode_line_pointer++] = c; + if ((c == 13) || (c == 10)) { + uint8_t i; + for (i = gcode_line_pointer; i < GCODE_LINE_BUFFER_LEN; i++) + gcode_line[i] = 0; + if (gcode_line_pointer > 2) + gcode_parse_line(gcode_line); + gcode_line_pointer = 0; + } +} + +void gcode_parse_line(uint8_t *c) { + enum { + STATE_FIND_WORD, + STATE_FIND_VALUE, + STATE_SEMICOLON_COMMENT, + STATE_BRACKET_COMMENT, + } state = STATE_FIND_WORD; + + uint8_t i; // string index + uint8_t w = 0; // current gcode word + uint8_t checksum = 0; + + seen_mask = 0; + + // calculate checksum + for(i = 0; c[i] != '*' && c[i] != 0; i++) + checksum = checksum ^ c[i]; + + // extract gcode words from line + for (i = 0; c[i] != 0 && c[i] != 13 && c[i] != 10; i++) { + switch (state) { + case STATE_FIND_WORD: + // start of word + if (char2index(c[i]) < 255) { + w = char2index(c[i]); + state = STATE_FIND_VALUE; + } + // comment until end of line + if (c[i] == ';') + state = STATE_SEMICOLON_COMMENT; + // comment until close bracket + if (c[i] == '(') + state = STATE_BRACKET_COMMENT; + break; + case STATE_FIND_VALUE: + if ((c[i] >= '0' && c[i] <= '9') || c[i] == '-') { + uint8_t *ep; + float v = strtod((const char *) &c[i], (char **) &ep); + state = STATE_FIND_WORD; + if (ep > &c[i]) { +// sersendf_P(PSTR("[seen %c: %lx->"), w + 'A', seen_mask); + seen_mask |= (1L << w); +// sersendf_P(PSTR("%lx]"), seen_mask); + words[w] = v; + i = ep - c - 1; + } + } + break; + case STATE_BRACKET_COMMENT: + if (c[i] == ')') + state = STATE_FIND_WORD; + break; + case STATE_SEMICOLON_COMMENT: + // dummy entry to suppress compiler warning + break; + } // switch (state) + } // for i=0 .. newline + + // TODO: process line just read + + if (SEEN(iAsterisk)) { + if (checksum != words[iAsterisk]) { + if (seen_mask & iAsterisk) + sersendf_P(PSTR("rs %d "), ((uint8_t) words[iAsterisk])); + sersendf_P(PSTR("Bad checksum, received %d, expected %d\n"), ((uint8_t) words[iAsterisk]), checksum); + seen_mask = 0; + return; + } + } + + if (SEEN(iN)) { + if (((uint32_t) words[iN]) != line_number) { + sersendf_P(PSTR("Bad line number, received %ld, expected %ld\n"), ((uint32_t) words[iN]), line_number); + seen_mask = 0; + return; + } + line_number++; + } + + serial_writestr_P(PSTR("ok ")); + + // patch words into next_target struct + // TODO: eliminate next_target, update gcode_process to use words[] directly + + next_target.flags = 0; + if (SEEN(iG)) { + next_target.seen_G = 1; + next_target.G = words[iG]; +// sersendf_P(PSTR("G:%d/"), next_target.G); + } + if (SEEN(iM)) { + next_target.seen_M = 1; + next_target.M = words[iM]; +// sersendf_P(PSTR("M:%d/"), next_target.M); + } + if (SEEN(iX)) { + next_target.seen_X = 1; + next_target.target.X = words[iX] * STEPS_PER_MM_X; +// sersendf_P(PSTR("X:%ld/"), next_target.target.X); + } + if (SEEN(iY)) { + next_target.seen_Y = 1; + next_target.target.Y = words[iY] * STEPS_PER_MM_Y; +// sersendf_P(PSTR("Y:%ld/"), next_target.target.Y); + } + if (SEEN(iZ)) { + next_target.seen_Z = 1; + next_target.target.Z = words[iZ] * STEPS_PER_MM_Z; +// sersendf_P(PSTR("Z:%ld/"), next_target.target.Z); + } + if (SEEN(iE)) { + next_target.seen_E = 1; + next_target.target.E = words[iE] * STEPS_PER_MM_E; +// sersendf_P(PSTR("E:%ld/"), next_target.target.E); + } + if (SEEN(iF)) { + next_target.seen_F = 1; + next_target.target.F = words[iF]; +// sersendf_P(PSTR("F:%ld/"), next_target.target.F); + } + if (SEEN(iS)) { + next_target.seen_S = 1; + // if this is temperature, multiply by 4 to convert to quarter-degree units + // cosmetically this should be done in the temperature section, + // but it takes less code, less memory and loses no precision if we do it here instead + if ((next_target.M == 104) || (next_target.M == 109)) + next_target.S = words[iS] * 4.0; + // if this is heater PID stuff, multiply by PID_SCALE because we divide by PID_SCALE later on + else if ((next_target.M >= 130) && (next_target.M <= 132)) + next_target.S = words[iS] * PID_SCALE; + else + next_target.S = words[iS]; +// sersendf_P(PSTR("S:%d/"), next_target.S); + } + if (SEEN(iP)) { + next_target.seen_P = 1; + next_target.P = words[iP]; +// sersendf_P(PSTR("P:%u/"), next_target.P); + } + if (SEEN(iT)) { + next_target.seen_T = 1; + next_target.T = words[iT]; +// sersendf_P(PSTR("T:%d/"), next_target.T); + } + if (SEEN(iN)) { + next_target.seen_N = 1; + next_target.N = words[iN]; +// sersendf_P(PSTR("N:%lu/"), next_target.N); + } + next_target.N_expected = line_number; + if (SEEN(iAsterisk)) { + next_target.seen_checksum = 1; + next_target.checksum_read = words[iAsterisk]; + } + next_target.checksum_calculated = checksum; + + process_gcode_command(); + serial_writechar('\n'); + + seen_mask = 0; +} + +void gcode_init(void) { + // gcc guarantees us all variables are initialised to 0. + + // assume a G1 by default + next_target.seen_G = 1; + next_target.G = 1; + + #ifndef E_ABSOLUTE + next_target.option_e_relative = 1; + #endif +} + +/***************************************************************************\ +* * +* Request a resend of the current line - used from various places. * +* * +* Relies on the global variable next_target.N being valid. * +* * +\***************************************************************************/ + +void request_resend(void) { + serial_writestr_P(PSTR("rs ")); + serwrite_uint8(next_target.N); + serial_writechar('\n'); +} diff --git a/attic/input-float/gcode_parse.h b/attic/input-float/gcode_parse.h new file mode 100644 index 0000000..73e44c1 --- /dev/null +++ b/attic/input-float/gcode_parse.h @@ -0,0 +1,73 @@ +#ifndef _GCODE_PARSE_H +#define _GCODE_PARSE_H + +#include + +#include "dda.h" + +// 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.\n +/// resulting value is \f$ mantissa * 10^{-(exponent - 1)} * ((sign * 2) - 1)\f$ +typedef struct { + uint32_t mantissa; ///< the actual digits of our floating point number + uint8_t exponent :7; ///< scale mantissa by \f$10^{-exponent}\f$ + uint8_t sign :1; ///< positive or negative? +} decfloat; + +/// this holds all the possible data from a received command +typedef struct { + struct { + uint8_t seen_G :1; + uint8_t seen_M :1; + uint8_t seen_X :1; + uint8_t seen_Y :1; + uint8_t seen_Z :1; + uint8_t seen_E :1; + uint8_t seen_F :1; + uint8_t seen_S :1; + uint8_t seen_P :1; + uint8_t seen_T :1; + uint8_t seen_N :1; + uint8_t seen_checksum :1; ///< seen a checksum? + uint8_t seen_semi_comment :1; ///< seen a semicolon? + uint8_t seen_parens_comment :1; ///< seen an open parenthesis + uint8_t option_all_relative :1; ///< relative or absolute coordinates? + uint8_t option_e_relative :1; ///< same for e axis (M82/M83) + uint8_t option_inches :1; ///< inches or millimeters? + }; + + uint8_t G; ///< G command number + uint8_t M; ///< M command number + TARGET target; ///< target position: X, Y, Z, E and F + + int16_t S; ///< S word (various uses) + uint16_t P; ///< P word (various uses) + + uint8_t T; ///< T word (tool index) + + uint32_t N; ///< line number + uint32_t N_expected; ///< expected line number + + uint8_t checksum_read; ///< checksum in gcode command + uint8_t checksum_calculated; ///< checksum we calculated +} GCODE_COMMAND; + +/// the command being processed +extern GCODE_COMMAND next_target; + +void gcode_init(void); + +// once we have a whole line, process it +void gcode_parse_line(uint8_t *c); + +// uses the global variable next_target.N +void request_resend(void); + +#endif /* _GCODE_PARSE_H */