## Motor Control Algorithm

9 posts / 0 new
Author
Message

Hi all.......

I'm trying to control a dc motor to spin an aluminum disc at an exact speed using by varying its PWM duty cycle. The disc has 32 holes punched in it, with a IR LED/phototransistor pair making a beautiful square wave as the disc rotates.

Now, I'm trying to use this feedback to control the speed of the motor. I want the square wave from the rotary encoder to be 500Hz. Below is my first attempt at writing the control algorithm, but it doesn't work. The PWM duty cycle starts at 100%, but decrements every time a hole goes past the encoder, and resets back to 100% when it reaches 0%.

If someone could take a look and offer suggestions, I'd really appreciate it!

Thanks!

```
#include
#include
#include
#include
#include

typedef uint8_t byte;
typedef uint16_t word;

#define aByte(bit) (1 << bit) // byte with bit set

#define IRLedPin PA1
#define photoTransistorPin PA0
#define motorPin PB2

#define setPortAPinForOutput(pin) (DDRA |= aByte(pin))
#define setPortAPin(pin) (PORTA |= aByte(pin))
#define clearPortAPin(pin) (PORTA &= ~(aByte(pin)))

#define setPortBPinForOutput(pin) (DDRB |= aByte(pin))
#define setPortBPin(pin) (PORTB |= aByte(pin))
#define clearPortBPin(pin) (PORTB &= ~(aByte(pin)))

volatile byte overflows;

#define desiredTicksPerInterrupt 5000

ISR(PCINT0_vect)
{
/*

if timer's not running
zero timer
start it with app. prescaler (/8 i think; (20000000/8)/200 holes per second gives 12,500 max clock ticks between holes at slowest speed we care about, well within range of the 16-bit timer)

if timer is running
stop it
decide whether running too fast or too slow, or if overflow happened, just max out pwm

Want 500 holes per second
So we want (20000000/8)/500 = 5000 clock ticks between interrupts.

*/

if(TCCR1B == 0x00 && !(PINA & (1 << photoTransistorPin)))  //If timer is stopped and it was a falling edge
{
overflows = 0;  //Reset overflows counter
TCNT1 = 0x0000;  //Reset timer
TCCR1B = _BV(CS11); //Start timer with /8 prescaler
} else if (!(PINA & (1 << photoTransistorPin))){                // If the timer isn't stopped...
TCCR1B = ~_BV(CS11); //Stop timer.
if (overflows > 0)  //If overfow happened...
{
OCR0A = 0xFF;  //Max out PWM.
} else {            //If it didn't...

if(TCNT1 > desiredTicksPerInterrupt)
{
if(OCR0A + 1 <= 0xFF)
{
OCR0A++;  //turn up the duty cycle
}
}

if(TCNT1 < desiredTicksPerInterrupt)
{
OCR0A--;  //turn down the duty cycle
}

}

}

}

ISR(TIM1_OVF_vect)
{
overflows++;  //increment overflows counter when an overflow happens
}

int main(void)
{

//set clock divider to /1
CLKPR = (1 << CLKPCE);
CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

setPortAPinForOutput(IRLedPin);
setPortAPin(IRLedPin);

setPortBPinForOutput(motorPin);

TCCR0A = _BV(COM0A1) | _BV(WGM00); //Toggle OC0A on Compare Match, Set WGM00 for Phase Correct PWM
TCCR0B = _BV(CS00);  //Start clock, no prescaler
OCR0A = 0xFF; //Start at 100% duty cycle for spin-up

GIMSK = _BV(PCIE0);  //Enable pin change interrupt 0
PCMSK0 = _BV(PCINT0);  //Enable pin change interrupt on PA0

sei();  //Enable interrupts

for(;;){
}
return 0;   /* never reached */
}
```

When I did something like that once I used a deadband to keep it stable. It was just a simple bang-bang servo using a timer to measure the speed and PWM, as you are doing. They needed something much more stable for the production unit, so they implemented PDI control. It wasn't easy.

Leon Heller G1HSM

I've done that sort of thing by applying a delta to the PWM that is proportional to the difference between the measured speed and the desired speed. Also, some sort of digital "low-pass" on the difference helps. This is basically one part of the PID algorithm. It takes some tuning to get the delta right and the low-pass time constant right.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Quote:
The PWM duty cycle starts at 100%, but decrements every time a hole goes past the encoder, and resets back to 100% when it reaches 0%.

It could act a little crazy this way. I suggest putting in limits to the % value. When decrementing reaches zero, just stop at zero without 'resetting'. Similarly, when incrementing, limit the max count to 100.

Tom Pappano
Tulsa, Oklahoma

First up look at using the input capture feature for measuring the input frequency. Then look at applying a low pass filter to the measurements then update the pwm at a lower rate rather than on each input interrupt. At the moment the disc cannot accelerate much in the time between each input interrupt thus your pwm value will overshoot. You might want to write some code to measure how fast you can accelerate you disc - this information can be used to better 'tune' your control algorithm.

Ah, ok, I think I got my problem figured out. It oscillates a little, but the range is within acceptable limits.

Thanks for the help!

Now, my micro freezes at random intervals.....guess I should make a new thread...

Incidentally was the problem that you had the control inverted? Also one source of slight oscillation may be that your inequalities for actual vs desired clock ticks per hole don't have an equals sign.
Generally I would recommend more of a proportional or proportional integral controller for this kind of application, as a few people have mentioned earlier in the thread.