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

Chapter 7: Microcontroller Interrupts and Timers

TCNT1H = 0; // clear timer1 counter

TCNT1L = 0;

TCNT2 = 0; // clear timer2 counter

// wait for timer2 compareflag while ( !(TIFR2 && (1<<OCF2A)) );

TCCR1B = 0; // stop timer1

sei(); // __enable_interrupt(); // enable global interrupt

if ( (TIFR1 && (1<<TOV1)) )

{

temp = 0xFFFF; // if timer1 overflows, set the temp to 0xFFFF

}

else

{// read out the timer1 counter value tempL = TCNT1L;

temp = TCNT1H; temp = (temp << 8); temp += tempL;

}

if (temp > 6250)

{

OSCCAL--; //RC oscillator runs to fast, decrease OSCCAL

}

else if (temp < 6120)

{

OSCCAL++;//RC oscillator runs to slow, increase OSCCAL

}

else

calibrate = TRUE;

// the interRC is correct

TCCR1B = (1<<CS10); // start timer1

}

}

OSCCAL_calibration() function – detailed explanation

The ‘System Clock and Clock Options’ section of the ATmega169 data book tells more than you’ll ever want to know about the ATmega169's clock. Let’s focus on only what we need for our system.

123

Chapter 7: Microcontroller Interrupts and Timers

The Clock Prescale Register, CLKPR, is discussed beginning on page 30 of the data book. Bit 7 of CLKPR is the prescaler enable bit, CLKPCE (an alias for bit 7), so the statement:

CLKPR = (1<<CLKPCE);

enables the Clock Prescaler Change.

The Clock Prescaler Select Bits CLKPS0, CLKPS1, CLKPS2, and CLKPS3 are alias for the lower 4 bits of CLKPR and are used to select a clock division factor. Since registers are preset to 0, we OR CLKPS1 and CLKPS0 as 1 and get 0011 which provides a clock division factor of 8, this divides the 8 MHz oscillator by 8 giving a 1 MHz clock:

CLKPR = (1<<CLKPS1) | (1<<CLKPS0);

The Timer/Counter2 Interrupt Mask Register, TIMSK2, is set to 0 to disable the OCIE2A, output compare, and TOIE2, overflow enable, interrupts (p 141 data book if you want the gory details).

TIMSK2 = 0;

We must set the Asynchronous Timer/Counter2, AS2, bit 3 of the Asynchronous State Register, ASSR, to allow an external clock connected to the Timer Oscillator, TOSC1, pin 24 of the microcontroller to be used to for asynchronous operation of timer2 (32,768kHz):

ASSR = (1<<AS2);

The Output Compare Register A, OCR2A, contains a value, 200, that will be continuously compared with the Timer Count 2, TCNT2, register

OCR2A = 200;

The Timer/Counter0 Interrupt Mask Register, TIMSK0, is set to 0 to delete any interrupt sources (p 93 data book).

TIMSK0 = 0;

124

Chapter 7: Microcontroller Interrupts and Timers

The Timer/Counter1 Control Register B, TCCR1B and the Timer/Counter2 Control Register A, TCCR2A, are set to start timer1 and timer2 with no prescaling:

TCCR1B = (1<<CS10);

TCCR2A = (1<<CS20);

After setting all these registers we wait for the TCNT2 and the TCRU2B registers to be set from temporary memory, by waiting for the Timer/Counter2 Update Busy, TCN2UB, and the Timer/Counter2 Update Register Busy,TCCR2A, bits of the ASSR register to be cleared (p 139 data book).

while((ASSR & 0x01) | (ASSR & 0x04));

Wait a while for the crystal to stabilize:

Delay(1000);

ALL THIS AND WE HAVEN’T EVEN STARTED CALIBRATING YET!

Getting registers set properly to do much of anything in a microcontroller can be a long and frustrating exercise, and another good reason to steal code where possible.

We start calibrating by running a loop in which you make adjustments to the internal oscillator and comparing the results to the external clock, looping until you get it right.

Set a flag:

unsigned char calibrate = FALSE;

to terminate the loop when true:

while(!calibrate)

On each pass you do the following:

125

Chapter 7: Microcontroller Interrupts and Timers

Disable global interrupts:

cli();

Clear the timer interrupt flags:

TIFR1 = 0xFF;

TIFR2 = 0xFF;

Clear the timer counts:

TCNT1H = 0;

TCNT1L = 0;

TCNT2 = 0;

Wait for the timer to reach the count

while ( !(TIFR2 && (1<<OCF2A)) );

Stop the timer:

TCCR1B = 0;

Enable global interrupts

sei();

Has Timer/Counter1 overflowed? If so set our temp variable to 0xFFFF.

if ( (TIFR1 && (1<<TOV1)) )

{

temp = 0xFFFF;//if timer1 overflows, set the temp to 0xFFFF

}

Otherwise read the timer1 counter value into the temp variable

else

{

tempL = TCNT1L; temp = TCNT1H; temp = (temp << 8);

126

Chapter 7: Microcontroller Interrupts and Timers

temp += tempL;

}

Is temp greater than 6250? If so decrement the Oscillator Calibration Register, OSCCAL

if (temp > 6250)

{

OSCCAL--;//RC oscillator runs to fast, decrease OSCCAL

}

Otherwise, if temp is less than 6120, increment OSCCAL.

else if (temp < 6120)

{

OSCCAL++; //RC oscillator runs to slow, increase OSCCAL

}

If temp is between 6250 and 6120 the calibration is complete and we can go home.

else

calibrate = TRUE; // the interRC is correct

But before we turn out the light, we start the timer:

TCCR1B = (1<<CS10); // start timer1

Now if you are beginning to think all this is mighty confusing, you are finally beginning to understand the core truth of microcontroller programming. It IS mighty confusing. And frustrating and bug infested and time consuming and ego destroying and… well, you name it. But finally getting something working is the greatest pleasure known to mankind, (if you overlook sex, eating, parenting, and anything else you like to do). A word of advice: if you don’t actually get some pleasure from working out these puzzles you don’t need to be doing this for a career. Try professional knife fighting… you’ll survive longer. This complexity is primary reason to ‘reuse’ code. And speaking of stealing them naked, the following project uses code lifted directly from the WinAVR port of the ATMEL Butterfly code.

127