diff --git a/AstroEQ-PIC-Firmware/Synta_PIC_Optimized.pde b/AstroEQ-PIC-Firmware/Synta_PIC_Optimized.pde new file mode 100644 index 0000000000000000000000000000000000000000..69c7a3b261766ab931fd652be4deb6a7c14d9f26 --- /dev/null +++ b/AstroEQ-PIC-Firmware/Synta_PIC_Optimized.pde @@ -0,0 +1,1014 @@ +/* + Code ported by Joël Collet 2013 for Pinguino adaptation + + Thanks to Thomas Carpenter 2013 for the original code + With thanks Chris over at the EQMOD Yahoo group for explaining the Skywatcher protocol + + + Equatorial mount tracking system for integration with EQMOD using the Skywatcher/Syntia + communication protocol. + + Works with EQ5, HEQ5, and EQ6 mounts (Not EQ3-2, they have a different gear ratio) + + Current Version: 0.1 +*/ + +//#include "synta_cmd.h" //Synta Communications Protocol. + +//The system needs to be configured to suit your mount. +//Either use the instructions below and calculate the values yourself, +//or use the "Initialisation Variable Calculator.xlsx" file. +// +// The line to configure the mount follows these instructions! +// +// +// Format: +// +//Synta Synta( e [1281], a, b, g [16], s); +// +// e = version number (this doesnt need chaning) +// +// a = number of steps per complete axis revolution. +// aVal = µsteps/steps * motor steps per revolution where µsteps/steps maximum value is 32 for drv8825 and 16 for 4988 boards +// where ratio is the gear ratio from motor to axis. E.g. for an HEQ5 mount, this is 705 +// and motor steps is how many steps per one revolution of the motor. E.g. a 1.8degree stepper has 200 steps/revolution +// +// b = sidereal rate. This again depends on number of microsteps per step. +// bVal = 620 * aVal / 86164.0905 (round UP to nearest integer) +// +// g = high speed multiplyer. This is the EQMOD recommended default. In highspeed mode, the step rate will be 16 times larger. +//actually the g value is closely related to the stepping mode choosed in high speed mode +//that is if you are to go in high speed mode from say 1/64 µstep to 1/8 µtep, the g value is 8, not 16!! +// otherwise your speed is 2 times slower than wanted +// s = steps per worm revolution +// sVal = aVal / Worm Gear Teeth +// where worm gear teeth is how many teeth on the main worm gear. E.g. for an HEQ5 this is 135 +// +// +// Examples: +// +// mount specifics can be found here: http://tech.groups.yahoo.com/group/EQMOD/database?method=reportRows&tbl=5 +// +// Summary of mounts (Untested): +// +// HEQ5 Pro/Syntrek/SynscanUpgrade: +// aVal = 9024000 +// bVal = 64933 +// sVal = 66845 +// scalar = 1 +// +// EQ6 Pro/Syntrek/SynscanUpgrade: +// aVal = 9024000 +// bVal = 64933 +// sVal = 50133 +// scalar = 1 +// +// EQ5 Synscan/SynscanUpgrade: +// aVal = 9011200 +// bVal = 64841 +// sVal = 31289 +// scalar = 1 +// +// +// Other mounts: +// aVal = µsteps/step * motor steps per revolution * ratio (where µsteps/steps depends of the driver board and mode used +// bVal = 620 * aVal / 86164.0905 (round to nearest whole number) +// sVal = aVal / Worm Gear Teeth (round to nearest whole number) +// +// +//Create an instance of the mount +//If aVal is roughly greater than 10000000 (ten million), the number needs to be scaled down by some factor +//to make it smaller than that limit. In my case I have chosen 10 as it nicely scales the +//a and s values. Hence below, I have set scalar to 10. + +//Define the two axes (swap the 0 and 1 if R.A. and Dec. motors are reversed) +//------------------------------------------------------------- +//DO NOT EDIT ANYTHING BELOW THIS LINE------------------------- +//------------------------------------------------------------- + +#include <pic18fregs.h> +#include <typedef.h> +#include <macro.h> +#include <math.h> + +#define INT_ENABLE 1 +#define INT_DISABLE 0 +#define INT_ENABLE_PRIORITY 1 +#define INT_DISABLE_PRIORITY 0 +#define INT_HIGH_PRIORITY 1 +#define INT_LOW_PRIORITY 0 + +#define T3_PS_1_8 0b11111111 // 1:8 Prescale value +#define T3_PS_1_1 0b11001111 // 1:1 Prescale value + +#define T1_PS_1_1 0b11001111 // 1:1 prescale value +#define T1_PS_1_8 0b11111111 // 1:8 prescale value + + +#ifndef USERINT //pour activer les interruptions +#define USERINT +#endif + +#ifndef INT0INT //pour activer les interruptions high +#define INT0INT +#endif + + +#define NORMALGGOTOSPEED 320L //This is defined as Speed with the letter 'L' appended +#define FASTGOTOSPEED 176L +//#define USEHIGHSPEEDMODE 1 +//#define HAVEST4 1 //in case we want to have ST4 input +//#define HAVEENCODER 1 //if we want to implement encoder (as in the GT mount) +// We can not have both ST4 and ENCODER..not enough interruptible pins + + +//variable definition + +unsigned long gotoPosn[2] = {0,0}; //where to slew to +unsigned int timerOVF[2]; //Clock section of each of the 64 microsteps +unsigned long timerCountRate; +unsigned long maxTimerOVF; //Minimum allowable clock count +u8 rate_modifier = 2; //2 =(0,25); 1=(0,5), 0 =(1x) for guiding 0.75x is not proposed so far, due to more complex handling +#define PULSEWIDTH 5 //4µs, dans delayus + +//Pins +//const char statusPin = 13; //RA4, USERLED +#ifdef HAVEST4 +const char ST4Pin[4] = {4,5,6,7}; // RB4 to RB7 ST4 guiding input +#endif +#ifdef HAVEENCODER +const char ENCODERPin[4] = {4,5,6,7}; // RB4 to RB7 RA and DEC encoders input +#endif + +const char resetPin[2] = {12,17}; //RC2,RA5 + +const char dirPin[2] = {0,1}; //RB0,RB1 +const char enablePin[2] = {2,3}; //RB2,RB3 +const char stepPin[2] = {10,11}; //RC0 et RC1 //RA DEC +const char modePins[2][2] = {{13,14},{15,16}}; //RA0,RA1,RA2,RA3 {RA{Mode1,Mode2}, DEC{Mode1,Mode2}} Modepins. +//see datasheet for pololu card, mode0 = ms1 +//drv8825 32 µpas, mode0,1,2 (0,1,0) =1/4 steps (0,1,1)=1/32 steps =>mode0 always connected to 0 (which allows also 1/16 with (0,0,1) +//a4988, 16 µpas, ms123(1,0,0)=1/2 steps (1,1,1)=1/16steps => ms1 => always 1 +//if g value is 16 then we want +//drv8825 in 1/2 step mode mode0,1,2, (1,0,0) and (1,1,1) for 1/32 steps mode 1=mode2 +//a4988 in fullstep mode ms123=(0,0,0), need to connect differently, that is ms1=ms2=ms3! +//maybe one mode pin command can be used for that purpose, and we gain 2 more pins, for example for emergency stop/limit detection +#ifdef USEHIGHSPEEDMODE +boolean highSpeed[2] = {false,false}; //whether number of microsteps is dropped by 8 (16?) due to high speed (to increase Torque) - + //If you want to use it, uncomment the line: '#define USEHIGHSPEEDMODE 1' + //and connect the mode pins of the DRV8824PWP chips to the pins shown +#endif + +// + +//synta command part +#define numberOfCommands 17 + +#include "string.h" +//#include "stdlib.h" +#include "stdio.h" + +const char startInChar = ':'; +const char startOutChar = '='; +const char errorChar = '!'; +const char endChar = '\r'; +const char command[numberOfCommands][3] = { {'e', 0, 6}, + {'a', 0, 6}, + {'b', 0, 6}, + {'g', 0, 2}, + {'s', 0, 6}, + {'K', 0, 0}, + {'E', 6, 0}, + {'j', 0, 6}, + {'f', 0, 3}, + {'G', 2, 0}, + {'H', 6, 0}, + {'M', 6, 0}, + {'I', 6, 0}, + {'J', 0, 0}, + {'P', 1, 0}, + {'F', 0, 0}, + {'L', 0, 0} }; + +unsigned long _jVal[2]; //_jVal: Current position +unsigned long _IVal[2]; //_IVal: speed to move if in slew mode +byte _GVal[2]; //_GVal: slew/goto mode +unsigned long _HVal[2]; //_HVal: steps to move if in goto mode +//unsigned int _flag[2]; //_fVal: 00ds000g000f; d = dir, s = stopped, g = goto, f = energised +char stepDir[2]; +byte dir[2]; +byte FVal[2]; +byte gotoEn[2]; +byte stopped[2]; +unsigned long _eVal[2]; //_eVal: Version number +unsigned long _aVal[2]; //_aVal: Steps per axis revolution +unsigned long _bVal[2]; //_bVal: Sidereal Rate of axis +byte _gVal[2]; //_gVal: Speed scalar for highspeed slew +unsigned long _sVal[2]; //_sVal: Steps per worm gear revolution + +boolean validPacket; +char commandString[11]; +char commandIndex; + +byte _axis; +char _command; +//byte _scalar; +//byte div_scalar; + +//Command part +//command Structures --------------------------------------------------------- +// +// Definition of the commands used by the Synta protocol, and variables in which responses +// are stored +// +// Data structure of flag Flag: +// flag = xxxx00ds000g000f where bits: +// x = dont care +// d = dir +// s = stopped +// g = goto +// f = energised +// +// Only dir can be used to set the direction, but stepDir method can be used +// to returns it in a more useful format +// +//---------------------------------------------------------------------------- + +void Synta_clearBuffer(char* buf, byte len){ + strncpy(buf,"",len); +} + +char Synta_cmd_getLength(char cmd, boolean sendRecieve){ +byte i; + for(i = 0;i < numberOfCommands;i++){ + if(command[i][0] == cmd){ + if(sendRecieve){ + return command[i][1]; + } else { + return command[i][2]; + } + } + } + return -1; +} + +unsigned int Synta_cmd_fVal(byte target){ //calculate the status flag to be returned to eqmod + return ((dir[target] << 9)|(stopped[target] << 8)|(gotoEn[target] << 4)|(FVal[target] << 0)); +} + + +void Synta_init(unsigned long eVal,unsigned long aVal,unsigned long bVal,byte gVal,unsigned long sVal){ + byte i; + validPacket = 0; + _axis = 0; + commandIndex = 0; + Synta_clearBuffer(commandString,sizeof(commandString)); + //_scalar = scalar;* + for( i = 0;i < 2;i++){ + dir[i] = 0; + stopped[i] = 1; + gotoEn[i] = 0; + FVal[i] = 0; + _jVal[i] = 0x800000; //Current position, 0x800000 is the centre + _IVal[i] = 0; //Recieved Speed + _GVal[i] = 1; //Mode recieved from :G command + _HVal[i] = 0; //Value recieved from :H command + _eVal[i] = eVal; //version number + _aVal[i] = aVal; //steps/axis + _bVal[i] = bVal; //sidereal rate + _gVal[i] = gVal; //High speed scalar + _sVal[i] = sVal; //steps/worm rotation + } + +} + + +void Synta_error(char* buf){ + buf[0] = errorChar; + buf[1] = endChar; + buf[2] = 0; +} + +boolean Synta_validateCommand(byte len){ +char requiredLength; +byte i; + _command = commandString[0]; //first byte is command + _axis = commandString[1] - 49; //second byte is axis + if(_axis > 1){ + return 0; //incorrect axis + } + requiredLength = Synta_cmd_getLength(_command,1); //get the required length of this command + len -= 3; //Remove the command and axis bytes, aswell as the end char; + if(requiredLength != len){ //If invalid command, or not required length + return 0; + } + + + for(i = 0;i < len;i++){ + commandString[i] = commandString[i + 2]; + } + commandString[i] = 0; //Null + return 1; +} + +char Synta_recieveCommand(char* dataPacket, char character){ + if(validPacket){ + if (character == startInChar){ + goto error; //new command without old finishing! (dataPacket contains error message) + } + + commandString[commandIndex++] = character; //Add character to current string build + + if(character == endChar){ + if(Synta_validateCommand(commandIndex)){ + strcpy(dataPacket,commandString); //Return decoded packet + validPacket = 0; + return 1; //Successful decode (dataPacket contains decoded packet) + } else { + goto error; //Decode Failed (dataPacket contains error message) + } + } else if (commandIndex == sizeof(commandString)){ + goto error; //Message too long! (dataPacket contains error message) + } + } else if (character == startInChar){ + //Begin new command + + commandIndex = 0; + validPacket = 1; + Synta_clearBuffer(commandString,sizeof(commandString)); + } + + return 0; //Decode not finished (dataPacket unchanged) +error: + Synta_error(dataPacket); + validPacket = 0; + return -1; +} + + +unsigned long Synta_hexToLong(char* hex){ + + +unsigned long valeur = 0; +unsigned long val=0; +int i; +for (i=0;i<3;i++) + { + valeur=(int)hex[i*2+1]; + if (valeur <58) { val += (valeur-48)<<8*i;} else {val += (valeur-55)<<8*i;} + valeur=(int)hex[i*2]; + if (valeur <58) { val += (valeur-48)<<(8*i+4);} else {val += (valeur-55)<<(8*i+4);} + } + + return val; +} + +void long_tohex(unsigned long dita){ + byte bytes[3]; + bytes[0] = (dita >> 16) & 0xFF; + bytes[1] = (dita >> 8) & 0xFF; + bytes[2] = (dita) & 0xFF; + Serial.printf("=%02X%02X%02X\r",bytes[2],bytes[1],bytes[0]); +} +void byte_tohex( byte dita){ + dita &= 0xFF; + Serial.printf("=%02X\r",dita); +} +void int_tohex( int dita){ + dita &= 0xFFF; + Serial.printf("=%03X\r",dita); +} + + +// end of synta command part + +// hardware part +void speedCurve(unsigned long currentSpeed, char dir, byte steps, byte motor){ //dir = -1 or +1. - means accellerate + unsigned long speedPerStep = (maxTimerOVF - timerOVF[motor]); + unsigned int microDelay; + int i; + //maxTimerOVF is always largest + speedPerStep /= steps; + speedPerStep >>= 1; + // Serial.printf("in speed curve %i\r", speedPerStep); //debug + + if (speedPerStep == 0){ + speedPerStep = 1; //just in case - prevents rounding from resulting in delay of 0. + } + microDelay = currentSpeed >> 1; //number of uS to wait for speed (rate is in 0,666 of a microsecond) + // Serial.printf("in speed curve delay %i\r", microDelay);//debug + + for(i = 0;i < steps;i++){ + delayMicroseconds(microDelay); + if(motor){ //StepStep DEC + // Serial.printf("in speed curve DE \r");//debug + + PORTCbits.RC1=1; + delayMicroseconds(5); + PORTCbits.RC1=0; + } else {//Step RA + + // Serial.printf("in speed curve RA \r");//debug + PORTCbits.RC0=1; + delayMicroseconds(5); + PORTCbits.RC0=0; + } + microDelay += (dir * speedPerStep); + } +} + +void decellerate(byte decellerateSteps, byte motor){ + #if defined(USEHIGHSPEEDMODE) + if(highSpeed[motor]){ + //Disable Highspeed mode on board + highSpeed[motor] = false; + digitalWrite(modePins[motor][0],HIGH); //(1/32 steps for drv8825 et 1/16 for a4988) + digitalWrite(modePins[motor][1],HIGH); + if(motor){ //DEC back to normal rythm, count 8 times less than in high speed + + // Serial.printf("in speed decel DE \r");//debug + //PIE2bits.CCP2IE = INT_DISABLE; + CCPR2H = high8(timerOVF[motor]);//set compare value, need to check if we should disable the interrupt before setting register + CCPR2L = low8(timerOVF[motor]);//then re-enable it. if needed, uncomment and also add everywhere wher we set CCPR + //PIE2bits.CCP2IE = INT_ENABLE; //the time for this set/unset is anyway very short (few 10 µs max), so not important + } else {//RA + // Serial.printf("in speed decel RA \r");//debug + + //PIE1bits.CCP1IE = INT_DISABLE; + CCPR1H = high8(timerOVF[motor]);//set compare value + CCPR1L = low8(timerOVF[motor]); + //PIE1bits.CCP1IE = INT_DISABLE; + } + + } + #endif + //x steps to slow down to maxTimerOVF + // Serial.printf("in decell \r");//debug + + speedCurve(timerOVF[motor], 1, decellerateSteps, motor); + //Now at minimum speed, so safe to stop. +} + +void accellerate(byte accellerateSteps, byte motor){ + //x steps to speed up to timerOVF[Synta.axis()] + //Serial.printf("in accel \r");//Debug + + speedCurve(maxTimerOVF, -1, accellerateSteps, motor); + //Now at speed, so check highspeed mode and then return. Timers will be used from now on. + #if defined(USEHIGHSPEEDMODE) + if(highSpeed[motor]){//Enable Highspeed mode on board + digitalWrite(modePins[motor][0],LOW); //A4988 + digitalWrite(modePins[motor][1],LOW); + // digitalWrite(modePins[motor][0],HIGH); //drv8825 + // digitalWrite(modePins[motor][1],LOW); + if(motor){ //DEC 8 times less µsteps (from 1/16 to 1/2 or 1/32 to 1/4, more torque available, count 8times more + CCPR2H = high8(_gVal[motor]*timerOVF[motor]);//set compare value,8 times longer we can do it because in highspeed mode the timer value + CCPR2L = low8(_gVal[motor]*timerOVF[motor]); //is always less than 65535/8 =8192 + //Serial.printf("in accel DEC\r");//Debug + + + } else {//RA + CCPR1H = high8(_gVal[motor]*timerOVF[motor]);//set compare value 8 times longer + CCPR1L = low8(_gVal[motor]*timerOVF[motor]); + //Serial.printf("in accel RA\r");//Debug + + } + } + #endif +} + + + + +void motorStopp(byte motor, byte steps){ + if (!stopped[motor]){ + if(motor){ //CCP2=DEC + PIE2bits.CCP2IE = INT_DISABLE; + } else { + PIE1bits.CCP1IE = INT_DISABLE; + } + //Only decellerate if not already stopped - for some reason EQMOD stops both axis when slewing, even if one isn't currently moving? + if (gotoEn[motor]){ + gotoEn[motor]=0; //Cancel goto mode + }// + decellerate(steps,motor); + _jVal[motor]=_jVal[motor] + stepDir[motor]*steps; + stopped[motor]=1; //mark as stopped*/ + } +} + + +void motorStop(byte motor){ + byte numstep; + numstep =60; + // Serial.printf("stop\r");//debug + + motorStopp(motor, numstep); //Default number of steps. Replaces function prototype from Version < 5.0 +} + +void checkIfDone(byte motor){ + + //il faut introduire une variable de type multiplier (decallage de type rolling) a la place de gval + //cette variable est mise a 0 si on est pas e high speed dans la fonction accelerate et remise à 0dans la focntion decellerate si elle à une autre valeur + //sinon elle est mise à gval binaire (si gval =2, multilpier =1, si gval =4 multilpier =2, si 8 multiliper=3 si 16 multiliper=4 + //cela permet d'eviter le test et de reintroduire la mise a jour du biniou dans l'inetrruption + //le test gotoEn est aussi das l'interrupt, mais à la fin avec une variable motor settee dans le traitement de l'interruption + //justea apres la levee du flag (si on est en fin de goto, on appelle motor stop (qui disable l'interrupt) + //masi l'inetrrupt n'est pas cleare c'est pas bon! + //ou alors on cleare dans motorstop, auc hoix a verifier ce qui est mieux! + //maybe it can be made more efficient to perform these operation in the inetrruption, but more effeciently, e.g using + // shift function instead of multiply. also the test can be removed, since we can set the shift value to 0. + //Shifting value will e setlled in the accelerate code and cleared in the deceleratte one + //only the gotoEn is to be tested. but it can be placed also in theinterrupt, once motors are stepped + // (ideally we can set a byte flag telin which motor was stepped, to avoid doubling the codefor this gotoen test + //take care also to clear the interrupt flag before going into the motorstop code (that's cleaner i think) + + + byte num; + #if defined(USEHIGHSPEEDMODE) + if(highSpeed[motor]){ + _jVal[motor]=_jVal[motor] + stepDir[motor]*_gVal[motor]; + + } else { + #else + if(1){ //Always do the next line. Here to account for the extra close brace + //} -> this one is supposed to be commented out! (keeps the IDE happy) + #endif + _jVal[motor]=_jVal[motor] + stepDir[motor]; + } + if(gotoEn[motor]){ + long stepsLeft = gotoPosn[motor] - _jVal[motor]; + stepsLeft *= stepDir[motor]; + num =120; + if(stepsLeft <= num){ + motorStopp(motor, stepsLeft); //will decellerate the rest of the way. + } + } +} + + //motorStep is removed since scalar is removed and unused + +/*void motorStep(byte motor){ +static char divider[2] = {0}; +divider[motor] += stepDir[motor]; +if(stepDir[motor] < 0){ + if (divider[motor] < 0){ + divider[motor] = _scalar - 1; + checkIfDone(motor); + } + } else { + if (divider[motor] >= _scalar){ + divider[motor] = 0; + checkIfDone(motor); + } + } +}*/ + +void motorEnable(){ +// char s; + // s = _axis; + digitalWrite(enablePin[_axis],LOW); + FVal[_axis]=1; +} + + + +void motorStart(byte motor, byte steps){ + digitalWrite(dirPin[motor],dir[motor]); //set the direction + stopped[motor]= 0; + accellerate(steps,motor); //accellerate to calculated rate in given number of steps steps + _jVal[motor]=_jVal[motor] + stepDir[motor]*steps; + if(motor){ //DEC + CCPR2H = high8(timerOVF[motor]);//set compare value + CCPR2L = low8(timerOVF[motor]); + PIE2bits.CCP2IE = INT_ENABLE; //enable interrupt (stepping is started) + //T3CONbits.TMR3ON = ON; //don't know if we start counter only when motors run or if we let counter running all the time + } else {//RA + CCPR1H = high8(timerOVF[motor]);//set compare value + CCPR1L = low8(timerOVF[motor]); + PIE1bits.CCP1IE = INT_ENABLE; + //T1CONbits.TMR1ON = ON;// + } +} + +void calculateRate(byte motor){ +//seconds per step = IVal / BVal; for lowspeed move +//or seconds per step = IVal / (BVal * gVal); for highspeed move +// +//whether to use highspeed move or not is determined by the GVal; +// +//clocks per step = timerCountRate * seconds per step + unsigned long rate; + float float_rate; + unsigned long divisor = _bVal[motor]; + if((_GVal[motor] == 2)||(_GVal[motor] == 1)){ //Normal Speed + #if defined(USEHIGHSPEEDMODE) + //check if at high torque/high speed + if (_IVal[motor] < 30){ + highSpeed[motor] = true; + } else { + highSpeed[motor] = false; + } + #endif + } else if ((_GVal[motor] == 0)||(_GVal[motor] == 3)){ //High Speed + divisor *= _gVal[motor]; + #if defined(USEHIGHSPEEDMODE) + //check if at very high speed/high torque + if (_IVal[motor] < (30 * _gVal[motor])){ + highSpeed[motor] = true; + } else { + highSpeed[motor] = false; + } + #endif + } + rate = timerCountRate * _IVal[motor]; + // Serial.printf("calcul rate %li\r",rate);//debug + + float_rate = (float)rate / divisor; //Calculate the quotient + // Serial.printf("calcul rate %f\r",float_rate); //Debug + + //Then convert the remainder into a decimal number (division of a small number by a larger one, improving accuracy) + float_rate *= 10.0; //corrects for timer count rate being set to 1/10th of the required to prevent numbers getting too big for an unsigned long. + + // Serial.printf("calcul rate %f\r",float_rate);//Debug + //If there is now any integer part of the remainder after the multiplication by 10, it needs to be added back on to the rate and removed from the remainder. + //rate = (unsigned long)floorf(float_rate); + // Serial.printf("calcul rate %li\r",rate); // debug + +rate =(unsigned long)1*float_rate; + //Serial.printf("calcul rate %li\r",rate); // debug + + if ((float_rate - (float)rate)>0.5 ) { //rounding to the nearest integer + rate +=1; // is there a better solution? + } + // Serial.printf("calcul rate %li\r",rate);//Debug +//Now truncate to an unsigned int with a sensible max value (the int is to avoid register issues with the 16 bit timer) + if(rate > 64997UL){ + rate = 64997UL; + } else if (rate < 20UL) { //to allow for goto higher than 400 ...but without the highspeed option itis hard to achieve + rate = 20L; + } + timerOVF[motor] = rate; + Serial.printf("calcul rate %li\r",rate); // + + // Serial.printf("calcul rate %li\r",rate);//Debug +} + +void slewMode(){ + byte numstep; +// numstep =Synta.scalar(); + //numstep =30/_scalar; + numstep = 30; + motorStart(_axis, numstep); //Begin PWM +} + +void gotoMode(){ + byte num; +// num =Synta.scalar(); + // num =140/_scalar; + num = 140; + if (_HVal[_axis] < num){ + if (_HVal[_axis] < 2){ + _HVal[_axis]=2; + } + //To short of a goto to use the timers, so do it manually + // accellerate( (_HVal[_axis] / 2) * _scalar, _axis); + // decellerate( (_HVal[_axis] / 2) * _scalar, _axis); + accellerate( (_HVal[_axis] / 2), _axis); + decellerate( (_HVal[_axis] / 2), _axis); + + _jVal[_axis]=_jVal[_axis] + stepDir[_axis] * _HVal[_axis] ; + } else { + gotoPosn[_axis] = _jVal[_axis] + stepDir[_axis] * _HVal[_axis]; //current position + distance to travel + // num =Synta.scalar(); + //num =200/_scalar; + num = 200; + if (_HVal[_axis] < num){ + _IVal[_axis]= NORMALGGOTOSPEED; + calculateRate(_axis); + // num =Synta.scalar(); + //num =20/_scalar; + num =20; + motorStart(_axis,num); //Begin PWM + } else { + _IVal[_axis]= FASTGOTOSPEED; //Higher speed goto + calculateRate(_axis); + //num =Synta.scalar(); + //num =60/_scalar + num =60; + motorStart(_axis,num); //Begin PWM + } + gotoEn[_axis]=1; + } +} + +//i know the following is not so nice compared to the original solution, but the original solution do not work under pinguino +// there is issues with the data pointers, which prevent to use the assemble_response function, so have done this way +// also, this is smaller in code size!! + + +void decodeCommand(char command, char* packetIn){ //each command is axis specific. The axis being modified can be retrieved by calling Synta.axis() + unsigned long responseData; //data for response + switch(command){ + case 'e': //readonly, return the eVal (version number) + responseData = _eVal[_axis]; //response to the e command is stored in the eVal function for that axis. + long_tohex(responseData); + break; + case 'a': //readonly, return the aVal (steps per axis) + //responseData = _aVal[_axis]; //response to the a command is stored in the aVal function for that axis. + //responseData /= _scalar; + responseData = _aVal[_axis]; + long_tohex(responseData); + break; + case 'b': //readonly, return the bVal (sidereal rate) + responseData = _bVal[_axis]; //response to the b command is stored in the bVal function for that axis. + //responseData = _bVal[_axis]; + //responseData /= _scalar; + //if (_bVal[_axis] % _scalar){ + // responseData++; //round up + //} + long_tohex(responseData); + break; + case 'g': //readonly, return the gVal (high speed multiplier) + responseData = _gVal[_axis]; //response to the g command is stored in the gVal function for that axis. + byte_tohex(responseData); + break; + case 's': //readonly, return the sVal (steps per worm rotation) + responseData = _sVal[_axis]; //response to the s command is stored in the sVal function for that axis. + //responseData = _sVal[_axis]; + //responseData /= _scalar; + long_tohex(responseData); + break; + case 'f': //readonly, return the fVal (axis status) + responseData = Synta_cmd_fVal(_axis); //response to the f command is stored in the fVal function for that axis. + int_tohex(responseData); + break; + case 'j': //readonly, return the jVal (current position) + responseData = _jVal[_axis]; //response to the j command is stored in the jVal function for that axis. + long_tohex(responseData); + break; + case 'P': + if ((packetIn[0] - 48) == 0) { + rate_modifier = 0; + } else if ((packetIn[0] - 48) == 2) { + rate_modifier = 1; + } else if ((packetIn[0] - 48) == 3) { + rate_modifier = 2; + } + Serial.printf("=\r"); + break; + + case 'K': //stop the motor, return empty response + motorStop(_axis); //No specific response, just stop the motor (Feel free to customise motorStop function to suit your needs + //responseData = 0; + Serial.printf("=\r"); + break; + case 'L': //emergency stop, return empty response + motorStopp(0,1); //No specific response, just stop the motor (Feel free to customise motorStop function to suit your needs + motorStopp(1,1); //No specific response, just stop the motor (Feel free to customise motorStop function to suit your needs + //responseData = 0; + Serial.printf("=\r"); + break; + case 'G': //set mode and direction, return empty response + _GVal[_axis]= (packetIn[0] - 48); //Store the current mode for the axis + dir[_axis]= (packetIn[1] - 48); //Store the current direction for that axis + //responseData = 0; + Serial.printf("=\r"); + // Serial.printf("Gval %i\r",_GVal[_axis]);//debug + break; + case 'H': //set goto position, return empty response (this sets the number of steps to move from cuurent position if in goto mode) + _HVal[_axis] = Synta_hexToLong(packetIn); //set the goto position container (convert string to long first) + //responseData = 0; + Serial.printf("=\r"); + // Serial.printf("Hval %i\r",_HVal[_axis]);//debug + + break; + case 'I': //set slew speed, return empty response (this sets the speed to move at if in slew mode) + responseData = Synta_hexToLong(packetIn); + if (responseData > 650L){ + responseData = 650; //minimum speed to prevent timer overrun + } + _IVal[_axis]= responseData; //set the speed container (convert string to long first) + //Serial.printf("Ival %i\r",_IVal[_axis]);//debug + calculateRate(_axis); //Used to convert speed into the number of 0.5usecs per step. Result is stored in TimerOVF + //responseData = 0; + Serial.printf("=\r"); + + break; + case 'E': //set the current position, return empty response + _jVal[_axis]= Synta_hexToLong(packetIn); //set the current position (used to sync to what EQMOD thinks is the current position at startup + //responseData = 0; + Serial.printf("=\r"); + // Serial.printf("jval %i\r",_jVal[_axis]);//debug + + break; + case 'F': //Enable the motor driver, return empty response + motorEnable(); //This enables the motors - gives the motor driver board power + //responseData = 0; + Serial.printf("=\r"); + break; + default: //Return empty response (deals with commands that don't do anything before the response sent (i.e 'J'), or do nothing at all (e.g. 'M', 'L') ) + //responseData = 0; + Serial.printf("=\r"); + break; + } + if(command == 'J'){ //J tells + if(_GVal[_axis] & 1){ + // Serial.printf("slew Gval %i\r",_GVal[_axis]);//debug + + //This is the funtion that enables a slew type move. + slewMode(); //Slew type + } else { + // Serial.printf("goto Gval %i\r",_GVal[_axis]);//debug + + //This is the function for goto mode. You may need to customise it for a different motor driver + gotoMode(); //Goto Mode + } + } +} + + +void configureTimer(){ + unsigned long rate; + + //interruptions + RCONbits.IPEN = 1; // Enable HP/LP interrupts + INTCONbits.GIEH = 1; // Enable HP interrupts + INTCONbits.GIEL = 1; // Enable LP interrupts + INTCONbits.RBIE = INT_DISABLE; //RB4 à7 => ST4 guiding need some test and validation + // we use timer3 and timer1 in count/compare mode. when timer registers (TMR) equals the value setted + // in Compare register (CCPR), then the timer is zeroed and count again from 0 and an interrupt is raised + // in the interrupt we will toggle the stepping output. since the timer is automatically (hardware) zeroed, + // the only latency seen will be due to the if test (mainly this will be a pure delay at the first interrupt occurrence + // then we can assume that between to output toggling the time will be exactly equal to the one setted in register + // anyway if there is some jitter due to the if test in interrupt,this will be only "noise" in the exact stepping moment + // hence we will stay at the rate programmed (no drift) + PIE2bits.CCP2IE = INT_DISABLE; // CCP2=> DEC + PIE1bits.CCP1IE = INT_DISABLE; // CCP1=> RA + + timerCountRate = 150000; +//Set prescaler to F_CPU/8 //DEC + T3CON = 0b10111000; + IPR2bits.CCP2IP = INT_HIGH_PRIORITY; //is this mandatory? maybe LOW_PRIORITY is OK + CCP2CON = 0b00001011; + PIR2bits.CCP2IF = 0; + TMR3H = 0; // timer reset + TMR3L = 0; +//Set prescaler to F_CPU/8 //RA + T1CON = 0b10110000; + IPR1bits.CCP1IP = INT_HIGH_PRIORITY; //is this mandatory? maybe LOW_PRIORITY is OK + CCP1CON = 0b00001011; + PIR1bits.CCP1IF = 0; + TMR1H = 0; // timer reset + TMR1L = 0; + rate = timerCountRate * 650L; //min rate of just under the sidereal rate + rate /= _bVal[_axis]; + maxTimerOVF = 10 * rate; //corrects for timer count rate being set to 1/10th of the required to prevent numbers getting too big for an unsigned long. + + CCPR2H = high8(maxTimerOVF);//initialize the compare registers + CCPR2L = low8(maxTimerOVF); + CCPR1H = high8(maxTimerOVF); + CCPR1L = low8(maxTimerOVF); + T3CONbits.TMR3ON = ON; //start counting (but compare interrupt is not enabled here, only when motor starts) + T1CONbits.TMR1ON = ON; + #ifdef HAVEST4 + INTCONbits.RBIE = INT_ENABLE; //RB4 à7 => ST4 guiding need some test and validation + INTCONbits.RBIF = 0; + #endif + #ifdef HAVEENCODER + INTCONbits.RBIE = INT_ENABLE; //RB4 à7 => ST4 guiding need some test and validation + INTCONbits.RBIF = 0; + #endif + +} + + + +void setup() { +byte i; +Synta_init(1281, 9024000, 64933, 16, 50133); //EQ6 parameters (care: gval for the configuration 1/16 step to 1/2 is 8, not 16 +//Synta synta(1281, 9024000, 64933, 16, 50133, 1); //care is to be taken with bval..sometimes roounding can wrongly affect the value +//Synta synta(1281, 4512000, 32466, 16, 25067, 1);//this case will give ival = 619 in eqmod... so bval sould be 32467 instead + + +//div_scalar = _scalar/2; + +pinMode(USERLED,OUTPUT); + +for( i = 0;i < 2;i++){ //for each motor... + pinMode(enablePin[i],OUTPUT); //enable the Enable pin + digitalWrite(enablePin[i],HIGH); //IC disabled + pinMode(stepPin[i],OUTPUT); //enable the step pin + digitalWrite(stepPin[i],LOW); //default is low + pinMode(dirPin[i],OUTPUT); //enable the direction pin + digitalWrite(dirPin[i],LOW); //default is low + #if defined(USEHIGHSPEEDMODE) + pinMode(modePins[i][0],OUTPUT); //enable the mode pins + pinMode(modePins[i][1],OUTPUT); //enable the mode pins + digitalWrite(modePins[i][0],HIGH); //default is low 1/16 or 1/32 steps + digitalWrite(modePins[i][1],HIGH); //default is high + #endif + pinMode(resetPin[i],OUTPUT); //enable the reset pin + digitalWrite(resetPin[i],LOW); //active low reset + delay(1); //allow ic to reset + digitalWrite(resetPin[i],HIGH); //complete reset + #ifdef HAVEST4 + pinMode(ST4Pin[i],INPUT); //enable the ST4 pin + pinMode(ST4Pin[i+1],INPUT); //enable the ST4 pin + #endif + #ifdef HAVEENCODER + pinMode(ST4Pin[i],INPUT); //enable the ST4 pin + pinMode(ST4Pin[i+1],INPUT); //enable the ST4 pin + #endif + +} +// start Serial port: +Serial.begin(9600); //SyncScan runs at 9600Baud, use a serial port of your choice +while(Serial.available()){ + Serial.read(); //Clear the buffer +} +configureTimer(); //setup the motor pulse timers. + +} + +void loop() { + char recievedChar; + char decoded; + char decodedPacket[11]; //temporary store for completed command ready to be processed +// Serial.printf("%u\r",div_scalar); +// Serial.printf("%i\r",maxTimerOVF); + if (Serial.available()) { //is there a byte in buffer + digitalWrite(USERLED,LOW); //Turn on the LED to indicate activity. + recievedChar = Serial.read(); //get the next character in buffer + decoded = Synta_recieveCommand(decodedPacket,recievedChar);//once full command packet recieved, decodedPacket returns either error packet, or valid packet + if (decoded == 1){ //Valid Packet + decodeCommand(_command,decodedPacket); //decode the valid packet + } else if (decoded == -1){ //Error + Serial.printf(decodedPacket); //send the error packet (recieveCommand() already generated the error packet, so just need to send it) + } //otherwise command not yet fully recieved, so wait for next byte + } + digitalWrite(USERLED,HIGH); //Turn off the LED to indicate activity. + +} + + +void userhighinterrupt(){ + if (PIE1bits.CCP1IE && PIR1bits.CCP1IF){ //CCP1 =RA =Timer1 on steppe + PORTCbits.RC0=1; + delayMicroseconds(PULSEWIDTH); + PORTCbits.RC0=0; + PIR1bits.CCP1IF=0; + checkIfDone(0); + } else if (PIE2bits.CCP2IE && PIR2bits.CCP2IF){ //CCP2 =DEC on stepper + PORTCbits.RC1=1; + delayMicroseconds(PULSEWIDTH); + PORTCbits.RC1=0; + PIR2bits.CCP2IF=0; + checkIfDone(1); + } +} + +void userinterrupt(){ + #ifdef HAVEST4 + + // RA guiding: is low priority so far, but need to check if either guiding should be high priority + // or , why not, tracking and slewing be low priority... + boolean RAp,RAm; + boolean DEp,DEm; + static s16 RAcorrection=0; + static s16 DEcorrection= 0; + if (INTCONbits.RBIE && INTCONbits.RBIF){ //get a signal on either RA+,RA-, DE+, DE- + RAp = PORTBbits.RB7; //RA+ + RAm = PORTBbits.RB6; //RA- + DEp = PORTBbits.RB5; //DEC+ au cas Ou + DEm = PORTBbits.RB4; //DEC- au cas ou + if (RAp) // RA + we move faster + RAcorrection = timerOVF[0]>>rate_modifier; //(correction by 0.25 ou 0.5) + else if (RAm)//RA- slower + RAcorrection = -(timerOVF[0]>>rate_modifier); //(correction by 0.25 ou 0.5) + else //no RA correction + RAcorrection = 0; + CCPR1L = low8(timerOVF[0]+RAcorrection); + CCPR1H = high8(timerOVF[0]+RAcorrection); + DEcorrection = timerOVF[1]>>rate_modifier; + CCPR2L = low8(DEcorrection); + CCPR2H = high8(DEcorrection); + if (DEp){ // DEC + we move at 0.25 or 0.5 sideral rate, toward west + PIE2bits.CCP2IE = INT_ENABLE; + digitalWrite(dirPin[1],LOW); //default is low (DE+ + }else if (DEm){//DEC- toward east + PIE2bits.CCP2IE = INT_ENABLE; + digitalWrite(dirPin[1],HIGH); //default is low (DE- + }else //no DEC correction + PIE2bits.CCP2IE = INT_DISABLE; + INTCONbits.RBIF = 0; + } + #endif + + #ifdef HAVEENCODER + //stuff to decode quadrature encoders + //see here fore example http://playground.arduino.cc/Main/RotaryEncoders#Example4 + #endif + + +}