Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C Programming for microcontrollers (Joe Pardue, 2005).pdf
Скачиваний:
277
Добавлен:
12.08.2013
Размер:
4.55 Mб
Скачать

Chapter 7: Microcontroller Interrupts and Timers

Pulse Width Modulation – LED Brightness Control

When we continuously turn a port pin on and off at equal intervals, we get a pulse train like the system clock. The pulse frequency is the number of pulses we generate in one second, usually referred to as Hz pronounced ‘hurts’ named after the Hertz rental car company. Okay, I lied; it is actually named for… hey, you can Google this as easily as I can.

For an equal interval pulse train the pin is high half the time and low half the time. This is called a 50% duty cycle (Figure 17). We can vary the duty cycle from 0%, always off, to 100%, always on, or anything in between.

We have set up our LEDs so that the Port D pins source +3v to turn them on. When the pin is set to low, 0 volts, no current flows so the LEDs are off. The current flows thru 330-ohm resistors providing 9 mA of current and a power of

.027 watts. That’s not much power for light bulbs, but enough for LEDs. In the precision blinking project we were only giving the LED power half the time. The on/off time doubles for each LED, but they are all on for only half the time, so they are using only .027/2 = 0.0135watts. By cutting the on time in half, we get a 25% duty cycle and 0.0135/2 = 0.00675 watts, less power and less light output. Hey, I think I see a way to control the LED brightness. If we keep the frequency of pulses constant, but lower or raise the on time it is on, we can control the power to the LED and the light output from it.

50% Duty Cycle

75% Duty Cycle

25% Duty Cycle

Figure 17: Pulse Width Modulation Duty Cycle

134

Chapter 7: Microcontroller Interrupts and Timers

We have already seen that the human eye perceives fast blinking LEDs as being constantly on. Our eyes also see rapidly pulsed light as having brightness somewhere between the peak and the average. This means that a high intensity pulse with a low duty cycle pulse looks brighter than it would powered by a direct current providing the same power as the average of the pulsed signal. Our perceptual peculiarity gives us a way to provide a brighter seeming light with less power if we use PWM. So not only can we control the brightness, we can do a trick to fool the eye into thinking it’s seeing something brighter even though we are using less power. This is good news for our power use, but bad news in trying to extrapolate duty cycle to perceived brightness. Cutting the duty cycle in half does not translate into a halving of the perceived brightness.

Let’s write a program to allow us to play with the frequency and the duty cycle then we can play with the parameters and see how we think they affect brightness.

Is it hard to write the PWM code? Nope, waveform generation bits in the TCC0RA WGM00 = 0 to WGM01 = 0 and WGM00 = 1.

all we have to do is change the register from WGM01 = 1 and

Create a new directory, PWM, and copy the .c and .h files and the makefile from the Precision Blinking directory. In the Demonstrator files milliSecInit routine change:

// Set Clear on Timer Compare (CTC) mode, CLK/8 prescaler TCCR0A = (1<<WGM01)|(0<<WGM00)|(1<<CS01);

to:

// Set PWM Phase Correct mode, CLK/8 prescaler TCCR0A = (0<<WGM01)|(1<<WGM00)|(1<<CS01);

and in the SIGNAL(SIG_OUTPUT_COMPARE0) change:

PORTD = milliseconds++;

to:

if(PORTD &= 1) cbi(PORTD, 0); else sbi(PORTD, 0);

135

Chapter 7: Microcontroller Interrupts and Timers

Fire up HyperTerminal and try some ctc values noticing how LED 0 changes brightness.

That was sooooo. easy. If you feel cheated because it was soooo easy, then read the section on Timer/counter0 in the data book As usual these programs are soooo easy once you know how to do them, but a major bear and a half trying to decipher the data book to get the few tidbits you really need.

136

Chapter 7: Microcontroller Interrupts and Timers

Pulse Width Modulation - Motor Speed Control

Let’s modify the LED PWM software a little and use it to control the speed of a motor. But first, Let’s design and build the hardware. We’ll use parts from the JAMECO parts list: a 9v motor, a 9v battery, a 9v battery connector, a 4N28 optoisolator, a TIP115 power transistor, a 330 Ohm resistor, and a 2.2K Ohm resistor. I’ve included the optoisolator for the simple reason that it helps lessen the possibility that we’ll destroy the Butterfly when messing with this circuit. I actually managed to burn up both the optoisolator and the power transistor when fooling with this design so, at least for me, this is not overkill. Since this is not an electronics text, we won’t learn anything about how the circuit works. Just follow the illustrations and you shouldn’t have any problems.

Figure 18: Motor Speed Control Schematic and Parts

137

Chapter 7: Microcontroller Interrupts and Timers

To PORTD PIN 1

330 Ohm to 4N28 pin 1

4N28 pin 2 to +3v GND

TIP115

