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

Chapter 7: Microcontroller Interrupts and Timers

Projects

Precision Blinking

Let’s use interrupts and timers to provide precise control over the blink rate for an LED. We’ll let the PC send data as a character string to the microcontroller and let the microcontroller set a timer interrupt to trip at the specified rate and toggle an LED.

First Let’s think about setting a timer to throw an interrupt every millisecond. The USART initialization sets the system oscillator to 2 MHz. We set the Timer0 prescaler

to clk/8 which gives a 250 kHz input to the timer/counter. Then we set a compare value of 250 so the timer throws an interrupt every 250 counts: 250000/250 = 1000, and we get interrupted a thousand times a second, almost like having a toddler around.

We set timer0 to do a compare interrupt:

TIMSK0 = (1<<OCIE0A);

Then we set the timer0 compare register to 250:

OCR0A = 250;

Finally we set the Timer/Counter Control Register A to the Clear Timer on Compare waveform and the prescaler to divide the clock by 8:

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

But, heck Let’s get fancy and allow ourselves to change the compare value by sending data from the PC. We write the MilliSec_init and the set_OCR)A functions:

void MilliSec_init(unsigned char count)

{

// Initialize Timer0.

128

Chapter 7: Microcontroller Interrupts and Timers

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

//Sets the compare value set_OCR0A(count);

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

}

void set_OCR0A(unsigned char count)

{

// Sets the compare value OCR0A = count;

}

Now we can initialize the timer when the program starts and change the compare value when we feel like it. Let’s reuse the PC_Comm code to generate an annoying LED precision blinker that’s actually an 8-bit counter. As you will see, or rather won’t see, you can’t see the LED blinking at the fastest rates in the lower 4 bits, but you can see blinking in the slower upper 4 bits. The lowest bit toggles the LED 1000 times a second, it is on for 1000th of a second then off for 1000th of a second, which yields a blink period of 500 Hz. Each LED blinks at half the rate of the prior LED, so the blink periods for each LED are:

LED0 = 500 Hz.

LED1 = 250 Hz.

LED2 = 125 Hz.

LED3 = 62.5 Hz.

LED4 = 31.25 Hz.

LED5 = 15.625 Hz.

LED6 = 7.8125 Hz.

LED7 = 3.90625 Hz.

If we tell the Butterfly to set the compare to 125, then the interrupt occurs at 2000 Hz and the fastest blink period becomes 1000 Hz.

129

Chapter 7: Microcontroller Interrupts and Timers

What happens if we send it 100? Well, 250000/100 = 2500, so we would get a 1250 Hz blink.

How do we get a 60 Hz blink? We can get LED3 to blink at 60 Hz if the base rate is 480 Hz, which we can get from 960 interrupts per second, which we could get from a compare count of 260.41666… and we ain’t gonna get that for two reasons: one, the count overflows at 255 and two, we are dealing with integers. If we set the compare to 130 we get a rate of 1923.076923 which yields 60.096…Hz on LED5, pretty darn close, but we have an error of 1 – (60/60.09615385 ) * 100 = 0.16 %, not bad at all. But is it close enough? Only you can decide that

Create a new directory Precision Blinking and copy the PC_Comm. and Demonstrator .c and .h files from the PC_Comm. directory.

In Programmers Notepad change Demonstrator.h to:

// Demonstrator.h Precision Blinking version

#include <avr/signal.h> #include <inttypes.h>

void initializer(void); void parseInput(char *);

int parse_ctc(char *); void set_ctc(int);

void MilliSec_init(unsigned char count); void set_OCR0A(unsigned char count);

In Programmers Notepad change Demonstrator.c to:

// Demonstrator.c Precision Blinking version

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

unsigned char milliseconds = 0;

void initializer()

{

// Calibrate the oscillator:

130

Chapter 7: Microcontroller Interrupts and Timers

OSCCAL_calibration();

//Initialize the USART USARTinit();

//set PORTD for output DDRD = 0xFF;

MilliSec_init(250); // default to 1000 Hz

// say hello

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

sendString("You are talking to the Precision Blinking demo.\r");

}

void parseInput(char s[])

{

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

{

case 'c':

if( (s[1] == 't') && (s[2] == 'c')) parse_ctc(s);

break; case 'd':

if((s[1]=='e')&&(s[2]=='m')&&(s[3]=='o')&&(s[4]=='?')) sendString("You are talking to the Precision Blinking demo.\r"); break;

default:

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

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

}

s[0] = '\0';

}

int parse_ctc(char s[])

{

char ctc[11];

unsigned char i = 3, j = 0;

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

{

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

{

131

Chapter 7: Microcontroller Interrupts and Timers

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

}

else

{

sendString("Error - Parse_ctc received a non integer: "); sendChar(s[i]);

sendChar('\r'); return 0;

}

}

ctc[j] = '\0';

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

{

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

}

else

{

set_ctc(atoi(ctc));

}

return 1;

}

void set_ctc(int count)

{

char ctc[11];

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

sendString(ctc);

sendChar('\r');

MilliSec_init(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 MilliSec_init(unsigned char count)

{

// Initialize Timer0.

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

132