예제 #1
0
/**
 * 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 (;;) { }
}
예제 #2
0
/// 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);

}
예제 #3
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"));

}
예제 #4
0
// -------------------------------------------------------
// 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();
}
예제 #5
0
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);
}
예제 #6
0
/// 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"));

}
예제 #7
0
// -------------------------------------------------------
// 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 {
예제 #8
0
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*/
                }
        }
}
예제 #10
0
파일: mendel.c 프로젝트: Myndale/ESPrint
/// 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"));

}
예제 #11
0
// -------------------------------------------------------
// 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();
}
예제 #12
0
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()
예제 #13
0
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);
            }
예제 #14
0
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
	}
}
예제 #17
0
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()
예제 #18
0
/// 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
	}
}
예제 #19
0
/** 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
}
예제 #20
0
/** \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);
}