2017-02-19 16:50:07 +00:00
# include "queue.h"
2018-07-12 00:05:39 +00:00
# include "motor.h"
# include "pinio.h"
2018-09-25 02:41:58 +00:00
# include "homing.h"
2017-02-19 16:50:07 +00:00
# include "serial.h"
2018-09-07 23:41:22 +00:00
# include "gcode_parser.h"
2017-02-19 16:50:07 +00:00
2018-09-07 23:41:22 +00:00
GCODE_PARAM BSS gcode_params [ 8 ] ;
static volatile uint8_t current_parameter = 0 ;
2018-07-12 00:05:39 +00:00
uint8_t option_all_relative = 0 ;
2018-09-07 23:41:22 +00:00
TARGET BSS next_target ;
2017-02-19 16:50:07 +00:00
2017-02-19 18:15:53 +00:00
// Parser is implemented as a finite state automata (DFA)
// This is pointer holds function with actions expected for current progress, each of these functions
2018-07-12 00:05:39 +00:00
// represent one possible state
2018-09-07 23:41:22 +00:00
uint8_t ( * parser_current_state ) ( uint8_t c ) ;
2017-02-19 16:50:07 +00:00
/// convert a floating point input value into an integer with appropriate scaling.
2017-02-19 18:15:53 +00:00
/// \param mantissa the actual digits of our floating point number
/// \param exponent scale mantissa by \f$10^{-exponent}\f$
/// \param sign positive or negative?
2017-02-19 16:50:07 +00:00
/// \param multiplicand multiply by this amount during conversion to integer
///
/// Tested for up to 42'000 mm (accurate), 420'000 mm (precision 10 um) and
/// 4'200'000 mm (precision 100 um).
static int32_t decfloat_to_int ( uint32_t mantissa , uint8_t exponent , uint8_t sign , uint16_t multiplicand ) {
// exponent=1 means we've seen a decimal point but no digits after it, and e=2 means we've seen a decimal point with one digit so it's too high by one if not zero
if ( exponent )
exponent - - ;
// This raises range for mm by factor 1000 and for inches by factor 100.
// It's a bit expensive, but we should have the time while parsing.
while ( exponent & & multiplicand % 10 = = 0 ) {
multiplicand / = 10 ;
exponent - - ;
}
mantissa * = multiplicand ;
if ( exponent )
mantissa = ( mantissa + powers [ exponent ] / 2 ) / powers [ exponent ] ;
return sign ? - ( int32_t ) mantissa : ( int32_t ) mantissa ;
}
void parser_reset ( )
{
2018-09-07 23:41:22 +00:00
uint8_t i ;
parser_current_state = start_parsing_parameter ;
2017-02-19 16:50:07 +00:00
current_parameter = 0 ;
for ( i = 0 ; i < 8 ; + + i )
{
2018-09-07 23:41:22 +00:00
gcode_params [ i ] . name = 0 ;
gcode_params [ i ] . value = 0 ;
gcode_params [ i ] . exponent = 0 ;
gcode_params [ i ] . is_negative = 0 ;
2017-02-19 16:50:07 +00:00
}
}
void parser_init ( )
{
2017-02-19 21:56:59 +00:00
# ifdef STEPS_PER_M_Z
2017-02-19 16:50:07 +00:00
next_target . F = SEARCH_FEEDRATE_Z ;
2017-02-19 21:56:59 +00:00
# else
next_target . F = SEARCH_FEEDRATE_Y ;
# endif
# ifdef STEPS_PER_M_E
2017-02-19 16:50:07 +00:00
next_target . e_multiplier = 256 ;
2017-02-19 21:56:59 +00:00
# endif
2017-02-19 16:50:07 +00:00
next_target . f_multiplier = 256 ;
parser_reset ( ) ;
}
2018-07-12 00:05:39 +00:00
// This function executes all possible commands after those are parsed
// Tinker with this to add new commands
uint8_t process_command ( )
2017-02-19 16:50:07 +00:00
{
2018-07-12 00:05:39 +00:00
DDA * dda ;
uint8_t result = 0 ;
if ( option_all_relative )
next_target . axis [ X ] = next_target . axis [ Y ] = 0 ;
2017-02-19 16:50:07 +00:00
for ( int i = 1 ; i < = current_parameter ; + + i )
{
2018-09-07 23:41:22 +00:00
switch ( gcode_params [ i ] . name )
2017-02-19 16:50:07 +00:00
{
case ' X ' :
2018-09-07 23:41:22 +00:00
next_target . axis [ X ] = decfloat_to_int ( gcode_params [ i ] . value , gcode_params [ i ] . exponent , gcode_params [ i ] . is_negative , 1000 ) ;
2017-02-19 16:50:07 +00:00
break ;
case ' Y ' :
2018-09-07 23:41:22 +00:00
next_target . axis [ Y ] = decfloat_to_int ( gcode_params [ i ] . value , gcode_params [ i ] . exponent , gcode_params [ i ] . is_negative , 1000 ) ;
2017-02-19 16:50:07 +00:00
break ;
case ' F ' :
2018-09-07 23:41:22 +00:00
next_target . F = decfloat_to_int ( gcode_params [ i ] . value , gcode_params [ i ] . exponent , gcode_params [ i ] . is_negative , 1 ) ;
2017-02-19 16:50:07 +00:00
break ;
}
}
2018-07-12 00:05:39 +00:00
// convert relative to absolute
if ( option_all_relative ) {
next_target . axis [ X ] + = startpoint . axis [ X ] ;
next_target . axis [ Y ] + = startpoint . axis [ Y ] ;
}
2018-09-07 23:41:22 +00:00
// gcode_params[0] is always a operation with code
switch ( gcode_params [ 0 ] . name )
2017-02-19 16:50:07 +00:00
{
case ' G ' :
2018-09-07 23:41:22 +00:00
switch ( gcode_params [ 0 ] . value )
2018-07-12 00:05:39 +00:00
{
2018-09-09 19:27:40 +00:00
case 0 :
2018-09-25 02:41:58 +00:00
//? Example: G0
2018-07-12 00:05:39 +00:00
//?
//? Linear move
enqueue ( & next_target ) ; break ;
2018-09-25 02:41:58 +00:00
case 1 :
//? Example: G1
//?
//? Linear move with tool down
enqueue_home ( & next_target , 0 , 0xf0 ) ; break ;
2018-07-12 00:05:39 +00:00
case 28 :
//? Example: G28
//?
2018-09-25 02:41:58 +00:00
//? home all axis
queue_wait ( ) ; // wait for queue to empty
home ( ) ;
2018-07-12 00:05:39 +00:00
break ;
case 90 :
//? Example: G90
//?
//? Absolute positioning
option_all_relative = 0 ;
break ;
case 91 :
//? Example: G91
//?
//? Relative positioning
option_all_relative = 1 ;
break ;
default :
result = STATE_ERROR ;
}
2017-02-19 16:50:07 +00:00
break ;
case ' M ' :
2018-09-07 23:41:22 +00:00
switch ( gcode_params [ 0 ] . value )
2018-07-12 00:05:39 +00:00
{
case 0 :
//? Example: M0
//?
//? Stop or unconditional stop
ATOMIC_START
dda = queue_current_movement ( ) ;
if ( dda ! = NULL )
{
update_current_position ( ) ;
memcpy ( & startpoint , & current_position , sizeof ( TARGET ) ) ;
dda - > live = 0 ; dda - > done = 1 ;
# ifdef LOOKAHEAD
dda - > id - - ;
# endif
queue_flush ( ) ;
queue_step ( ) ;
dda_new_startpoint ( ) ;
}
ATOMIC_END
break ;
case 112 :
//? Example: M112
//?
//? Any moves in progress are immediately terminated, then the controller
//? shuts down. All motors are turned off. Only way to
//? restart is to press the reset button on the master microcontroller.
//? See also M0.
//?
timer_stop ( ) ;
queue_flush ( ) ;
cli ( ) ;
break ;
case 114 :
//? Example: M114
//?
//? Get current pos
update_current_position ( ) ;
sersendf_P ( PSTR ( " X:%lq,Y:%lq,F:%lu \n " ) ,
current_position . axis [ X ] , current_position . axis [ Y ] ,
current_position . F ) ;
break ;
case 119 :
//? Example: M119
//?
//? Endstops status
//power_on();
endstops_on ( ) ;
2018-09-09 19:27:40 +00:00
delay_us ( 1000 ) ; // allow the signal to stabilize
# if defined(X_MIN_PIN)
sersendf_P ( PSTR ( " X:%d " ) , x_min ( ) ) ;
2018-07-12 00:05:39 +00:00
# endif
2018-09-09 19:27:40 +00:00
# if defined(Y_MIN_PIN)
sersendf_P ( PSTR ( " Y:%d " ) , y_min ( ) ) ;
# endif
serial_writestr_P ( PSTR ( " \n " ) ) ;
2018-07-12 00:05:39 +00:00
endstops_off ( ) ;
break ;
case 202 : //set acceleration
break ;
case 222 : //set speed
break ;
default :
result = STATE_ERROR ;
}
2017-02-19 16:50:07 +00:00
break ;
2018-07-12 00:05:39 +00:00
default :
result = STATE_ERROR ;
2017-02-19 16:50:07 +00:00
}
2018-07-12 00:05:39 +00:00
return result ;
2017-02-19 16:50:07 +00:00
}
2017-02-19 18:15:53 +00:00
uint8_t gcode_syntax_error ( uint8_t c )
2017-02-19 16:50:07 +00:00
{
2017-02-19 18:15:53 +00:00
return STATE_ERROR ;
2017-02-19 16:50:07 +00:00
}
uint8_t start_parsing_parameter ( uint8_t c )
{
//ignore
if IS_WHITECHAR ( c )
return 0 ;
// uppercase
if ( c > = ' a ' & & c < = ' z ' )
c & = ~ 32 ;
if IS_LETTER ( c )
{
2018-09-07 23:41:22 +00:00
gcode_params [ current_parameter ] . name = c ;
parser_current_state = start_parsing_number ;
2017-02-19 16:50:07 +00:00
return 0 ;
}
2018-09-07 23:41:22 +00:00
parser_current_state = gcode_syntax_error ;
2017-02-19 18:15:53 +00:00
return STATE_ERROR ;
2017-02-19 16:50:07 +00:00
}
uint8_t parse_digit ( uint8_t c )
{
//process digit
if IS_DIGIT ( c )
{
// this is simply mantissa = (mantissa * 10) + atoi(c) in different clothes
2018-09-07 23:41:22 +00:00
gcode_params [ current_parameter ] . value = ( gcode_params [ current_parameter ] . value < < 3 ) +
( gcode_params [ current_parameter ] . value < < 1 ) + ATOI ( c ) ;
2017-02-19 16:50:07 +00:00
2018-09-07 23:41:22 +00:00
if ( gcode_params [ current_parameter ] . exponent )
+ + gcode_params [ current_parameter ] . exponent ;
2017-02-19 16:50:07 +00:00
return 0 ;
}
//this digit is a end of parameter
if IS_WHITECHAR ( c )
{
2018-09-07 23:41:22 +00:00
parser_current_state = start_parsing_parameter ;
2017-02-19 16:50:07 +00:00
+ + current_parameter ;
return 0 ;
}
// all done, this digit is a end of instruction
if ( c = = ' \n ' | | c = = ' \r ' ) {
//process instruction
2018-07-12 00:05:39 +00:00
c = process_command ( ) ;
2017-02-19 16:50:07 +00:00
parser_reset ( ) ;
2018-07-12 00:05:39 +00:00
return c ;
2017-02-19 16:50:07 +00:00
}
if ( c = = ' . ' )
{
2018-09-07 23:41:22 +00:00
gcode_params [ current_parameter ] . exponent = 1 ;
2017-02-19 16:50:07 +00:00
return 0 ;
}
2018-09-07 23:41:22 +00:00
parser_current_state = gcode_syntax_error ;
2017-02-19 18:15:53 +00:00
return STATE_ERROR ;
2017-02-19 16:50:07 +00:00
}
2017-02-19 18:15:53 +00:00
uint8_t start_parsing_number ( uint8_t c )
2017-02-19 16:50:07 +00:00
{
2018-09-07 23:41:22 +00:00
parser_current_state = parse_digit ;
2017-02-19 16:50:07 +00:00
//negative number
if ( c = = ' - ' )
{
2018-09-07 23:41:22 +00:00
gcode_params [ current_parameter ] . is_negative = 1 ;
2017-02-19 16:50:07 +00:00
return 0 ;
}
if IS_DIGIT ( c )
{
parse_digit ( c ) ;
return 0 ;
}
2018-09-07 23:41:22 +00:00
parser_current_state = gcode_syntax_error ;
2017-02-19 18:15:53 +00:00
return STATE_ERROR ;
2017-02-19 16:50:07 +00:00
}
2018-07-12 00:05:39 +00:00
// parsing of new instruction starts from HERE
2017-02-19 16:50:07 +00:00
uint8_t gcode_parse_char ( uint8_t c ) {
uint8_t result , checksum_char = c ;
2018-09-07 23:41:22 +00:00
result = parser_current_state ( c ) ;
2017-02-19 16:50:07 +00:00
if IS_ENDING ( c )
{
2017-02-19 18:15:53 +00:00
if ( result = = STATE_ERROR ) //error
2018-09-07 23:41:22 +00:00
{
parser_reset ( ) ;
2017-02-19 16:50:07 +00:00
return 2 ;
2018-09-07 23:41:22 +00:00
}
2017-02-19 16:50:07 +00:00
return 1 ;
}
return 0 ;
2018-07-12 00:05:39 +00:00
}