/** * Safety procedure: if something goes wrong, for example an opto is triggered during normal movement, * we shut down the entire machine. * @param msg The reason why the machine did an emergency stop */ void dda_emergency_shutdown(PGM_P msg) { // Todo: is it smart to enable all interrupts again? e.g. can we create concurrent executions? sei(); // Enable interrupts to print the message serial_writestr_P(PSTR("error: emergency stop:")); if(msg!=NULL) serial_writestr_P(msg); serial_writestr_P(PSTR("\r\n")); delay_ms(20); // Delay so the buffer can be flushed - otherwise the message is never sent timer_stop(); queue_flush(); power_off(); cli(); for (;;) { } }
/// go to the next move. /// be aware that this is sometimes called from interrupt context, sometimes not. /// Note that if it is called from outside an interrupt it must not/can not by /// be interrupted such that it can be re-entered from within an interrupt. /// The timer interrupt MUST be disabled on entry. This is ensured because /// the timer was disabled at the start of the ISR or else because the current /// move buffer was dead in the non-interrupt case (which indicates that the /// timer interrupt is disabled). void next_move() { while ((queue_empty() == 0) && (movebuffer[mb_tail].live == 0)) { // next item uint8_t t = mb_tail + 1; t &= (MOVEBUFFER_SIZE - 1); DDA* current_movebuffer = &movebuffer[t]; // tail must be set before setTimer call as setTimer // reenables the timer interrupt, potentially exposing // mb_tail to the timer interrupt routine. mb_tail = t; if (current_movebuffer->waitfor_temp) { #ifndef REPRAP_HOST_COMPATIBILITY serial_writestr_P(PSTR("Waiting for target temp\n")); #endif current_movebuffer->live = 1; setTimer(HEATER_WAIT_TIMEOUT); } else { dda_start(current_movebuffer); } } if (queue_empty()) setTimer(0); }
void init(void) { // set up watchdog wd_init(); // set up serial serial_init(); // set up inputs and outputs io_init(); // set up timers timer_init(); // read PID settings from EEPROM heater_init(); // set up default feedrate current_position.F = startpoint.F = next_target.target.F = SEARCH_FEEDRATE_Z; // start up analog read interrupt loop, if anything uses analog as determined by ANALOG_MASK in your config.h analog_init(); // set up temperature inputs temp_init(); // enable interrupts sei(); // reset watchdog wd_reset(); // say hi to host serial_writestr_P(PSTR("Start\nok\n")); }
// ------------------------------------------------------- // This is the one function called by the timer interrupt. // It calls a few other functions, though. // ------------------------------------------------------- /// Take a step or go to the next move. void queue_step() { // do our next step DDA* current_movebuffer = &movebuffer[mb_tail]; if (current_movebuffer->live) { if (current_movebuffer->waitfor_temp) { setTimer(HEATER_WAIT_TIMEOUT); if (temp_achieved()) { current_movebuffer->live = current_movebuffer->waitfor_temp = 0; serial_writestr_P(PSTR("Temp achieved\n")); } #if STEP_INTERRUPT_INTERRUPTIBLE sei(); #endif } else { // NOTE: dda_step makes this interrupt interruptible after steps have been sent but before new speed is calculated. dda_step(current_movebuffer); } } // fall directly into dda_start instead of waiting for another step // the dda dies not directly after its last step, but when the timer fires and there's no steps to do if (current_movebuffer->live == 0) next_move(); }
void sersendf(char *format, ...) { va_list args; va_start(args, format); uint16_t i = 0; uint8_t c, j = 0; while ((c = format[i++])) { if (j) { switch(c) { case 'l': j = 4; case 'u': if (j == 4) serwrite_uint32(va_arg(args, uint32_t)); else serwrite_uint16(va_arg(args, unsigned int)); j = 0; break; case 'd': if (j == 4) serwrite_int32(va_arg(args, int32_t)); else serwrite_int16(va_arg(args, int)); j = 0; break; case 'p': case 'x': serial_writestr_P(str_ox); if (j == 4) serwrite_hex32(va_arg(args, uint32_t)); else serwrite_hex16(va_arg(args, unsigned int)); j = 0; break; case 'c': serial_writechar(va_arg(args, unsigned int)); j = 0; break; case 's': serial_writestr(va_arg(args, uint8_t *)); j = 0; break; default: j = 0; break; } } else { if (c == '%') { j = 2; } else { serial_writechar(c); } } } va_end(args); }
/// Startup code, run when we come out of reset void init(void) { halInit(); chSysInit(); #if defined(PORT_LED1) && defined(PIN_LED1) palSetPadMode(PORT_LED1, PIN_LED1, PAL_MODE_OUTPUT_PUSHPULL); #endif #if defined(PORT_LED2) && defined(PIN_LED2) palSetPadMode(PORT_LED2, PIN_LED2, PAL_MODE_OUTPUT_PUSHPULL); #endif // set up watchdog wd_init(); // set up serial serial_init(); // set up G-code parsing gcode_init(); // set up inputs and outputs io_init(); // set up timers timer_init(); // read PID settings from EEPROM heater_init(); // set up dda dda_init(); // start up analog read interrupt loop, // if any of the temp sensors in your config.h use analog interface analog_init(); // set up temperature inputs temp_init(); // enable interrupts enable_irq(); // reset watchdog wd_reset(); // say hi to host serial_writestr_P(PSTR("start\nok\n")); }
// ------------------------------------------------------- // This is the one function called by the timer interrupt. // It calls a few other functions, though. // ------------------------------------------------------- void queue_step() { // do our next step if (movebuffer[mb_tail].live) { if (movebuffer[mb_tail].waitfor_temp) { setTimer(movebuffer[mb_tail].c >> 8); if (temp_achieved()) { movebuffer[mb_tail].live = movebuffer[mb_tail].waitfor_temp = 0; serial_writestr_P(PSTR("Temp achieved\n")); } #if STEP_INTERRUPT_INTERRUPTIBLE sei(); #endif } else {
void queue_step(){ DDA *current_movebuffer = &movebuffer[mb_tail]; if(current_movebuffer->live){ if(current_movebuffer->waitfor_temp){ setTimer(HEATER_WAIT_TIMEOUT); if(temp_achieved()){ current_movebuffer->live = current_movebuffer->waitfor_temp = 0; serial_writestr_P(PSTR("Temp achieved\n")); }//end if temp achieved }//endif waitfortemp else{ dda_step(current_movebuffer); } }//end if current_movebuffer->live if(current_movebuffer->live == 0) next_move(); }//queue_step()
void check_temp_achieved() { DDA* current_movebuffer = &movebuffer[mb_tail]; if (current_movebuffer->live) { if (current_movebuffer->waitfor_temp) { // setTimer(HEATER_WAIT_TIMEOUT); reset_idle(); if (temp_achieved()) { current_movebuffer->live = current_movebuffer->waitfor_temp = 0; serial_writestr_P(PSTR("Temp achieved\n")); debug_led_set_pattern(0,0); } /*#if STEP_INTERRUPT_INTERRUPTIBLE sei(); #endif*/ } } }
/// Startup code, run when we come out of reset void init(void) { // set up watchdog wd_init(); // set up serial serial_init(); // set up G-code parsing gcode_init(); // set up inputs and outputs io_init(); // set up timers timer_init(); // read PID settings from EEPROM heater_init(); // set up dda dda_init(); // start up analog read interrupt loop, // if any of the temp sensors in your config.h use analog interface analog_init(); // set up temperature inputs temp_init(); // enable interrupts sei(); // reset watchdog wd_reset(); // prepare the power supply power_init(); // say hi to host serial_writestr_P(PSTR("start\nok\n")); }
// ------------------------------------------------------- // This is the one function called by the timer interrupt. // It calls a few other functions, though. // ------------------------------------------------------- /// Take a step or go to the next move. void queue_step() { // do our next step DDA* current_movebuffer = &movebuffer[mb_tail]; if (current_movebuffer->live) { if (current_movebuffer->waitfor_temp) { setTimer(HEATER_WAIT_TIMEOUT); if (temp_achieved()) { current_movebuffer->live = current_movebuffer->waitfor_temp = 0; serial_writestr_P(PSTR("Temp achieved\n")); } } else { // NOTE: dda_step makes this interrupt interruptible for some time, // see STEP_INTERRUPT_INTERRUPTIBLE. dda_step(current_movebuffer); } } // Start the next move if this one is done. if (current_movebuffer->live == 0) next_move(); }
void process_gcode_command() { uint32_t backup_f; // convert relative to absolute if (next_target.option_all_relative) { next_target.target.X += startpoint.X; next_target.target.Y += startpoint.Y; next_target.target.Z += startpoint.Z; } // E relative movement. // Matches Sprinter's behaviour as of March 2012. if (next_target.option_all_relative || next_target.option_e_relative) next_target.target.e_relative = 1; else next_target.target.e_relative = 0; // implement axis limits #ifdef X_MIN if (next_target.target.X < X_MIN * 1000.) next_target.target.X = X_MIN * 1000.; #endif #ifdef X_MAX if (next_target.target.X > X_MAX * 1000.) next_target.target.X = X_MAX * 1000.; #endif #ifdef Y_MIN if (next_target.target.Y < Y_MIN * 1000.) next_target.target.Y = Y_MIN * 1000.; #endif #ifdef Y_MAX if (next_target.target.Y > Y_MAX * 1000.) next_target.target.Y = Y_MAX * 1000.; #endif #ifdef Z_MIN if (next_target.target.Z < Z_MIN * 1000.) next_target.target.Z = Z_MIN * 1000.; #endif #ifdef Z_MAX if (next_target.target.Z > Z_MAX * 1000.) next_target.target.Z = Z_MAX * 1000.; #endif // The GCode documentation was taken from http://reprap.org/wiki/Gcode . if (next_target.seen_T) { //? --- T: Select Tool --- //? //? Example: T1 //? //? Select extruder number 1 to build with. Extruder numbering starts at 0. next_tool = next_target.T; } if (next_target.seen_G) { uint8_t axisSelected = 0; switch (next_target.G) { case 0: //? G0: Rapid Linear Motion //? //? Example: G0 X12 //? //? In this case move rapidly to X = 12 mm. In fact, the RepRap firmware uses exactly the same code for rapid as it uses for controlled moves (see G1 below), as - for the RepRap machine - this is just as efficient as not doing so. (The distinction comes from some old machine tools that used to move faster if the axes were not driven in a straight line. For them G0 allowed any movement in space to get to the destination as fast as possible.) //? backup_f = next_target.target.F; next_target.target.F = MAXIMUM_FEEDRATE_X * 2L; enqueue(&next_target.target); next_target.target.F = backup_f; break; case 1: //? --- G1: Linear Motion at Feed Rate --- //? //? Example: G1 X90.6 Y13.8 E22.4 //? //? Go in a straight line from the current (X, Y) point to the point (90.6, 13.8), extruding material as the move happens from the current extruded length to a length of 22.4 mm. //? enqueue(&next_target.target); break; // G2 - Arc Clockwise // unimplemented // G3 - Arc Counter-clockwise // unimplemented case 4: //? --- G4: Dwell --- //? //? Example: G4 P200 //? //? In this case sit still doing nothing for 200 milliseconds. During delays the state of the machine (for example the temperatures of its extruders) will still be preserved and controlled. //? queue_wait(); // delay if (next_target.seen_P) { for (;next_target.P > 0;next_target.P--) { clock(); delay_ms(1); } } break; case 20: //? --- G20: Set Units to Inches --- //? //? Example: G20 //? //? Units from now on are in inches. //? next_target.option_inches = 1; break; case 21: //? --- G21: Set Units to Millimeters --- //? //? Example: G21 //? //? Units from now on are in millimeters. (This is the RepRap default.) //? next_target.option_inches = 0; break; case 30: //? --- G30: Go home via point --- //? //? Undocumented. enqueue(&next_target.target); // no break here, G30 is move and then go home case 28: //? --- G28: Home --- //? //? Example: G28 //? //? This causes the RepRap machine to move back to its X, Y and Z zero endstops. It does so accelerating, so as to get there fast. But when it arrives it backs off by 1 mm in each direction slowly, then moves back slowly to the stop. This ensures more accurate positioning. //? //? If you add coordinates, then just the axes with coordinates specified will be zeroed. Thus //? //? G28 X0 Y72.3 //? //? will zero the X and Y axes, but not Z. The actual coordinate values are ignored. //? queue_wait(); if (next_target.seen_X) { #if defined X_MIN_PIN home_x_negative(); #elif defined X_MAX_PIN home_x_positive(); #endif axisSelected = 1; } if (next_target.seen_Y) { #if defined Y_MIN_PIN home_y_negative(); #elif defined Y_MAX_PIN home_y_positive(); #endif axisSelected = 1; } if (next_target.seen_Z) { #if defined Z_MAX_PIN home_z_positive(); #elif defined Z_MIN_PIN home_z_negative(); #endif axisSelected = 1; } // there's no point in moving E, as E has no endstops if (!axisSelected) { home(); } break; case 90: //? --- G90: Set to Absolute Positioning --- //? //? Example: G90 //? //? All coordinates from now on are absolute relative to the origin //? of the machine. This is the RepRap default. //? //? If you ever want to switch back and forth between relative and //? absolute movement keep in mind, X, Y and Z follow the machine's //? coordinate system while E doesn't change it's position in the //? coordinate system on relative movements. //? // No wait_queue() needed. next_target.option_all_relative = 0; break; case 91: //? --- G91: Set to Relative Positioning --- //? //? Example: G91 //? //? All coordinates from now on are relative to the last position. //? // No wait_queue() needed. next_target.option_all_relative = 1; break; case 92: //? --- G92: Set Position --- //? //? Example: G92 X10 E90 //? //? Allows programming of absolute zero point, by reseting the current position to the values specified. This would set the machine's X coordinate to 10, and the extrude coordinate to 90. No physical motion will occur. //? queue_wait(); if (next_target.seen_X) { startpoint.X = next_target.target.X; axisSelected = 1; } if (next_target.seen_Y) { startpoint.Y = next_target.target.Y; axisSelected = 1; } if (next_target.seen_Z) { startpoint.Z = next_target.target.Z; axisSelected = 1; } if (next_target.seen_E) { startpoint.E = next_target.target.E; axisSelected = 1; } if (axisSelected == 0) { startpoint.X = next_target.target.X = startpoint.Y = next_target.target.Y = startpoint.Z = next_target.target.Z = startpoint.E = next_target.target.E = 0; } dda_new_startpoint(); break; case 161: //? --- G161: Home negative --- //? //? Find the minimum limit of the specified axes by searching for the limit switch. //? if (next_target.seen_X) home_x_negative(); if (next_target.seen_Y) home_y_negative(); if (next_target.seen_Z) home_z_negative(); break; case 162: //? --- G162: Home positive --- //? //? Find the maximum limit of the specified axes by searching for the limit switch. //? if (next_target.seen_X) home_x_positive(); if (next_target.seen_Y) home_y_positive(); if (next_target.seen_Z) home_z_positive(); break; // unknown gcode: spit an error default: sersendf_P(PSTR("E: Bad G-code %d"), next_target.G); // newline is sent from gcode_parse after we return return; } } else if (next_target.seen_M) { uint8_t i; switch (next_target.M) { case 0: //? --- M0: machine stop --- //? //? Example: M0 //? //? http://linuxcnc.org/handbook/RS274NGC_3/RS274NGC_33a.html#1002379 //? Unimplemented, especially the restart after the stop. Fall trough to M2. //? case 2: case 84: // For compatibility with slic3rs default end G-code. //? --- M2: program end --- //? //? Example: M2 //? //? http://linuxcnc.org/handbook/RS274NGC_3/RS274NGC_33a.html#1002379 //? queue_wait(); for (i = 0; i < NUM_HEATERS; i++) temp_set(i, 0); power_off(); break; case 112: //? --- M112: Emergency Stop --- //? //? Example: M112 //? //? Any moves in progress are immediately terminated, then RepRap shuts down. All motors and heaters are turned off. //? It can be started again by pressing the reset button on the master microcontroller. See also M0. //? timer_stop(); queue_flush(); power_off(); cli(); for (;;) wd_reset(); break; case 6: //? --- M6: tool change --- //? //? Undocumented. tool = next_tool; break; case 82: //? --- M82 - Set E codes absolute --- //? //? This is the default and overrides G90/G91. //? M82/M83 is not documented in the RepRap wiki, behaviour //? was taken from Sprinter as of March 2012. //? //? While E does relative movements, it doesn't change its //? position in the coordinate system. See also comment on G90. //? // No wait_queue() needed. next_target.option_e_relative = 0; break; case 83: //? --- M83 - Set E codes relative --- //? //? Counterpart to M82. //? // No wait_queue() needed. next_target.option_e_relative = 1; break; // M3/M101- extruder on case 3: case 101: //? --- M101: extruder on --- //? //? Undocumented. if (temp_achieved() == 0) { enqueue(NULL); } #ifdef DC_EXTRUDER heater_set(DC_EXTRUDER, DC_EXTRUDER_PWM); #elif E_STARTSTOP_STEPS > 0 do { // backup feedrate, move E very quickly then restore feedrate backup_f = startpoint.F; startpoint.F = MAXIMUM_FEEDRATE_E; SpecialMoveE(E_STARTSTOP_STEPS, MAXIMUM_FEEDRATE_E); startpoint.F = backup_f; } while (0); #endif break; // M102- extruder reverse // M5/M103- extruder off case 5: case 103: //? --- M103: extruder off --- //? //? Undocumented. #ifdef DC_EXTRUDER heater_set(DC_EXTRUDER, 0); #elif E_STARTSTOP_STEPS > 0 do { // backup feedrate, move E very quickly then restore feedrate backup_f = startpoint.F; startpoint.F = MAXIMUM_FEEDRATE_E; SpecialMoveE(-E_STARTSTOP_STEPS, MAXIMUM_FEEDRATE_E); startpoint.F = backup_f; } while (0); #endif break; case 104: //? --- M104: Set Extruder Temperature (Fast) --- //? //? Example: M104 S190 //? //? Set the temperature of the current extruder to 190<sup>o</sup>C //? and return control to the host immediately (''i.e.'' before that //? temperature has been reached by the extruder). For waiting, see M116. //? //? Teacup supports an optional P parameter as a zero-based temperature //? sensor index to address (e.g. M104 P1 S100 will set the temperature //? of the heater connected to the second temperature sensor rather //? than the extruder temperature). //? if ( ! next_target.seen_S) break; #ifdef HEATER_EXTRUDER if ( ! next_target.seen_P) next_target.P = HEATER_EXTRUDER; // else use the first available device #endif temp_set(next_target.P, next_target.S); break; case 105: //? --- M105: Get Temperature(s) --- //? //? Example: M105 //? //? Request the temperature of the current extruder and the build base //? in degrees Celsius. For example, the line sent to the host in //? response to this command looks like //? //? <tt>ok T:201 B:117</tt> //? //? Teacup supports an optional P parameter as a zero-based temperature //? sensor index to address. //? #ifdef ENFORCE_ORDER queue_wait(); #endif if ( ! next_target.seen_P) next_target.P = TEMP_SENSOR_none; temp_print(next_target.P); break; case 7: case 106: //? --- M106: Set Fan Speed / Set Device Power --- //? //? Example: M106 S120 //? //? Control the cooling fan (if any). //? //? Teacup supports an optional P parameter as a zero-based heater //? index to address. The heater index can differ from the temperature //? sensor index, see config.h. #ifdef ENFORCE_ORDER // wait for all moves to complete queue_wait(); #endif #ifdef HEATER_FAN if ( ! next_target.seen_P) next_target.P = HEATER_FAN; // else use the first available device #endif if ( ! next_target.seen_S) break; heater_set(next_target.P, next_target.S); break; case 110: //? --- M110: Set Current Line Number --- //? //? Example: N123 M110 //? //? Set the current line number to 123. Thus the expected next line after this command will be 124. //? This is a no-op in Teacup. //? break; #ifdef DEBUG case 111: //? --- M111: Set Debug Level --- //? //? Example: M111 S6 //? //? Set the level of debugging information transmitted back to the host to level 6. The level is the OR of three bits: //? //? <Pre> //? #define DEBUG_PID 1 //? #define DEBUG_DDA 2 //? #define DEBUG_POSITION 4 //? </pre> //? //? This command is only available in DEBUG builds of Teacup. if ( ! next_target.seen_S) break; debug_flags = next_target.S; break; #endif case 114: //? --- M114: Get Current Position --- //? //? Example: M114 //? //? This causes the RepRap machine to report its current X, Y, Z and E coordinates to the host. //? //? For example, the machine returns a string such as: //? //? <tt>ok C: X:0.00 Y:0.00 Z:0.00 E:0.00</tt> //? #ifdef ENFORCE_ORDER // wait for all moves to complete queue_wait(); #endif update_current_position(); sersendf_P(PSTR("X:%lq,Y:%lq,Z:%lq,E:%lq,F:%lu"), current_position.X, current_position.Y, current_position.Z, current_position.E, current_position.F); #ifdef DEBUG if (DEBUG_POSITION && (debug_flags & DEBUG_POSITION)) { sersendf_P(PSTR(",c:%lu}\nEndpoint: X:%ld,Y:%ld,Z:%ld,E:%ld,F:%lu,c:%lu}"), movebuffer[mb_tail].c, movebuffer[mb_tail].endpoint.X, movebuffer[mb_tail].endpoint.Y, movebuffer[mb_tail].endpoint.Z, movebuffer[mb_tail].endpoint.E, movebuffer[mb_tail].endpoint.F, #ifdef ACCELERATION_REPRAP movebuffer[mb_tail].end_c #else movebuffer[mb_tail].c #endif ); print_queue(); } #endif /* DEBUG */ // newline is sent from gcode_parse after we return break; case 115: //? --- M115: Get Firmware Version and Capabilities --- //? //? Example: M115 //? //? Request the Firmware Version and Capabilities of the current microcontroller //? The details are returned to the host computer as key:value pairs separated by spaces and terminated with a linefeed. //? //? sample data from firmware: //? FIRMWARE_NAME:Teacup FIRMWARE_URL:http://github.com/triffid/Teacup_Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:1 TEMP_SENSOR_COUNT:1 HEATER_COUNT:1 //? sersendf_P(PSTR("FIRMWARE_NAME:Teacup FIRMWARE_URL:http://github.com/triffid/Teacup_Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:%d TEMP_SENSOR_COUNT:%d HEATER_COUNT:%d"), 1, NUM_TEMP_SENSORS, NUM_HEATERS); // newline is sent from gcode_parse after we return break; case 116: //? --- M116: Wait --- //? //? Example: M116 //? //? Wait for temperatures and other slowly-changing variables to arrive at their set values. enqueue(NULL); break; case 119: //? --- M119: report endstop status --- //? Report the current status of the endstops configured in the //? firmware to the host. power_on(); endstops_on(); delay_ms(10); // allow the signal to stabilize #if defined(X_MIN_PIN) sersendf_P(PSTR("x_min:%d "), x_min()); #endif #if defined(X_MAX_PIN) sersendf_P(PSTR("x_max:%d "), x_max()); #endif #if defined(Y_MIN_PIN) sersendf_P(PSTR("y_min:%d "), y_min()); #endif #if defined(Y_MAX_PIN) sersendf_P(PSTR("y_max:%d "), y_max()); #endif #if defined(Z_MIN_PIN) sersendf_P(PSTR("z_min:%d "), z_min()); #endif #if defined(Z_MAX_PIN) sersendf_P(PSTR("z_max:%d "), z_max()); #endif #if ! (defined(X_MIN_PIN) || defined(X_MAX_PIN) || \ defined(Y_MIN_PIN) || defined(Y_MAX_PIN) || \ defined(Z_MIN_PIN) || defined(Z_MAX_PIN)) sersendf_P(PSTR("no endstops defined")); #endif endstops_off(); break; #ifdef EECONFIG case 130: //? --- M130: heater P factor --- //? Undocumented. #ifdef HEATER_EXTRUDER if ( ! next_target.seen_P) next_target.P = HEATER_EXTRUDER; // else use the first available device #endif if (next_target.seen_S) pid_set_p(next_target.P, next_target.S); break; case 131: //? --- M131: heater I factor --- //? Undocumented. #ifdef HEATER_EXTRUDER if ( ! next_target.seen_P) next_target.P = HEATER_EXTRUDER; #endif if (next_target.seen_S) pid_set_i(next_target.P, next_target.S); break; case 132: //? --- M132: heater D factor --- //? Undocumented. #ifdef HEATER_EXTRUDER if ( ! next_target.seen_P) next_target.P = HEATER_EXTRUDER; #endif if (next_target.seen_S) pid_set_d(next_target.P, next_target.S); break; case 133: //? --- M133: heater I limit --- //? Undocumented. #ifdef HEATER_EXTRUDER if ( ! next_target.seen_P) next_target.P = HEATER_EXTRUDER; #endif if (next_target.seen_S) pid_set_i_limit(next_target.P, next_target.S); break; case 134: //? --- M134: save PID settings to eeprom --- //? Undocumented. heater_save_settings(); break; #endif /* EECONFIG */ #ifdef DEBUG case 136: //? --- M136: PRINT PID settings to host --- //? Undocumented. //? This comand is only available in DEBUG builds. if ( ! next_target.seen_P) next_target.P = HEATER_EXTRUDER; heater_print(next_target.P); break; #endif case 140: //? --- M140: Set heated bed temperature --- //? Undocumented. #ifdef HEATER_BED if ( ! next_target.seen_S) break; temp_set(HEATER_BED, next_target.S); #endif break; #ifdef DEBUG case 240: //? --- M240: echo off --- //? Disable echo. //? This command is only available in DEBUG builds. debug_flags &= ~DEBUG_ECHO; serial_writestr_P(PSTR("Echo off")); // newline is sent from gcode_parse after we return break; case 241: //? --- M241: echo on --- //? Enable echo. //? This command is only available in DEBUG builds. debug_flags |= DEBUG_ECHO; serial_writestr_P(PSTR("Echo on")); // newline is sent from gcode_parse after we return break; #endif /* DEBUG */ // unknown mcode: spit an error default: sersendf_P(PSTR("E: Bad M-code %d"), next_target.M); // newline is sent from gcode_parse after we return } // switch (next_target.M) } // else if (next_target.seen_M) } // process_gcode_command()
void dda_create(DDA *dda, TARGET *target) { uint32_t distance, c_limit, c_limit_calc; // initialise DDA to a known state dda->allflags = 0; if (debug_flags & DEBUG_DDA) serial_writestr_P(PSTR("\n{DDA_CREATE: [")); // we end at the passed target memcpy(&(dda->endpoint), target, sizeof(TARGET)); dda->x_delta = ABS(target->X - startpoint.X); dda->y_delta = ABS(target->Y - startpoint.Y); dda->z_delta = ABS(target->Z - startpoint.Z); dda->e_delta = ABS(target->E - startpoint.E); dda->x_direction = (target->X >= startpoint.X)?1:0; dda->y_direction = (target->Y >= startpoint.Y)?1:0; dda->z_direction = (target->Z >= startpoint.Z)?1:0; dda->e_direction = (target->E >= startpoint.E)?1:0; if (debug_flags & DEBUG_DDA) { if (dda->x_direction == 0) serial_writechar('-'); serwrite_uint32(dda->x_delta); serial_writechar(','); if (dda->y_direction == 0) serial_writechar('-'); serwrite_uint32(dda->y_delta); serial_writechar(','); if (dda->z_direction == 0) serial_writechar('-'); serwrite_uint32(dda->z_delta); serial_writechar(','); if (dda->e_direction == 0) serial_writechar('-'); serwrite_uint32(dda->e_delta); serial_writestr_P(PSTR("] [")); } dda->total_steps = dda->x_delta; if (dda->y_delta > dda->total_steps) dda->total_steps = dda->y_delta; if (dda->z_delta > dda->total_steps) dda->total_steps = dda->z_delta; if (dda->e_delta > dda->total_steps) dda->total_steps = dda->e_delta; if (debug_flags & DEBUG_DDA) { serial_writestr_P(PSTR("ts:")); serwrite_uint32(dda->total_steps); } if (dda->total_steps == 0) { dda->nullmove = 1; } else { // get steppers ready to go steptimeout = 0; power_on(); dda->x_counter = dda->y_counter = dda->z_counter = dda->e_counter = -(dda->total_steps >> 1); // since it's unusual to combine X, Y and Z changes in a single move on reprap, check if we can use simpler approximations before trying the full 3d approximation. if (dda->z_delta == 0) distance = approx_distance(dda->x_delta * UM_PER_STEP_X, dda->y_delta * UM_PER_STEP_Y); else if (dda->x_delta == 0 && dda->y_delta == 0) distance = dda->z_delta * UM_PER_STEP_Z; else distance = approx_distance_3(dda->x_delta * UM_PER_STEP_X, dda->y_delta * UM_PER_STEP_Y, dda->z_delta * UM_PER_STEP_Z); if (distance < 2) distance = dda->e_delta * UM_PER_STEP_E; if (debug_flags & DEBUG_DDA) { serial_writestr_P(PSTR(",ds:")); serwrite_uint32(distance); } // pre-calculate move speed in millimeter microseconds per step minute for less math in interrupt context // mm (distance) * 60000000 us/min / step (total_steps) = mm.us per step.min // note: um (distance) * 60000 == mm * 60000000 // so in the interrupt we must simply calculate // mm.us per step.min / mm per min (F) = us per step // break this calculation up a bit and lose some precision because 300,000um * 60000 is too big for a uint32 // calculate this with a uint64 if you need the precision, but it'll take longer so routines with lots of short moves may suffer // 2^32/6000 is about 715mm which should be plenty // changed * 10 to * (F_CPU / 100000) so we can work in cpu_ticks rather than microseconds. // timer.c setTimer() routine altered for same reason // changed distance * 6000 .. * F_CPU / 100000 to // distance * 2400 .. * F_CPU / 40000 so we can move a distance of up to 1800mm without overflowing uint32_t move_duration = ((distance * 2400) / dda->total_steps) * (F_CPU / 40000); // similarly, find out how fast we can run our axes. // do this for each axis individually, as the combined speed of two or more axes can be higher than the capabilities of a single one. c_limit = 0; c_limit_calc = ( (dda->x_delta * (UM_PER_STEP_X * 2400L)) / dda->total_steps * (F_CPU / 40000) / MAXIMUM_FEEDRATE_X) << 8; if (c_limit_calc > c_limit) c_limit = c_limit_calc; c_limit_calc = ( (dda->y_delta * (UM_PER_STEP_Y * 2400L)) / dda->total_steps * (F_CPU / 40000) / MAXIMUM_FEEDRATE_Y) << 8; if (c_limit_calc > c_limit) c_limit = c_limit_calc; c_limit_calc = ( (dda->z_delta * (UM_PER_STEP_Z * 2400L)) / dda->total_steps * (F_CPU / 40000) / MAXIMUM_FEEDRATE_Z) << 8; if (c_limit_calc > c_limit) c_limit = c_limit_calc; c_limit_calc = ( (dda->e_delta * (UM_PER_STEP_E * 2400L)) / dda->total_steps * (F_CPU / 40000) / MAXIMUM_FEEDRATE_E) << 8; if (c_limit_calc > c_limit) c_limit = c_limit_calc; #ifdef ACCELERATION_REPRAP // c is initial step time in IOclk ticks dda->c = (move_duration / startpoint.F) << 8; if (dda->c < c_limit) dda->c = c_limit; dda->end_c = (move_duration / target->F) << 8; if (dda->end_c < c_limit) dda->end_c = c_limit; if (debug_flags & DEBUG_DDA) { serial_writestr_P(PSTR(",md:")); serwrite_uint32(move_duration); serial_writestr_P(PSTR(",c:")); serwrite_uint32(dda->c >> 8); } if (dda->c != dda->end_c) { uint32_t stF = startpoint.F / 4; uint32_t enF = target->F / 4; // now some constant acceleration stuff, courtesy of http://www.embedded.com/columns/technicalinsights/56800129?printable=true uint32_t ssq = (stF * stF); uint32_t esq = (enF * enF); int32_t dsq = (int32_t) (esq - ssq) / 4; uint8_t msb_ssq = msbloc(ssq); uint8_t msb_tot = msbloc(dda->total_steps); // the raw equation WILL overflow at high step rates, but 64 bit math routines take waay too much space // at 65536 mm/min (1092mm/s), ssq/esq overflows, and dsq is also close to overflowing if esq/ssq is small // but if ssq-esq is small, ssq/dsq is only a few bits // we'll have to do it a few different ways depending on the msb locations of each if ((msb_tot + msb_ssq) <= 30) { // we have room to do all the multiplies first if (debug_flags & DEBUG_DDA) serial_writechar('A'); dda->n = ((int32_t) (dda->total_steps * ssq) / dsq) + 1; } else if (msb_tot >= msb_ssq) { // total steps has more precision if (debug_flags & DEBUG_DDA) serial_writechar('B'); dda->n = (((int32_t) dda->total_steps / dsq) * (int32_t) ssq) + 1; } else { // otherwise if (debug_flags & DEBUG_DDA) serial_writechar('C'); dda->n = (((int32_t) ssq / dsq) * (int32_t) dda->total_steps) + 1; } if (debug_flags & DEBUG_DDA) { sersendf_P(PSTR("\n{DDA:CA end_c:%lu, n:%ld, md:%lu, ssq:%lu, esq:%lu, dsq:%lu, msbssq:%u, msbtot:%u}\n"), (long unsigned int)dda->end_c >> 8, (long int)dda->n, (long unsigned int)move_duration, (long unsigned int)ssq, (long unsigned int)esq, (long unsigned int)dsq, msb_ssq, msb_tot); }
void request_resend(void *next_target) { serial_writestr_P(PSTR("rs ")); serwrite_uint8(PARAMETER_asint(L_N)); serial_writechar('\n'); }
void request_resend(void) { serial_writestr_P(PSTR("rs ")); serwrite_uint8(next_target.N); serial_writechar('\n'); }
/// Character Received - add it to our command /// \param c the next character to process void gcode_parse_char(uint8_t 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, STEPS_PER_IN_X, 0); else next_target.target.X = decfloat_to_int(&read_digit, STEPS_PER_M_X, 1); 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, STEPS_PER_IN_Y, 0); else next_target.target.Y = decfloat_to_int(&read_digit, STEPS_PER_M_Y, 1); 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, STEPS_PER_IN_Z, 0); else next_target.target.Z = decfloat_to_int(&read_digit, STEPS_PER_M_Z, 1); 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, STEPS_PER_IN_E, 0); else next_target.target.E = decfloat_to_int(&read_digit, STEPS_PER_M_E, 1); 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, 1); else next_target.target.F = decfloat_to_int(&read_digit, 1, 0); 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, 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 = decfloat_to_int(&read_digit, PID_SCALE, 0); else next_target.S = decfloat_to_int(&read_digit, 1, 0); if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) serwrite_uint16(next_target.S); break; case 'P': next_target.P = decfloat_to_int(&read_digit, 1, 0); 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, 0); if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) serwrite_uint32(next_target.N); break; case '*': next_target.checksum_read = decfloat_to_int(&read_digit, 1, 0); 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 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: // can't do ranges in switch..case, so process actual digits here. if (c >= '0' && c <= '9') { if (read_digit.exponent < DECFLOAT_EXP_MAX && ((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++; } } #ifdef DEBUG else { // invalid serial_writechar('?'); serial_writechar(c); serial_writechar('?'); } #endif } } 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, c); // 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 #ifdef DEBUG sersendf_P(PSTR("received line %lu\n"),next_target.N); #endif serial_writestr_P(PSTR("ok ")); process_gcode_command(); serial_writechar('\n'); #ifdef DEBUG print_queue(); serial_writechar('\n'); #endif // 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_relative) { next_target.target.X = next_target.target.Y = next_target.target.Z = 0; #ifdef E_ABSOLUTE next_target.target.E = 0; #endif } #ifndef E_ABSOLUTE // E always relative next_target.target.E = 0; #endif } }
void process_gcode_command() { uint32_t backup_f; // convert relative to absolute if (next_target.option_all_relative) { next_target.target.axis[X] += startpoint.axis[X]; next_target.target.axis[Y] += startpoint.axis[Y]; next_target.target.axis[Z] += startpoint.axis[Z]; } // E relative movement. // Matches Sprinter's behaviour as of March 2012. if (next_target.option_e_relative) next_target.target.e_relative = 1; else next_target.target.e_relative = 0; if (next_target.option_all_relative && !next_target.option_e_relative) next_target.target.axis[E] += startpoint.axis[E]; // implement axis limits #ifdef X_MIN if (next_target.target.axis[X] < (int32_t)(X_MIN * 1000.)) next_target.target.axis[X] = (int32_t)(X_MIN * 1000.); #endif #ifdef X_MAX if (next_target.target.axis[X] > (int32_t)(X_MAX * 1000.)) next_target.target.axis[X] = (int32_t)(X_MAX * 1000.); #endif #ifdef Y_MIN if (next_target.target.axis[Y] < (int32_t)(Y_MIN * 1000.)) next_target.target.axis[Y] = (int32_t)(Y_MIN * 1000.); #endif #ifdef Y_MAX if (next_target.target.axis[Y] > (int32_t)(Y_MAX * 1000.)) next_target.target.axis[Y] = (int32_t)(Y_MAX * 1000.); #endif #ifdef Z_MIN if (next_target.target.axis[Z] < (int32_t)(Z_MIN * 1000.)) next_target.target.axis[Z] = (int32_t)(Z_MIN * 1000.); #endif #ifdef Z_MAX if (next_target.target.axis[Z] > (int32_t)(Z_MAX * 1000.)) next_target.target.axis[Z] = (int32_t)(Z_MAX * 1000.); #endif // The GCode documentation was taken from http://reprap.org/wiki/Gcode . if (next_target.seen_T) { //? --- T: Select Tool --- //? //? Example: T1 //? //? Select extruder number 1 to build with. Extruder numbering starts at 0. next_tool = next_target.T; } if (next_target.seen_G) { uint8_t axisSelected = 0; switch (next_target.G) { case 0: //? G0: Rapid Linear Motion //? //? Example: G0 X12 //? //? In this case move rapidly to X = 12 mm. In fact, the RepRap firmware uses exactly the same code for rapid as it uses for controlled moves (see G1 below), as - for the RepRap machine - this is just as efficient as not doing so. (The distinction comes from some old machine tools that used to move faster if the axes were not driven in a straight line. For them G0 allowed any movement in space to get to the destination as fast as possible.) //? temp_wait(); backup_f = next_target.target.F; next_target.target.F = MAXIMUM_FEEDRATE_X * 2L; enqueue(&next_target.target); next_target.target.F = backup_f; break; case 1: //? --- G1: Linear Motion at Feed Rate --- //? //? Example: G1 X90.6 Y13.8 E22.4 //? //? Go in a straight line from the current (X, Y) point to the point (90.6, 13.8), extruding material as the move happens from the current extruded length to a length of 22.4 mm. //? temp_wait(); enqueue(&next_target.target); break; // G2 - Arc Clockwise // unimplemented // G3 - Arc Counter-clockwise // unimplemented case 4: //? --- G4: Dwell --- //? //? Example: G4 P200 //? //? In this case sit still doing nothing for 200 milliseconds. During delays the state of the machine (for example the temperatures of its extruders) will still be preserved and controlled. //? queue_wait(); // delay if (next_target.seen_P) { for (;next_target.P > 0;next_target.P--) { clock(); delay_ms(1); } } break; case 20: //? --- G20: Set Units to Inches --- //? //? Example: G20 //? //? Units from now on are in inches. //? next_target.option_inches = 1; break; case 21: //? --- G21: Set Units to Millimeters --- //? //? Example: G21 //? //? Units from now on are in millimeters. (This is the RepRap default.) //? next_target.option_inches = 0; break; case 28: //? --- G28: Home --- //? //? Example: G28 //? //? This causes the RepRap machine to search for its X, Y and Z //? endstops. It does so at high speed, so as to get there fast. When //? it arrives it backs off slowly until the endstop is released again. //? Backing off slowly ensures more accurate positioning. //? //? If you add axis characters, then just the axes specified will be //? seached. Thus //? //? G28 X Y72.3 //? //? will zero the X and Y axes, but not Z. Coordinate values are //? ignored. //? queue_wait(); if (next_target.seen_X) { #if defined X_MIN_PIN home_x_negative(); #elif defined X_MAX_PIN home_x_positive(); #endif axisSelected = 1; } if (next_target.seen_Y) { #if defined Y_MIN_PIN home_y_negative(); #elif defined Y_MAX_PIN home_y_positive(); #endif axisSelected = 1; } if (next_target.seen_Z) { #if defined Z_MIN_PIN home_z_negative(); #elif defined Z_MAX_PIN home_z_positive(); #endif axisSelected = 1; } // there's no point in moving E, as E has no endstops if (!axisSelected) { home(); } break; case 90: //? --- G90: Set to Absolute Positioning --- //? //? Example: G90 //? //? All coordinates from now on are absolute relative to the origin //? of the machine. This is the RepRap default. //? //? If you ever want to switch back and forth between relative and //? absolute movement keep in mind, X, Y and Z follow the machine's //? coordinate system while E doesn't change it's position in the //? coordinate system on relative movements. //? // No wait_queue() needed. next_target.option_all_relative = 0; break; case 91: //? --- G91: Set to Relative Positioning --- //? //? Example: G91 //? //? All coordinates from now on are relative to the last position. //? // No wait_queue() needed. next_target.option_all_relative = 1; break; case 92: //? --- G92: Set Position --- //? //? Example: G92 X10 E90 //? //? Allows programming of absolute zero point, by reseting the current position to the values specified. This would set the machine's X coordinate to 10, and the extrude coordinate to 90. No physical motion will occur. //? queue_wait(); if (next_target.seen_X) { startpoint.axis[X] = next_target.target.axis[X]; axisSelected = 1; } if (next_target.seen_Y) { startpoint.axis[Y] = next_target.target.axis[Y]; axisSelected = 1; } if (next_target.seen_Z) { startpoint.axis[Z] = next_target.target.axis[Z]; axisSelected = 1; } if (next_target.seen_E) { startpoint.axis[E] = next_target.target.axis[E]; axisSelected = 1; } if (axisSelected == 0) { startpoint.axis[X] = next_target.target.axis[X] = startpoint.axis[Y] = next_target.target.axis[Y] = startpoint.axis[Z] = next_target.target.axis[Z] = startpoint.axis[E] = next_target.target.axis[E] = 0; } dda_new_startpoint(); break; case 161: //? --- G161: Home negative --- //? //? Find the minimum limit of the specified axes by searching for the limit switch. //? #if defined X_MIN_PIN if (next_target.seen_X) home_x_negative(); #endif #if defined Y_MIN_PIN if (next_target.seen_Y) home_y_negative(); #endif #if defined Z_MIN_PIN if (next_target.seen_Z) home_z_negative(); #endif break; case 162: //? --- G162: Home positive --- //? //? Find the maximum limit of the specified axes by searching for the limit switch. //? #if defined X_MAX_PIN if (next_target.seen_X) home_x_positive(); #endif #if defined Y_MAX_PIN if (next_target.seen_Y) home_y_positive(); #endif #if defined Z_MAX_PIN if (next_target.seen_Z) home_z_positive(); #endif break; // unknown gcode: spit an error default: sersendf_P(PSTR("E: Bad G-code %d\n"), next_target.G); return; } } else if (next_target.seen_M) { uint8_t i; switch (next_target.M) { case 0: //? --- M0: machine stop --- //? //? Example: M0 //? //? http://linuxcnc.org/handbook/RS274NGC_3/RS274NGC_33a.html#1002379 //? Unimplemented, especially the restart after the stop. Fall trough to M2. //? case 2: case 84: // For compatibility with slic3rs default end G-code. //? --- M2: program end --- //? //? Example: M2 //? //? http://linuxcnc.org/handbook/RS274NGC_3/RS274NGC_33a.html#1002379 //? queue_wait(); for (i = 0; i < NUM_HEATERS; i++) temp_set(i, 0); power_off(); serial_writestr_P(PSTR("\nstop\n")); break; case 6: //? --- M6: tool change --- //? //? Undocumented. tool = next_tool; break; #ifdef SD case 20: //? --- M20: list SD card. --- sd_list("/"); break; case 21: //? --- M21: initialise SD card. --- //? //? Has to be done before doing any other operation, including M20. sd_mount(); break; case 22: //? --- M22: release SD card. --- //? //? Not mandatory. Just removing the card is fine, but results in //? odd behaviour when trying to read from the card anyways. M22 //? makes also sure SD card printing is disabled, even with the card //? inserted. sd_unmount(); break; case 23: //? --- M23: select file. --- //? //? This opens a file for reading. This file is valid up to M22 or up //? to the next M23. sd_open(gcode_str_buf); break; case 24: //? --- M24: start/resume SD print. --- //? //? This makes the SD card available as a G-code source. File is the //? one selected with M23. gcode_sources |= GCODE_SOURCE_SD; break; case 25: //? --- M25: pause SD print. --- //? //? This removes the SD card from the bitfield of available G-code //? sources. The file is kept open. The position inside the file //? is kept as well, to allow resuming. gcode_sources &= ! GCODE_SOURCE_SD; break; #endif /* SD */ case 82: //? --- M82 - Set E codes absolute --- //? //? This is the default and overrides G90/G91. //? M82/M83 is not documented in the RepRap wiki, behaviour //? was taken from Sprinter as of March 2012. //? //? While E does relative movements, it doesn't change its //? position in the coordinate system. See also comment on G90. //? // No wait_queue() needed. next_target.option_e_relative = 0; break; case 83: //? --- M83 - Set E codes relative --- //? //? Counterpart to M82. //? // No wait_queue() needed. next_target.option_e_relative = 1; break; // M3/M101- extruder on case 3: case 101: //? --- M101: extruder on --- //? //? Undocumented. temp_wait(); #ifdef DC_EXTRUDER heater_set(DC_EXTRUDER, DC_EXTRUDER_PWM); #endif break; // M5/M103- extruder off case 5: case 103: //? --- M103: extruder off --- //? //? Undocumented. #ifdef DC_EXTRUDER heater_set(DC_EXTRUDER, 0); #endif break; case 104: //? --- M104: Set Extruder Temperature (Fast) --- //? //? Example: M104 S190 //? //? Set the temperature of the current extruder to 190<sup>o</sup>C //? and return control to the host immediately (''i.e.'' before that //? temperature has been reached by the extruder). For waiting, see M116. //? //? Teacup supports an optional P parameter as a zero-based temperature //? sensor index to address (e.g. M104 P1 S100 will set the temperature //? of the heater connected to the second temperature sensor rather //? than the extruder temperature). //? if ( ! next_target.seen_S) break; if ( ! next_target.seen_P) #ifdef HEATER_EXTRUDER next_target.P = HEATER_EXTRUDER; #else next_target.P = 0; #endif temp_set(next_target.P, next_target.S); break; case 105: //? --- M105: Get Temperature(s) --- //? //? Example: M105 //? //? Request the temperature of the current extruder and the build base //? in degrees Celsius. For example, the line sent to the host in //? response to this command looks like //? //? <tt>ok T:201 B:117</tt> //? //? Teacup supports an optional P parameter as a zero-based temperature //? sensor index to address. //? #ifdef ENFORCE_ORDER queue_wait(); #endif if ( ! next_target.seen_P) next_target.P = TEMP_SENSOR_none; temp_print(next_target.P); break; case 7: case 106: //? --- M106: Set Fan Speed / Set Device Power --- //? //? Example: M106 S120 //? //? Control the cooling fan (if any). //? //? Teacup supports an optional P parameter as a zero-based heater //? index to address. The heater index can differ from the temperature //? sensor index, see config.h. #ifdef ENFORCE_ORDER // wait for all moves to complete queue_wait(); #endif if ( ! next_target.seen_P) #ifdef HEATER_FAN next_target.P = HEATER_FAN; #else next_target.P = 0; #endif if ( ! next_target.seen_S) break; heater_set(next_target.P, next_target.S); break; case 110: //? --- M110: Set Current Line Number --- //? //? Example: N123 M110 //? //? Set the current line number to 123. Thus the expected next line after this command will be 124. //? This is a no-op in Teacup. //? break; #ifdef DEBUG case 111: //? --- M111: Set Debug Level --- //? //? Example: M111 S6 //? //? Set the level of debugging information transmitted back to the host to level 6. The level is the OR of three bits: //? //? <Pre> //? #define DEBUG_PID 1 //? #define DEBUG_DDA 2 //? #define DEBUG_POSITION 4 //? </pre> //? //? This command is only available in DEBUG builds of Teacup. if ( ! next_target.seen_S) break; debug_flags = next_target.S; break; #endif /* DEBUG */ case 112: //? --- M112: Emergency Stop --- //? //? Example: M112 //? //? Any moves in progress are immediately terminated, then the printer //? shuts down. All motors and heaters are turned off. Only way to //? restart is to press the reset button on the master microcontroller. //? See also M0. //? timer_stop(); queue_flush(); power_off(); cli(); for (;;) wd_reset(); break; case 114: //? --- M114: Get Current Position --- //? //? Example: M114 //? //? This causes the RepRap machine to report its current X, Y, Z and E coordinates to the host. //? //? For example, the machine returns a string such as: //? //? <tt>ok C: X:0.00 Y:0.00 Z:0.00 E:0.00</tt> //? #ifdef ENFORCE_ORDER // wait for all moves to complete queue_wait(); #endif update_current_position(); sersendf_P(PSTR("X:%lq,Y:%lq,Z:%lq,E:%lq,F:%lu\n"), current_position.axis[X], current_position.axis[Y], current_position.axis[Z], current_position.axis[E], current_position.F); if (mb_tail_dda != NULL) { if (DEBUG_POSITION && (debug_flags & DEBUG_POSITION)) { sersendf_P(PSTR("Endpoint: X:%ld,Y:%ld,Z:%ld,E:%ld,F:%lu,c:%lu}\n"), mb_tail_dda->endpoint.axis[X], mb_tail_dda->endpoint.axis[Y], mb_tail_dda->endpoint.axis[Z], mb_tail_dda->endpoint.axis[E], mb_tail_dda->endpoint.F, #ifdef ACCELERATION_REPRAP mb_tail_dda->end_c #else mb_tail_dda->c #endif ); } print_queue(); } break; case 115: //? --- M115: Get Firmware Version and Capabilities --- //? //? Example: M115 //? //? Request the Firmware Version and Capabilities of the current microcontroller //? The details are returned to the host computer as key:value pairs separated by spaces and terminated with a linefeed. //? //? sample data from firmware: //? FIRMWARE_NAME:Teacup FIRMWARE_URL:http://github.com/traumflug/Teacup_Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:1 TEMP_SENSOR_COUNT:1 HEATER_COUNT:1 //? sersendf_P(PSTR("FIRMWARE_NAME:Teacup FIRMWARE_URL:http://github.com/traumflug/Teacup_Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:%d TEMP_SENSOR_COUNT:%d HEATER_COUNT:%d\n"), 1, NUM_TEMP_SENSORS, NUM_HEATERS); break; case 116: //? --- M116: Wait --- //? //? Example: M116 //? //? Wait for temperatures and other slowly-changing variables to arrive at their set values. temp_set_wait(); break; case 119: //? --- M119: report endstop status --- //? Report the current status of the endstops configured in the //? firmware to the host. power_on(); endstops_on(); delay_ms(10); // allow the signal to stabilize { #if ! (defined(X_MIN_PIN) || defined(X_MAX_PIN) || \ defined(Y_MIN_PIN) || defined(Y_MAX_PIN) || \ defined(Z_MIN_PIN) || defined(Z_MAX_PIN)) serial_writestr_P(PSTR("No endstops defined.")); #else const char* const open = PSTR("open "); const char* const triggered = PSTR("triggered "); #endif #if defined(X_MIN_PIN) serial_writestr_P(PSTR("x_min:")); x_min() ? serial_writestr_P(triggered) : serial_writestr_P(open); #endif #if defined(X_MAX_PIN) serial_writestr_P(PSTR("x_max:")); x_max() ? serial_writestr_P(triggered) : serial_writestr_P(open); #endif #if defined(Y_MIN_PIN) serial_writestr_P(PSTR("y_min:")); y_min() ? serial_writestr_P(triggered) : serial_writestr_P(open); #endif #if defined(Y_MAX_PIN) serial_writestr_P(PSTR("y_max:")); y_max() ? serial_writestr_P(triggered) : serial_writestr_P(open); #endif #if defined(Z_MIN_PIN) serial_writestr_P(PSTR("z_min:")); z_min() ? serial_writestr_P(triggered) : serial_writestr_P(open); #endif #if defined(Z_MAX_PIN) serial_writestr_P(PSTR("z_max:")); z_max() ? serial_writestr_P(triggered) : serial_writestr_P(open); #endif } endstops_off(); serial_writechar('\n'); break; #ifdef EECONFIG case 130: //? --- M130: heater P factor --- //? Undocumented. // P factor in counts per degreeC of error if ( ! next_target.seen_P) #ifdef HEATER_EXTRUDER next_target.P = HEATER_EXTRUDER; #else next_target.P = 0; #endif if (next_target.seen_S) pid_set_p(next_target.P, next_target.S); break; case 131: //? --- M131: heater I factor --- //? Undocumented. // I factor in counts per C*s of integrated error if ( ! next_target.seen_P) #ifdef HEATER_EXTRUDER next_target.P = HEATER_EXTRUDER; #else next_target.P = 0; #endif if (next_target.seen_S) pid_set_i(next_target.P, next_target.S); break; case 132: //? --- M132: heater D factor --- //? Undocumented. // D factor in counts per degreesC/second if ( ! next_target.seen_P) #ifdef HEATER_EXTRUDER next_target.P = HEATER_EXTRUDER; #else next_target.P = 0; #endif if (next_target.seen_S) pid_set_d(next_target.P, next_target.S); break; case 133: //? --- M133: heater I limit --- //? Undocumented. if ( ! next_target.seen_P) #ifdef HEATER_EXTRUDER next_target.P = HEATER_EXTRUDER; #else next_target.P = 0; #endif if (next_target.seen_S) pid_set_i_limit(next_target.P, next_target.S); break; case 134: //? --- M134: save PID settings to eeprom --- //? Undocumented. heater_save_settings(); break; #endif /* EECONFIG */ #ifdef DEBUG case 136: //? --- M136: PRINT PID settings to host --- //? Undocumented. //? This comand is only available in DEBUG builds. if ( ! next_target.seen_P) #ifdef HEATER_EXTRUDER next_target.P = HEATER_EXTRUDER; #else next_target.P = 0; #endif heater_print(next_target.P); break; #endif /* DEBUG */ case 140: //? --- M140: Set heated bed temperature --- //? Undocumented. #ifdef HEATER_BED if ( ! next_target.seen_S) break; temp_set(HEATER_BED, next_target.S); #endif break; case 220: //? --- M220: Set speed factor override percentage --- if ( ! next_target.seen_S) break; // Scale 100% = 256 next_target.target.f_multiplier = (next_target.S * 64 + 12) / 25; break; case 221: //? --- M221: Control the extruders flow --- if ( ! next_target.seen_S) break; // Scale 100% = 256 next_target.target.e_multiplier = (next_target.S * 64 + 12) / 25; break; #ifdef DEBUG case 240: //? --- M240: echo off --- //? Disable echo. //? This command is only available in DEBUG builds. debug_flags &= ~DEBUG_ECHO; serial_writestr_P(PSTR("Echo off\n")); break; case 241: //? --- M241: echo on --- //? Enable echo. //? This command is only available in DEBUG builds. debug_flags |= DEBUG_ECHO; serial_writestr_P(PSTR("Echo on\n")); break; #endif /* DEBUG */ // unknown mcode: spit an error default: sersendf_P(PSTR("E: Bad M-code %d\n"), next_target.M); } // switch (next_target.M) } // else if (next_target.seen_M) } // process_gcode_command()
/// Character Received - add it to our command /// \param c the next character to process void gcode_parse_char(uint8_t 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 = parse_number.AsInt( false ); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_uint8(next_target.G); break; case 'M': next_target.M = parse_number.AsInt( false ); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_uint8(next_target.M); break; case 'X': next_target.target.X = parse_number.AsPosition( next_target.option_inches ); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_int32(next_target.target.X); break; case 'Y': next_target.target.Y = parse_number.AsPosition( next_target.option_inches ); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_int32(next_target.target.Y); break; case 'Z': next_target.target.Z = parse_number.AsPosition( next_target.option_inches ); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_int32(next_target.target.Z); break; case 'E': next_target.target.E = parse_number.AsPosition( next_target.option_inches ); //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 next_target.target.F = parse_number.AsInt( next_target.option_inches ); //if (next_target.option_inches) // next_target.target.F = decfloat_to_int(&read_digit, 25400, 1); //else // next_target.target.F = decfloat_to_int(&read_digit, 1, 0); //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 = parse_number.AsScaledInt( 4 ); //decfloat_to_int(&read_digit, 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 = parse_number.AsScaledInt( PID_SCALE );// decfloat_to_int(&read_digit, PID_SCALE, 0); else next_target.S = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_uint16(next_target.S); break; case 'P': next_target.P = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_uint16(next_target.P); break; case 'T': next_target.T = parse_number.AsInt( false ); //read_digit.mantissa; //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_uint8(next_target.T); break; case 'N': next_target.N = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_uint32(next_target.N); break; case '*': next_target.checksum_read = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0); //if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO)) // serwrite_uint8(next_target.checksum_read); break; } // reset for next field last_field = 0; parse_number.Clear(); } } // 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 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 '-': parse_number.AddChar(c); break; case '.': parse_number.AddChar(c); break; #ifdef DEBUG case ' ': case '\t': case 10: case 13: // ignore break; #endif default: parse_number.AddChar(c); } } 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, c); // 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 // teacup waits until after the processing of the gcode to send the eol... // FIXME - what to do here... 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_relative) { next_target.target.X = next_target.target.Y = next_target.target.Z = 0; #ifdef E_ABSOLUTE next_target.target.E = 0; #endif } #ifndef E_ABSOLUTE // E always relative next_target.target.E = 0; #endif } }
/** Initialise serial subsystem. Set up baud generator and interrupts, clear buffers. */ void serial_init() { // Turn on UART power. LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 12); // Enable fifos and default RX trigger level. LPC_UART->FCR = 1 << 0 // FIFO Enable - 0 = Disabled, 1 = Enabled. | 0 << 1 // Rx Fifo Reset. | 0 << 2 // Tx Fifo Reset. | 0 << 6; // Rx irq trigger level. // 0 = 1 char, 1 = 4 chars, 2 = 8 chars, 3 = 14 chars. // Disable IRQs. LPC_UART->IER = 0 << 0 // Rx Data available irq enable. | 0 << 1 // Tx Fifo empty irq enable. | 0 << 2; // Rx Line Status irq enable. LPC_SYSCON->UARTCLKDIV = 0x1; /** Calculating the neccessary values for a proper baud rate is pretty complex and, as system clock and baudrate are known at compile time and never changed at runtime, unneccessary. However, how to get these values? Well, we do kind of an easter-egg. If parameters are not known, we calculate them at runtime anyways, and also report them to the user. So she can insert them here and after doing so, whoops, serial fast and binary small :-) Replacing this calculation with fixed values makes the binary a whopping 564 bytes smaller. */ #if (__SYSTEM_CLOCK == 48000000UL) && (BAUD == 115200) #define UART_DLM 0x00 #define UART_DLL 0x17 #define UART_FDR 0xF2 //#elif (__SYSTEM_CLOCK == xxx) && (BAUD == xxx) // Define more combinations here, Teacup reports the neccessary values // at startup time. #endif #ifdef UART_DLM // Set LCR[DLAB] to enable writing to divider registers. LPC_UART->LCR |= (1 << 7); // Set divider values. LPC_UART->DLM = UART_DLM; LPC_UART->DLL = UART_DLL; LPC_UART->FDR = UART_FDR; // Clear LCR[DLAB]. LPC_UART->LCR &= ~(1 << 7); #else /** Calculate baud rate at runtime and later report it to the user. This code is taken as-is from MBEDs serial_api.c, just reformatted whitespace and comments. */ uint32_t baudrate = BAUD; uint32_t PCLK = __SYSTEM_CLOCK; /** First we check to see if the basic divide with no DivAddVal/MulVal ratio gives us an integer result. If it does, we set DivAddVal = 0, MulVal = 1. Otherwise, we search the valid ratio value range to find the closest match. This could be more elegant, using search methods and/or lookup tables, but the brute force method is not that much slower, and is more maintainable. */ uint16_t DL = PCLK / (16 * baudrate); uint8_t DivAddVal = 0; uint8_t MulVal = 1; int hit = 0; uint16_t dlv; uint8_t mv, dav; if ((PCLK % (16 * baudrate)) != 0) { // Checking for zero remainder. int err_best = baudrate, b; for (mv = 1; mv < 16 && ! hit; mv++) { for (dav = 0; dav < mv; dav++) { /** baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul)) solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul)) mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding. For many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision. Note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding. */ if ((mv * PCLK * 2) & 0x80000000) // 1 bit headroom. dlv = ((((2 * mv * PCLK) / (baudrate * (dav + mv))) / 16) + 1) / 2; else // 2 bits headroom, use more precision. dlv = ((((4 * mv * PCLK) / (baudrate * (dav + mv))) / 32) + 1) / 2; // Datasheet says, if DLL == DLM == 0, then 1 is used instead, // since divide by zero is ungood. if (dlv == 0) dlv = 1; // Datasheet says if dav > 0 then DL must be >= 2. if ((dav > 0) && (dlv < 2)) dlv = 2; // Integer rearrangement of the baudrate equation (with rounding). b = ((PCLK * mv / (dlv * (dav + mv) * 8)) + 1) / 2; // Check to see how we went. b = b - baudrate; if (b < 0) b = -b; if (b < err_best) { err_best = b; DL = dlv; MulVal = mv; DivAddVal = dav; if (b == baudrate) { hit = 1; break; } } } } } // Set results like above. LPC_UART->LCR |= (1 << 7); LPC_UART->DLM = (DL >> 8) & 0xFF; LPC_UART->DLL = (DL >> 0) & 0xFF; LPC_UART->FDR = (uint32_t) DivAddVal << 0 | (uint32_t) MulVal << 4; LPC_UART->LCR &= ~(1 << 7); #endif /* UART_DLM, ! UART_DLM */ // Serial format. LPC_UART->LCR = (8 - 5) << 0 // 8 data bits. | (1 - 1) << 2 // 1 stop bit. | 0 << 3 // Parity disabled. | 0 << 4; // 0 = odd parity, 1 = even parity. // Pinout the UART. No need to set GPIO stuff, like data direction. LPC_IOCON->RXD_CMSIS = 0x01 << 0 // Function RXD. | 0x02 << 3; // Pullup enabled. LPC_IOCON->TXD_CMSIS = 0x01 << 0 // Function TXD. | 0x00 << 3; // Pullup inactive. #ifndef UART_DLM /** Baud rate settings were calculated at runtime, report them to the user to allow her to insert them above. This is possible, because we just completed setting up the serial port. Be generous with delays, on new hardware delays might be too quick as well. Uhm, yes, lots of code. 1420 bytes binary size together with the baudrate calculation above. But it's #ifdef'd out when parameters are set above and doing the calculation always at runtime would always add 400 bytes binary size. */ delay_ms(500); serial_writestr_P(PSTR("\nSerial port parameters were calculated at ")); serial_writestr_P(PSTR("runtime.\nInsert these values to the list of ")); serial_writestr_P(PSTR("known settings in serial-arm.c:\n")); sersendf_P(serial_writechar, PSTR(" UART_DLM %sx\n"), (DL >> 8) & 0xFF); sersendf_P(serial_writechar, PSTR(" UART_DLL %sx\n"), (DL >> 0) & 0xFF); sersendf_P(serial_writechar, PSTR(" UART_FDR %sx\n"), (DivAddVal << 0) | (MulVal << 4)); serial_writestr_P(PSTR("Doing so will speed up serial considerably.\n\n")); #endif }
/** \brief Simplified printf \param format pointer to output format specifier string stored in FLASH. \param ... output data Implements only a tiny subset of printf's format specifiers :- %[ls][udcx%] l - following data is (32 bits)\n s - following data is short (8 bits)\n none - following data is 16 bits. u - unsigned int\n d - signed int\n c - character\n x - hex\n % - send a literal % character Example: \code sersendf_P(PSTR("X:%ld Y:%ld temp:%u.%d flags:%sx Q%su/%su%c\n"), target.X, target.Y, current_temp >> 2, (current_temp & 3) * 25, dda.allflags, mb_head, mb_tail, (queue_full()?'F':(queue_empty()?'E':' '))) \endcode */ void sersendf_P(PGM_P format, ...) { va_list args; va_start(args, format); uint16_t i = 0; uint8_t c = 1, j = 0; while ((c = pgm_read_byte(&format[i++]))) { if (j) { switch(c) { case 's': j = 1; break; case 'l': j = 4; break; case 'u': if (j == 4) serwrite_uint32(va_arg(args, uint32_t)); else serwrite_uint16(va_arg(args, uint16_t)); j = 0; break; case 'd': if (j == 4) serwrite_int32(va_arg(args, int32_t)); else serwrite_int16(va_arg(args, int16_t)); j = 0; break; case 'c': serial_writechar(va_arg(args, uint16_t)); j = 0; break; case 'x': serial_writestr_P(str_ox); if (j == 4) serwrite_hex32(va_arg(args, uint32_t)); else if (j == 1) serwrite_hex8(va_arg(args, uint16_t)); else serwrite_hex16(va_arg(args, uint16_t)); j = 0; break; /* case 'p': serwrite_hex16(va_arg(args, uint16_t));*/ default: serial_writechar(c); j = 0; break; } } else { if (c == '%') { j = 2; } else { serial_writechar(c); } } } va_end(args); }