## Thursday, 7 June 2007

### Torque talk

I updated my motor control circuit to use a 0.1Ω current sense resistor to give me a full scale current of about 0.6A and then set up the ADC to provide negative feedback to control the speed. I read up on the classical control method which is PID, finding a good article on it here www.embedded.com. Before embarking on that complexity I decided to try a simple more intuitive method. I wrote a program which turns the motor on and monitors the current until it falls to a specified level, indicating that the motor is generating the correct back e.m.f. i.e. running at the correct speed. It then turns the motor off and waits for the start of another cycle.

This algorithm works reasonably well after playing with a few numbers to get it relatively stable. It does however suffer from a beating problem. I am not sure exactly why but I think it must be the PWM pulse rate beating with the commutation of the motor. Here is a video of a scope trace of the current waveform. You can hear the motor in the background as the speed is swept from maximum to minimum and you can see the current pulses getting shorter and higher. Just to to confuse matters the scope trace is beating with the camera frame rate.

I don't think the beating will matter much but it is a bit annoying. Playing with the PWM frequency has a big effect on it. In the end I settled for 200Hz, but I may play about with it a bit more.

One thing that I hadn't realised, but is obvious really, is that when you slow down a motor with PWM it loses a lot of torque at low speeds but when you add feedback it gets it all back again.

Here is the simple piece of code :-
#include "msp430x20x3.h"

#define LED BIT0
#define MOTOR BIT2
#define FLOATING (BIT1 + BIT3)
#define CURRENT BIT4
#define GND BIT5
#define I2CPINS (BIT6 + BIT7)

#define OUTPUTS (I2CPINS + MOTOR + LED + FLOATING)

typedef unsigned int word;
typedef char bool;
#define true 1
#define false 0

int main( void )
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
//
// Set MCLK to 16 MHz, SMCLK = 2 MHz
//
BCSCTL2 = SELM_0 | DIVM_0 | DIVS_3; // MCLK = DCO, SMCLK = DCO / 8
BCSCTL1 = CALBC1_16MHZ | XT2OFF;
DCOCTL = CALDCO_16MHZ;
//
// Set up I/0
//
P1OUT = MOTOR; // Motor off
P1DIR = OUTPUTS; // Define output pins
SD16AE = CURRENT + GND; // Configure analogue inputs
//
//
SD16CTL = SD16XDIV_2 + SD16DIV_0 + SD16REFON; // MCLK / 16 = 1MHz
SD16CCTL0 = SD16DF + SD16OSR_32 + SD16IE + SD16SC; // OSR = 32
SD16INCTL0 = SD16INTDLY_0 + SD16INCH_2; // Delay 4 clocks; channel A2
//
// Enable interrupts and wait forever
//
_BIS_SR(GIE);
for(;;);
}

int current;
int target_current = 5000;
#define CYCLE 200

#pragma vector=SD16_VECTOR
{
static int count;

current += (adc - current) >> 1; // Moving average filter
if(++count >= CYCLE) {
count = 0;
P1OUT &= ~MOTOR; // Motor on
P1OUT |= LED;
if(target_current < 0)
target_current = 5000;
target_current += 10;
}
if(count > 30 && current < target_current) {
P1OUT |= MOTOR; // Turn the motor off
P1OUT &= ~LED;
}
}
Note that in the end I am not using the PWM capability of the MSP430 timer as I did the motor control in the ADC interrupt handler.

The next task is to get it talking to the rest of HydraRaptor using an I²C bus.