Motor Control Algorithm

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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
		adjust pwm freq
	 
	 
	 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 */
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think I'd time the period between the holes, or between the leading edges. Then subtract the 2ms to get a positive or negative error. Use C * the error to adjust the duty cycle up or down. Start at like 50% till you start seeing pulses.

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ap note AVR221 is a particularly good description of the PID algorithm. You won't regret learning to make PID controllers.

http://www.atmel.com/dyn/resources/prod_documents/doc2558.pdf