diff --git a/.cproject b/.cproject new file mode 100644 index 000000000..ddced78d3 --- /dev/null +++ b/.cproject @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 000000000..94ec9251a --- /dev/null +++ b/.project @@ -0,0 +1,26 @@ + + + grbl_atmega + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/atmega328p.ld b/atmega328p.ld new file mode 100644 index 000000000..5b8c238c8 --- /dev/null +++ b/atmega328p.ld @@ -0,0 +1,228 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr") +OUTPUT_ARCH(avr:5) +MEMORY +{ + text (rx) : ORIGIN = 0, LENGTH = 32K + data (rw!x) : ORIGIN = 0x800060, LENGTH = 2K + eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 1K + fuse (rw!x) : ORIGIN = 0x820000, LENGTH = 1K + lock (rw!x) : ORIGIN = 0x830000, LENGTH = 1K + signature (rw!x) : ORIGIN = 0x840000, LENGTH = 1K +} +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : + { + *(.rel.text) + *(.rel.text.*) + *(.rel.gnu.linkonce.t*) + } + .rela.text : + { + *(.rela.text) + *(.rela.text.*) + *(.rela.gnu.linkonce.t*) + } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.rodata : + { + *(.rel.rodata) + *(.rel.rodata.*) + *(.rel.gnu.linkonce.r*) + } + .rela.rodata : + { + *(.rela.rodata) + *(.rela.rodata.*) + *(.rela.gnu.linkonce.r*) + } + .rel.data : + { + *(.rel.data) + *(.rel.data.*) + *(.rel.gnu.linkonce.d*) + } + .rela.data : + { + *(.rela.data) + *(.rela.data.*) + *(.rela.gnu.linkonce.d*) + } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + /* Internal text space or external memory. */ + .text : + { + *(.vectors) + KEEP(*(.vectors)) + /* For data that needs to reside in the lower 64k of progmem. */ + *(.progmem.gcc*) + *(.progmem*) + . = ALIGN(2); + __trampolines_start = . ; + /* The jump trampolines for the 16-bit limited relocs will reside here. */ + *(.trampolines) + *(.trampolines*) + __trampolines_end = . ; + /* For future tablejump instruction arrays for 3 byte pc devices. + We don't relax jump/call instructions within these sections. */ + *(.jumptables) + *(.jumptables*) + /* For code that needs to reside in the lower 128k progmem. */ + *(.lowtext) + *(.lowtext*) + __ctors_start = . ; + *(.ctors) + __ctors_end = . ; + __dtors_start = . ; + *(.dtors) + __dtors_end = . ; + KEEP(SORT(*)(.ctors)) + KEEP(SORT(*)(.dtors)) + /* From this point on, we don't bother about wether the insns are + below or above the 16 bits boundary. */ + *(.init0) /* Start here after reset. */ + KEEP (*(.init0)) + *(.init1) + KEEP (*(.init1)) + *(.init2) /* Clear __zero_reg__, set up stack pointer. */ + KEEP (*(.init2)) + *(.init3) + KEEP (*(.init3)) + *(.init4) /* Initialize data and BSS. */ + KEEP (*(.init4)) + *(.init5) + KEEP (*(.init5)) + *(.init6) /* C++ constructors. */ + KEEP (*(.init6)) + *(.init7) + KEEP (*(.init7)) + *(.init8) + KEEP (*(.init8)) + *(.init9) /* Call main(). */ + KEEP (*(.init9)) + *(.text) + . = ALIGN(2); + *(.text.*) + . = ALIGN(2); + *(.fini9) /* _exit() starts here. */ + KEEP (*(.fini9)) + *(.fini8) + KEEP (*(.fini8)) + *(.fini7) + KEEP (*(.fini7)) + *(.fini6) /* C++ destructors. */ + KEEP (*(.fini6)) + *(.fini5) + KEEP (*(.fini5)) + *(.fini4) + KEEP (*(.fini4)) + *(.fini3) + KEEP (*(.fini3)) + *(.fini2) + KEEP (*(.fini2)) + *(.fini1) + KEEP (*(.fini1)) + *(.fini0) /* Infinite loop after program termination. */ + KEEP (*(.fini0)) + _etext = . ; + } > text + .data : AT (ADDR (.text) + SIZEOF (.text)) + { + PROVIDE (__data_start = .) ; + *(.data) + *(.data*) + *(.rodata) /* We need to include .rodata here if gcc is used */ + *(.rodata*) /* with -fdata-sections. */ + *(.gnu.linkonce.d*) + . = ALIGN(2); + _edata = . ; + PROVIDE (__data_end = .) ; + } > data + .bss : AT (ADDR (.bss)) + { + PROVIDE (__bss_start = .) ; + *(.bss) + *(.bss*) + *(COMMON) + PROVIDE (__bss_end = .) ; + } > data + __data_load_start = LOADADDR(.data); + __data_load_end = __data_load_start + SIZEOF(.data); + /* Global data not cleared after reset. */ + .noinit : + { + PROVIDE (__noinit_start = .) ; + *(.noinit*) + PROVIDE (__noinit_end = .) ; + _end = . ; + PROVIDE (__heap_start = .) ; + } > data + .eeprom : + { + *(.eeprom*) + __eeprom_end = . ; + } > eeprom + .fuse : + { + KEEP(*(.fuse)) + KEEP(*(.lfuse)) + KEEP(*(.hfuse)) + KEEP(*(.efuse)) + } > fuse + .lock : + { + KEEP(*(.lock*)) + } > lock + .signature : + { + KEEP(*(.signature*)) + } > signature + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} diff --git a/config.h b/config.h index e345bdd82..29a5b1094 100644 --- a/config.h +++ b/config.h @@ -33,7 +33,6 @@ #define BAUD_RATE 57600 // #define DEBUG_IGNORE_SENSORS // set for debugging - #define CONFIG_X_STEPS_PER_MM 32.80839895 //microsteps/mm #ifndef NANOTEC_STEPPER_09 #define CONFIG_Y_STEPS_PER_MM 32.80839895 //microsteps/mm @@ -195,3 +194,4 @@ // // x = ~x; // toggles ALL the bits in x. +#define MM_PER_INCH 25.4 diff --git a/gcode.c b/gcode.c index 20401529b..c7c9ed045 100644 --- a/gcode.c +++ b/gcode.c @@ -32,27 +32,34 @@ #include "planner.h" #include "stepper.h" -#define MM_PER_INCH (25.4) - -#define NEXT_ACTION_NONE 0 -#define NEXT_ACTION_SEEK 1 -#define NEXT_ACTION_FEED 2 -#define NEXT_ACTION_DWELL 3 -#define NEXT_ACTION_HOMING_CYCLE 4 -#define NEXT_ACTION_SET_COORDINATE_OFFSET 5 -#define NEXT_ACTION_AIR_ASSIST_ENABLE 6 -#define NEXT_ACTION_AIR_ASSIST_DISABLE 7 -#define NEXT_ACTION_AUX1_ASSIST_ENABLE 8 -#define NEXT_ACTION_AUX1_ASSIST_DISABLE 9 +enum { + NEXT_ACTION_NONE = 0, + NEXT_ACTION_SEEK, + NEXT_ACTION_FEED, + NEXT_ACTION_CW_ARC, + NEXT_ACTION_CCW_ARC, + NEXT_ACTION_RASTER, + NEXT_ACTION_DWELL, + NEXT_ACTION_STOP, + NEXT_ACTION_HOMING_CYCLE, + NEXT_ACTION_SET_COORDINATE_OFFSET, + NEXT_ACTION_AIR_ASSIST_ENABLE, + NEXT_ACTION_AIR_ASSIST_DISABLE, + NEXT_ACTION_AUX1_ASSIST_ENABLE, + NEXT_ACTION_AUX1_ASSIST_DISABLE, #ifdef DRIVEBOARD - #define NEXT_ACTION_AUX2_ASSIST_ENABLE 10 - #define NEXT_ACTION_AUX2_ASSIST_DISABLE 11 + NEXT_ACTION_AUX2_ASSIST_ENABLE, + NEXT_ACTION_AUX2_ASSIST_DISABLE, #endif + NEXT_ACTION_SET_ACCELERATION, + NEXT_ACTION_SET_PPI, +}; #define OFFSET_G54 0 #define OFFSET_G55 1 #define BUFFER_LINE_SIZE 80 + char rx_line[BUFFER_LINE_SIZE]; char *rx_line_cursor; @@ -61,7 +68,7 @@ uint8_t line_checksum_ok_already; #define FAIL(status) gc.status_code = status; typedef struct { - uint8_t status_code; // return codes + GCODE_STATUS status_code; // return codes uint8_t motion_mode; // {G0, G1} bool inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} bool absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} @@ -71,6 +78,7 @@ typedef struct { double offsets[6]; // coord system offsets {G54_X,G54_Y,G54_Z,G55_X,G55_Y,G55_Z} uint8_t offselect; // currently active offset, 0 -> G54, 1 -> G55 uint8_t nominal_laser_intensity; // 0-255 percentage + raster_info_t raster; // Raster State } parser_state_t; static parser_state_t gc; @@ -107,7 +115,7 @@ void gcode_init() { void gcode_process_line() { uint8_t chr = '\0'; int numChars = 0; - int status_code = STATUS_OK; + int status_code = GCODE_STATUS_OK; uint8_t skip_line = false; uint8_t print_extended_status = false; @@ -115,7 +123,7 @@ void gcode_process_line() { chr = serial_read(); // blocks until there is data if (numChars + 1 >= BUFFER_LINE_SIZE) { // +1 for \0 // reached line size, other side sent too long lines - stepper_request_stop(STATUS_LINE_BUFFER_OVERFLOW); + stepper_request_stop(GCODE_STATUS_LINE_BUFFER_OVERFLOW); break; } else if (chr <= ' ') { // ignore control characters and space @@ -142,21 +150,35 @@ void gcode_process_line() { printString("!"); // report harware is in stop mode status_code = stepper_stop_status(); // report stop conditions - if ( status_code == STATUS_POWER_OFF) { - printString("P"); // Stop: Power Off - } else if (status_code == STATUS_LIMIT_HIT) { - printString("L"); // Stop: Limit Hit - } else if (status_code == STATUS_SERIAL_STOP_REQUEST) { - printString("R"); // Stop: Serial Request - } else if (status_code == STATUS_RX_BUFFER_OVERFLOW) { - printString("B"); // Stop: Rx Buffer Overflow - } else if (status_code == STATUS_LINE_BUFFER_OVERFLOW) { - printString("I"); // Stop: Line Buffer Overflow - } else if (status_code == STATUS_TRANSMISSION_ERROR) { - printString("T"); // Stop: Serial Transmission Error - } else { - printString("O"); // Stop: Other error - printInteger(status_code); + switch (status_code) { + case GCODE_STATUS_POWER_OFF: + printString("P"); // Stop: Power Off + break; + + case GCODE_STATUS_LIMIT_HIT: + printString("L"); // Stop: Limit Hit + break; + + case GCODE_STATUS_SERIAL_STOP_REQUEST: + printString("R"); // Stop: Serial Request + break; + + case GCODE_STATUS_RX_BUFFER_OVERFLOW: + printString("B"); // Stop: Rx Buffer Overflow + break; + + case GCODE_STATUS_LINE_BUFFER_OVERFLOW: + printString("I"); // Stop: Line Buffer Overflow + break; + + case GCODE_STATUS_TRANSMISSION_ERROR: + printString("T"); // Stop: Serial Transmission Error + break; + + default: + printString("O"); // Stop: Other error + printInteger(status_code); + break; } } else { if (rx_line[0] == '*' || rx_line[0] == '^') { @@ -169,7 +191,7 @@ void gcode_process_line() { if (rx_checksum < 128) { printString(rx_line); printString(" -> checksum outside [128,255]"); - stepper_request_stop(STATUS_TRANSMISSION_ERROR); + stepper_request_stop(GCODE_STATUS_TRANSMISSION_ERROR); } char *itr = rx_line_cursor; uint16_t checksum = 0; @@ -191,7 +213,7 @@ void gcode_process_line() { printString("^"); } else { // '*' printString(rx_line); - stepper_request_stop(STATUS_TRANSMISSION_ERROR); + stepper_request_stop(GCODE_STATUS_TRANSMISSION_ERROR); // line_checksum_ok_already = false; } } else { // we got a good line @@ -216,19 +238,27 @@ void gcode_process_line() { if (rx_line_cursor[0] != '?') { // process the next line of G-code status_code = gcode_execute_line(rx_line_cursor); - // report parse errors - if (status_code == STATUS_OK) { - // pass - } else if (status_code == STATUS_BAD_NUMBER_FORMAT) { - printString("N"); // Warning: Bad number format - } else if (status_code == STATUS_EXPECTED_COMMAND_LETTER) { - printString("E"); // Warning: Expected command letter - } else if (status_code == STATUS_UNSUPPORTED_STATEMENT) { - printString("U"); // Warning: Unsupported statement - } else { - printString("W"); // Warning: Other error - printInteger(status_code); - } + switch (status_code) { + case GCODE_STATUS_OK: + break; + + case GCODE_STATUS_BAD_NUMBER_FORMAT: + printString("N"); // Warning: Bad number format + break; + + case GCODE_STATUS_EXPECTED_COMMAND_LETTER: + printString("E"); // Warning: Expected command letter + break; + + case GCODE_STATUS_UNSUPPORTED_STATEMENT: + printString("U"); // Warning: Unsupported statement + break; + + default: + printString("W"); // Warning: Other error + printInteger(status_code); + break; + } } else { print_extended_status = true; } @@ -294,11 +324,13 @@ uint8_t gcode_execute_line(char *line) { double unit_converted_value; uint8_t next_action = NEXT_ACTION_NONE; double target[3]; + double vector[3] = {0.0, 0.0, 0.0}; + double n = -1.0; double p = 0.0; int cs = 0; int l = 0; bool got_actual_line_command = false; // as opposed to just e.g. G1 F1200 - gc.status_code = STATUS_OK; + gc.status_code = GCODE_STATUS_OK; //// Pass 1: Commands while(next_statement(&letter, &value, line, &char_counter)) { @@ -309,6 +341,29 @@ uint8_t gcode_execute_line(char *line) { case 0: gc.motion_mode = next_action = NEXT_ACTION_SEEK; break; case 1: gc.motion_mode = next_action = NEXT_ACTION_FEED; break; case 4: next_action = NEXT_ACTION_DWELL; break; + case 8: + // Special case to append raster data + if (line[char_counter] == 'D') { + uint32_t len; + char_counter++; + + len = strlen(line) - char_counter; + + if (len >= RASTER_BUFFER_SIZE || len > 70) + { + gc.status_code = GCODE_STATUS_RX_BUFFER_OVERFLOW; + stepper_request_stop(gc.status_code); + } + else + { + planner_raster_data(&line[char_counter], len, false); + } + + return gc.status_code; + } else { + next_action = NEXT_ACTION_RASTER; + } + break; case 10: next_action = NEXT_ACTION_SET_COORDINATE_OFFSET; break; case 20: gc.inches_mode = true; break; case 21: gc.inches_mode = false; break; @@ -317,7 +372,7 @@ uint8_t gcode_execute_line(char *line) { case 55: gc.offselect = OFFSET_G55; break; case 90: gc.absolute_mode = true; break; case 91: gc.absolute_mode = false; break; - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + default: FAIL(GCODE_STATUS_UNSUPPORTED_STATEMENT); break; } break; case 'M': @@ -330,7 +385,7 @@ uint8_t gcode_execute_line(char *line) { case 84: next_action = NEXT_ACTION_AUX2_ASSIST_ENABLE;break; case 85: next_action = NEXT_ACTION_AUX2_ASSIST_DISABLE;break; #endif - default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + default: FAIL(GCODE_STATUS_UNSUPPORTED_STATEMENT); break; } break; } @@ -352,7 +407,7 @@ uint8_t gcode_execute_line(char *line) { } switch(letter) { case 'F': - if (unit_converted_value <= 0) { FAIL(STATUS_BAD_NUMBER_FORMAT); } + if (unit_converted_value <= 0) { FAIL(GCODE_STATUS_BAD_NUMBER_FORMAT); } if (gc.motion_mode == NEXT_ACTION_SEEK) { gc.seek_rate = unit_converted_value; } else { @@ -367,6 +422,7 @@ uint8_t gcode_execute_line(char *line) { } got_actual_line_command = true; break; + case 'N': n = value; break; case 'P': // dwelling seconds or CS selector if (next_action == NEXT_ACTION_SET_COORDINATE_OFFSET) { cs = trunc(value); @@ -404,6 +460,37 @@ uint8_t gcode_execute_line(char *line) { gc.feed_rate, gc.nominal_laser_intensity ); } break; + case NEXT_ACTION_RASTER: + if (p > 0.0) { + gc.raster.dot_size = p; + } + if (got_actual_line_command) { + gc.raster.x_off = vector[X_AXIS]; + gc.raster.y_off = vector[Y_AXIS]; + if (vector[Z_AXIS] < 0) { + gc.raster.invert = 1; + } else { + gc.raster.invert = 0; + } + + // Setup the raster... + planner_raster(target[X_AXIS] + gc.offsets[3 * gc.offselect + X_AXIS], + target[Y_AXIS] + gc.offsets[3 * gc.offselect + Y_AXIS], + target[Z_AXIS] + gc.offsets[3 * gc.offselect + Z_AXIS], + gc.feed_rate, gc.nominal_laser_intensity, &gc.raster); + } + + if (n >= 0.0) { + // End of the raster. + planner_raster_data(NULL, 0, true); + + // Always increment on N regardless of D. + if (gc.raster.x_off != 0.0) + target[Y_AXIS] += gc.raster.dot_size; + else if (gc.raster.y_off != 0.0) + target[X_AXIS] -= gc.raster.dot_size; + } + break; case NEXT_ACTION_DWELL: planner_dwell(p, gc.nominal_laser_intensity); break; @@ -507,12 +594,12 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t *letter = line[*char_counter]; if((*letter < 'A') || (*letter > 'Z')) { - FAIL(STATUS_EXPECTED_COMMAND_LETTER); + FAIL(GCODE_STATUS_EXPECTED_COMMAND_LETTER); return(0); } (*char_counter)++; if (!read_double(line, char_counter, double_ptr)) { - FAIL(STATUS_BAD_NUMBER_FORMAT); + FAIL(GCODE_STATUS_BAD_NUMBER_FORMAT); return(0); }; return(1); @@ -525,19 +612,35 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t static int read_double(char *line, uint8_t *char_counter, double *double_ptr) { char *start = line + *char_counter; char *end; - - *double_ptr = strtod(start, &end); - if(end == start) { - return(false); - }; - - *char_counter = end - line; - return(true); -} + char *search; + char mod_char = 0; + + // Quick search for any X's (don't want G0X0Y0 interpreting as a hex value!). + // The alternative was sscanf, but that adds 15K of code. + for (search = line + *char_counter; *search != 0x00; search++) + { + if (*search == 'X' || *search == 'Y' || *search == 'Z' || *search == 'E') { + // Temporarily replace this with string terminator + mod_char = *search; + *search = 0; + break; + } + } + *double_ptr = strtod(start, &end); + // Revert the string (if needed) + if (mod_char != 0) + *search = mod_char; + // Nothing found + if (end == start) + return (false); + // Update our char counter + *char_counter = end - line; + return (true); +} /* diff --git a/gcode.h b/gcode.h index f3c2d815e..644866664 100644 --- a/gcode.h +++ b/gcode.h @@ -25,19 +25,21 @@ #include #include -#define STATUS_OK 0 -#define STATUS_RX_BUFFER_OVERFLOW 1 -#define STATUS_LINE_BUFFER_OVERFLOW 2 -#define STATUS_TRANSMISSION_ERROR 3 -#define STATUS_BAD_NUMBER_FORMAT 4 -#define STATUS_EXPECTED_COMMAND_LETTER 5 -#define STATUS_UNSUPPORTED_STATEMENT 6 -#define STATUS_SERIAL_STOP_REQUEST 7 -#define STATUS_LIMIT_HIT 8 -#define STATUS_POWER_OFF 9 -// #define STATUS_DOOR_OPEN 10 -// #define STATUS_CHILLER_OFF 11 - +typedef enum { + GCODE_STATUS_OK = 0, + GCODE_STATUS_RX_BUFFER_OVERFLOW, + GCODE_STATUS_LINE_BUFFER_OVERFLOW, + GCODE_STATUS_TRANSMISSION_ERROR, + GCODE_STATUS_BAD_NUMBER_FORMAT, + GCODE_STATUS_EXPECTED_COMMAND_LETTER, + GCODE_STATUS_UNSUPPORTED_STATEMENT, + GCODE_STATUS_SERIAL_STOP_REQUEST, + GCODE_STATUS_LIMIT_HIT, + GCODE_STATUS_POWER_OFF, + GCODE_STATUS_DOOR_OPEN, + GCODE_STATUS_CHILLER_OFF, + GCODE_STATUS_ARC_RADIUS_ERROR, +} GCODE_STATUS; // Initialize the parser void gcode_init(); diff --git a/planner.c b/planner.c index 99629fc07..065b669a0 100644 --- a/planner.c +++ b/planner.c @@ -28,12 +28,20 @@ // The number of linear motions that can be in the plan at any give time -#define BLOCK_BUFFER_SIZE 16 // do not make bigger than uint8_t +#define BLOCK_BUFFER_SIZE 15 // do not make bigger than uint8_t +#define RASTER_BUFFER_COUNT 2 static block_t block_buffer[BLOCK_BUFFER_SIZE]; // ring buffer for motion instructions static volatile uint8_t block_buffer_head; // index of the next block to be pushed static volatile uint8_t block_buffer_tail; // index of the block to process now +// Raster buffer. +// We are severely memory constrained, so each (gcode) line of raster data is queued as a new +// planner block rather than waiting for the end of the raster. The raster buffers are grabbed +// on an "is free" basis and a pointer to it stored in the planner block. +static raster_t raster_buffer[RASTER_BUFFER_COUNT]; +static raster_info_t raster_current; + static int32_t position[3]; // The current position of the tool in absolute steps static volatile bool position_update_requested; // make sure to update to stepper position on next occasion static double previous_unit_vec[3]; // Unit vector of previous path line segment @@ -51,8 +59,8 @@ static void reduce_entry_speed_forward(block_t *previous, block_t *current); static void planner_recalculate(); - void planner_init() { + int i; block_buffer_head = 0; block_buffer_tail = 0; clear_vector(position); @@ -62,13 +70,20 @@ void planner_init() { position_update_requested = false; clear_vector_double(previous_unit_vec); previous_nominal_speed = 0.0; + + // Mark the raster buffers as free. + for (i=0; itype = TYPE_LINE; + // Setup the block type + if (raster == NULL) { + block->block_type = BLOCK_TYPE_LINE; + } else { + block->block_type = BLOCK_TYPE_RASTER_LINE; + block->raster = raster; + } // set nominal laser intensity block->nominal_laser_intensity = nominal_laser_intensity; @@ -119,7 +139,7 @@ void planner_line(double x, double y, double z, double feed_rate, uint8_t nomina block->millimeters = sqrt( (delta_mm[X_AXIS]*delta_mm[X_AXIS]) + (delta_mm[Y_AXIS]*delta_mm[Y_AXIS]) + (delta_mm[Z_AXIS]*delta_mm[Z_AXIS]) ); - double inverse_millimeters = 1.0/block->millimeters; // store for efficency + double inverse_millimeters = 1.0/block->millimeters; // store for efficency // calculate nominal_speed (mm/min) and nominal_rate (step/min) // minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c @@ -197,6 +217,84 @@ void planner_line(double x, double y, double z, double feed_rate, uint8_t nomina stepper_wake_up(); } +// Process a raster. +// Rasters can be +/- in the x or y directions (not z). +// The sign of x_off/y_off specify the raster direction. +// The value of x_off/y_off specify the offset (acceleration margin) before the actual raster. +void planner_raster(double x, double y, double z, + double feed_rate, + uint8_t nominal_laser_intensity, + raster_info_t *info) { + + memcpy(&raster_current, info, sizeof(raster_info_t)); + + // Store the parameters + raster_current.x = x; + raster_current.y = y; + raster_current.z = z; + raster_current.feed_rate = feed_rate; + + // Move to the starting point. (Assumes we have space before the limits are hit) + // then accelerate to operating feed_rate. + // Hopefully by then we'll have the first block of raster data... + if (raster_current.x_off > 0) { + planner_movement(x - raster_current.x_off, y, z, CONFIG_SEEKRATE, 0, NULL); + planner_movement(x, y, z, feed_rate, 0, NULL); + } else if (raster_current.y_off > 0) { + planner_movement(x, y - raster_current.y_off, z, CONFIG_SEEKRATE, 0, NULL); + planner_movement(x, y, z, feed_rate, 0, NULL); + } + + raster_current.intensity = nominal_laser_intensity; +} + +// There isn't enough RAM to buffer a whole raster, so take and process piecemeal. +void planner_raster_data(char *data, uint8_t len, bool last) { + int i; + + if (len > 0) { + raster_t* raster = NULL; + for (i=0; ifree = false; + break; + } + } + + // Append the data to the inactive buffer. + memcpy(raster->data, data, len); + raster->data_length = len; + raster->intensity = raster_current.intensity; + raster->invert = raster_current.invert; + + // Shuffle the stepper along. + if (raster_current.x_off > 0) { + planner_movement(raster_current.x + raster_current.length, raster_current.y, raster_current.z, raster_current.feed_rate, 0, raster); + } else if (raster_current.y_off > 0) { + planner_movement(raster_current.x, raster_current.y + raster_current.length, raster_current.z, raster_current.feed_rate, 0, raster); + } + } + + // If this is the last one, end properly. + if (last) { + if (raster_current.x_off > 0) { + planner_movement(raster_current.x + raster_current.length + raster_current.x_off, raster_current.y, raster_current.z, CONFIG_SEEKRATE, 0, NULL); + } else if (raster_current.y_off > 0) { + planner_movement(raster_current.x, raster_current.y + raster_current.length + raster_current.y_off, raster_current.z, CONFIG_SEEKRATE, 0, NULL); + } + } +} + +// Add a new linear movement to the buffer. x, y and z is +// the signed, absolute target position in millimeters. Feed rate specifies the speed of the motion. +void planner_line(double x, double y, double z, + double feed_rate, + uint8_t laser_pwm) { + planner_movement(x, y, z, feed_rate, laser_pwm, NULL); +} + void planner_dwell(double seconds, uint8_t nominal_laser_intensity) { // // Execute dwell in seconds. Maximum time delay is > 18 hours, more than enough for any application. @@ -214,7 +312,7 @@ void planner_dwell(double seconds, uint8_t nominal_laser_intensity) { void planner_command(uint8_t type) { // calculate the buffer head and check for space - int next_buffer_head = next_block_index( block_buffer_head ); + int next_buffer_head = next_block_index( block_buffer_head ); while(block_buffer_tail == next_buffer_head) { // buffer full condition // good! We are well ahead of the robot. Rest here until buffer has room. // sleep_mode(); @@ -224,7 +322,7 @@ void planner_command(uint8_t type) { block_t *block = &block_buffer[block_buffer_head]; // set block type command - block->type = type; + block->block_type = type; // Move buffer head block_buffer_head = next_buffer_head; diff --git a/planner.h b/planner.h index 96e49fc98..a9dae1255 100644 --- a/planner.h +++ b/planner.h @@ -23,29 +23,61 @@ #include "config.h" - -// Command types the planner and stepper can schedule for execution -#define TYPE_LINE 0 -#define TYPE_AIR_ASSIST_ENABLE 1 -#define TYPE_AIR_ASSIST_DISABLE 2 -#define TYPE_AUX1_ASSIST_ENABLE 3 -#define TYPE_AUX1_ASSIST_DISABLE 4 -#define TYPE_AUX2_ASSIST_ENABLE 5 -#define TYPE_AUX2_ASSIST_DISABLE 6 - -#define planner_control_air_assist_enable() planner_command(TYPE_AIR_ASSIST_ENABLE) -#define planner_control_air_assist_disable() planner_command(TYPE_AIR_ASSIST_DISABLE) -#define planner_control_aux1_assist_enable() planner_command(TYPE_AUX1_ASSIST_ENABLE) -#define planner_control_aux1_assist_disable() planner_command(TYPE_AUX1_ASSIST_DISABLE) +#define RASTER_BUFFER_SIZE 70 + +// Command types the planner and stepper can schedule for execution +typedef enum { + BLOCK_TYPE_LINE, + BLOCK_TYPE_RASTER_LINE, + BLOCK_TYPE_AIR_ASSIST_ENABLE, + BLOCK_TYPE_AIR_ASSIST_DISABLE, + BLOCK_TYPE_AUX1_ASSIST_ENABLE, + BLOCK_TYPE_AUX1_ASSIST_DISABLE, + BLOCK_TYPE_AUX2_ASSIST_ENABLE, + BLOCK_TYPE_AUX2_ASSIST_DISABLE, +} BLOCK_TYPE; + +typedef struct _raster_info { + uint8_t intensity; + uint8_t invert; + + double dot_size; + double x_off; + double y_off; + + double x; + double y; + double z; + double feed_rate; + + uint32_t length; +} raster_info_t; + +// Raster structure, used by gcode, planner and stepper. +typedef struct _raster { + bool free; + + uint8_t data[RASTER_BUFFER_SIZE]; + uint32_t data_length; + + uint8_t intensity; + uint8_t invert; +} raster_t; + + +#define planner_control_air_assist_enable() planner_command(BLOCK_TYPE_AIR_ASSIST_ENABLE) +#define planner_control_air_assist_disable() planner_command(BLOCK_TYPE_AIR_ASSIST_DISABLE) +#define planner_control_aux1_assist_enable() planner_command(BLOCK_TYPE_AUX1_ASSIST_ENABLE) +#define planner_control_aux1_assist_disable() planner_command(BLOCK_TYPE_AUX1_ASSIST_DISABLE) #ifdef DRIVEBOARD - #define planner_control_aux2_assist_enable() planner_command(TYPE_AUX2_ASSIST_ENABLE) - #define planner_control_aux2_assist_disable() planner_command(TYPE_AUX2_ASSIST_DISABLE) + #define planner_control_aux2_assist_enable() planner_command(BLOCK_TYPE_AUX2_ASSIST_ENABLE) + #define planner_control_aux2_assist_disable() planner_command(BLOCK_TYPE_AUX2_ASSIST_DISABLE) #endif // This struct is used when buffering the setup for each linear movement "nominal" values are as specified in // the source g-code and may never actually be reached if acceleration management is active. typedef struct { - uint8_t type; // Type of command, eg: TYPE_LINE, TYPE_AIR_ASSIST_ENABLE + BLOCK_TYPE block_type; // Type of command, eg: TYPE_LINE, TYPE_AIR_ASSIST_ENABLE // Fields used by the bresenham algorithm for tracing the line uint32_t steps_x, steps_y, steps_z; // Step count along each axis uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) @@ -65,12 +97,24 @@ typedef struct { int32_t rate_delta; // The steps/minute to add or subtract when changing speed (must be positive) uint32_t accelerate_until; // The index of the step event on which to stop acceleration uint32_t decelerate_after; // The index of the step event on which to start decelerating - + raster_t *raster; // Which raster buffer we're using. } block_t; - + // Initialize the motion plan subsystem void planner_init(); +// Process a raster. +// Rasters can be +/- in the x or y directions (not z). +// The sign of x_off/y_off specifies the raster direction. +// The value of x_off/y_off specifies the offset (acceleration margin) before the actual raster. +// raster and raster_len contain the pointer and length of buffer containing 0-255 PWM values for each dot. +void planner_raster(double x, double y, double z, + double feed_rate, + uint8_t nominal_laser_intensity, + raster_info_t *info); + +void planner_raster_data(char *data, uint8_t len, bool last); + // Add a new linear movement to the buffer. // x, y and z is the signed, absolute target position in millimaters. // Feed rate specifies the speed of the motion. diff --git a/serial.c b/serial.c index 9210bfed0..2f1e56eb7 100644 --- a/serial.c +++ b/serial.c @@ -162,7 +162,7 @@ SIGNAL(USART_RX_vect) { uint8_t data = UDR0; if (data == CHAR_STOP) { // special stop character, bypass buffer - stepper_request_stop(STATUS_SERIAL_STOP_REQUEST); + stepper_request_stop(GCODE_STATUS_SERIAL_STOP_REQUEST); } else if (data == CHAR_RESUME) { // special resume character, bypass buffer stepper_stop_resume(); @@ -181,7 +181,7 @@ SIGNAL(USART_RX_vect) { if (next_head == rx_buffer_tail) { // buffer is full, other side sent too much data - stepper_request_stop(STATUS_RX_BUFFER_OVERFLOW); + stepper_request_stop(GCODE_STATUS_RX_BUFFER_OVERFLOW); } else { rx_buffer[head] = data; rx_buffer_head = next_head; diff --git a/stepper.c b/stepper.c index e055b41ee..156f318dc 100644 --- a/stepper.c +++ b/stepper.c @@ -58,7 +58,6 @@ #define CYCLES_PER_MICROSECOND (F_CPU/1000000) //16000000/1000000 = 16 #define CYCLES_PER_ACCELERATION_TICK (F_CPU/ACCELERATION_TICKS_PER_SECOND) // 16MHz/100 = 160000 - static int32_t stepper_position[3]; // real-time position in absolute steps static block_t *current_block; // A pointer to the block currently being traced @@ -116,7 +115,7 @@ void stepper_init() { acceleration_tick_counter = 0; current_block = NULL; stop_requested = false; - stop_status = STATUS_OK; + stop_status = GCODE_STATUS_OK; busy = false; // start in the idle state @@ -210,6 +209,8 @@ ISR(TIMER2_OVF_vect) { // config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. // The bresenham line tracer algorithm controls all three stepper outputs simultaneously. ISR(TIMER1_COMPA_vect) { + uint32_t raster_index; + uint8_t intensity; if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt busy = true; if (stop_requested) { @@ -225,13 +226,13 @@ ISR(TIMER1_COMPA_vect) { #ifndef DEBUG_IGNORE_SENSORS // stop program when any limit is hit or the e-stop turned the power off if (SENSE_LIMITS) { - stepper_request_stop(STATUS_LIMIT_HIT); + stepper_request_stop(GCODE_STATUS_LIMIT_HIT); busy = false; return; } #ifndef DRIVEBOARD else if (SENSE_POWER_OFF) { - stepper_request_stop(STATUS_POWER_OFF); + stepper_request_stop(GCODE_STATUS_POWER_OFF); busy = false; return; } @@ -261,7 +262,8 @@ ISR(TIMER1_COMPA_vect) { busy = false; return; } - if (current_block->type == TYPE_LINE) { // starting on new line block + if (current_block->block_type == BLOCK_TYPE_LINE + || current_block->block_type == BLOCK_TYPE_RASTER_LINE) { // starting on new line block adjusted_rate = current_block->initial_rate; acceleration_tick_counter = CYCLES_PER_ACCELERATION_TICK/2; // start halfway, midpoint rule. adjust_speed( adjusted_rate ); // initialize cycles_per_step_event @@ -273,8 +275,22 @@ ISR(TIMER1_COMPA_vect) { } // process current block, populate out_bits (or handle other commands) - switch (current_block->type) { - case TYPE_LINE: + switch (current_block->block_type) { + case BLOCK_TYPE_RASTER_LINE: + raster_index = (step_events_completed * current_block->raster->data_length) / current_block->step_event_count; + + intensity = 0; + if (current_block->raster->invert == 0 && current_block->raster->data[raster_index] == '1') + intensity = current_block->raster->intensity; + else if (current_block->raster->invert != 0 && current_block->raster->data[raster_index] == '0') + intensity = current_block->raster->intensity; + + if (intensity != current_block->nominal_laser_intensity) { + current_block->nominal_laser_intensity = intensity; + control_laser_intensity(intensity); + } + //break; + case BLOCK_TYPE_LINE: ////// Execute step displacement profile by bresenham line algorithm out_bits = current_block->direction_bits; counter_x += current_block->steps_x; @@ -335,11 +351,14 @@ ISR(TIMER1_COMPA_vect) { // reset counter, midpoint rule // makes sure deceleration is performed the same every time acceleration_tick_counter = CYCLES_PER_ACCELERATION_TICK/2; - + // decelerating } else if (step_events_completed >= current_block->decelerate_after) { if ( acceleration_tick() ) { // scheduled speed change - adjusted_rate -= current_block->rate_delta; + if (adjusted_rate > current_block->rate_delta) + adjusted_rate -= current_block->rate_delta; + else + adjusted_rate = 0; if (adjusted_rate < current_block->final_rate) { // overshot adjusted_rate = current_block->final_rate; } @@ -362,38 +381,38 @@ ISR(TIMER1_COMPA_vect) { break; - case TYPE_AIR_ASSIST_ENABLE: + case BLOCK_TYPE_AIR_ASSIST_ENABLE: control_air_assist(true); current_block = NULL; planner_discard_current_block(); break; - case TYPE_AIR_ASSIST_DISABLE: + case BLOCK_TYPE_AIR_ASSIST_DISABLE: control_air_assist(false); current_block = NULL; planner_discard_current_block(); break; - case TYPE_AUX1_ASSIST_ENABLE: + case BLOCK_TYPE_AUX1_ASSIST_ENABLE: control_aux1_assist(true); current_block = NULL; planner_discard_current_block(); break; - case TYPE_AUX1_ASSIST_DISABLE: + case BLOCK_TYPE_AUX1_ASSIST_DISABLE: control_aux1_assist(false); current_block = NULL; planner_discard_current_block(); break; #ifdef DRIVEBOARD - case TYPE_AUX2_ASSIST_ENABLE: + case BLOCK_TYPE_AUX2_ASSIST_ENABLE: control_aux2_assist(true); current_block = NULL; planner_discard_current_block(); break; - case TYPE_AUX2_ASSIST_DISABLE: + case BLOCK_TYPE_AUX2_ASSIST_DISABLE: control_aux2_assist(false); current_block = NULL; planner_discard_current_block(); @@ -464,7 +483,12 @@ static uint32_t config_step_timer(uint32_t cycles) { static void adjust_speed( uint32_t steps_per_minute ) { // steps_per_minute is typicaly just adjusted_rate if (steps_per_minute < MINIMUM_STEPS_PER_MINUTE) { steps_per_minute = MINIMUM_STEPS_PER_MINUTE; } - cycles_per_step_event = config_step_timer((CYCLES_PER_MICROSECOND*1000000*60)/steps_per_minute); + cycles_per_step_event = config_step_timer( (CYCLES_PER_MICROSECOND*1000000/steps_per_minute) * 60 ); + + if (cycles_per_step_event == 0) + { + stepper_request_stop(GCODE_STATUS_BAD_NUMBER_FORMAT); + } // beam dynamics uint8_t adjusted_intensity = current_block->nominal_laser_intensity * ((float)steps_per_minute/(float)current_block->nominal_rate);