Diode 1N4001

2.2k Ohm from 4N28 pin51 to TIP115 pin 1

4N28 pin 4 to 9v GND

 

 

9v GND

 

Motor b to 9v GND

+9v to TIP115 pin 3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Motor a to TIP115 pin 2

 

 

 

 

 

 

 

 

Figure 19: Motor Speed Control Breadboard Labeled

Figure 20: Motor Speed Control Hardware

138

Chapter 7: Microcontroller Interrupts and Timers

The motor base is made with foam core board (you could use corrugated box board) cut and glued (crappily) to hold a motor. The upright on the left will be used in the next project to hold an optointerrupter.

Figure 21: Motor Base

Figure 22: Motor Wheel Stationary and Spinning

The wheel pattern is located in Appendix 6. Print it out and stick it to a piece of sturdy thin cardboard. I put some electrical tape on the motor shaft and made some radial cuts in the center of the wheel and slipped it over the tape. It works. The cutout will be used in the next project.

139

Chapter 7: Microcontroller Interrupts and Timers

// Demonstrator.c Motor Speed Control version

#include "PC_Comm.h" #include "Demonstrator.h"

#define PINB_MASK ((1<<PINB4)|(1<<PINB6)|(1<<PINB7)) #define PINE_MASK ((1<<PINE2)|(1<<PINE3))

unsigned char milliseconds = 0;

unsigned int second = 0; // count to 1000 and trigger one second event

unsigned int speed = 0; // IR detector count per second unsigned int lastspeed = 0; // IR detector count per second

void initializer()

{

//Calibrate the oscillator: OSCCAL_calibration();

//Initialize the USART USARTinit();

//Set for pin change on PINB0 PCMSK0 = (1 << PINB0); //

EIFR = (1 << 7); // flag for PCINT15-8 EIMSK = (1 << 7); // mask for PCINT15-8

DDRB = 0X00; // set PORTB for input

PORTB = 0xFF; // enable pullup on for input

// set PORTD for output //DDRD = 0xFF;

DDRD = (1 << PIND0); // set pin 0 to output

PORTD = (1 << PIND0); // set pin 0 to enable pullup milliSecInit(127); // 50% duty cycle 1kHz signal

// say hello

sendString("\rPC_Comm.c ready to communicate.\r"); // identify yourself specifically

sendString("You are talking to the Motor Speed Control demo.\r");

sendString("setxxx to set speed\r");

}

140

Chapter 7: Microcontroller Interrupts and Timers

void parseInput(char s[])

{

// parse first character switch (s[0])

{

case 's':

if( (s[1] == 'e') && (s[2] == 't')) parse_set(s);

break; case 'd':

if( (s[1] == 'e') && (s[2] == 'm') && (s[3] == 'o') && (s[4] == '?') )

sendString("You are talking to the Motor Speed Control

demo.\r");

break;

default:

sendString("\rYou sent: '"); sendChar(s[0]);

sendString("' - I don't understand.\r"); break;

}

s[0] = '\0';

}

int parse_set(char s[])

{

char set[11];

unsigned char i = 3, j = 0;

while( (s[i] != '\0') && (j <= 11) )

{

if( (s[i] >= '0') && (s[i] <= '9') )

{

set[j++] = s[i++];

}

else

{

sendString("Error - Parse_set received a non integer: ");

sendChar(s[i]);

sendChar('\r'); return 0;

}

}

141

Chapter 7: Microcontroller Interrupts and Timers

set[j] = '\0';

if(j>4)// must be < 256

{

sendString("Error - Parse_set number too large"); return 0;

}

else

{

set_speed(atoi(set));

}

return 1;

}

void set_speed(int count)

{

char speed[11];

sendString("Setting the Compare Timer Count to: "); itoa(count,speed,10);

sendString(speed);

sendChar('\r');

milliSecInit(count);

}

/*

The USART init set the system oscillator to 2 mHz. We set the Timer0 prescaler

to clk/8 which gives a 250 kHz input to the timer/counter. A compare of 250 throws

an interrupt every millisecond. */

void milliSecInit(unsigned char count)

{

// Enable timer0 compare interrupt TIMSK0 = (1<<OCIE0A);

//Sets the compare value setOCR0A(count);

//Set PWM Phase Correct mode, CLK/8 prescaler

142

Chapter 7: Microcontroller Interrupts and Timers

TCCR0A = (0<<FOC0A)|(0<<WGM01)|(1<<WGM00)|(1<<CS01);

}

void setOCR0A(unsigned char count)

{

// Sets the compare value OCR0A = count;

}

// Interrupt occurs twice per Millisec, timed for PWM SIGNAL(SIG_OUTPUT_COMPARE0)

{

// Toggle PORtD pin 0 if(PORTD &= 1) cbi(PORTD, 0); else sbi(PORTD, 0);

}

143