Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4071
Скачиваний: 1
Timers |
183 |
|||||||||||
TSR |
Bit 7 |
Bit 6 |
Bit 5 |
Bit 4 |
Bit 3 |
Bit 2 |
Bit 1 |
Bit 0 |
||||
0x13 |
ICF1 |
OCF1 |
TOV |
ICF2 |
OCF2 |
|||||||
OCF2 |
Bit 3 |
Output Compare Flag 2. This bit is set when the |
||||||||||
content of the free-running counter matches the |
||||||||||||
contents of output compare register 2. OCF2 is |
||||||||||||
cleared by accessing the TSR (specifically the |
||||||||||||
OCF2) followed by an access to the low byte of |
||||||||||||
the output compare register 2, 0x1f. The output |
||||||||||||
compare flag 2 is undetermined at power on and |
||||||||||||
is unaffected by reset. |
||||||||||||
ICF2 |
Bit 4 |
Input Capture Flag. This bit is set when a nega |
||||||||||
tive edge is sensed at TCAP2. It is cleared by an |
||||||||||||
access of the timer status register followed by an |
||||||||||||
access of the low byte of the input capture regis |
||||||||||||
ter, 0x1d. The input capture 2 flag is undetermined |
||||||||||||
at power on and is unaffected by reset. |
||||||||||||
TOF |
Bit 5 |
Timer Overflow Bit. This bit is set by a transition |
||||||||||
of the free-running counter from a 0xffff to a |
||||||||||||
0x0000. It is cleared by accessing the TSR with the |
||||||||||||
TOF set followed by an access of the free-running |
||||||||||||
counter low byte, 0x19. The TOF bit is undeter |
||||||||||||
mined at power on and is unaffected by reset. |
||||||||||||
OCF1 |
Bit 6 |
Output Compare Flag 1. This bit is set when the |
||||||||||
content of the free-running counter matches the |
||||||||||||
contents of output compare register 1. OCF2 is |
||||||||||||
cleared by accessing the TSR (specifically the |
||||||||||||
OCF1) followed by an access to the low byte of the |
||||||||||||
output compare register 1, 0x17. The output com |
||||||||||||
pare flag 1 is undetermined at power on and is |
||||||||||||
unaffected by reset. |
||||||||||||
ICF1 |
Bit 7 |
Input Capture Flag 1. This bit is set when the |
||||||||||
proper edge is sensed at TCAP1. The edge is se |
||||||||||||
lected by the IEDG1 bit in the TCR. It is cleared |
||||||||||||
by an access of the timer status register followed |
||||||||||||
by an access of the low byte of the input capture |
||||||||||||
register, 0x15. The input capture 1 flag is unde |
||||||||||||
termined at power on and is unaffected by reset. |
||||||||||||
184 Chapter 4 Small 8-Bit Systems
To clear bits in the TSR, the program must first access the TSR followed by an access of the LSB of the register associated with the bit that must be reset in the TSR. This sequence can lead to problems in dealing with the counter register. Suppose you are attempting to mea sure an elapsed time and are reading the counter register at random times and you also will read the TSR to service timer requirements. It is possible in these circumstances to accidentally reset the TOF bit when it is undesired. To avoid this problem, an alternate counter regis ter has been designed into the M68HC05 devices. The alternate register always contains the same values as the prime register, but the TOF bit in the TSR is not reset when the alternate register is read.
Counter Register
The counter register is found in the memory locations 0x18 and 0x19. The least significant byte of the counter is in 0x19. An alter nate counter register is found in addresses 0x1a and 0x1b with 0x1b being the least significant byte of this register. These registers are clocked at the same time and are incremented from low values to higher values. The counters are clocked at one-fourth of the internal processor clock, which in turn is one-half the oscillator frequency. The clocking frequency is one-eighth the crystal frequency, and the clocking period is 2 microseconds when the crystal frequency is 4 MHz. These ratios are not adjustable in the M68HC05B6.
The free-running counter values can be read at any time. A read sequence that reads only the least significant byte will receive the count value at the time of the read. If the most significant byte of either counter is read, the count value will be received and the con tents of the least significant byte will be transferred to a buffer. This value will remain in the buffer until the program reads the contents of the least significant byte of the register. The value received for this read is the buffered value saved when the most significant byte was read. The most significant byte, MSB, can be read several times prior to reading the least significant byte, LSB, and the contents of the buffer will remain unchanged. After the MSB has been read and the LSB has been buffered, the free-running counter continues to be incremented at its normal rate. If the MSB/LSB read sequence is started, it is necessary to read the LSB to complete the sequence.
The counter is 16 bits, and when the register overflows from
Timers 185
0xffff to 0x0000, the timer overflow (TOF) bit is set. This event can cause an interrupt if the TOIE bit is set. Since the register is clocked at 2 microseconds, the interval between TOF is 0.131072 seconds.
Input Capture Registers
There are two input capture registers called ICR1 and ICR2. ICR1 is found at addresses 0x14 and 0x15, and ICR2 is located at addresses 0x1c and 0x1d. The lower address always contains the MSB of a 16-bit number. With the exception of the edge detection system discussed in the TCR section, these two registers operate the same. ICR1 can be set to respond to either a rising edge or a falling edge on the timer compare input pin TCAP1. If IEDG1 is 0, ICR1 re sponds to a falling edge on TCAP1. Otherwise, if IEDG1 is 1, ICR1 responds to a rising edge on TCAP1. ICR2 responds only to a fall ing edge on TCAP2. An interrupt will also accompany an input capture if the corresponding ICIE bit is set in the TCR.
The contents of the free-running counter are transferred to the input capture registers each clock cycle. Therefore, the registers con tain a value that corresponds to the most recent input capture. After a read of the most significant byte of the input capture register, the transfer of new data to the least significant byte of the input capture register is inhibited until this byte is read. At no time during this sequence is the counter register inhibited.
Output Compare Registers
There are two output compare registers. OCR1 is found at ad dress locations 0x16 and 0x17 while OCR2 is located at 0x1e and 0x1f. Again the lower addresses contain the MSB of these 16-bit numbers. These registers may be read or written at any time regard less of the timer hardware. If the output compare functions are not utilized, these four bytes can be used for data storage. Their contents are not altered at reset. There is only one output compare interrupt bit that is used for both output compares in the system.
The contents of the output compare registers are compared with the contents of the counter register each cycle of the counter register. If a match is found with either output compare register, the corre sponding output compare flag—OCF1 or OCF2— bit is set. Also, the value of proper output level bit—OLVL1 or OLVL2—is trans
186 Chapter 4 Small 8-Bit Systems
ferred to the proper output pin, TCMP1 or TCMP2. If the OCIE bit is set in the timer control register, an interrupt will accompany the out put compare.
There are times when it is desirable to force an output compare from a program. The FOLV1 and FOLV2 bits can be used for this purpose. These bits will always read 0, but writing a 1 to these bits in the TCR will cause transfer of the corresponding OLVL1 or OLVL2 bit to the specified output compare bit, either TCMP1 or TCMP2. This output does not affect the compare flags, so no interrupt is generated.
Programming the 16-bit Timer
We will examine several different uses of the 16-bit timer system in this section. The first is merely a repeat of the simple timer pro grammed in the section on the 15-bit system. Here we merely want to keep track of time, hours, minutes, and seconds in memory. No provisions are made yet for reading the time values or to change the values; these problems will be discussed later.
A listing of this program is shown below. In this case, the header file for the M68HC05B6 is used. A listing of this file is found on the CD-ROM. This program will make use of an output compare to gen erate periodic interrupts to the microcontroller. We will use output compare register 1. It will be set up so that when the first output compare interrupt occurs, the contents of the output compare regis ter will be incremented by 500. Since the clocking time of the counter register is 2 microseconds, 500 2-microsecond periods will allow an output compare every 1 millisecond. This occurrence will be treated in the interrupt service routine.
#include “hc05b6.h”
int hrs, mts, sec; /* global variables */ long count=1000;
struct bothbytes /* 16 bit int structure */
{
int hi; int lo;
};
Timers 187
union both /* and union */
{
long l;
struct bothbytes b;
};
union both time_count; registera ac;
main()
{
TCR.OCIE=1; /* enable output compare interrupt */
CLI(); /* enable all interrupts */
FOREVER
{
if(sec>59) /* do clock things each minute */
{
sec=0;
if(++mts>59)
{
mts=0;
if(++hrs>12)
hrs=1;
}
}
WAIT();
}
}
void __TIMER_OC(void) /* time interrupt service routine */
{
if(TSR.OCF2==1) /* is this interrupt due to OC2?*/
{ |
||
ac=OCLO2; |
/* Yes. read |
OCLO2 to disable */ |
return; |
/* the interrupt and exit */ |
|
} |
/* the routine |
*/ |
188 Chapter 4 Small 8-Bit Systems
/* the program gets here every millisecond */ time_count.b.hi = OCHI1;
ac = TSR; /* Arm OCF1 bit clear */ time_count.b.lo = OCLO1; /* Clear OCF1 bit */ time_count.l += 500; /* 500 counts per ms */ OCHI1 = time_count.b.hi;
OCLO1 = time_count.b.lo;
if(--count==0) return ;
else
{
sec++; /* here every second */ count=1000;/* reset count to 1 second */
}
}
Listing 4-4: Timer Using Output Compares
The listing shows that microcontroller executing programs are broken into three distinct sections. The first section is referred to as the initialization section. In this case, the initialization section is the first two lines following the main() invocation. In the initialization section, the code executed sets up the operation of the microcontroller. Generally, this code is executed only once. Therefore, initialization of volatile memory values, setting up of interrupts, establishment of I/O ports and data direction registers are all completed in the initial ization of the program. Unless there is a pressing reason, the main system interrupts should not be enabled during initialization.
The second section is the applications section. The applications section is usually a loop that contains all of the code to be handled routinely by the microcontroller. All input or output operations should take place within the applications section.
The third section of the program is the collection of interrupt service routines (asynchronous service section). These routines are called when appropriate interrupts are generated. In general, interrupt service rou tines should be short and do as little as possible to service the specified interrupt. When an interrupt is serviced, the status register of the microcontroller is saved and the system interrupt is disabled. Therefore, unless the programmer takes special care to re-enable the interrupts, no
Timers 189
other interrupt can be serviced while the microcontroller is executing an interrupt service routine. This operation does not lead to missed inter rupts, but it can cause an inordinate delay in service of an interrupt.
In some cases, it is possible that data might be lost if an interrupt is not handled expeditiously. For example, a high-speed serial port might notify the microcontroller that its receiving data buffer is full by an interrupt when the interrupt is disabled. In such a case, if the interrupt service routine being executed is not completed before the next serial data are received, the data in the buffer will be lost.
Sometimes it is necessary to pass data between the applications portion of the program and the interrupt service routine. These data can be stored in global memory, and both routines can access them. Here is a case where you must examine the assembly code generated by the compiler to make certain that no problems will be generated in passing of data between these routines. Problems can be created by passing information this way, but they can be avoided if the pro gram rigorously avoids loading data that is changed in an interrupt service routine into a register in the application section of the pro gram. We will see instances of this problem later.
If you go back to Listing 4-4, you’ll see that the program organi zation discussed above is also used. This organization will be found for every program in this book. We will refer to the initialization, the applications, and the asynchronous service sections of the program. This arrangement works quite well, and is reliable. There should be a strong justification if alternate program forms are to be used.
When programming the 15-bit timer, we found that asking a com piler designed specifically for an 8-bit machine to work in 16-bit quantities often created unwieldy code. A programmer, however, does not always have the freedom to work with 8-bit quantities only. All of the time registers in this system are 16 bits wide, and the time values contained in these registers must be processed. These registers are al ways located in two adjacent 8-bit memory locations. One method for handling the 8/16-bit dichotomy is to use a union. The code sequence
struct bothbytes
{
int hi; int lo;
};
190 Chapter 4 Small 8-Bit Systems
creates a structure that contains two bytes. This structure is com bined with a type long in the union below. Remember that a union provides space to hold its largest member and the different members of the union will occupy the same memory space. Therefore, the memory space to store the long l is the exact same memory to store the structure b. b.hi will occupy the most significant byte of l, and b.lo will occupy the least significant byte of l. It is now easy to deal with the bytes of the 16-bit quantity when moving the data around, and equally easy to invoke 16-bit arithmetic operations on the long combination of the two bytes.
union both
{
long l;
struct bothbytes b;
};
The statement
union both time_count;
declares that time_count is a union of the type both. In the in terrupt service routine, the members of time_count are handled with little difficulty, as shown below.
The type registera defined as ac above is unique to the M68HC05 compiler. This type specifies that the variable ac will be stored in the accumulator. There is also a registerx type for the index register.
In the main program, the output compare interrupt is enabled and the system interrupt is enabled with the CLI() instruction in the initialization section. The program then enters an endless loop in which the clock is serviced every time sec becomes 60. The param eter sec will be changed by the interrupt service routine every second so that the system should be a satisfactory clock. This loop is the initialization section.
The last instruction in the FOREVER loop is a WAIT() instruc tion. This instruction places the processor into the wait mode. In this mode, processor operations are halted and the microcontroller op eration is configured to reduce energy consumption. The operation of the internal timers proceed as usual. The part is removed from the wait mode by either a reset or the occurrence of an interrupt. The
Timers 191
interrupt can be either an internal or an external interrupt. Since the output compare timer is set up and its interrupt is enabled, when the internal counter matches the contents of the output compare register, an interrupt will occur and remove the processor from the wait mode.
Sometimes, it is desirable to get a measure of the fraction of the time that the microcontroller is used to execute its program. The use of the wait mode provides an excellent mechanism for measurement of this usage. If there is an extra output port pin available, this pin can be set just prior to entering the wait mode. The pin can then be reset as the first instruction in the interrupt service routine. To make this measurement more accurate, you can set another output pin to the on condition all of the time. Measure these two outputs with an averaging DC voltmeter. One hundred times the ratio of the cycling output to the fixed output is the percentage of time that the microcontroller is available to execute other code.
Examine the following code sequence:
time_count.b.hi = OCHI1;
ac = TSR; /* Arm OCF1 bit clear*/ time_count.b.lo = OCLO1; /* Clear OCF1 bit */
time_count.l += 500; /* 500 counts per millisecond */
OCHI1 = time_count.b.hi; OCLO1 = time_count.b.lo;
The first instruction copies the high byte of the output compare register into the high byte of the structure b in the union time_count. When the TSR is copied into the a register, the system is set up to clear the OCF1 bit. Then, the low byte of the output compare register is moved into the low byte of the structure b. These operations leave the 16-bit contents of the output compare register in the union time_count.l. Note that the high byte is moved from OCHI1, the TSR is accessed, and then the low byte is moved from OCLO1. This sequence, accessing OCLO1 after the TSR has been accessed will clear the output compare flag 1, OCF1, and remove the interrupt source. 500 is added to this 16-bit number, and the result is copied back into the output compare register 1 a byte at a time.
While it is usually best to keep the interrupt service routines short, sometimes other operations can be completed within these routines that can be useful. Recall that in the EEPROM programming routine