/* ******************************************************************************************** * * * filename: stepper_motor_with_UNO.ino * * * * function: emulation of the SGS stepper motor controller Type C200S * * * * project: not assigned * * * * Ed. When Who What * * -------------------------------------------------------------------------------------------- * * * * 20.02.20 Lutz stepper motor controller with Arduino UNO * * 06.03.20 " baud rate changed from 38400 to 9600 * * -------------------------------------------------------------------------------------------- */ #include // #include #include /* only if needed */ #include /* library for I2C devices (display) */ #include /* Arduino´s software interrupt library */ #define BUSY 1 #define IDLE 0 #define TRUE 1 #define FALSE 0 #define DEBUGSERIAL 0 /* 1 for diagnostics, 0 is normal mode compilation */ #define DEBUGLCD 1 /* 1 for diagnostics, 0 is normal mode compilation */ /* output pin 11 defining the direction of the travel.*/ /* Has to be hooked up to the dir-input of the */ /* stepper motor power stage. */ /* If the travel direction is assigned '-' in the */ /* command, the dir_pin is reset to 0 meaning eigther */ /* clockwise or ccw turning, according to your power */ /* stage and motor wiring. */ /* A missing '-' in the travel command means */ /* opposite direction in respect to meaning above */ /* which is supposed to be a positive or '+' direction*/ const int travel_dirPin = 11; /* travel. In this case the dir_pin is set to 1 */ /* via this output pin 10 a positive pulse is given */ /* out meaning transition from former low */ /* to high, next pause, next transition from high */ /* to low. */ /* Seen under programming aspects: reset, pause, set */ /* in other words: the travel pulse for the controller*/ const int travel_pulsePin = 10; /* is active low */ char string[100]; /* buffer for chars to be sent via UART1 */ /* ASCII coded travel parameters coming in via rs232: */ int startstopspeed; /* speed that can be astablished without using a ramp */ int ramplength; /* number of steps for acceleration/deceleration ramp */ int topspeed; /* constant speed in between acceleration/deceleration*/ /* internal data: /* this set of data is needed to do all the internal */ /* computation needed. Their lifetime span extends */ /* from the start of a travel to its end */ /* whenever the steppermotor shaft rotates with a */ /* certain speed above the startstopspeed a necessary */ /* deceleration distance has built up needed later on */ /* to bring the shaft to a controlled standstill. */ /* Accordingly a hard stop of the rotor shaft above */ /* the startstopspeed will result in irretrievable */ long int decel_dist; /* step losses */ long int rampspeed; /* temporary speed when going up/down the ramp */ int speedchanger; /* the increase of speed within a speed step according to the /* formula: speedchanger = ( topspeed - startstopspeed ) / ramplength */ /* the corrected increase of the last speed on ramp up.*/ /* This var. is necessary because there is a restriction in */ /* the way of producing frequencies by modification of */ /* the timer´s division ratio: not all frequencies are */ /* met precisely. Some of them simply do not match */ int correcturup; /* what they should be */ int correcturdown; /* same for ramp down */ /* controls the program flow within the branches of the ISR. */ /* In loop() the ISRflow is set to 0 */ /* 0 means: make the ISR conduct a drive command. */ /* Further meanings: */ /* -1, set at the beginning of loop() means to put */ /* the ISR into idle mode at the lowest repetition rate. */ /* After the ISR has processed a complete travel */ int ISRflow; /* command, it resets ISRflow back to -1 */ /* for storing of ascii character coming from function Serial.read() */ /* on receipt of the 7 bit ASCII char it is cast in int according*/ char pollch; /* to the needs of Serial.read() */ /* a flag saying that there is a busy travel command in progress */ /* is is set to BUSY in PO_decode_params() at a moment, when all */ /* necessary travel parameter ASCII data is processsed */ int travel_inprogress; /* is is reset to IDLE in the IRS when the travel order is completed */ /* a test tool summing up all by the ISR given out step pulses */ /* belonging to a travel order. */ /* For diagnostic purposes a couter check can be done by */ /* comparison with traveldistance */ /* reset in loop(), after all parameters for a travel order */ long int travelstep_count; /* are assembled and decoded */ char S_argument[5]; /* was 10 arrays in which the travel commands in their ACSII */ char T_argument[5]; /* notation are stored when received via rs232. */ char R_argument[5]; /* Now (10.02.2020) a check for overflow wrtiting is done */ char G_argument[10]; int S_arg_counter; /* used to index the numerical ASCII data notation, */ int T_arg_counter; /* following certain ASCII data identifiers like */ int R_arg_counter; /* 'S', 'T','R','G' */ int G_arg_counter; /* its values '+', '-' are in ASCII notation. */ /* Note: sign_of_G_command is set to ASCII '\0' in */ /* case the actual drivedistance turns out to be zero.*/ /* this takes place in PO_decode_params() */ /* Example: A V1 order tells that the absolute position */ /* of the motor shaft is -8000 and the current travel */ char sign_of_G_command; /* order is G-8000 */ char sign_of_g_command; int is_a_g_command; /* a flag telling that a current g+- command is to */ int is_a_G_command; /* be processed */ int K_hardstop; /* flag for signalising the ISR to abort the travel in progress */ int Z_softstop; /* flag for signalising the ISR to end the travel in */ /* progress in a controlled way */ /* the argument of the G+- order in binary notation */ long int G_asb_travel_order; /* Note: this is an absolute travel target position */ /* a drive order of type G is used to drive the motor */ /* shaft to the target position. */ /* Note as this is fundamental: */ /* A relative travel called signed_travel done by the */ /* motor mostly differs from this position, since the */ /* history is what counts here. */ /* Example: if the last absolute G_abs_position of */ /* the motor is -1000 and the following G_asb_travel_order */ /* defines a travel target of +5000, the motor has to */ /* to do a signed_travel of 6000 steps in plus direction */ /* there is one exception to the rule above: Soft stop */ /* induced by the Z order in controlled mode: */ /* The deceleration ramp is diven down and the */ /* G_abs_position differs from the absolute target */ long int G_abs_position; /* position defined before */ /* aux. variable for the actual travel of the motor */ /* according to the following calculation: */ /* = G_asb_travel_order - G_abs_position *) */ /* *) this calculation is done in PO_decode_params() */ /* only the signed travel is processed by the ISR. */ long int signed_travel; /* The IRS doea not care for the history */ /* a summing up position, reached by one or more */ long int driven_abs_pos; /* travels that can be ended by the Z-command */ /* a work copy of signed_travel according to the */ /* following: drivedistance = signed_travel; */ long int drivedistance; /* This is done in function PO_decode_params() */ long int ISR_case2_length; /* collectors or counters for partial travels related */ long int ISR_case3_length; /* to certain phases or branches in the ISR */ long int ISR_case4_length; long int ISR_case5_length; /* synchronizes the display of travel results for */ int diag_serial = FALSE; /* diagnostics via rs232 */ int diag_lcd = FALSE; /* diagnostic via lcd */ int min_rampspeed = 9999; /* for debugging purpose */ int max_rampspeed = 0; /* for debugging purpose */ int diag_rampspeed[200]; /* for debugging purpose */ int diag_index = 0; /* for debugging purpose */ LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE ); //LiquidCrystal_I2C lcd(0x27, 20, 4); /* set the LCD address to 0x27 for a 16 chars */ /* ************************************ setup() ********************************************* */ /* ******************************************************************************************** */ /* ******************************************************************************************** */ void setup() { pinMode(travel_dirPin, OUTPUT); pinMode(travel_pulsePin, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); /* make use of the onboard LED */ Serial.begin( 9600 ); // was 38400 /* enable serial port which is in use by */ /* definition of the LCD pinout */ /* binary code program loader */ lcd.begin(20, 04); /* enable LCD-display of 20 chars, 4 lines */ lcd.clear(); /* initialization on start-up as th stepper pulses */ digitalWrite(travel_pulsePin, HIGH ); /* are active low */ lcd.backlight(); /* switch on backlight illumination */ //lcd.noBacklight(); } /* program execution will continue with loop() */ /* ---------------- Travel_ISR() for stepper pulse generation ----------------- */ /* Interrupt service routine, managing the execution of a travel order in real */ /* time. Within the switch structure it is self modifying thus the main program does not have to care about that. */ /* Under each condition each section of the ISR must issue a pulse, in order not */ /* to produce time gaps. */ /* The execution path from the starting point of the ISR up to the switch is a common */ /* code path where some polling for external events like Z or K is done */ void Travel_ISR() { static char toggle = 0; /* needed just below, nowhere else */ /* used for ISR flow control in order to delay the */ /* make the built-in LED toggle each time the ISR is invoked.*/ /* As long as none of the following case-branches has been */ /* executed, the transition change of pin LED_BUILTIN is 10 */ /* times per second, leading to an on/off frequency of 5Hz */ if( toggle ^= 1 ) { digitalWrite(LED_BUILTIN, HIGH); } else { digitalWrite(LED_BUILTIN, LOW); } if(K_hardstop == TRUE ) /* if the ISR must brutally abort the motor´s travel in progress */ { ISRflow = 8; /* no more stepper pulses to be given out, abruptly */ K_hardstop = FALSE; } if(Z_softstop == TRUE ) /* polling whether the ISR is to abort the motor´s progressing travel brutally */ { drivedistance = decel_dist; /* soft break: overriding the current drive distance */ Z_softstop = FALSE; /* in order to invoke motor´s smooth deceleration to full standstill */ } /* now the branching begins according to the different */ switch( ISRflow ) /* tasks and phases of the travel */ { case 1: /* this is the starting point for the processing of any travel order. */ /* Here some preparations and devisions are done before the travel really starts */ Timer1.initialize( 1000000l/startstopspeed ); /* set timer´s period to startstopspeed */ /* if endless travel mode is in progress */ if (is_a_g_command == TRUE ) { if( sign_of_g_command == '-') /* this defines the sign bit for the */ digitalWrite(travel_dirPin, LOW ); /* the motor power module */ if( sign_of_g_command == '+') /* if positive */ digitalWrite(travel_dirPin, HIGH ); drivedistance = 12345l; /* simulating enough travel for acceleration */ ISRflow = 20; /* continue with ISRflow == 2; */ break; } if( is_a_G_command == TRUE ) { if( sign_of_G_command == '-') /* if negative */ digitalWrite(travel_dirPin, LOW ); if( sign_of_G_command == '+') /* if positive */ digitalWrite(travel_dirPin, HIGH ); if( sign_of_G_command == '\0' ) /* if a null-travel-span order is to be executed */ { ISRflow = 5; /* skip most of the ISR branching */ break; /* yet no stepper pulse is applied */ } /* check for very short drive distances of size 1, or 2 steps */ /* excluding ramp driving */ /* The minimum drivedistance is equal 1 */ /* If we are in a range of drive distance < 3 steps, */ /* only the present startstopspeed is applicable. */ /* For drivedistances >= 3 we enter the acceleration phase.*/ /* In order to be time optimal, after each setting of the step period */ /* a stepper pulse should be issued with next interrupt */ if( drivedistance < 3 ) { ISRflow = 5; /* skip the acceleration driving phase. In case 4 */ break; /* the rest of drivedistance will be driven */ } else ISRflow = 2; /* continue with ISRflow == 2; */ } break; case 20: /* Here: is_a_g_command == TRUE This is the endless travel mode being in progress */ /* This is the acceleration branch */ digitalWrite(travel_pulsePin, LOW ); /* travel pulse to the motor */ delayMicroseconds(3); travelstep_count++; /* for diagnostic purposes */ ISR_case2_length++; digitalWrite(travel_pulsePin, HIGH ); /* there is no: drivedistance--; here, meaning endless travel */ /* comparison: rest of travel versus deceleration travel*/ /* this is needed because a Z command might be issued */ if( drivedistance > (decel_dist+1) ) /* +1 to be on the save side */ { /* here drivedistance > decel_dist, so let´s accelerate */ if( (rampspeed + speedchanger) < topspeed ) /* full acceleration possible? */ { rampspeed += speedchanger; /* für 10.02 or future hier speedchanger reduzieren: speedchanger = speedchanger - speedchanger / 8 */ Timer1.initialize( 1000000l/rampspeed ); /* result of div. in us */ decel_dist++; /* deceletarion travel is increasing */ if( rampspeed > max_rampspeed ) max_rampspeed = rampspeed; break; } else { correcturup = topspeed - rampspeed; rampspeed += correcturup; /* calculate final speed */ Timer1.initialize( 1000000l/rampspeed ); /* result of div. in us */ decel_dist++; /* deceletarion travel is increasing */ ISRflow = 30; /* chriteria 1 is valid, top speed is set, leave */ if( rampspeed > max_rampspeed ) max_rampspeed = rampspeed; break; } } /* end of if check for sufficiently long travel to come to top speed */ else /* carry on with driving at constant velocity */ { ISRflow = 4; /* switch to deceleration travel phase */ break; } /* end of if clause for acceleration trial */ case 2: /* Here: is_a_G_command == TRUE meaning we are in the acceleration */ /* phase of a the G command, here the initial drive distance was > 2. */ /* There are 2 chriteria why to leave this acceleration phase: */ /* 1: on a long enough travel the top speed can be realized*/ /* 2: travel is too short to come to top speed */ digitalWrite(travel_pulsePin, LOW ); /* travel pulse to the motor */ delayMicroseconds(3); travelstep_count++; /* for diagnostic purposes */ ISR_case2_length++; digitalWrite(travel_pulsePin, HIGH ); drivedistance--; /* make drive_distance up to date */ /* comparison: rest of travel versus deceleration travel*/ if( drivedistance > (decel_dist+1) ) /* +1 to be on the save side */ { /* here drivedistance > decel_dist, so let´s accelerate */ if( (rampspeed + speedchanger) < topspeed ) /* full acceleration possible? */ { rampspeed += speedchanger; /* für 10.02 or future hier speedchanger reduzieren: speedchanger = speedchanger - speedchanger / 8 */ Timer1.initialize( 1000000l/rampspeed ); /* result of div. in us */ decel_dist++; /* deceletarion travel is increasing */ if( rampspeed > max_rampspeed ) max_rampspeed = rampspeed; break; } else { correcturup = topspeed - rampspeed; rampspeed += correcturup; /* calculate final speed */ Timer1.initialize( 1000000l/rampspeed ); /* result of div. in us */ decel_dist++; /* deceletarion travel is increasing */ ISRflow = 3; /* chriteria 1 is valid, top speed is set, leave */ if( rampspeed > max_rampspeed ) max_rampspeed = rampspeed; break; } } /* end of if check for sufficiently long travel to come to top speed */ else /* carry on with driving at constant velocity */ { ISRflow = 4; /* switch to deceleration travel phase */ break; } /* end of if clause for acceleration trial */ case 30: /* Here: is_a_g_command == TRUE meaning we are in the endless travel phase with constant velocity */ /* where the rest of travel also has to be compared with the decelaration travel */ if( drivedistance > decel_dist ) /* do not change */ { digitalWrite(travel_pulsePin, LOW ); delayMicroseconds(3); travelstep_count++; /* for diagnostic purposes */ ISR_case3_length++; digitalWrite(travel_pulsePin, HIGH ); /* there is no: drivedistance--; here, meaning endless travel */ } else ISRflow = 4; /* next time deceleration */ break; case 3: /* Here: is_a_G_command == TRUE meaning we are in the travel phase with constant velocity */ if( drivedistance > decel_dist + 1 ) /* do not change */ { digitalWrite(travel_pulsePin, LOW ); delayMicroseconds(3); travelstep_count++; /* for diagnostic purposes */ ISR_case3_length++; digitalWrite(travel_pulsePin, HIGH ); drivedistance--; } else ISRflow = 4; /* next time deceleration */ break; case 4: /* Here the case branches for the G and g commands are reunited. */ /* the deceleration ramp is to be driven down */ /* symmetrically to the ramp up travel phase in case of Gcommand */ if( (rampspeed - speedchanger) > startstopspeed ) /* full acceleration possible? */ { rampspeed -= speedchanger; /* calculate next speed */ decel_dist--; if( min_rampspeed > rampspeed ) min_rampspeed = rampspeed; } else { correcturdown = rampspeed - startstopspeed; rampspeed -= correcturdown; /* calculate last ramp speed */ decel_dist--; if( min_rampspeed > rampspeed ) min_rampspeed = rampspeed; ISRflow = 5; /* deceletarion travel phase has come to its end */ } digitalWrite(travel_pulsePin, LOW ); delayMicroseconds(3); travelstep_count++; /* for diagnostic purposes */ ISR_case4_length++; digitalWrite(travel_pulsePin, HIGH ); drivedistance--; /* actualise new position of motor */ Timer1.initialize( 1000000l/rampspeed ); /* result of div. in us */ break; case 5: /* this is a special case: If a tiny travel is to be */ /* driven, do it at start/stop speed. */ /* Also implemented due to needs of the Z-command. */ /* and because of very small drivedistances */ /* giving out the last pulse of a short travel distance */ if(drivedistance == 0l) /* was <= , should be changed to == */ { ISRflow = 6; /* no more stepper pulses to be given out */ break; } else { digitalWrite(travel_pulsePin, LOW ); delayMicroseconds(3); travelstep_count++; /* for diagnostic purposes */ ISR_case5_length++; digitalWrite(travel_pulsePin, HIGH ); drivedistance--; /* update new position of motor */ break; } break; case 6: /* here all stepping pulses for a complete travel order are given out */ /* this is also the entry point for K-command processing: brutal stop */ /* at full angular speed of the rotating shaft */ travel_inprogress = IDLE; /* travel has come to its end, from now on just be idling */ if( is_a_G_command == TRUE ) { if( sign_of_G_command == '+' ) /* number of steps actually driven */ driven_abs_pos += (ISR_case2_length + ISR_case3_length + ISR_case4_length + ISR_case5_length) ; if( sign_of_G_command == '-' ) /* number of steps actually driven */ driven_abs_pos -= (ISR_case2_length + ISR_case3_length + ISR_case4_length + ISR_case5_length) ; } if( is_a_g_command == TRUE ) /* no summation in case of a g command */ driven_abs_pos = 0l; ISRflow = 7; break; case 7: /* here all is done: keep ISR just idling with low start/stop speed */ ISRflow = 0; /* means the default state in the switch structure */ Timer1.initialize(50000l); /* the ISR gets invoked each 50,000 us or 0.05 sec, equal to 20Hz */ #if DEBUGSERIAL diag_serial = TRUE; /* TRUE means to display diagnostic results */ #endif #if DEBUGLCD diag_lcd = TRUE; /* TRUE means to display diagnostic results */ #endif break; case 8: /* special handling of the K travel hard abortion */ travel_inprogress = IDLE; /* travel has come to its end, from now on just be idling */ driven_abs_pos = 0l; ISRflow = 0; /* means the default state in the switch structure */ Timer1.initialize(50000l); /* the ISR gets invoked each 50,000 us or 0.05 sec, equal to 20Hz */ #if DEBUGSERIAL diag_serial = TRUE; /* TRUE means to display diagnostic results */ #endif #if DEBUGLCD diag_lcd = TRUE; /* TRUE means to display diagnostic results */ #endif break; default: /* default matches with ISRflow == 0 */ break; } /* end of switch */ } /* end of ISR function */ /* -------- decoding of the parameters of a PO travel order ------------------- */ /* the PO-order is followed be the following parameters/subsequent ASCII strings */ /* Sxxx\r, Txxx\r, Rxxx\r, G(+/-)xxxxxxx\r, g(+/-)\r and E\r */ /* */ /* In case of: */ /* g(+/-)\r a variable named "sign_of_g_command" is touched which by default */ /* is NULL. Next the var "endless travel" is switched to TRUE, used as a flag */ /* for the ISR */ /* */ /* In case of: */ /* G(+/-)xxxxxxx\r a variable named "is_a_G_command" is turned TRUE which by */ /* default is FALSE */ /* */ void PO_decode_params() { int decoder; /* used for decoding stepper travel command ASCII strings */ S_arg_counter = 0; /* initialisation */ T_arg_counter = 0; R_arg_counter = 0; G_arg_counter = 0; sign_of_g_command = '\0'; /* by default ASCII zero, meaning: no g+- travel */ is_a_g_command = FALSE;/* a message for the ISR */ is_a_G_command = FALSE; ISR_case2_length = 0l; /* collecting drive length in the shape of amounts of */ ISR_case3_length = 0l; /* stepper pulses in different branches of the ISR */ ISR_case4_length = 0l; ISR_case5_length = 0l; lcd.setCursor( 0, 0 ); lcd.print( " " ); lcd.setCursor( 0, 1 ); lcd.print( " " ); decoder = 1; /* entry state for the following switch command */ do { /* waiting for input characters to come */ while( !(Serial.available()>0) ) /* keep on looping as long as there is no char available */ ; int pollch = Serial.read(); /* get the character */ switch( decoder ) { case 1: /* meaning: a valid programming command took place */ switch( pollch ) { case 'S': /* command for setting the start / stop speed */ decoder = 3; /* define the way how to do the further decoding */ break; case 'T': /* command for setting the top speed */ decoder = 4; break; case 'R': /* command for setting the ramp lenght */ decoder = 5; break; case 'G': /* command for setting the go to absolute pos. */ decoder = 6; break; case 'g': /* command for setting infinitive driving */ decoder = 7; break; case 'E': /* command: Execute */ decoder = 10; break; default: /* if nothing from the above, check for */ decoder = 2; /* ASCII '\r' next time */ } /* end of switch */ break; case 2: /* here nothing valid was detected */ if( pollch == '\r' ) /* is it CR? */ { Serial.print("E4\r"); decoder = 1; /* try again in order to get valid travel parameters */ } break; case 3: /* collect the argument of the S command */ if(S_arg_counter == 0) /* new 31.01.20 */ { S_argument[0] = '\0'; /* initialize for next time */ S_argument[1] = '\0'; S_argument[2] = '\0'; S_argument[3] = '\0'; } if(isDigit(pollch)) S_argument[S_arg_counter++] = pollch; if(S_arg_counter > 3) /* for example '0', '0', '1', '2', meaning 4 chars */ { Serial.print("E2\r"); S_arg_counter = 0; /* new 31.01.20 */ decoder = 1; } else if(pollch == '\r') /*the last char to be expected is '\r' */ { Serial.print("Y\r"); /* all goes well */ S_arg_counter = 0; /* new 31.01.20 */ sscanf(&S_argument[0], "%d", &startstopspeed ); decoder = 1; } break; case 4: /* collect the argument of the T command */ if(T_arg_counter == 0) /* new 31.01.20 */ { T_argument[0] = '\0'; /* initialize for next time */ T_argument[1] = '\0'; T_argument[2] = '\0'; T_argument[3] = '\0'; } if(isDigit(pollch)) T_argument[T_arg_counter++] = pollch; if(T_arg_counter > 3) /* for example '0', '0', '1', '2', meaning 4 chars */ { Serial.print("E2\r"); T_arg_counter = 0; /* new 31.01.20 */ decoder = 1; } else if(pollch == '\r') /*the last char to be expected is '\r' */ { Serial.print("Y\r"); /* all goes well */ T_arg_counter = 0; /* new 31.01.20 */ sscanf(&T_argument[0], "%d", &topspeed ); decoder = 1; } break; case 5: /* collect the argument of the R command */ if(R_arg_counter == 0) /* new 31.01.20 */ { R_argument[0] = '\0'; /* initialize for next time */ R_argument[1] = '\0'; R_argument[2] = '\0'; R_argument[3] = '\0'; } if(isDigit(pollch)) R_argument[R_arg_counter++] = pollch; if(R_arg_counter > 3) /* for example '0', '0', '1', '2', meaning 4 chars */ { Serial.print("E2\r"); R_arg_counter = 0; /* new 31.01.20 */ decoder = 1; } else if(pollch == '\r') /*the last char to be expected is '\r' */ { Serial.print("Y\r"); /* all goes well */ R_arg_counter = 0; /* new 31.01.20 */ sscanf(&R_argument[0], "%d", &ramplength ); decoder = 1; } break; case 6: /* collect the argument "sign" of the G command */ if( pollch == '+' ) sign_of_G_command = '+'; /* a message to the ISR */ if( pollch == '-' ) sign_of_G_command = '-'; is_a_G_command = TRUE; /* a message to the ISR, too */ decoder = 8; break; case 7: /* the g+- command is postdecessed by a sign char */ if( pollch == '+' ) /* it is processed in the same way as the G comm. */ { sign_of_g_command = '+'; is_a_g_command = TRUE; } if( pollch == '-' ) { sign_of_g_command = '-'; is_a_g_command = TRUE; } decoder = 9; break; case 8: /* get the numeric data of the G+- command */ if(G_arg_counter == 0) /* new 31.01.20 */ { G_argument[0] = '\0'; /* initialize for next time with string terminators */ G_argument[1] = '\0'; G_argument[2] = '\0'; G_argument[3] = '\0'; G_argument[4] = '\0'; G_argument[5] = '\0'; G_argument[6] = '\0'; G_argument[7] = '\0'; G_argument[8] = '\0'; } if(isDigit(pollch)) G_argument[G_arg_counter++] = pollch; if(G_arg_counter > 7) /* for example 00001278 (8 or more chars ) */ { Serial.print("E2\r"); G_arg_counter = 0; /* new 31.01.20 */ decoder = 1; } else if(pollch == '\r') /*the last char to be expected is '\r' */ { Serial.print("Y\r"); /* all goes well */ G_arg_counter = 0; /* new 31.01.20 */ sscanf(&G_argument[0], "%ld", (long int) &G_asb_travel_order ); decoder = 1; } break; case 9: /* check for the terminator of the g+- command */ if( pollch != '\r' ) { decoder = 0; /* leave the while loop and check for next char */ break; } else /* command terminated properly */ decoder = 1; /* wait for E command */ break; case 10: /* this is the terminator of the E command */ if( pollch != '\r' ) { decoder = 0; /* leave the while loop and check for next char */ break; } else /* command terminated properly */ { Serial.print("Y\r"); /* all goes well */ /* here the evaluation of the argments */ /* G- or g-command ? */ if(is_a_G_command == TRUE) { if( sign_of_G_command == '-') G_asb_travel_order = -(G_asb_travel_order); /* absolute drive calculations */ /* drivedistance is signed */ signed_travel = G_asb_travel_order - G_abs_position; /* this is the calculated size, not applicable in case of a */ G_abs_position += signed_travel; /* K travel abortion command */ drivedistance = signed_travel; /* a work copy for the ISR */ if( drivedistance == 0l ) /* if it is a null travel */ { sign_of_G_command = '\0'; decoder = 0; /* no reason to travel at all, all done */ break; /* end of case 10 for this special case */ } if( drivedistance < 0l ) /* in case of a neg. travel argument */ sign_of_G_command = '-'; /* define a direction bit for ISR */ if( drivedistance > 0l ) /* in case of a neg. travel argument */ /* 06.02.2020 */ sign_of_G_command = '+'; /* define a direction bit for ISR */ /* for the ISR make it always ABS. */ drivedistance= abs (drivedistance); } startstopspeed *= 10; /* by SGS controller convention */ topspeed *= 10; /* as a consequence, the smallest speed */ /* difference is 10 steps / s */ rampspeed = startstopspeed; /* start with lowest frequency */ speedchanger = round ( (float) (topspeed - startstopspeed) / ramplength ); decel_dist = 0l; /* initialisation */ travel_inprogress = BUSY; /* will be reset to IDLE state by the ISR */ decoder = 0; /* this flag tells the ISR to start the travel order at the point case 0, here all is done*/ } break; /* end of case 10 */ } /* end of switch */ }while(decoder != 0); /* end of while */ } /* end of function PO_decode_params() */ void send_results_serial() { Serial.print("\nDiagnostic display:\r\n"); // sprintf( &string[0], "diag_index (changes in speed) = %d\r\n", diag_index ); // Serial.print(&string[0]); // Serial.print("diag_rampspeed =\r\n"); // for( ; diag_index > 0; diag_index--) // { // sprintf( &string[0], " %d", diag_rampspeed[diag_index-1] ); // Serial.print(&string[0]); // if(diag_index % 15 == 0) // Serial.print("\r\n"); // } sprintf( &string[0], "speedchanger = %d\r\n", speedchanger ); Serial.print(&string[0]); sprintf( &string[0], "ramplength = %d\r\n", ramplength ); Serial.print(&string[0]); sprintf( &string[0], "max_rampspeed = %d\r\n", max_rampspeed ); Serial.print(&string[0]); sprintf( &string[0], "min_rampspeed = %d\r\n", min_rampspeed ); Serial.print(&string[0]); sprintf( &string[0], "ISR_case2_length = %ld\r\n", ISR_case2_length ); Serial.print(&string[0]); sprintf( &string[0], "ISR_case3_length = %ld\r\n", ISR_case3_length ); Serial.print(&string[0]); sprintf( &string[0], "ISR_case4_length = %ld\r\n", ISR_case4_length ); Serial.print(&string[0]); sprintf( &string[0], "ISR_case5_length = %ld\r\n", ISR_case5_length ); Serial.print(&string[0]); sprintf( &string[0], "travelstep_count = %ld\r\n", travelstep_count ); Serial.print(&string[0]); Serial.print("End\r\n"); } void send_results_lcd() { lcd.setCursor( 0, 1 ); lcd.print( "Ist: " ); lcd.setCursor( 6, 1 ); lcd.print( travelstep_count, DEC ); } /* ------------------------------------ loop() --------------------------------- */ /* this is the cyclic part of the program. */ /* */ /* since it contains its own loop structure in the form of a do-while command, */ /* loop() can be replaced with main() when ported to other IDEs */ void loop() { /* the following flags are used in order to memorise the */ int F_flag; /* start character of an ASCII sequence for a travel order */ int Z_flag; int P_flag; int I_flag; int K_flag; int V_flag; int rubbish_flag; /* if nothing valid was received as travel order, it is considered waste */ /* needed for analyzing the ASCII sequence coming from outside */ /* 0 means that a new travel order is expected */ int main_decoder; /* 1 means that the travel order has the size of 2 characters */ int wait_for_Z_slowing_down; /* flag telling to wait until a Z-break is finished */ ISRflow = 0; /* prepare the ISR for idling when getting released later */ Timer1.initialize(50000l); /* the ISR gets invoked each 50,000 us or 0.05 sec, equal to 20Hz */ // (100000l); /* the ISR gets invoked each 100000us or each 0.1 sec*/ Timer1.attachInterrupt(Travel_ISR); /* assign ISR needed to process a travel order */ interrupts(); /* now the ISR is invoked in the idle mode */ /* flush the input buffer once on entry */ while( Serial.available() > 0 ) /* no specific flush() function found in the libraries */ { Serial.read(); } /* initialisierung of appropriate variables */ F_flag = 0; Z_flag = 0; P_flag = 0; I_flag = 0; K_flag = 0; V_flag = 0; rubbish_flag = 0; travel_inprogress = IDLE; /* no busy travel command in progress */ main_decoder = 0; sign_of_g_command = '\0'; sign_of_G_command = '\0'; G_abs_position = 0l; driven_abs_pos = 0L; G_asb_travel_order = 0l; wait_for_Z_slowing_down = FALSE; /* meaning that we are not in a situation to wait for a soft stop triggered by a "Z" command */ K_hardstop = FALSE; /* meaning: no brutal travel abortion is active */ Z_softstop = FALSE; /* in order to invoke motor´s smooth deceleration to full standstill */ /* end of the linear run through the program */ do /* start of an endless looping without escape in order to do */ { /* the complete task. In this aspect a standard main() would behave no differently than this cyclic Arduino loop(); */ do { /* ************************************************************ */ /* this is the inner loop, in which other activities can be done */ /* concurrently to the actual task namely stepper control */ /* ************************************************************ */ /* put more code here, like this one: */ /* just for one time */ if( diag_serial == TRUE ) { send_results_serial(); diag_serial = FALSE; } if( diag_lcd == TRUE ) { send_results_lcd(); diag_lcd = FALSE; } /* when polling for new travel orders, it makes sense to wait until */ /* the order in progress (if any) has been processed or shortened. */ /* That is done below: if a Z-command was issued and the soft */ /* stopped travel is completed, we can go on */ if( (wait_for_Z_slowing_down == TRUE) && (travel_inprogress == IDLE) ) { wait_for_Z_slowing_down = FALSE; G_abs_position = driven_abs_pos; } }while( !(Serial.available()>0) ); /* keep on looping as long as there is no char available */ int pollch = Serial.read(); /* a valid char ascii is present, so get it as int */ /* according to the demands of the function */ /* it is assumed that at this place characters for a travel order are coming in and every associated char. will be treated as being part of a travel order */ /* every char. will be processed by an appropriate case related portion of code */ switch(main_decoder) { case 0: /* check for certain chars. being regarded valid start chars. for travel- and state reporting orders */ if( pollch == 'F') { F_flag = 1; main_decoder = 1; break; } if( pollch == 'Z' ) { Z_flag = 1; main_decoder = 1; break; } if( pollch == 'P' ) { P_flag = 1; main_decoder = 1; break; } if( pollch == 'I' ) { I_flag = 1; main_decoder = 1; break; } if( pollch == 'V' ) { V_flag = 1; main_decoder = 1; break; } if( pollch == 'K' ) { K_flag = 1; main_decoder = 1; break; } rubbish_flag = 1; main_decoder = 1; break; /* if none of these commands, a filter is put to skip them as it is rubbish */ case 1: /* there is a char-history of exactly one char., here */ if( (F_flag == 1) && (pollch == '\r') ) /* checking for a F command by examinating the proper consecutive char */ { if(travel_inprogress == BUSY) Serial.print("B\r"); /* in busy state, travel command execution in progress */ else Serial.print("R\r"); /* being ready since no travel command execution in progress */ F_flag = 0; /* a complete and valid order has been detected */ main_decoder = 0; /* prepare for analysing another travel order */ } if( (Z_flag == 1) && (pollch == '\r') ) /* checking for a Z command by examinating the proper following char */ { if(travel_inprogress == BUSY) /* if travelling is still in progress (not finished yet) */ { Z_softstop = TRUE; /* tell the ISR to decelerate. Reset is done in the ISR */ Serial.print("Y\r"); /* let´s acknowledge the received request at once and positively */ wait_for_Z_slowing_down = TRUE; /* delay next invoke */ } else Serial.print("E4\r"); /* there is no reason to issue a "Z" command while the motor´s shaft is not turning */ Z_flag = 0; /* a complete and valid order has been detected, no need to memorize anything any more */ main_decoder = 0; /* prepare for analysing next travel order */ } if( (K_flag == 1) && (pollch == '\r') ) /* a hard stop is requested, kill the travel order in progress */ { if(travel_inprogress == BUSY) /* if travelling is still in progress (not finished yet) */ { K_hardstop = TRUE; /* telling the ISR to break the motor´s travel brutally. Will be reset in the ISR */ Serial.print("Y\r"); /* all goes well */ } else Serial.print("E4\r"); /* there is no reason to issue a "Z" command while the motor´s shaft is not turning */ K_flag = 0; /* the complete and valid K-order has been processed to its end */ main_decoder = 0; /* prepare for analysing next travel order */ } if( (P_flag == 1) && (pollch == 'O') ) /* if "PO" is encountered correspoding activity is needed */ main_decoder = 2; if( (I_flag == 1) && (pollch == '1') ) /* checking for the '1' */ main_decoder = 3; if( (V_flag == 1) && (pollch == '1') ) /* checking for the '1' */ main_decoder = 4; /* it is supposed that all cmds are terminated by CR */ if( (rubbish_flag == 1) && (pollch == '\r') ) { Serial.print("E4\r"); rubbish_flag = 0; /* a non valid order has been rejected by an error message */ main_decoder = 0; /* prepare for analysing next travel order */ } break; case 2: /* processing the P-command after having collected the second char 'O' */ if(pollch == '\r') { if(travel_inprogress == BUSY) /* if travelling is still in progress (not finished yet) */ // if no travelling active ?????? Serial.print("B\r"); /* in busy state, travel command execution in progress */ else { Serial.print("Y\r"); /* all goes well */ PO_decode_params(); /* decode the travel parameters presented in ASCII data */ travelstep_count = 0l; /* 04.02.20 new */ /* here travel_inprogress is in BUSY state, so all travel parameters are decoded and */ /* the nominal value to be driven by the ISR can be displayed */ #if DEBUGLCD lcd.setCursor( 0, 0 ); lcd.print( "Soll: " ); lcd.setCursor( 6, 0 ); lcd.print( drivedistance, DEC ); #endif /* *************************************************************** */ ISRflow = 1; /* was 0 means: apply a drive command processed by the ISR */ /* *************************************************************** */ } P_flag = 0; /* a complete and valid order has been detected, no need to memorize anything any more */ main_decoder = 0; /* prepare for analysing next travel order */ } break; case 3: /* processing the I-command after the second char '1' */ if(pollch == '\r') { driven_abs_pos = 0L; /* initialize the absolute position counter with zero */ G_abs_position = 0L; Serial.print( "Y\r"); /* all goes well */ I_flag = 0; /* a complete and valid order has been detected, no need to memorize anything any more */ main_decoder = 0; /* prepare for analysing next travel order */ } break; case 4: /* processing the V-command after the second char '1' */ if(pollch == '\r') if(travel_inprogress == BUSY) /* if travelling active */ Serial.print( "B\r"); else { sprintf( &string[0], "V%ld\r", driven_abs_pos ); /* l for long */ Serial.print(&string[0]); V_flag = 0; /* a complete and valid order has been detected, no need to memorize anything any more */ main_decoder = 0; /* prepare for analysing next travel order */ } break; } /* end of switch */ }while(1); } /* end of file */