Move the branch input-float into an attic.
This was a very interesting approach, but for the forseeable future it's unlikely the code will replace the current one.
This commit is contained in:
parent
fc4bfca06a
commit
569adeecd1
|
|
@ -0,0 +1,672 @@
|
|||
From cb05c9dda6bb8da6dbf6bbbbc64e79b069d8b2a0 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Moon <triffid.hunter@gmail.com>
|
||||
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 <string.h>
|
||||
+#include <stdlib.h>
|
||||
|
||||
+#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
|
||||
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
#include "gcode_parse.h"
|
||||
|
||||
/** \file
|
||||
\brief Parse received G-Codes
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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');
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef _GCODE_PARSE_H
|
||||
#define _GCODE_PARSE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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 */
|
||||
Loading…
Reference in New Issue