#define TEST
/**************************************************************************
Motors.c
ME 218B Project Hats Off
Team Firefox
March 7, 2010
The robot uses 2 DC motors. Implements PWM
Versions:
2.1 02-24-10 Lock wheel speeds, smarter duty cycle
limitation (pos and neg)
2.0 02-21-10 Acceleration limits, position control, min
controllable speed
1.1 02-19-10 Motors stop at same time when one reaches
desired number of encoder counts.
1.0 02-17-10 PID control works but commanding zero speed as
each motor
reaches target
position doesn't work (robot spins wildly
near end of maneuver)
***************************************************************************/
/*-------------------------- Include Files
-------------------------------*/
#include
"Motors.h"
typedef enum { FORWARD,
BACKWARD
} DIR_t;
typedef enum { LOCKED,
OPPOSITE,
UNLOCKED
} LOCKMODE_t;
/*-------------------------- Module Definitions
-----------------------------*/
//#defines pins for portability
#define DCMOTOR0 BIT1HI //PP1
#define DCMOTOR1 BIT3HI //PP3
#define POTPIN 7 //PTAD07
#define _MS_ *3000 //=3MHz = (24MHz / 8) from Timer prescale
//Takes ~21[ms]
before overflow
#define TIME (20 _MS_) //Number of
seconds I want to wait during OC
#define printf (void)printf
/*-------------------------- Module Functions
-----------------------------*/
static void Motor0(int
DC); //command
duty cycle [-100, 100]
static void Motor1(int
DC); //command
duty cycle [-100, 100]
static void Timers_Init(void); //initializes
input capture for encoder readings
//and output
compare for PID control
/*-------------------------- Module Variables
-----------------------------*/
static unsigned int
uPeriod0, uPeriod1; //period of
last encoder tick for each motor 1.34 [us]
static DIR_t dir0, dir1; //encoder
direction
static char newEdge0, newEdge1; //indicates if an encoder pulse has occured
in last 87 [ms]
static int
desSpd0, desSpd1;
//desired speeds of motors 0 and 1
static int
curSpd0, curSpd1;
//current speeds of motors 0 and 1
(calculated every 20 [ms]
static int
duty0, duty1; //current
duty cycles of motors
static LOCKMODE_t curLockMode;
static long EncoderCount0, EncoderCount1; //encoder tick count
static char EncoderTimeout0, EncoderTimeout1; //used to time out encoder pulses when motor is stopped
static long desEncoderCount0
= 0, desEncoderCount1 = 0; //desired number of
encoder steps
static char desEncoderFlag0
= 0, desEncoderFlag1 = 0; //indicates that motors should stop after reaching
desEncoderCount
static unsigned int
disablePID = 0; //disables speed
control
/*--------------------------
const static int
minControlRPM = 40; //Minimum speed
at which PID can control (actual value based on PID loop time
//is 27.4 RPM
const static float Kp = 0.024, Ki = 0.006;
//speed control
Kp = 0.012, Ki =
0.004
const static float Kp_Pos = 2; //1.6//0.6; //position control: CmdVelocity
= Kp_pos*ErrorPosition
[Encoder Counts]
const static int
maxPosAccel = 250, maxNegAccel
= 1000; //70;//max accel rate in [RPM/Control
const static int
DutyOffset = 8; //add 2 extra duty
points to motor 1 cuz he's slow
//const static
// linear distance cals
const static int
EncoderTicksPerInch = 288; //assumes GR = 1.33
and wheel diameter = 110 [mm]
const static int
EncoderTicksPerRev = 7329;// //7420 //7343; //assumes distance between wheels = 8
[in] and all the
above
const static int
maxMotorSpeed = 4500; //calc: 5830 // [rpm]
of motor = 53*output shaft speed @14V
//debug variables
static unsigned int
lastPrint = 0;
static unsigned int
minUPeriod = 130;
static unsigned int
maxUPeriod = 21000;
void Motors_Init(void) {
Timers_Init(); //setup IC for
encoder and OC for PID control
MyTimer_Init();
//Make sure 1 ms timer is running
lastPrint = MyTimer_GetTime();
//setup PWM
using PWM modules
PWM0_Init(1,1);
PWMPRCLK = (PWMPRCLK &~ _S12_PCKA2 &~ _S12_PCKA0) | _S12_PCKA1; //set prescale to 24MHz/4 =6MHz
PWMSCLA =
2; //set
postscale to --> 6MHz/(4*2)=7.5kHz
setPWM0Period(200); //period
contains 100 ticks -->PWM frequency = 15kHz
PWM1_Init(1,1);
PWMPRCLK = (PWMPRCLK &~ _S12_PCKA2 &~ _S12_PCKA0) | _S12_PCKA1; //set prescale to 24MHz/4 =6MHz
PWMSCLA =
2; //set postscale
to --> 6MHz/(4*2)=7.5kHz
setPWM1Period(200); //period
contains 100 ticks -->PWM frequency = 15kHz
Motor0(0);
Motor1(0);
//reset
encoder counts
EncoderCount0 =
0;
EncoderCount1 =
0;
//assume
motors are stopped, so timeout period calculation
EncoderTimeout0 =
1;
EncoderTimeout1 =
1;
//set
motors as unlocked
curLockMode = UNLOCKED;
}
//use only for debugging, will invalidate getState() function if callled
void Motors_debugMove(int duty1, int duty0) {
if
(duty0 != 0 || duty1 != 0) {
disablePID = 1;
Motor0(duty0);
Motor1(duty1);
}
else
disablePID = 0;
}
void Motors_debugPrint(char motorNum) {
//print
debugging information to the screen
DIR_t
myDir0, myDir1;
char
myNewEdge0, myNewEdge1;
unsigned int myUPeriod0, myUPeriod1;
int myDuty0, myDuty1;
DisableInterrupts;
//retrieve
shared variables between encoder IC interrupt and this print statement
myDir0 =
dir0;
myDir1 =
dir1;
myNewEdge0 =
newEdge0;
myNewEdge1 =
newEdge1;
myUPeriod0 =
uPeriod0;
myUPeriod1 =
uPeriod1;
myDuty0 =
duty0;
myDuty1 =
duty1;
EnableInterrupts;
if
(motorNum
== 0)
(void)printf("curRPM0:%d, desRPM0:%d, duty0:%d, period0:%d\r\n",curSpd0, desSpd0, duty0, uPeriod0);
else
(void)printf("curRPM1:%d, desRPM1:%d, duty1:%d, period1:%d\r\n",curSpd1, desSpd1, duty1, uPeriod1);
}
void Motors_resetPosition(void) {
//reset
encoder counts
EncoderCount0 =
0;
EncoderCount1 =
0;
}
MOTOR_State_t Motors_getState(void) {
char
physicallyStopped;
char
controlStopped;
controlStopped = ((desSpd0 == 0) && (desSpd1 == 0));
physicallyStopped = ((curSpd0 == 0) && (curSpd1 == 0));
if (controlStopped) {
if (physicallyStopped) return MOTORS_STOPPED;
else return MOTORS_STOPPING;
}
else return
MOTORS_MOVING;
}
float Motors_getNetDistance(void) {
//return
average of the encoder counts since last command
return (((float)EncoderCount0 + (float)EncoderCount1)/(2*EncoderTicksPerInch));
}
float Motors_getDegreesTurned(void) {
float
temp;
temp = (360*((float)EncoderCount0 - (float)EncoderCount1)/(2*EncoderTicksPerRev));
if (temp < 0) temp *= -1;
return temp;
}
long Motors_getEncoderCount(int
motorNum) {
if (motorNum == 0) return EncoderCount0;
else return EncoderCount1;
}
//assumes inputs speeds = [-100, 100]
void Motors_moveDiff(int spd1, int spd0) {
disablePID = 1; //temporarily turn off PID control
if (spd0 == 0) desSpd0 = 0;
else {
desSpd0 = (int)((long)spd0*maxMotorSpeed/100);
//make
sure commanded speed isn't slower than min controllable speed
if (desSpd0 > 0 && desSpd0 < minControlRPM) desSpd0 = minControlRPM;
if (desSpd0 < 0 && desSpd0 > -1*minControlRPM) desSpd0 = -1*minControlRPM;
}
if (spd1 == 0) desSpd1 = 0;
else {
desSpd1 = (int)((long)spd1*maxMotorSpeed/100);
//make
sure commanded speed isn't slower than min controllable speed
if (desSpd1 > 0 && desSpd1 < minControlRPM) desSpd1 = minControlRPM;
if (desSpd1 < 0 && desSpd1 > -1*minControlRPM) desSpd1 = -1*minControlRPM;
}
desEncoderFlag0 =
0;
desEncoderFlag1 =
0;
if (spd1 == spd0) curLockMode = LOCKED;
else {
if (spd1 == -1*spd0) curLockMode = OPPOSITE;
else curLockMode = UNLOCKED;
}
disablePID = 0; //reengage PID control
}
void Motors_moveStraight(int
spd) {
Motors_moveDiff(spd, spd);
}
void Motors_turn(MOTOR_TurnDir_t dir, int spd) {
if (dir == CW) Motors_moveDiff(spd, -1*spd);
else Motors_moveDiff(-1*spd, spd);
}
void Motors_moveStraightStop(int
spd, float inches) {
float
myDistance = 0;
if (spd < 0) myDistance
= -1*inches;
else myDistance = inches;
Motors_moveDiff(spd,spd); //set motor speeds
Motors_resetPosition();
//reset encoder counts
//set
number of encoder counts to move
desEncoderCount0 = (int)(myDistance*EncoderTicksPerInch);
desEncoderCount1 = (int)(myDistance*EncoderTicksPerInch);
desEncoderFlag0 =
1; //indicate wheel should stop after given # of encoder
counts
desEncoderFlag1 =
1;
}
void Motors_turnStop(MOTOR_TurnDir_t dir, int spd, unsigned int degree) {
long
myEncoderTicks = 0;
Motors_turn(dir, spd); //set motor speeds
//set
number of encoder counts to move
myEncoderTicks = ((long)degree)*EncoderTicksPerRev/360;
if (dir == CCW) {
desEncoderCount0 =
myEncoderTicks;
desEncoderCount1 = -1*myEncoderTicks;
}
else {
desEncoderCount0 = -1*myEncoderTicks;
desEncoderCount1 =
myEncoderTicks;
}
Motors_resetPosition();
desEncoderFlag0 =
1; //indicate wheel should stop after given # of encoder
counts
desEncoderFlag1 =
1;
}
//Initialize interrupts
//Encoder0 Channel A is Timer 1, PT4
//Encoder1 Channel A is Timer 1, PT6
//OC for PID control is Timer 1 PT5
void Timers_Init(void){
TIM1_TSCR1 =
_S12_TEN; //Turn on
timer system to allow IC
TIM1_TSCR2 = (TIM1_TSCR2 & ~_S12_PR2) |_S12_PR1 | _S12_PR0; //pre-scale /8 = 3MHz
TIM1_TIOS =
_S12_IOS5; //set Cap/Comp 5 to OC. Rest are IC.
/** InputCapture with interrupt of Encoder0 Channel A.
Use Timer1 Channel4
set to PT4 on E128 **/
TIM1_TCTL3 = (TIM1_TCTL3 &~ _S12_EDG4B) | _S12_EDG4A; //capture
rising edges
TIM1_TFLG1 =
_S12_C4F; //Clear
IC4 flag
TIM1_TIE |=
_S12_C4I; //Enable IC4
interrupt
/** InputCapture with interrupt of Encoder1 Channel A.
Use Timer1 Channel6
set to PT6 on E128 **/
TIM1_TCTL3 = (TIM1_TCTL3 &~ _S12_EDG6B) | _S12_EDG6A; //capture
rising edges
TIM1_TFLG1 =
_S12_C6F; //Clear
IC4 flag
TIM1_TIE |=
_S12_C6I; //Enable IC4
interrupt
/** OutputCompare with interrupt on
Use Timer1 Channel5
set to PT5 on E128 **/
TIM1_TCTL1 =
TIM1_TCTL1 & ~(_S12_OL5 | _S12_OM5); //No pin connected for OC5
TIM1_TC5 =
TIM1_TCNT + TIME; //Schedule
first rise for OC
TIM1_TFLG1 =
_S12_C5F; //Clear
OC5 flag
TIM1_TIE |=
_S12_C5I; //Enable
OC5 interrupt
EnableInterrupts;
}
//PID Control Interrupt Response Routine is called every 20 [ms]
//Measured runtime < 200 [us]
void interrupt
_Vec_tim1ch5 EncoderOC_PID_DC(void){
static long intErr0=0, intErr1=0; //integrated errors
static int lastCmdSpd0=0, lastCmdSpd1=0; //last commanded speed for acceleration control
int errSpd0, errSpd1;
long
errPos0, errPos1;
float
tempFloat;
int curMaxDuty0, curMinDuty0, curMaxDuty1, curMinDuty1;
int cmdSpd0, cmdSpd1; //these are same as
desired speed BUT with acceleration and position
//control constraints imposed
DIR_t
myDir0, myDir1;
char
myNewEdge0, myNewEdge1;
unsigned int myUPeriod0, myUPeriod1;
//PTT |=
BIT7HI; //debugging
//retrieve
shared variables between encoder IC interrupt and this OC control interrupt
myDir0 =
dir0;
myDir1 =
dir1;
myNewEdge0 =
newEdge0;
myNewEdge1 =
newEdge1;
myUPeriod0 =
uPeriod0;
myUPeriod1 =
uPeriod1;
//write shared
variables
newEdge0 =
0;
newEdge1 =
0;
if
(myNewEdge0 == 0) EncoderTimeout0 = 1;
if
(myNewEdge1 == 0) EncoderTimeout1 = 1;
TIM1_TFLG1 =
_S12_C5F; //Clear
OC5 flag
TIM1_TC5 =
TIM1_TCNT + TIME; //Program
next compare
EnableInterrupts; //Call
to ensure Encoder IC interrupts
//check if
commanding duty cycle directly for debugging purposes
if
(disablePID
== 1) {
//do not implement PID control
return;
}
//convert uPeriods to speeds [RPM]
if (myNewEdge0 == 0) curSpd0 = 0;
else {
curSpd0 = (int)((long)1800000/myUPeriod0); // motor speed
if (myDir0 == BACKWARD) curSpd0 *= -1;
}
if (myNewEdge1 == 0) curSpd1 = 0;
else {
curSpd1 = (int)((long)1800000/myUPeriod1); // motor speed
if (myDir1== BACKWARD) curSpd1 *= -1;
}
cmdSpd0 =
desSpd0;
cmdSpd1 =
desSpd1;
//Determine Commanded Speed for Motor1 (slower motor)
//Calc
speed limit based on nearing end position
if (desEncoderFlag1 == 1) {
errPos1 =
desEncoderCount1 -
EncoderCount1;
//calculate
cmd speed based off this error
if (errPos1 < 0) cmdSpd1 = (int)(Kp_Pos*errPos1) - minControlRPM;
else cmdSpd1
= (int)(Kp_Pos*errPos1) + minControlRPM;
if (desSpd1 >= 0) {
if((cmdSpd1 > desSpd1) || (cmdSpd1 < 0)) cmdSpd1 = desSpd1;
}
else {
if((cmdSpd1 < desSpd1) || (cmdSpd1 > 0)) cmdSpd1 = desSpd1;
}
}
//Calc
speed limit based on acceleration limits
//if
((cmdSpd1 - lastCmdSpd1) > maxPosAccel) cmdSpd1 =
lastCmdSpd1 + maxPosAccel;
//else if
((cmdSpd1 - lastCmdSpd1) < -1*maxNegAccel) cmdSpd1
= lastCmdSpd1 - maxNegAccel;
//Determine Commanded Speed for Motor0 (faster motor)
if (curLockMode == LOCKED) cmdSpd0 = cmdSpd1;
else if
(curLockMode
== OPPOSITE) cmdSpd0 = -1*cmdSpd1;
else {
//Calc
speed limit based on nearing end position
if (desEncoderFlag0 == 1) {
//calculate
position error
//this
error should NEVER be zero (or we wouldn't be in position control mode)
errPos0 =
desEncoderCount0 -
EncoderCount0;
//calculate
cmd speed based off this error
if (errPos0 < 0) cmdSpd0 = (int)(Kp_Pos*errPos0) - minControlRPM;
else cmdSpd0
= (int)(Kp_Pos*errPos0) + minControlRPM;
//cmdSpd from position control should only limit desSpd, never increase it
if (desSpd0 >= 0) {
if((cmdSpd0 > desSpd0) || (cmdSpd0 < 0)) cmdSpd0 = desSpd0;
}
else {
if((cmdSpd0 < desSpd0) || (cmdSpd0 > 0)) cmdSpd0 = desSpd0;
}
}
//Calc
speed limit based on acceleration limits
//if
((cmdSpd0 - lastCmdSpd0) > maxPosAccel) cmdSpd0 =
lastCmdSpd0 + maxPosAccel;
//else
if ((cmdSpd0 - lastCmdSpd0) < -1*maxNegAccel)
cmdSpd0 = lastCmdSpd0 - maxNegAccel;
}
//update
for last cmd speed for acceration
limit purposes
lastCmdSpd0 =
cmdSpd0;
lastCmdSpd1 =
cmdSpd1;
//determine
speed error for PID control of speed
errSpd0 =
cmdSpd0 - curSpd0;
errSpd1 =
cmdSpd1 - curSpd1;
intErr0 +=
errSpd0;
intErr1 +=
errSpd1;
//calculate
authority
tempFloat = Kp*errSpd0 + Ki*intErr0;
duty0 = (int)tempFloat;
tempFloat = Kp*errSpd1 + Ki*intErr1;
duty1 = (int)tempFloat;
//quick
check for zero duty
if ((curSpd0 == 0) && (cmdSpd0 == 0)) duty0 = 0;
if ((curSpd1 == 0) && (cmdSpd1 == 0)) duty1 = 0;
//Calculate
authority limit for both motors (based on speed->backEMF)
tempFloat = curSpd0/407; //calculate back EMF
curMaxDuty0 = (int)( 157 + 12.5*tempFloat); //(( Imax*Rcoils) + EMF)*200[Duty Cycle]/16[V];
curMinDuty0 = (int)(-157 + 12.5*tempFloat); //((-Imax*Rcoils) + EMF)*200/16;
if (curMaxDuty0 > 200) curMaxDuty0 = 200;
if (curMinDuty0 < -200) curMinDuty0 = -200;
tempFloat = curSpd1/407; //calculate back EMF
curMaxDuty1 = (int)( 157 + 12.5*tempFloat); //(( Imax*Rcoils) + EMF)*200/16;
curMinDuty1 = (int)(-157 + 12.5*tempFloat); //((-Imax*Rcoils) + EMF)*200/16;
if (curMaxDuty1 > 200) curMaxDuty1 = 200;
if (curMinDuty1 < -200) curMinDuty1 = -200;
//saturate
duty cycle and prevent integrator wind-up
if (duty0 < curMinDuty0) {
duty0 =
curMinDuty0;
intErr0 -=
errSpd0;
}
else {
if (duty0 > curMaxDuty0) {
duty0 =
curMaxDuty0;
intErr0 -=
errSpd0;
}
}
if (duty1 < curMinDuty1) {
duty1 =
curMinDuty1;
intErr1 -=
errSpd1;
}
else {
if (duty1 > curMaxDuty1) {
duty1 =
curMaxDuty1;
intErr1 -=
errSpd1;
}
}
//update
duty cycle to implement speed control
Motor0(duty0);
Motor1(duty1);
//PTT
&= BIT7LO;
}
//Encoder0 Channel A tick interrupt for
Motor 0
//Interrupt Response Routine is called when TFLG1bit4 is set
//meaning interrupt of rising edge has occurred
void interrupt
_Vec_tim1ch4 Encoder0ICRoutine(void){
static unsigned int lastEdge0, tempPeriodCalc;
static int i = 0;
TIM1_TFLG1 =
_S12_C4F; //Clear
IC4 flag
if
(EncoderTimeout0
== 1) {
EncoderTimeout0 =
0; //can't make period calculation until a second pulse is
received
}
else
{
tempPeriodCalc = TIM1_TC4 - lastEdge0;
//check to
make sure period calculation is realistic
if ((tempPeriodCalc < minUPeriod) || (tempPeriodCalc
> maxUPeriod)) {
//ignore
this edge, must be noise
return;
}
uPeriod0 =
tempPeriodCalc;
newEdge0 =
1; //flag that an encoder tick has occured
}
//capture last
edge timing
lastEdge0 =
TIM1_TC4;
if
(PTP & BIT4HI) {
EncoderCount0--;
dir0 = BACKWARD;
//printf("BACKWARD0\r\n"); //remove
}
else
{
EncoderCount0++;
dir0 =
FORWARD;
}
//check if
motor should stop MOTORS_MOVING
if
(desEncoderFlag0
== 1) {
if (desEncoderCount0 == EncoderCount0) {
desSpd0 = 0; //stop the motor
desEncoderFlag0 = 0;
if (curLockMode != UNLOCKED) {
desSpd1 =
0;
desEncoderFlag1 =
0;
}
}
}
}
//Encoder tick interrupt for Motor 1
//Interrupt Response Routine is called when TFLG1bit6 is set
//meaning interrupt of rising edge has occurred
void interrupt
_Vec_tim1ch6 Encoder1ICRoutine(void){
static int i = 0;
static unsigned int lastEdge1, tempPeriodCalc;
TIM1_TFLG1 =
_S12_C6F; //Clear IC5 flag
if
(EncoderTimeout1
== 1) {
EncoderTimeout1 =
0; //can't make period calculation until a second pulse is
received
}
else
{
tempPeriodCalc = TIM1_TC6 - lastEdge1;
if ((tempPeriodCalc < minUPeriod) || (tempPeriodCalc
> maxUPeriod)) {
//ignore
this edge, must be noise
return;
}
uPeriod1 =
tempPeriodCalc;
newEdge1 =
1; //flag that an encoder tick has occured
}
lastEdge1 =
TIM1_TC6;
if
(PTP & BIT5HI) {
EncoderCount1++;
dir1 = FORWARD;
}
else
{
EncoderCount1--;
dir1 = BACKWARD;
}
//check if
motor should stop MOTORS_MOVING
if
(desEncoderFlag1
== 1) {
if (desEncoderCount1 == EncoderCount1) {
desSpd1 = 0; //stop the motor
desEncoderFlag1 =
0;
if (curLockMode != UNLOCKED) {
desSpd0 =
0;
desEncoderFlag0 =
0;
}
}
}
}
//set duty cycle on motor 0 = where duty = [0, 200]
static void Motor0(int
inputDC){
int DC
= -1*inputDC; // motor 1 needs to
spin "backwards" for the bot to go forward
if(DC > 0){ //CW
PTP &=
~DCMOTOR0; //PTT = PTT
& BIT0LO
setPWM0DutyCycle(DC);
}
else if (DC < 0){ //CCW
PTP |=
DCMOTOR0;
setPWM0DutyCycle(200+DC);
}
else
{
//stop
PTP &=
~DCMOTOR0;
setPWM0DutyCycle(DC);
}
}
//set duty cycle on motor 1
static void Motor1(int
DC){
//motor 1 is
slower than motor 0
if(DC > 0){
DC+=DutyOffset;
if (DC > 200) DC = 200;
PTP &=
~DCMOTOR1;
setPWM1DutyCycle(DC);
}
else if (DC < 0){
if (DC < -200) DC = -200;
PTP |=
DCMOTOR1;
setPWM1DutyCycle(200+DC);
}
else
{
PTP &=
~DCMOTOR1;
setPWM1DutyCycle(DC);
}
}