diff --git a/mendel/arduino.h b/mendel/arduino.h index b2fc8ee..246f8fd 100644 --- a/mendel/arduino.h +++ b/mendel/arduino.h @@ -11,6 +11,12 @@ #define MASK(PIN) (1 << PIN) #endif +/* + magic I/O routines + + now you can simply SET_OUTPUT(STEP); WRITE(STEP, 1); WRITE(STEP, 0); +*/ + #define _READ(IO) (IO ## _RPORT & MASK(IO ## _PIN)) #define _WRITE(IO, v) do { if (v) { IO ## _WPORT |= MASK(IO ## _PIN); } else { IO ## _WPORT &= ~MASK(IO ## _PIN); }; } while (0) #define _TOGGLE(IO) (IO ## _RPORT = MASK(IO ## _PIN)) @@ -33,6 +39,10 @@ /* ports and functions + + added as necessary or if I feel like it- not a comprehensive list! + + probably needs some #ifdefs for various chip types */ // UART diff --git a/mendel/clock.c b/mendel/clock.c index d1df4d6..75e0d00 100644 --- a/mendel/clock.c +++ b/mendel/clock.c @@ -19,7 +19,8 @@ volatile uint32_t clock = 0; // 1/4 second tick uint8_t clock_counter_250ms = 0; -volatile uint8_t clock_flag_250ms = 0; +uint8_t clock_counter_1s = 0; +volatile uint8_t clock_flag = 0; void clock_setup() { // use system clock @@ -39,17 +40,19 @@ void clock_setup() { } ISR(TIMER2_COMPA_vect) { -// WRITE(SCK, 0); // global clock #ifdef GLOBAL_CLOCK clock++; #endif // 1/4 second tick if (++clock_counter_250ms == 250) { - clock_flag_250ms = 255; + clock_flag |= CLOCK_FLAG_250MS; clock_counter_250ms = 0; + if (++clock_counter_1s == 4) { + clock_flag |= CLOCK_FLAG_1S; + clock_counter_1s = 0; + } } -// WRITE(SCK, 1); } #ifdef GLOBAL_CLOCK diff --git a/mendel/clock.h b/mendel/clock.h index 2edf7ef..59612fb 100644 --- a/mendel/clock.h +++ b/mendel/clock.h @@ -9,25 +9,24 @@ void clock_setup(void); uint32_t clock_read(void); #endif -extern volatile uint8_t clock_flag_250ms; +extern volatile uint8_t clock_flag; #define CLOCK_FLAG_250MS 1 -// #define CLOCK_FLAG_250MS_TEMPCHECK 1 -// #define CLOCK_FLAG_250MS_REPORT 2 -// #define CLOCK_FLAG_250MS_STEPTIMEOUT 4 +#define CLOCK_FLAG_1S 2 /* ifclock() {} so we can do stuff like: - ifclock(CLOCK_FLAG_250MS_REPORT) { + ifclock(CLOCK_FLAG_250MS) { report(); } or: - ifclock(CLOCK_FLAG_250MS_STEPTIMEOUT) + ifclock(CLOCK_FLAG_1S) disable_steppers(); */ -#define ifclock(F) for (;clock_flag_250ms & (F);clock_flag_250ms &= ~(F)) + +#define ifclock(F) for (;clock_flag & (F);clock_flag &= ~(F)) #endif /* _CLOCK_H */ diff --git a/mendel/dda.c b/mendel/dda.c index 703e18b..044fd00 100644 --- a/mendel/dda.c +++ b/mendel/dda.c @@ -174,25 +174,6 @@ void dda_create(DDA *dda, TARGET *target) { serwrite_uint32(dda->total_steps); serial_writechar(','); } - // if (dda->f_delta > dda->total_steps) { - // dda->f_scale = dda->f_delta / dda->total_steps; - // if (dda->f_scale > 3) { - // dda->f_delta = dda->total_steps; - // } - // else { - // // if we boost the number of steps here, many will only be F-steps which take no time- maybe we should calculate move_distance first? - // dda->f_scale = 1; - // dda->total_steps = dda->f_delta; - // } - // } - // else { - // dda->f_scale = 1; - // } - // - // if (DEBUG) { - // serwrite_uint32(dda->total_steps); serial_writechar(','); - // } - dda->x_counter = dda->y_counter = dda->z_counter = dda->e_counter = dda->f_counter = -(dda->total_steps >> 1); @@ -206,8 +187,6 @@ void dda_create(DDA *dda, TARGET *target) { if (distance < 2) distance = dda->e_delta * UM_PER_STEP_E; - // if (distance < 2) - // distance = dda->f_delta; if (DEBUG) { serwrite_uint32(distance); serial_writechar(','); @@ -241,12 +220,6 @@ void dda_create(DDA *dda, TARGET *target) { void dda_start(DDA *dda) { // called from interrupt context: keep it simple! -// if ( -// (current_position.X == dda->endpoint.X) && -// (current_position.Y == dda->endpoint.Y) && -// (current_position.Z == dda->endpoint.Z) && -// (current_position.E == dda->endpoint.E) -// ) { if (dda->nullmove) { // just change speed? current_position.F = dda->endpoint.F; @@ -310,123 +283,110 @@ void dda_step(DDA *dda) { if (DEBUG) serial_writechar('!'); - // turn 'L' light OFF so it's obvious if we froze in this routine -// if (DEBUG) -// WRITE(SCK, 0); - -// do { -// WRITE(SCK, 0); - - step_option &= ~(X_CAN_STEP | Y_CAN_STEP | Z_CAN_STEP | E_CAN_STEP | F_CAN_STEP); + step_option &= ~(X_CAN_STEP | Y_CAN_STEP | Z_CAN_STEP | E_CAN_STEP | F_CAN_STEP); // step_option |= can_step(x_min(), x_max(), current_position.X, dda->endpoint.X, dda->x_direction) & X_CAN_STEP; // step_option |= can_step(y_min(), y_max(), current_position.Y, dda->endpoint.Y, dda->y_direction) & Y_CAN_STEP; // step_option |= can_step(z_min(), z_max(), current_position.Z, dda->endpoint.Z, dda->z_direction) & Z_CAN_STEP; - step_option |= can_step(0 , 0 , current_position.X, dda->endpoint.X, dda->x_direction) & X_CAN_STEP; - step_option |= can_step(0 , 0 , current_position.Y, dda->endpoint.Y, dda->y_direction) & Y_CAN_STEP; - step_option |= can_step(0 , 0 , current_position.Z, dda->endpoint.Z, dda->z_direction) & Z_CAN_STEP; - step_option |= can_step(0 , 0 , current_position.E, dda->endpoint.E, dda->e_direction) & E_CAN_STEP; - step_option |= can_step(0 , 0 , current_position.F, dda->endpoint.F, dda->f_direction) & F_CAN_STEP; + step_option |= can_step(0 , 0 , current_position.X, dda->endpoint.X, dda->x_direction) & X_CAN_STEP; + step_option |= can_step(0 , 0 , current_position.Y, dda->endpoint.Y, dda->y_direction) & Y_CAN_STEP; + step_option |= can_step(0 , 0 , current_position.Z, dda->endpoint.Z, dda->z_direction) & Z_CAN_STEP; + step_option |= can_step(0 , 0 , current_position.E, dda->endpoint.E, dda->e_direction) & E_CAN_STEP; + step_option |= can_step(0 , 0 , current_position.F, dda->endpoint.F, dda->f_direction) & F_CAN_STEP; - if (step_option & X_CAN_STEP) { - dda->x_counter -= dda->x_delta; - if (dda->x_counter < 0) { - step_option |= REAL_MOVE; + if (step_option & X_CAN_STEP) { + dda->x_counter -= dda->x_delta; + if (dda->x_counter < 0) { + step_option |= REAL_MOVE; - x_step(); - if (dda->x_direction) - current_position.X++; - else - current_position.X--; + x_step(); + if (dda->x_direction) + current_position.X++; + else + current_position.X--; - dda->x_counter += dda->total_steps; + dda->x_counter += dda->total_steps; + } + } + + if (step_option & Y_CAN_STEP) { + dda->y_counter -= dda->y_delta; + if (dda->y_counter < 0) { + step_option |= REAL_MOVE; + + y_step(); + if (dda->y_direction) + current_position.Y++; + else + current_position.Y--; + + dda->y_counter += dda->total_steps; + } + } + + if (step_option & Z_CAN_STEP) { + dda->z_counter -= dda->z_delta; + if (dda->z_counter < 0) { + step_option |= REAL_MOVE; + + z_step(); + if (dda->z_direction) + current_position.Z++; + else + current_position.Z--; + + dda->z_counter += dda->total_steps; + } + } + + if (step_option & E_CAN_STEP) { + dda->e_counter -= dda->e_delta; + if (dda->e_counter < 0) { + step_option |= REAL_MOVE; + + e_step(); + if (dda->e_direction) + current_position.E++; + else + current_position.E--; + + dda->e_counter += dda->total_steps; + } + } + + if (step_option & F_CAN_STEP) { + dda->f_counter -= dda->f_delta; + // since we don't allow total_steps to be defined by F, we may need to step multiple times if f_delta is greater than total_steps + while (dda->f_counter < 0) { + + dda->f_counter += dda->total_steps; + + if (dda->f_direction) { + current_position.F += 1; + if (current_position.F > dda->endpoint.F) + current_position.F = dda->endpoint.F; } - } - - if (step_option & Y_CAN_STEP) { - dda->y_counter -= dda->y_delta; - if (dda->y_counter < 0) { - step_option |= REAL_MOVE; - - y_step(); - if (dda->y_direction) - current_position.Y++; - else - current_position.Y--; - - dda->y_counter += dda->total_steps; + else { + current_position.F -= 1; + if (current_position.F < dda->endpoint.F) + current_position.F = dda->endpoint.F; } + + step_option |= F_REAL_STEP; } + } - if (step_option & Z_CAN_STEP) { - dda->z_counter -= dda->z_delta; - if (dda->z_counter < 0) { - step_option |= REAL_MOVE; - - z_step(); - if (dda->z_direction) - current_position.Z++; - else - current_position.Z--; - - dda->z_counter += dda->total_steps; - } - } - - if (step_option & E_CAN_STEP) { - dda->e_counter -= dda->e_delta; - if (dda->e_counter < 0) { - step_option |= REAL_MOVE; - - e_step(); - if (dda->e_direction) - current_position.E++; - else - current_position.E--; - - dda->e_counter += dda->total_steps; - } - } - - if (step_option & F_CAN_STEP) { - dda->f_counter -= dda->f_delta; - // since we don't allow total_steps to be defined by F, we may need to step multiple times if f_delta is greater than total_steps - while (dda->f_counter < 0) { - - dda->f_counter += dda->total_steps; - - if (dda->f_direction) { - current_position.F += 1; - if (current_position.F > dda->endpoint.F) - current_position.F = dda->endpoint.F; - } - else { - current_position.F -= 1; - if (current_position.F < dda->endpoint.F) - current_position.F = dda->endpoint.F; - } - - step_option |= F_REAL_STEP; - } - } - - // this generates too much debug output for all but the slowest step rates - if (0 && DEBUG) { - serial_writechar('['); - serwrite_hex8(step_option); - serial_writechar(':'); - serwrite_int32(current_position.F); - serial_writechar('/'); - serwrite_int32(dda->endpoint.F); - serial_writechar('#'); - serwrite_uint32(dda->move_duration); - serial_writechar(']'); - } - -// WRITE(SCK, 1); - -// } while ( ((step_option & REAL_MOVE ) == 0) && -// ((step_option & F_CAN_STEP) != 0) ); -// } while (0); + // this generates too much debug output for all but the slowest step rates + if (0 && DEBUG) { + serial_writechar('['); + serwrite_hex8(step_option); + serial_writechar(':'); + serwrite_int32(current_position.F); + serial_writechar('/'); + serwrite_int32(dda->endpoint.F); + serial_writechar('#'); + serwrite_uint32(dda->move_duration); + serial_writechar(']'); + } if (step_option & REAL_MOVE) // we stepped, reset timeout @@ -453,8 +413,4 @@ void dda_step(DDA *dda) { // turn off step outputs, hopefully they've been on long enough by now to register with the drivers // if not, too bad. or insert a (very!) small delay here, or fire up a spare timer or something unstep(); - - // turn 'L' light back on again -// if (DEBUG) -// WRITE(SCK, 1); } diff --git a/mendel/dda.h b/mendel/dda.h index 4249810..8a3679a 100644 --- a/mendel/dda.h +++ b/mendel/dda.h @@ -10,6 +10,7 @@ types */ +// target is simply a point in space/time typedef struct { int32_t X; int32_t Y; @@ -18,8 +19,8 @@ typedef struct { uint32_t F; } TARGET; +// this is a digital differential analyser data struct, holding both the initial values and the counters as it progresses typedef struct { -// TARGET currentpoint; TARGET endpoint; uint8_t x_direction :1; @@ -51,9 +52,13 @@ typedef struct { variables */ +// steptimeout is set to zero when we step, and increases over time so we can turn the motors off when they've been idle for a while extern uint8_t steptimeout; +// startpoint holds the end position of the most recently created DDA, so we know where the next one starts extern TARGET startpoint; + +// current_position holds the machine's current position. this is only updated when we step, or when G92 (set home) is received. extern TARGET current_position; /* diff --git a/mendel/dda_queue.c b/mendel/dda_queue.c index b2dbc78..b768a08 100644 --- a/mendel/dda_queue.c +++ b/mendel/dda_queue.c @@ -8,7 +8,6 @@ uint8_t mb_head = 0; uint8_t mb_tail = 0; DDA movebuffer[MOVEBUFFER_SIZE]; - uint8_t queue_full() { if (mb_tail == 0) return mb_head == (MOVEBUFFER_SIZE - 1); @@ -21,6 +20,7 @@ uint8_t queue_empty() { } void enqueue(TARGET *t) { + // don't call this function when the queue is full, but just in case, wait for a move to complete and free up the space for the passed target while (queue_full()) delay(WAITING_DELAY); @@ -31,24 +31,24 @@ void enqueue(TARGET *t) { dda_create(&movebuffer[h], t); - // if queue only has one space left, stop transmition - if (((h + 2) & (MOVEBUFFER_SIZE - 1)) == mb_tail) - xoff(); - mb_head = h; + #ifdef XONXOFF + // if queue is full, stop transmition + if (queue_full()) + xoff(); + #endif + // fire up in case we're not running yet enableTimerInterrupt(); } void next_move() { if (queue_empty()) { - // queue is empty -// disable_steppers(); -// setTimer(DEFAULT_TICK); disableTimerInterrupt(); } else { + // next item uint8_t t = mb_tail; t++; if (t == MOVEBUFFER_SIZE) @@ -56,8 +56,11 @@ void next_move() { dda_start(&movebuffer[t]); mb_tail = t; } - // restart transmission - xon(); + + #ifdef XONXOFF + // restart transmission + xon(); + #endif } void print_queue() { diff --git a/mendel/dda_queue.h b/mendel/dda_queue.h index 86a7185..c3176b0 100644 --- a/mendel/dda_queue.h +++ b/mendel/dda_queue.h @@ -7,6 +7,7 @@ variables */ +// this is the ringbuffer that holds the current and pending moves. extern uint8_t mb_head; extern uint8_t mb_tail; extern DDA movebuffer[MOVEBUFFER_SIZE]; @@ -15,10 +16,17 @@ extern DDA movebuffer[MOVEBUFFER_SIZE]; methods */ +// queue status methods uint8_t queue_full(void); uint8_t queue_empty(void); + +// add a new target to the queue void enqueue(TARGET *t); + +// called from step timer when current move is complete void next_move(void); + +// print queue status void print_queue(void); #endif /* _DDA_QUEUE */ diff --git a/mendel/gcode.c b/mendel/gcode.c index 594dc79..12479fa 100644 --- a/mendel/gcode.c +++ b/mendel/gcode.c @@ -69,6 +69,10 @@ int32_t decfloat_to_int(decfloat *df, int32_t multiplicand, int32_t denominator) return r; } +/* + public functions +*/ + void SpecialMoveXY(int32_t x, int32_t y, uint32_t f) { TARGET t = startpoint; t.X = x; @@ -91,10 +95,6 @@ void SpecialMoveE(int32_t e, uint32_t f) { enqueue(&t); } -/* - public functions -*/ - void scan_char(uint8_t c) { static uint8_t last_field = 0; @@ -102,38 +102,61 @@ void scan_char(uint8_t c) { if (c >= 'a' && c <= 'z') c &= ~32; - // process field + // 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)) { switch (last_field) { case 'G': next_target.G = read_digit.mantissa; - serwrite_uint8(next_target.G); + if (DEBUG) + serwrite_uint8(next_target.G); break; case 'M': next_target.M = read_digit.mantissa; - serwrite_uint8(next_target.M); + if (DEBUG) + serwrite_uint8(next_target.M); break; case 'X': - next_target.target.X = decfloat_to_int(&read_digit, STEPS_PER_MM_X, 1); - serwrite_int32(next_target.target.X); + if (next_target.option_inches) + next_target.target.X = decfloat_to_int(&read_digit, STEPS_PER_IN_X, 1); + else + next_target.target.X = decfloat_to_int(&read_digit, STEPS_PER_MM_X, 1); + if (DEBUG) + serwrite_int32(next_target.target.X); break; case 'Y': - next_target.target.Y = decfloat_to_int(&read_digit, STEPS_PER_MM_Y, 1); - serwrite_int32(next_target.target.Y); + if (next_target.option_inches) + next_target.target.Y = decfloat_to_int(&read_digit, STEPS_PER_IN_Y, 1); + else + next_target.target.Y = decfloat_to_int(&read_digit, STEPS_PER_MM_Y, 1); + if (DEBUG) + serwrite_int32(next_target.target.Y); break; case 'Z': - next_target.target.Z = decfloat_to_int(&read_digit, STEPS_PER_MM_Z, 1); - serwrite_int32(next_target.target.Z); + if (next_target.option_inches) + next_target.target.Z = decfloat_to_int(&read_digit, STEPS_PER_IN_Z, 1); + else + next_target.target.Z = decfloat_to_int(&read_digit, STEPS_PER_MM_Z, 1); + if (DEBUG) + serwrite_int32(next_target.target.Z); break; case 'E': - next_target.target.E = decfloat_to_int(&read_digit, STEPS_PER_MM_E, 1); - serwrite_uint32(next_target.target.E); + if (next_target.option_inches) + next_target.target.E = decfloat_to_int(&read_digit, STEPS_PER_IN_E, 1); + else + next_target.target.E = decfloat_to_int(&read_digit, STEPS_PER_MM_E, 1); + if (DEBUG) + 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 - next_target.target.F = decfloat_to_int(&read_digit, 1, 1); - serwrite_uint32(next_target.target.F); + if (next_target.option_inches) + next_target.target.F = decfloat_to_int(&read_digit, 254, 10); + else + next_target.target.F = decfloat_to_int(&read_digit, 1, 1); + if (DEBUG) + serwrite_uint32(next_target.target.F); break; case 'S': // if this is temperature, multiply by 4 to convert to quarter-degree units @@ -141,9 +164,13 @@ void scan_char(uint8_t c) { // but it takes less code, less memory and loses no precision if we do it here instead if (next_target.M == 104) next_target.S = decfloat_to_int(&read_digit, 4, 1); + // 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, 1); else next_target.S = decfloat_to_int(&read_digit, 1, 1); - serwrite_uint16(next_target.S); + if (DEBUG) + serwrite_uint16(next_target.S); break; case 'P': // if this is dwell, multiply by 1000 to convert seconds to milliseconds @@ -151,9 +178,11 @@ void scan_char(uint8_t c) { next_target.P = decfloat_to_int(&read_digit, 1000, 1); else next_target.P = decfloat_to_int(&read_digit, 1, 1); - serwrite_uint16(next_target.P); + if (DEBUG) + serwrite_uint16(next_target.P); break; } + // reset for next field last_field = 0; read_digit.sign = 0; read_digit.mantissa = 0; @@ -161,11 +190,13 @@ void scan_char(uint8_t c) { } } - // not in a comment? + // skip comments if ((option_bitfield & OPTION_COMMENT) == 0) { + // new field? if (indexof(c, alphabet) >= 0) { last_field = c; - serial_writechar(c); + if (DEBUG) + serial_writechar(c); } // process character @@ -223,6 +254,7 @@ void scan_char(uint8_t c) { default: // can't do ranges in switch..case, so process actual digits here if (c >= '0' && c <= '9') { + // 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++; @@ -230,14 +262,13 @@ void scan_char(uint8_t c) { } } - // got a command + // end of line if (c == 10) { serial_writechar(c); // process process_gcode_command(&next_target); - // save options -// option_bitfield = next_target.option; + // reset 'seen comment' option_bitfield &= ~OPTION_COMMENT; // reset variables @@ -252,7 +283,6 @@ void scan_char(uint8_t c) { } void process_gcode_command(GCODE_COMMAND *gcmd) { - // convert relative to absolute if (gcmd->option_relative) { gcmd->target.X += startpoint.X; @@ -261,7 +291,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { gcmd->target.E += startpoint.E; } - // explicitly make unseen values equal to startpoint, otherwise relative position mode is a clusterfuck and who knows what other bugs could occur? + // explicitly make unseen values equal to startpoint, otherwise relative position mode is a clusterfuck if (gcmd->seen_X == 0) gcmd->target.X = startpoint.X; if (gcmd->seen_Y == 0) @@ -288,14 +318,20 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { break; // G2 - Arc Clockwise + // unimplemented // G3 - Arc Counter-clockwise + // unimplemented // G4 - Dwell case 4: - xoff(); + #ifdef XONXOFF + xoff(); + #endif delay_ms(gcmd->P); - xon(); + #ifdef XONXOFF + xon(); + #endif break; // G20 - inches as units @@ -312,6 +348,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { case 30: enqueue(&gcmd->target); // no break here, G30 is move and then go home + // G28 - go home case 28: /* @@ -321,6 +358,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { SpecialMoveXY(-250L * STEPS_PER_MM_X, -250L * STEPS_PER_MM_Y, FEEDRATE_FAST_XY); startpoint.X = startpoint.Y = 0; + // move forward a bit SpecialMoveXY(5 * STEPS_PER_MM_X, 5 * STEPS_PER_MM_Y, FEEDRATE_SLOW_XY); // move back in to endstops slowly @@ -339,7 +377,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { SpecialMoveZ(-250L * STEPS_PER_MM_Z, FEEDRATE_FAST_Z); startpoint.Z = 0; - // move out a bit + // move forward a bit SpecialMoveZ(5 * STEPS_PER_MM_Z, FEEDRATE_SLOW_Z); // move back into endstop slowly @@ -354,7 +392,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { /* Home E */ - // extruder only runs one way anyway + // extruder only runs one way and we have no "endstop", just set this point as home startpoint.E = current_position.E = 0; break; @@ -374,7 +412,7 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { startpoint.X = startpoint.Y = startpoint.Z = startpoint.E = 0; break; - // TODO: spit an error + // unknown gcode: spit an error default: serial_writestr_P(PSTR("E: Bad G-code ")); serwrite_uint8(gcmd->G); @@ -435,14 +473,29 @@ void process_gcode_command(GCODE_COMMAND *gcmd) { case 106: WRITE(FAN_PIN, 1); break; - // M107- fan off case 107: WRITE(FAN_PIN, 0); break; #endif - // TODO: spit an error + // M130- heater P factor + case 130: + if (gcmd->seen_S) + p_factor = gcmd->S; + break; + // M131- heater I factor + case 131: + if (gcmd->seen_S) + i_factor = gcmd->S; + break; + // M132- heater D factor + case 132: + if (gcmd->seen_S) + d_factor = gcmd->S; + break; + + // unknown mcode: spit an error default: serial_writestr_P(PSTR("E: Bad M-code ")); serwrite_uint8(gcmd->M); diff --git a/mendel/gcode.h b/mendel/gcode.h index 4a7ff24..b0187ea 100644 --- a/mendel/gcode.h +++ b/mendel/gcode.h @@ -5,12 +5,14 @@ #include "dda.h" +// 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; uint32_t mantissa :24; uint32_t exponent :7; } decfloat; +// this holds all the possible data from a received command typedef struct { uint8_t seen_G :1; uint8_t seen_M :1; @@ -30,16 +32,26 @@ typedef struct { uint8_t M; TARGET target; - uint16_t S; + int16_t S; uint16_t P; } GCODE_COMMAND; +// the command being processed extern GCODE_COMMAND next_target; +// utility functions int8_t indexof(uint8_t c, const char *string); int32_t decfloat_to_int(decfloat *df, int32_t multiplicand, int32_t denominator); +// this is where we construct a move without a gcode command, useful for gcodes which require multiple moves eg; homing +void SpecialMoveXY(int32_t x, int32_t y, uint32_t f); +void SpecialMoveZ(int32_t z, uint32_t f); +void SpecialMoveE(int32_t e, uint32_t f); + +// accept the next character and process it void scan_char(uint8_t c); + +// when we have a whole line, feed it to this void process_gcode_command(GCODE_COMMAND *gcmd); #endif /* _GCODE_H */ diff --git a/mendel/machine.h b/mendel/machine.h index 9e29dc6..f3225f5 100644 --- a/mendel/machine.h +++ b/mendel/machine.h @@ -3,8 +3,8 @@ /* move buffer size, in number of moves - note that each move takes a fair chunk of ram (71 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 moves, as each move takes a bunch of math to set up + 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 @@ -12,9 +12,11 @@ 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 more speed than precision on Z, turn off microstepping + +// we need far more speed than precision on Z due to the threaded rod drive, turn off microstepping #define Z_STEPS_PER_REV 200.0 #define X_COG_CIRCUMFERENCE (4.77 * 16.0) @@ -22,6 +24,8 @@ // 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 @@ -30,6 +34,26 @@ #define EXTRUDER_INLET_DIAMETER 3.0 #define EXTRUDER_NOZZLE_DIAMETER 0.8 +// these feedrates are used during homing and G0 rapid moves +#define FEEDRATE_FAST_XY 6000 +#define FEEDRATE_SLOW_XY 300 + +#define FEEDRATE_FAST_Z 6000 +#define FEEDRATE_SLOW_Z 300 + +#define FEEDRATE_FAST_E 1200 + +// this is how many steps to suck back the filament by when we stop +#define E_STARTSTOP_STEPS 20 + +// extruder settings +#define TEMP_HYSTERESIS 2 + +/* + 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! +*/ + #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)) @@ -39,33 +63,19 @@ // 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)) + 0.5)) -#define FEEDRATE_FAST_XY 6000 -#define FEEDRATE_SLOW_XY 300 -#define FEEDRATE_FAST_Z 6000 -#define FEEDRATE_SLOW_Z 300 - -#define E_STARTSTOP_STEPS 20 -#define FEEDRATE_FAST_E 1200 - -/* - extruder settings -*/ - -#define TEMP_HYSTERESIS 2 - -/* - 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! -*/ +// 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_SHAFT_RADIUS * PI * EXTRUDER_INLET_DIAMETER)) + 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)) -/* - should be the same for all machines! ;) -*/ +// should be the same for all machines! ;) #define PI 3.1415926535 #endif /* _MACHINE_H */ diff --git a/mendel/mendel.c b/mendel/mendel.c index 901e433..eecd4a9 100644 --- a/mendel/mendel.c +++ b/mendel/mendel.c @@ -72,8 +72,6 @@ inline void init(void) { clock_setup(); // set up variables - - // slow default current_position.F = FEEDRATE_SLOW_Z; memcpy(&startpoint, ¤t_position, sizeof(TARGET)); memcpy(&(next_target.target), ¤t_position, sizeof(TARGET)); @@ -83,14 +81,10 @@ inline void init(void) { // say hi to host serial_writestr_P(PSTR("Start\n")); - - // start queue - //enableTimerInterrupt(); } +void clock_250ms(void); void clock_250ms() { - static uint8_t report = 0; - temp_tick(); if (steptimeout > (30 * 4)) @@ -98,18 +92,13 @@ void clock_250ms() { else steptimeout++; - report++; - if (report == 4) { - report = 0; - + ifclock (CLOCK_FLAG_1S) { if (DEBUG) { // current move serial_writestr_P(PSTR("DDA: f#")); - serwrite_int32(movebuffer[mb_head].f_counter); + serwrite_int32(movebuffer[mb_tail].f_counter); serial_writechar('/'); - // serwrite_uint16(movebuffer[mb_head].f_scale); - // serial_writechar('/'); - serwrite_int16(movebuffer[mb_head].f_delta); + serwrite_int16(movebuffer[mb_tail].f_delta); serial_writechar('\n'); // current position @@ -155,7 +144,7 @@ int main (void) for (;;) { // if queue is full, no point in reading chars- host will just have to wait - if (serial_rxchars() && !queue_full()) { + if ((serial_rxchars() != 0) && (queue_full() == 0)) { uint8_t c = serial_popchar(); scan_char(c); } @@ -163,66 +152,5 @@ int main (void) ifclock(CLOCK_FLAG_250MS) { clock_250ms(); } - -// ifclock(CLOCK_FLAG_250MS_TEMPCHECK) { -// temp_tick(); -// } -// -// ifclock(CLOCK_FLAG_250MS_STEPTIMEOUT) { -// if (steptimeout > (30 * 4)) -// disable_steppers(); -// else -// steptimeout++; -// } -// -// ifclock(CLOCK_FLAG_250MS_REPORT) { -// report++; -// if (report == 4) { -// report = 0; -// -// if (DEBUG) { -// // current move -// serial_writestr_P(PSTR("DDA: f#")); -// serwrite_int32(movebuffer[mb_head].f_counter); -// serial_writechar('/'); -// // serwrite_uint16(movebuffer[mb_head].f_scale); -// // serial_writechar('/'); -// serwrite_int16(movebuffer[mb_head].f_delta); -// serial_writechar('\n'); -// -// // current position -// serial_writestr_P(PSTR("Pos: ")); -// serwrite_int32(current_position.X); -// serial_writechar(','); -// serwrite_int32(current_position.Y); -// serial_writechar(','); -// serwrite_int32(current_position.Z); -// serial_writechar(','); -// serwrite_int32(current_position.E); -// serial_writechar(','); -// serwrite_uint32(current_position.F); -// serial_writechar('\n'); -// -// // target position -// serial_writestr_P(PSTR("Dst: ")); -// serwrite_int32(movebuffer[mb_tail].endpoint.X); -// serial_writechar(','); -// serwrite_int32(movebuffer[mb_tail].endpoint.Y); -// serial_writechar(','); -// serwrite_int32(movebuffer[mb_tail].endpoint.Z); -// serial_writechar(','); -// serwrite_int32(movebuffer[mb_tail].endpoint.E); -// serial_writechar(','); -// serwrite_uint32(movebuffer[mb_tail].endpoint.F); -// serial_writechar('\n'); -// } -// -// // Queue -// print_queue(); -// -// // temperature -// temp_print(); -// } -// } } } diff --git a/mendel/pinout.h b/mendel/pinout.h index 246a71d..4d0a687 100644 --- a/mendel/pinout.h +++ b/mendel/pinout.h @@ -39,7 +39,7 @@ #define STEPPER_ENABLE_PIN DIO9 // list of PWM-able pins and corresponding timers -// timer1 is reserved for step timing +// timer1 is used for step timing so don't use OC1A/OC1B (DIO9/DIO10) // OC0A DIO6 // OC0B DIO5 // OC1A DIO9 @@ -132,7 +132,7 @@ */ #ifdef STEPPER_ENABLE_PIN - // for connection to stepper driver ENABLE pins + // for connection to stepper driver ENABLE pins (negative asserted) // #define enable_steppers() WRITE(STEPPER_ENABLE_PIN, 0) // #define disable_steppers() WRITE(STEPPER_ENABLE_PIN, 1) // for connection to ATX PSU PWR_ON signal diff --git a/mendel/ringbuffer.c b/mendel/ringbuffer.c index de4ce94..2f2636e 100644 --- a/mendel/ringbuffer.c +++ b/mendel/ringbuffer.c @@ -43,7 +43,6 @@ void ringbuffer_writechar(ringbuffer *buf, uint8_t data) } } - uint8_t ringbuffer_peekchar(ringbuffer *buf, RB_BITS index) { return buf->data[_rb_mod(buf->read_pointer + index, buf->size)]; diff --git a/mendel/ringbuffer.h b/mendel/ringbuffer.h index d618d3b..7635ed8 100644 --- a/mendel/ringbuffer.h +++ b/mendel/ringbuffer.h @@ -4,6 +4,7 @@ #include #include +// ringbuffer head/tail/length precision. change to uint16_t if you want a buffer bigger than 252 bytes or so #define RB_BITS uint8_t typedef struct { @@ -13,15 +14,19 @@ typedef struct { volatile RB_BITS data[]; } ringbuffer; +// initialize a ringbuffer void ringbuffer_init(ringbuffer *buf, RB_BITS bufsize); +// return how many bytes can be read or written RB_BITS ringbuffer_canread(ringbuffer *buf); RB_BITS ringbuffer_canwrite(ringbuffer *buf); +// read bytes uint8_t ringbuffer_readchar(ringbuffer *buf); uint8_t ringbuffer_peekchar(ringbuffer *buf, RB_BITS index); RB_BITS ringbuffer_readblock(ringbuffer *buf, uint8_t *newbuf, RB_BITS size); +// write bytes void ringbuffer_writechar(ringbuffer *buf, uint8_t data); RB_BITS ringbuffer_writeblock(ringbuffer *buf, uint8_t *data, RB_BITS size); diff --git a/mendel/serial.c b/mendel/serial.c index d895e82..c431bce 100644 --- a/mendel/serial.c +++ b/mendel/serial.c @@ -121,15 +121,17 @@ void serial_writestr_P(PGM_P data) serial_writechar(r); } -void xoff() { - flowflags = FLOWFLAG_SEND_XOFF; - // enable TX interrupt so we can send this character - UCSR0B |= (1 << UDRIE0); -} +#ifdef XONXOFF + void xon() { + if (flowflags & FLOWFLAG_SENT_XOFF) + flowflags = FLOWFLAG_SEND_XON; + // enable TX interrupt so we can send this character + UCSR0B |= (1 << UDRIE0); + } -void xon() { - if (flowflags & FLOWFLAG_SENT_XOFF) - flowflags = FLOWFLAG_SEND_XON; - // enable TX interrupt so we can send this character - UCSR0B |= (1 << UDRIE0); -} + void xoff() { + flowflags = FLOWFLAG_SEND_XOFF; + // enable TX interrupt so we can send this character + UCSR0B |= (1 << UDRIE0); + } +#endif diff --git a/mendel/serial.h b/mendel/serial.h index 19597f7..2abc440 100644 --- a/mendel/serial.h +++ b/mendel/serial.h @@ -5,23 +5,31 @@ #include #include +// initialise serial subsystem void serial_init(void); +// return number of characters in the receive and send buffer uint16_t serial_rxchars(void); uint16_t serial_txchars(void); +// read one character uint8_t serial_popchar(void); +// send one character void serial_writechar(uint8_t data); +// read/write many characters uint16_t serial_recvblock(uint8_t *block, int blocksize); void serial_writeblock(void *data, int datalen); +// 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); -void xoff(void); -void xon(void); +#ifdef XONXOFF + // XON/XOFF flow control + void xoff(void); + void xon(void); +#endif #endif /* _SERIAL_H */ diff --git a/mendel/sermsg.h b/mendel/sermsg.h index 647a053..5566f66 100644 --- a/mendel/sermsg.h +++ b/mendel/sermsg.h @@ -3,11 +3,13 @@ #include +// functions for sending hexadecimal void serwrite_hex4(uint8_t v); void serwrite_hex8(uint8_t v); void serwrite_hex16(uint16_t v); void serwrite_hex32(uint32_t v); +// functions for sending decimal #define serwrite_uint8(v) serwrite_uint32(v) #define serwrite_int8(v) serwrite_int32(v) #define serwrite_uint16(v) serwrite_uint32(v) diff --git a/mendel/temp.c b/mendel/temp.c index 524f18e..56f9c86 100644 --- a/mendel/temp.c +++ b/mendel/temp.c @@ -39,8 +39,6 @@ uint8_t temp_flags = 0; #define TEMP_FLAG_PRESENT 1 #define TEMP_FLAG_TCOPEN 2 -#define PID_SCALE 1024L - uint16_t temp_read() { uint16_t temp; SPCR = MASK(MSTR) | MASK(SPE); @@ -55,6 +53,10 @@ uint16_t temp_read() { for (;(SPSR & MASK(SPIF)) == 0;); temp |= SPDR; + WRITE(SS, 0); + + SPCR = 0; + temp_flags = 0; if ((temp & 0x8002) == 0) { // got "device id" @@ -69,10 +71,6 @@ uint16_t temp_read() { } } - WRITE(SS, 0); - - SPCR = 0; - return 0; } @@ -110,15 +108,26 @@ void temp_tick() { uint16_t last_temp = current_temp; temp_read(); - int16_t t_delta = target_temp - current_temp; + int16_t t_error = target_temp - current_temp; // PID stuff - heater_p = t_delta; - heater_i += t_delta; - // note: D follows temp rather than error so there's no large derivative when the target temperature changes + // proportional + heater_p = t_error; + + // integral + heater_i += t_error; + // prevent integrator wind-up + if (heater_i > I_LIMIT) + heater_i = I_LIMIT; + else if (heater_i < -I_LIMIT) + heater_i = -I_LIMIT; + + // derivative + // note: D follows temp rather than error so there's no large derivative when the target changes heater_d = (current_temp - last_temp); - uint8_t pid_output = ( + // combine factors + int32_t pid_output_intermed = ( ( (((int32_t) heater_p) * p_factor) + (((int32_t) heater_i) * i_factor) + @@ -126,6 +135,15 @@ void temp_tick() { ) / PID_SCALE ); + // rebase and limit factors + uint8_t pid_output; + if (pid_output_intermed > 127) + pid_output = 255; + else if (pid_output_intermed < -128) + pid_output = 0; + else + pid_output = (pid_output_intermed + 128); + #ifdef HEATER_PIN_PWMABLE HEATER_PIN_PWMABLE = pid_output #else diff --git a/mendel/temp.h b/mendel/temp.h index 7b8312a..4ca4dbb 100644 --- a/mendel/temp.h +++ b/mendel/temp.h @@ -3,11 +3,31 @@ #include +// extruder heater PID factors +// google "PID without a PHD" if you don't understand this PID stuff +extern int32_t p_factor; +extern int32_t i_factor; +extern int32_t d_factor; + +#define PID_SCALE 1024L +#define I_LIMIT 4000 + +// read temperature from sensor uint16_t temp_read(void); + +// set target temperature void temp_set(uint16_t t); + +// return last read temperature uint16_t temp_get(void); + +// true if last read temp is close to target temp, false otherwise uint8_t temp_achieved(void); + +// send current temperature to host void temp_print(void); + +// periodically read temperature and update heater with PID void temp_tick(void); #endif /* _TIMER_H */ diff --git a/mendel/timer.c b/mendel/timer.c index 0dce8f9..de63983 100644 --- a/mendel/timer.c +++ b/mendel/timer.c @@ -7,7 +7,6 @@ #include "dda.h" ISR(TIMER1_COMPA_vect) { -// WRITE(SCK, 0); if (movebuffer[mb_tail].live) { // this interrupt can be interruptible // TODO: remove when not debugging @@ -19,19 +18,14 @@ ISR(TIMER1_COMPA_vect) { // cli(); // enableTimerInterrupt(); } -// WRITE(SCK, 1); - // perhaps we can fall directly into dda_start instead of waiting for another step - if (movebuffer[mb_tail].live == 0) { + // fall directly into dda_start instead of waiting for another step + if (movebuffer[mb_tail].live == 0) next_move(); - } - } void setupTimerInterrupt() { - //clear the registers - // no outputs TCCR1A = 0; // CTC mode @@ -43,6 +37,8 @@ void setupTimerInterrupt() setTimer(10000); } +// the following are all from reprap project 5D firmware + uint8_t getTimerResolution(const uint32_t delay) { // these also represent frequency: 1000000 / delay / 2 = frequency in hz. @@ -142,7 +138,6 @@ void delay_ms(uint32_t delay) { delayMicrosecondsInterruptible(delay * 1000); } -// from reprap project 5D firmware void delayMicrosecondsInterruptible(uint16_t us) { // for a one-microsecond delay, simply return. the overhead diff --git a/mendel/timer.h b/mendel/timer.h index 27410c3..a85ee1d 100644 --- a/mendel/timer.h +++ b/mendel/timer.h @@ -8,13 +8,16 @@ #define US * (F_CPU / 1000000) #define MS * (F_CPU / 1000) -#define DEFAULT_TICK (100 US) +// #define DEFAULT_TICK (100 US) #define WAITING_DELAY (10 MS) void setupTimerInterrupt(void); + uint8_t getTimerResolution(const uint32_t delay); void setTimerResolution(uint8_t r); + uint16_t getTimerCeiling(const uint32_t delay); +#define setTimerCeiling(c) OCR1A = c void setTimer(uint32_t delay); @@ -28,6 +31,4 @@ void delayMicrosecondsInterruptible(unsigned int us); #define enableTimerInterrupt() do { TIMSK1 |= (1<