Saturday 9 June 2007

Lenz's law

Explain this: I just connected my 1300W vacuum cleaner to a power meter. When running unobstructed it consumes 1100W. When I block the tube so the motor is audibly under more load the power drops to 900W. Power factor remains close to 1. Weird motor?

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
//
// Set up ADC
//
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
__interrupt void ADC_int(void)
{
static int count;

int adc = SD16MEM0;
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.

Tuesday 5 June 2007

RTFM

I made a start on my PWM control software this evening. The first thing that I discovered was that I had connected the motor drive to the wrong pin on the MSP430F2013. There are two pins that can be driven from the timer, TA0 and TA1, but only TA1 is capable of PWM. I should have studied the datasheet more carefully! Easily fixed but a bit of a time waster.

I wrote a small test program to try different PWM duty cycles and frequencies :-
#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)

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
P1SEL = MOTOR; // Motor = TA2 output
//
// Set up timer
//
TACCR0 = 20000; // 2 MHz / 100
TACCR1 = 15000; // 25%
TACCTL1 = OUTMOD_7; // Reset / set
TAR = 0; // Start counting from zero
TACTL = TASSEL_2 + MC_1 + TACLR; // SMCLK, upmode, clear
}
I found that 50-100 Hz seems to work well. 1KHz is too fast as the current doesn't have chance the build up to its full value due to the motor winding inductance.

Here is a graph of motor current versus duty cycle with no mechanical load :-


As the duty cycle gets smaller the motor slows down so it generates less back e.m.f. making the current increase.

I chose a current sense resistor of 0.27Ω which gives voltages up to 1.75V however the full scale voltage of the ADC is only 0.6V so I could do with it being smaller. I could just attenuate it with a potential divider but as it gets hot and wastes power it is better to reduce its value to 0.1Ω. Again, I should have paid more attention to the datasheet. I guess I will be taking a trip to Maplin tomorrow lunch time.