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

Chapter 8: C Pointers and Arrays

Does anybody know what time it is? A Real Time Clock.

Let’s combine our knowledge of interrupts with what we learned in the messenger project to make a simple real time clock. This was derived from the more capable clock in the Butterfly software.

A one second interrupt

We saw how to use the 32.768kHz watch crystal to calibrate the cpu clock oscillator in the chapter on timers and interrupts. While that calibration makes the oscillator accurate enough for communicating with the PC via the USART, it isn’t accurate enough to keep real time like a watch. We will use Timer/counter2 with the watch crystal as an input so that when the count reaches 32768, we will know that one-second has passed and can throw an interrupt allowing us to do something at exact one second intervals. Notice that 32768 is 0x8000 in hexadecimal and 1000000000000000 in binary, this is no accident since the crystals were designed to allow digital systems to keep real time (well, ‘real’ to humans anyway). The low speed (kilohertz verus mega or giga hertz) and precision timing allowed watches more accurate than expensive mechanical chronometers to be manufactured so cheaply that its not unusual to find one as a prize in a box of cereal, though they aren’t milk proof and are a bit too crunchy.

We start the software by using a delay loop to wait for the external crystal to stabilize.

for(int i = 0; i < 10; i++) _delay_loop_2(30000);

Disable global interrupts.

cli();

Clear the Timer/Counter 2 Output Interrupt Enable, TOIE2, bit in the Timer/Counter 2 Interrupt Mask Register, TIMSK2, to disable the timer output interrupt enable.

cbi(TIMSK2, TOIE2);

178

Chapter 8: C Pointers and Arrays

Select Timer2 asynchronous operation AS2: Asynchronous Timer/Counter2, AS2 bit in the Asynchronous Status Register, ASSR.

ASSR = (1<<AS2);

Clear the Timer/Counter 2, TCNT2, count register.

TCNT2 = 0;

Select the divide by 128 prescale factor in the Timer Counter Control Register, TCCR2A. The watch crystal pulses 32768 times in one second and 128*256 = 32768, so with a 128 prescaler, the timer counts to 256 and overflows once per second.

TCCR2A |= (1<<CS22) | (1<<CS20);

Wait for the TCN2UB: Timer/Counter2 Update Busy and the TCR2UB: Timer/Counter Control Register2 Update Busy bits of the, ASSR, to be cleared.

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

Clear the Timer/Counter2 Interrupt Flags Register, TIFR2.

TIFR2 = 0xFF;

Set the Timer/Counter 2 Output Interrupt Enable, TOIE2, bit in the Timer/Counter 2 Interrupt Mask Register, TIMSK2, to enable the timer output interrupt enable.

sbi(TIMSK2, TOIE2);

And finally, enable the global interrupts.

sei();

Now the SIGNAL(SIG_OVERFLOW2) function will be called once per second (as shown in the code section) so we can use it to keep track of seconds.

Converting Computer Time to Human Readable Time

We can keep a count of seconds, but what good does it do us if our watch reads 40241? If the count started at midnight then this number of seconds would

179

Chapter 8: C Pointers and Arrays

indicate that the time is ten minutes and 41 seconds after eleven in the morning. And that conversion is easy compared to the numbers you get if you set your watch at thirty-three seconds after three twenty in the afternoon on May 11th of 2005 and you are reading the number two years seventy-eight days six hours fourteen minutes and seven seconds later. So we are going to need to do some computing to convert the count to something we can read.

We’ll briefly explore one way to convert time from byte sized data to ASCII text strings that we can understand.

BCD - Binary Coded Decimal

Binay Coded Decimal is a coding trick that eases the storage and conversion of binary numbers, say a count of the crystal beats, to decimal numbers, like you’d want to display on a watch LCD. We can divide an 8-bit byte into two 4-bit nibbles each of which can represent a number with a range of 0 to 16. That’s one of the reasons for the use of hexadecimal notation discussed earlier. And it allows us to store as single decimal integers, 0 to 9, in a nibble and two in a byte, one integer in each nibble.

If a the decimal number in a byte is less than 99, we can convert it to a BCD byte using the following algorithm:

Set the initial byte (in C we use char) to some two digit value.

char initialbyte = 54;

Declare a variable for the upper nibble value.

char high = 0;

Count the tens in initialbyte.

while (initialbyte >= 10)

{

high++; initialbyte -= 10;

}

180

Chapter 8: C Pointers and Arrays

After this runs the initialbyte now contains only the ones integer from the original byte and high char contains the tens, that is: high = 5 and intialbyte = 4. We combine the high and low nibbles to get the converted byte.

convertedbyte = (high << 4) | initialbyte;

This algorithm is used in the CHAR2BCD2 function in the software.

Converting a byte to the ASCII equivalent decimal character using BCD.

We define two bytes Tens and Ones and a third byte, Number, which we set to a value in the range of 0 to 99.

char Tens = 0; char Ones = 0; char Number = 54;

We use the character to BCD algorithm written as the function CHAR2BCD2(char) in the software section below to convert the Number to the BCD equivalent in Tens.

Tens = CHAR2BCD2(Number);

Now Tens has the BCD of the tens in the upper nibble and of the ones in the lower nibble. We can convert this to an ASCII character for the integer by remembering that the numerical value of ASCII ‘0’ is 48 and each following char integer is a simple increment of the previous one. Meaning that adding the number 4 to the value of the ASCII character ‘0’, which is 48, yields the ASCII character ‘4’, (48+4 = 52 which is the ASCII decimal value of the character ‘4’). So the conversion of a decimal integer to its ASCII equivalent character is the simple addition of 48 to the decimal integer.

Since the CHAR2BCD2 function loaded both the tens and ones parts of Number into Tens, we need to extract the Ones and the Tens so that we can add 48 to get the ASCII characters for Number.

Ones = Tens;

Ones = (Ones & 0x0F) + '0';

181