Last active
November 5, 2016 04:51
-
-
Save gregmankes/2e46dd03448ce5a0f56e187dce0e5dbb to your computer and use it in GitHub Desktop.
A simple PI controller for a motor and encoder combination
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* interrupt routine for Rotary Encoders | |
tested with Noble RE0124PVB 17.7FINB-24 http://www.nobleusa.com/pdf/xre.pdf - available at pollin.de | |
and a few others, seems pretty universal | |
The average rotary encoder has three pins, seen from front: A C B | |
Clockwise rotation A(on)->B(on)->A(off)->B(off) | |
CounterCW rotation B(on)->A(on)->B(off)->A(off) | |
and may be a push switch with another two pins, pulled low at pin 8 in this case | |
[email protected] 20120107 | |
*/ | |
// usually the rotary encoders three pins have the ground pin in the middle | |
enum PinAssignments { | |
encoderPinA = 2, // rigth | |
encoderPinB = 3, // left | |
clearButton = 8 // another two pins | |
}; | |
int kp = 10; | |
int ki = 10; | |
byte u =0; | |
double dt = .001; | |
double Ii = 0; | |
double Il = 1; | |
double ei = 1; | |
double dspeed = 30; | |
double p = 0; | |
const int AIA = 11; // (pwm) pin 9 connected to pin A-IA | |
const int AIB = 5; // (pwm) pin 5 connected to pin A-IB | |
byte speed = 0; // change this (0-255) to control the speed of the motors | |
volatile int encoderPos = 0; // a counter for the dial | |
unsigned int lastReportedPos = 1; // change management | |
static boolean rotating=false; // debounce management | |
// interrupt service routine vars | |
boolean A_set = false; | |
boolean B_set = false; | |
int flag, count; | |
double time, f, rps; | |
void setup() { | |
pinMode(encoderPinA, INPUT); | |
pinMode(encoderPinB, INPUT); | |
pinMode(clearButton, INPUT); | |
// turn on pullup resistors | |
digitalWrite(encoderPinA, HIGH); | |
digitalWrite(encoderPinB, HIGH); | |
digitalWrite(clearButton, HIGH); | |
// encoder pin on interrupt 0 (pin 2) | |
attachInterrupt(0, doEncoderA, CHANGE); | |
// encoder pin on interrupt 1 (pin 3) | |
attachInterrupt(1, doEncoderB, CHANGE); | |
pinMode(AIA, OUTPUT); // set pins to output | |
pinMode(AIB, OUTPUT); | |
// initialize Timer1 | |
cli(); // disable global interrupts | |
TCCR1A = 0; // set entire TCCR1A register to 0 | |
TCCR1B = 0; // same for TCCR1B | |
// set compare match register to desired timer count: | |
OCR1A = 249; | |
// turn on CTC mode: | |
TCCR1B |= (1 << WGM12); | |
// Set CS10 and CS12 bits for 64 prescaler: | |
TCCR1B |= (1 << CS10); | |
//TCCR1B |= (1 << CS11); | |
TCCR1B |= (1 << CS11); | |
// enable timer compare interrupt: | |
TIMSK1 |= (1 << OCIE1A); | |
// enable global interrupts: | |
sei(); | |
Serial.begin(9600); // output | |
} | |
// main loop, work is done by interrupt service routines, this one only prints stuff | |
void loop() { | |
rotating = true; // reset the debouncer | |
if (lastReportedPos != encoderPos) { | |
// Serial.print("Index:"); | |
// Serial.println(encoderPos, DEC); | |
lastReportedPos = encoderPos; | |
} | |
if (digitalRead(clearButton) == LOW ) { | |
encoderPos = 0; | |
} | |
ei = dspeed - f; | |
Integral(); | |
Proportional(); | |
u = (ki*Ii) + p; | |
forward(); | |
Serial.println(f); | |
} | |
void Integral(){ | |
Ii = Il + (dt*ei); | |
Il = Ii; | |
} | |
void Proportional(){ | |
p = ei*kp; | |
} | |
void forward() | |
{ | |
analogWrite(AIA, u); | |
analogWrite(AIB, 0); | |
} | |
// Interrupt on A changing state | |
void doEncoderA(){ | |
// debounce | |
if ( rotating ) delay (1); // wait a little until the bouncing is done | |
// Test transition, did things really change? | |
if( digitalRead(encoderPinA) != A_set ) { // debounce once more | |
A_set = !A_set; | |
// adjust counter + if A leads B | |
if ( A_set && !B_set ){ | |
encoderPos += 1; | |
flag = 1; | |
} | |
rotating = false; // no more debouncing until loop() hits again | |
} | |
} | |
// Interrupt on B changing state, same as A above | |
void doEncoderB(){ | |
if ( rotating ) delay (1); | |
if( digitalRead(encoderPinB) != B_set ) { | |
B_set = !B_set; | |
// adjust counter - 1 if B leads A | |
if( B_set && !A_set ){ | |
encoderPos -= 1; | |
flag = 1; | |
} | |
rotating = false; | |
} | |
} | |
ISR(TIMER1_COMPA_vect){ | |
if(flag == 0){ | |
count++; | |
} | |
else{ | |
time = (double)count*.001; | |
f = 1/time; | |
rps = f/20; | |
count = 0; | |
flag = 0; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment