Documents »Application Note: Using the Motor Driver on the 3pi, Orangutan LV-168, and Baby Orangutan BView document on multiple pages.
1. IntroductionOrangutans’ integrated motor drivers set this robot controller line apart from other programmable microcontroller boards like the Basic Stamp and Arduino controllers. By tying a high-performance AVR microcontroller directly to a powerful dual H-bridge capable of driving two DC brushed motors, we have produced platforms that can serve as both the brains and brawn of your robot. This application note is intended to give you the information you need to easily interface with the motor drivers on your Orangutan LV-168 or Baby Orangutan B. 2. Motor Driver Truth Tables
We advise against using the fourth state in the above truth table (both motor inputs low). For the Orangutan LV-168, this state results in “brake high”, which is functionally equivalent to “brake low” but less safe (it’s easier to accidentally short power to ground while braking high). For the Baby Orangutan B, this state results in coasting; there is no danger involved in using this coast state, but alternating between drive and break produces a more linear relationship between motor RPM and PWM duty cycle than does alternating between drive and coast. As such, we suggest achieving variable speed by PWMing between drive and brake when using the Baby Orangutan. Motor 1 is controlled by pins PD5 and PD6 (i.e. OC0B and OC0A), and motor 2 is controlled by PD3 and PB3 (i.e. OC2B and OC2A). These pins are connected to the ATmega48/168’s four eight-bit hardware PWM outputs (PD5=OC0B, PD6=OC0A, PD3=OC2B, and PB3=OC2A), which allows you to achieve variable motor speeds through hardware timers rather than software. This frees the CPU to perform other tasks while motor speed is automatically maintained by the AVR timer hardware. 3. Simple Code to Drive the MotorsIf we don’t want to worry about variable motor speed, we can simply make use of the truth table values in the previous section to figure out how to drive our motors. The code to do this becomes simple AVR digital I/O manipulation: Initialize the motor control pins#include <avr/io.h> // use Data Direction Registers (DDRx) to // set the four motor control pins as outputs DDRD |= (1 << PD3) | (1 << PD5) | (1 << PD6); DDRB |= (1 << PB3); Motor 1 forward at full speedPORTD &= ~(1 << PD5); // drive pin PD5 low PORTD |= (1 << PD6); // drive pin PD6 high Motor 1 reverse at full speedPORTD |= (1 << PD5); // drive pin PD5 high PORTD &= ~(1 << PD6); // drive pin PD6 low Motor 1 brake lowPORTD |= (1 << PD5) | (1 << PD6); // drive pins PD5 and PD6 high Motor 2 forward at full speedPORTD &= ~(1 << PD3); // drive pin PD3 low PORTB |= (1 << PB3); // drive pin PB3 high Motor 2 reverse at full speedPORTD |= (1 << PD3); // drive pin PD3 high PORTB &= ~(1 << PB3); // drive pin PB3 low Motor 2 brake lowPORTD |= (1 << PD3); // drive pin PD3 high PORTB |= (1 << PB3); // drive pin PB3 high To achieve variable speeds, we can rapidly alternate between driving and braking a motor, but doing this in software can be taxing on the CPU. Fortunately, AVRs come with hardware timers that can rapidly toggle the motor driver states for us while leaving the CPU free for our higher-level computations. 4. Variable Speed Control Using Hardware PWMsThe ATmega48/168 used on the Orangutan LV-168 and the Baby Orangutan B has two eight-bit timers: Timer0 and Timer2. Each of these timers has two PWM output pins. The Timer0 PWM output pins are OC0A on PD6 and OC0B on PD5; these are the control lines for motor 1. The Timer2 PWM output pins are OC2A on PB3 and OC2B on PD3; these are the control lines for motor 2. Our general motor control strategy will be as follows:
5. PWM-Based Motor Control Code
#include <avr/io.h>
// Motor Control Macros -- pwm is an 8-bit value
// (i.e. ranges from 0 to 255)
#define M1_FORWARD(pwm) OCR0A = 0; OCR0B = pwm
#define M1_REVERSE(pwm) OCR0B = 0; OCR0A = pwm
#define M2_FORWARD(pwm) OCR2A = 0; OCR2B = pwm
#define M2_REVERSE(pwm) OCR2B = 0; OCR2A = pwm
// Motor Initialization routine -- this function must be called
// before you use any of the above macros
void motors_init()
{
// configure for inverted PWM output on motor control pins:
// set OCxx on compare match, clear on timer overflow
// Timer0 and Timer2 count up from 0 to 255
TCCR0A = TCCR2A = 0xF3;
// use the system clock/8 (=2.5 MHz) as the timer clock
TCCR0B = TCCR2B = 0x02;
// initialize all PWMs to 0% duty cycle (braking)
OCR0A = OCR0B = OCR2A = OCR2B = 0;
// set PWM pins as digital outputs (the PWM signals will not
// appear on the lines if they are digital inputs)
DDRD |= (1 << PD3) | (1 << PD5) | (1 << PD6);
DDRB |= (1 << PB3);
}
The following sample program demonstrates how these motor control macros can be used:
#define F_CPU 20000000 // system clock is 20 MHz
#include <util/delay.h> // uses F_CPU to achieve us and ms delays
// delay for time_ms milliseconds by looping
// time_ms is a two-byte value that can range from 0 - 65535
// a value of 65535 (0xFF) produces an infinite delay
void delay_ms(unsigned int time_ms)
{
// _delay_ms() comes from <util/delay.h> and can only
// delay for a max of around 13 ms when the system
// clock is 20 MHz, so we define our own longer delay
// routine based on _delay_ms()
unsigned int i;
for (i = 0; i < time_ms; i++)
_delay_ms(1);
}
int main()
{
motors_init();
M1_FORWARD(128); // motor 1 forward at half speed
M2_REVERSE(25); // motor 2 reverse at 10% speed
delay_ms(2000); // delay for 2s while motors run
M1_REVERSE(64); // motor 1 reverse at 25% speed
M2_FORWARD(0); // motor 2 stop/brake
// loop here forever to keep the program counter from
// running off the end of our program
while (1)
;
return 0;
}
6. Motor Voltage WaveformsThe following oscilloscope screen captures demonstrate how our motor control macros from Section 5 affect the motor driver outputs. These captures were taken using a Baby Orangutan B-168 running off of a 12 V battery with a motor connected. M1_FORWARD(63);
M1_FORWARD(191);
7. Using the Trimmer Pot for Motor ControlThe following sample program uses the motor control macros and functions defined in Section 5 to drive the motors in response to the trimmer potentiometer position. Motors 1 and 2 transition linearly from full forward to stationary to full reverse as the potentiometer voltage varies from 0 to 5 V. This program uses the ATmega48/168’s analog-to-digital converter to determine the position of the potentiometer.
#include <avr/io.h>
int main()
{
motors_init();
// ***** ADC INITIALIZATION *****
// On the Orangutan LV-168, the user trimpot can be optionally jumpered
// to ADC7 using a blue shorting block; the Orangutan LV-168 ships
// with this shorting block in place (this shorting block is required
// by this program). On the Baby Orangutan, the trimpot is permanently
// connected to ADC7.
ADCSRA = 0x87; // bit 7 set: ADC enabled
// bit 6 clear: don't start conversion
// bit 5 clear: disable autotrigger
// bit 4: ADC interrupt flag
// bit 3 clear: disable ADC interrupt
// bits 0-2 set: ADC clock prescaler is 128
ADMUX = 0x07; // bit 7 and 6 clear: voltage ref is Vref pin
// bit 5 clear: right-adjust result (10-bit ADC)
// bit 4 not implemented
// bits 0-3: ADC channel (channel 7)
while (1) // loop forever
{
long sum = 0;
unsigned int avg, i;
// Here we accumulate 500 conversion results to get an average ADC.
// According to the mega168 datasheet, it takes approximately 13
// ADC clock cycles to perform a conversion. We have configured
// the ADC run at IO clock / 128 = 20 MHz / 128 = 156 kHz, which
// means it has a conversion rate of around 10 kHz. As a result,
// it should take around 50 ms to accumulate 500 ADC samples.
for (i = 0; i < 500; i++)
{
ADCSRA |= ( 1 << ADSC ); // start conversion
while ( ADCSRA & ( 1 << ADSC )) // wait while converting
;
sum += ADC; // add in conversion result
}
avg = sum / 500; // compute the average ADC result
// set motors based on trimpot position (middle value = motor speed 0)
// avg is a 10-bit value and hence will range from 0 to 1023
if (avg <= 511)
{
M1_FORWARD((511 - avg) / 2);
M2_FORWARD((511 - avg) / 2);
}
else
{
M1_REVERSE((avg - 512) / 2);
M2_REVERSE((avg - 512) / 2);
}
}
return 0;
}
8. Differences between the Orangutan LV-168 and Baby Orangutan BEverything mentioned so far in this application note applies to both the Orangutan LV-168 and Baby Orangutan B, but there are some key motor control differences that need to be noted:
The Orangutan LV-168’s performance degrades for PWM frequencies above 10 kHz, which is why the examples in this document use 10 kHz PWMs. However, the Baby Orangutan B can handle PWM frequencies as high as 80 kHz. To achieve a PWM frequency of 80 kHz on the Baby Orangutan B, you need to set: TCCR0B = TCCR2B = 0x01; in the motors_init() function (rather than TCCR0B = TCCR2B = 0x02). This clocks the timers off of the 20 MHz system clock directly. Increasing the PWM frequency from 10 kHz to 80 kHz has the benefit of pushing it outside the range of human hearing, thereby eliminating PWM-induced motor whining, but it also increases power losses due to switching, which can cause the motor drivers to heat up faster and potentially trigger thermal shutdown sooner. Note that you can safely decrease the PWM frequency below 10 kHz on both the Orangutan LV-168 and Baby Orangutan B by using larger timer clock prescalers (e.g. a prescaler of 64 will produce a PWM frequency of 1.25 kHz). This will decrease switching power losses, but could lead to choppier motor motion. Another difference is the Baby Orangutan B can set the motor driver outputs to a high-impedance state that will let the motor coast instead of brake. The Orangutan LV-168 does not support high-impedance driver outputs. 9. Dealing with Motor NoiseOne major drawback to working with motors is the large amounts of electrical noise they produce. This noise can interfere with your sensors and can even impair your microcontroller by causing voltage dips on your regulated power line. Large enough voltage dips can corrupt the data in microcontroller registers or cause the microcontroller to reset. The main source of motor noise is the commutator brushes, which can bounce as the motor shaft rotates. This bouncing, when coupled with the inductance of the motor coils and motor leads, can lead to a lot of noise on your power line and can even induce noise in nearby lines. There are several precautions you can take to help reduce the effects of motor noise on your system: 1) Solder capacitors across your motor terminals. Capacitors are usually the most effective way to suppress motor noise, and as such we recommend you always solder at least one capacitor across your motor terminals. Typically you will want to use anywhere from one to three 0.1 uF capacitors, soldered as close to the motor casing as possible. If you use one capacitor, solder one lead to each of the motor’s two terminals as shown to the right above. For greater noise suppression, you can solder two capacitors to your motor, one from each motor terminal to the motor case as shown in the picture to the right. For the greatest noise suppression, you can solder in all three capacitors: one across the terminals and one from each terminal to the motor case. 2) Keep your motor and power leads as short as possible. This will limit the inductance of your leads and decrease the effect of noise on the system. You can decrease noise by twisting the motor leads so they spiral around each other. 3) Route your motor and power wires away from your signal lines. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Home
|
Contact
|
About
|
Forum
|
US toll free: 1-877-7-POLOLU |