/* Example of driving servomotor and reading encoder signals in various ways */ #include /* Needed to set up counter on pin 47 */ #include /* Needed to communicate with LS7366R (Counter Click) */ /* Serial input aspects are based closely upon: http://forum.arduino.cc/index.php?topic=396450 Example 4 - Receive a number as text and convert it to an int Modified to read a float */ /* LS7366R aspects very loosely based on concepts used in controlling the Robogaia 3-axis encoder shield though implementation is very different https://www.robogaia.com/3-axis-encoder-conter-arduino-shield.html */ /* Counting using Timer 5 (external counter input) based loosely on code from https://forum.arduino.cc/index.php?topic=59396.0 written by bubuldino */ /* Pins used for L298 driver */ #define enA 13 /* PWM output, also visible as LED */ #define in1 8 /* H bridge selection input 1 */ #define in2 9 /* H bridge selection input 2 */ #define minPercent -100.0 #define maxPercent 100.0 /* Encoder input pins (used for state machine and interrupts) */ #define channelA 2 #define channelB 3 /* Used to to initiate SPI communication to LS7366R chip (Counter click) */ #define chipSelectPin 10 /* Size of buffer used to store received characters */ #define numChars 32 /* Intervals in milliseconds for user-defined timed loops */ #define printInterval 1000 /* Global variables used in serial input */ char receivedChars[numChars]; // an array to store the received data float dataNumber = 0; // new for this version boolean newData = false; /* Global variables used for motor control and encoder reading */ double percentSpeed; double encoderValue; /* Used for state machine and encoder reading */ typedef enum states{state1=1, state2, state3, state4}; volatile long int count = 0; volatile long int error = 0; volatile states state; bool channelAState, channelBState; /* Used for handling overflows in Timer 5 */ volatile long int bigLaps; /* Global variables used for loop timing */ unsigned long prevMillisPrint = 0; /* stores last time values were printed */ unsigned long prevMillisControl = 0; /* stores last time control action was updated */ /* Overlapping regions of memory used to convert four bytes to a long integer */ union fourBytesToLong { long result; unsigned char bytes [4]; }; void setup() { Serial.begin(9600); Serial.println("Enter PWM duty cycle as a percentage (positive for forward, negative for reverse"); /* Set encoder pins as input but with pullup resistors to be compatible with various encoders */ pinMode(channelA, INPUT_PULLUP); pinMode(channelB, INPUT_PULLUP); channelAState = digitalRead(channelA); channelBState = digitalRead(channelB); initialiseEncoderStateMachine(); /* Find initial state based on inputs */ /* Set up and initialise pin used for selecting LS7366R counter: hi=inactive */ pinMode(chipSelectPin, OUTPUT); digitalWrite(chipSelectPin, HIGH); SetUpLS7366RCounter(); delay(100); /* Configure Timer 5 to count pulses on pin 47 */ pinMode(47, INPUT_PULLUP); // set pin to input with pullup resistor TCCR5A = 0; // No waveform generation needed. TCCR5B = (1<= printInterval) { // save the last time you printed output prevMillisPrint = currentMillis; printLoop(); } recvWithEndMarker(); if(convertNewNumber()) // Update value read from serial line { percentSpeed=dataNumber; driveMotorPercent(percentSpeed); } updateEncoderStateMachine(); } void driveMotorPercent(double percentSpeed) /* Output PWM and H bridge signals based on positive or negative duty cycle % */ { percentSpeed = constrain(percentSpeed, -100, 100); int regVal = map(percentSpeed, -100, 100, -255, 255); analogWrite(enA, (int)abs(regVal)); digitalWrite(in1, regVal>0); digitalWrite(in2, !(regVal>0)); } void printLoop() /* Print count and control information */ { /* Sample all counters one after the other to avoid delay-related offsets */ long encoderCountFromLS7366R = readEncoderCountFromLS7366R(); long encoderCountFromStateMC = count; long stateMCerror = error; long timer5Count = TCNT5 + bigLaps*65536; Serial.print("Count from LS7366R = "); Serial.print(encoderCountFromLS7366R); Serial.print(" from state m/c = "); Serial.print(encoderCountFromStateMC); Serial.print(" State m/c errors = "); Serial.print(stateMCerror); Serial.print(" Count from LS7366R/4 = "); Serial.print(encoderCountFromLS7366R/4); Serial.print(" from Timer 5 = "); Serial.print(timer5Count); Serial.print(" Percent speed = "); Serial.print(percentSpeed); Serial.print("\r\n"); } long readEncoderCountFromLS7366R() /* Reads the LS7366R chip to obtain up/down count from encoder. Reads four bytes separately then concverts them to a long integer using a union */ { fourBytesToLong converter; /* Union of four bytes and a long integer */ digitalWrite(chipSelectPin,LOW); /* Make LS7366R active */ SPI.transfer(0x60); // Request count converter.bytes[3] = SPI.transfer(0x00); /* Read highest order byte */ converter.bytes[2] = SPI.transfer(0x00); converter.bytes[1] = SPI.transfer(0x00); converter.bytes[0] = SPI.transfer(0x00); /* Read lowest order byte */ digitalWrite(chipSelectPin,HIGH); /* Make LS7366R inactive */ return converter.result; } void SetUpLS7366RCounter(void) /* Initialiseds LS7366R hardware counter on Counter Click board to read quadrature signals */ { /* Control registers in LS7366R - see LS7366R datasheet for this and subsequent control words */ unsigned char IR = 0x00, MRD0=0x00; // SPI initialization SPI.begin(); //SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI at 1Mhz (on 16Mhz clock) delay(10); /* Configure as free-running 4x quadrature counter */ digitalWrite(chipSelectPin,LOW); /* Select chip and initialise transfer */ /* Instruction register IR */ IR |= 0x80; /* Write to register (B7=1, B6=0) */ IR |= 0x08; /* Select register MDR0: B5=0, B4=0, B3=1 */ SPI.transfer(IR); /* Write to instruction register */ /* Mode register 0 */ MRD0 |= 0x03; /* 4x quadrature count: B0=1, B1=1 */ /* B2=B3=0: free running. B4=B5=0: disable index. */ /* B6=0: asynchronous index. B7: Filter division factor = 1. */ SPI.transfer(MRD0); digitalWrite(chipSelectPin,HIGH); /* Clear the counter i.e. set it to zero */ IR = 0x00; /* Clear the instructino register IR */ digitalWrite(chipSelectPin,LOW); /* Select chip and initialise transfer */ IR |= 0x20; /* Select CNTR: B5=1,B4=0,B3=0; CLR register: B7=0,B6=0 */ SPI.transfer(IR); /* Write to instruction register */ digitalWrite(chipSelectPin,HIGH); } void recvWithEndMarker() /* Receive data from serial port finishing with "newline" character. Based on http://forum.arduino.cc/index.php?topic=396450 Example 4 */ { static byte ndx = 0; char endMarker = '\n'; char rc; if (Serial.available() > 0) { rc = Serial.read(); if (rc != endMarker) { receivedChars[ndx] = rc; ndx++; if (ndx >= numChars) { ndx = numChars - 1; } } else { receivedChars[ndx] = '\0'; // terminate the string ndx = 0; newData = true; } } } bool convertNewNumber() /* Converts character string to floating point number only if there are new data to convert, otherwise returns false */ { if (newData) { dataNumber = 0.0; // new for this version dataNumber = atof(receivedChars); // new for this version newData = false; return true; } else { return false; } } void initialiseEncoderStateMachine() /* User written code to initialise state of state machine code based on input states */ { if (channelAState) { if(channelBState) { state = state3; } /* else.... a lot of code goes here! */ } } void updateEncoderStateMachine() /* User written code to update state and increment count of state machine */ { channelAState = digitalRead(channelA); channelBState = digitalRead(channelB); switch (state) { case state1: if (channelAState && !channelBState) { count++; state = state2; } /* else if .... a lot of code goes here! */ /* don't forget "break" at end of each case. */ } } ISR(TIMER5_OVF_vect ) { //when this runs, you had 65536 pulses counted. bigLaps++; }