Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 4060

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

166 Chapter 4 Small 8-Bit Systems

compile individually and examine if they create outlandish code. Once these small functions are all debugged, you can integrate them into your program as either inline code or as function calls. Hence, the fundamental rule of writing good high-level code for a microcontroller: the production of good C code for a microcontroller is a joint effort between the programmer and the compiler writer.

Erasure of EEPROM causes component wear, and most EEPROMs will wear out after a large number of erasures. The nature of the deg­ radation is that the component refuses to erase after many erasures. There has been no evidence that data retention is affected by repeated erasures. The number of erasures that can cause problems is tempera­ ture sensitive. Most of these devices are rated for 10,000 write/erase cycles at the maximum rated temperature for the part. At room tem­ perature, the number of write/erase cycles without damage can grow to several hundred thousand. In light of these facts, it is important that programs use care in rewriting the contents of EEPROM.

EXERCISES

1.The EEPROM in microcontrollers erase to the 1 state. Write a function that checks to determine if an erasure cycle erases the contents of a given location in memory.

2.Write a function that compares data to be programmed into EEPROM and determine if it is necessary to erase a byte contain­ ing data before it is reprogrammed. This approach will extend the life of the EEPROM.

Timers

The systems of timers placed on microcontrollers are among the most creative engineering efforts most people will ever see. The func­ tions of these timers cover literally dozens of different operations. This set of peripheral components mainly relieve the computer of much work associated with execution of the peripheral function. We will start with the simplest timer and outline different timer capabili­ ties in increasing complexity. The most basic timer in the M68HC05 family is called the 15-bit timer, and probably the most advanced timer system is the 16-bit timer. These different timer systems are literally unrelated in the features they offer. Another timer feature

Timers 167

offered in some microcontrollers is the computer operating prop­ erly (COP) timer. Each of the systems will be examined in some detail in the following paragraphs.

Multifunction Timer—15-Bit Timer

A timer of this nature can be found on the smallest of the M68HC05 components, such as the M68HC05J1 or the M68HC05P8, and it is not found on the M68HC05Bx devices discussed previously. This timer consists of an 8-bit ripple counter followed by an addi­ tional 7-bit counter. This counter chain is driven by a signal that is at one-fourth the internal clock frequency of the microcontroller. The internal clock frequency in turn is half the crystal frequency. This portion of the counter is completely uncontrolled. The program can, however, read the value of this counter at any time. A block diagram of this type of timer is shown in Figure 4-1.

Internal

Processor

Clock (XTAL ÷2)

Least Significant Eight Bits of 15 Stage Ripple Counter

÷2

÷2

÷2

÷2

÷2

÷2

÷2

÷2

Fixed

Divide By

4

MSB

LSB

Timer Count Register

$0009

TCR

TOF

RTIF

TOFE

RTIE

0

0

RT1

RT0

$0008

TCSR

RTI Rate Select

÷2

÷2

÷2

÷2

÷2

÷2

÷2

Most Significant Seven Bits of 15 Stage Ripple Counter

÷2

÷2

÷2

S

COP Timeout-Generate

Q

Internal MCU Reset

Service (Clear)

R

COP Watchdog

Figure 4-1 15-bit Timer Block Diagram


168 Chapter 4 Small 8-Bit Systems

STATUS REG

Bit 7

Bit 6

Bit 5

Bit 4

Bit 3

Bit 2

Bit 1

Bit 0

0x08

TOF

RTIF

TOFE

RTIE

0

0

RT1

RT0

RT0

Bit 0

Real Time Interrupt Select Rates. These bits con­

trol the rate at which the real-time interrupt and

the COP reset time will occur. Note that these two

RT1

Bit 1

bits are delivered to the RTI Rate Select register in

the diagram. Table 4-3 shows the various RTI and

COP rates that can be obtained for different values

of RTI and RT0. Reset sets both bits so that the

periodic rates will be the slowest possible when

the part comes out of reset. It is expected that these

bits will not be changed If these bits are altered,

the first cycle following the change will be wrong.

RTIE

Bit 4

Real Time Interrupt Enable. When this bit is set, a

CPU interrupt request is generated whenever the

RTIF is set.

TOFE

Bit 5

Timer Overflow Enable. When this bit is set, a

CPU interrupt request is generated whenever the

TOF is set.

RTIF

Bit 6

Real Time Interrupt Flag. This bit is set whenever the

output from the selected divider stages is set. If the

RTIEis also set, setting of this bit will request a CPU

interrupt. This bit is cleared by reset or by writing a

zero to it. It is not possible to write a 1 to this bit.

TOF

Bit 7

Timer Overflow Flag. This bit is set whenever the

8-bit ripple counter overflows from a 0xff to a 0x00.

This bit is cleared by reset or by writing a zero to

it. It is not possible to write a 1 to this bit. The

timing that results from selection of values for RT1

and RT0 are shown in Table 4-1. This table also

shows the real time interrupt rate for different val­

ues of the real time interrupt select rate bits.

The timer counter register at address 0x09 contains the value found in the 8-bit ripple counter. The contents of this counter can be read at any time, but it cannot be written to. When the part comes out of reset, the timer counter register contains a zero. 4096 clock cycles will follow, during which the TCR will count at the minimum rate.


Timers 169

At the close of this period, the initialization of the part is complete, and the TCR is again reset to zero prior to execution of the code identified by the reset vector. Whenever the RESET line is asserted, the TOF will be loaded with zeros.

Any program can read the TCR, so it is possible to generate asyn­ chronous time events faster than the TOF or the RTIF would indicate.

When the processor enters the WAIT mode after execution of a WAIT instruction, the CPU clock halts, but the timer clock continues to execute. If the interrupts are not masked, a timer interrupt, an exter­ nal interrupt or a reset will cause the device to exit the WAIT mode.

Table 4-3: RTI And COP Rates for Fxtal = 4.0 MHz

RT1

RT0

RTI Rate

Minimum

COP Reset

0

0

8.2 ms

57.3 ms

0

1

16.4 ms

114.7 ms

1

0

32.8 ms

229.4 ms

1

1

65.5 ms

458.8 ms

If a STOP instruction is executed, the timer clock is halted along with the CPU clock. The STOP mode is exited when an external interrupt occurs or the RESET line is asserted. In this case, the part performs as described above.

Most microcontrollers are placed in operation with no operator to intervene in the event of a problem. A COP timer will provide one means of recovering if the operation of the microcontroller gets lost. “Gets lost”? The situation that can cause a microcontroller to get lost is usually some type of voltage spike or glitch in the power supply operation. The program counter usually ends up with a value outside of the program, and no one knows what will happen. The COP is sim­ ply a timer that counts for a specified amount of time. If the COP timer has not been reset before the specified time elapse, the COP timer overflow causes an internal reset of the microcontroller. If the cause of the problem is a drop in power or other error, in most instances forcing a reset will bring the microcontroller back into normal operation.

The COP control register is located at address 0x7f0 in the M68HC05J1. To service the COP from the program, the program must merely write a zero to bit 0 of this address to reset the COP portion of the timer system.

170 Chapter 4 Small 8-Bit Systems

Good programming practice dictates that microcontroller-specific information be placed in a header file like that shown here:

#pragma portrw PORTA @ 0x00; #pragma portrw PORTB @ 0x01; #pragma portrw DDRA @ 0x04; #pragma portrw DDRB @ 0x05; #pragma portrw TCST @ 0x08; #pragma portrw TCR @ 0x09;

#pragma portrw __COPSVS @ 0x7f0; #pragma vector __TIMER @ 0x07f8; #pragma vector __IRQ @ 0x07fa; #pragma vector __SWI @ 0x07fc ; #pragma vector __RESET @ 0x07fe; #pragma has STOP ;

#pragma has WAIT ; #pragma has MUL ;

#pragma memory RAMPAGE0 [64] @ 0xc0; #pragma memory ROMPROG [1024] @ 0x300;

#define RT0 0 /* TSCR Bits */ #define RT1 1

#define RTIE 4 #define TOFE 5 #define RTIF 6 #define TOF 7

Listing 4-3: Header File For The M68HC05J1

The #pragma and several important #define commands are microcontroller specific. Therefore, to change the program from one microcontroller to another, the programmer need only change the microcontroller header file. Listing 4-3 is a header file for the M68HC05JJ1 controller. The first six entries identify the locations of the I/O ports, the data direction registers, timer status/control reg­ ister, and the timer counter Register. The next five entries specify the COP service address and the vector locations for this part. Note that the names associated with the vector locations all start with a double


Timers 171

underscore. These names are also listed in all upper-case letters. These entries are the names of the various interrupt service routines. Since C is case sensitive, the names of the functions to be used as interrupt service routines must have the same form.

The three #pragma entries identified as has notifies the com­ piler that the microcontroller has the STOP, WAIT, and MUL instructions. The next pair of entries defines the memory map for this microcontroller. Finally, the next six entries are #defines that identify the bits in the TCSR. Therefore, mnemonic representations of all registers and bits can be used in the C program.

Several header files for the M68HC05 family are found on the CD-ROM. The conventions in these files are to use bit names and register names that are identical to those used in the technical data books that describe the devices. Therefore, the programmer can safely use register names and bit names found in the books without having to look up the values in the header files. These files include commands to prevent listing of these files in the compiler listing output files.

Listed below is a simple program that shows the use of the 15-bit timer in the M68HC05J1. This program is not aimed at doing more than showing the use of the timer operation. The system will create an inaccurate clock in which the time in hours, minutes, and seconds will be recorded in memory, but no provision to display these values or even set the values will be considered at this time.

Most clocking operations should be interrupt driven. If a periodic interrupt can be generated, the operation of the clock will be transpar­ ent to any other operations being conducted in the microcontroller.

#include “hc05j1.h” enum {FALSE,TRUE); enum {OFF,ON};

#define FOREVER while(TRUE) #define MAX_SECONDS 59

#define MAX_MINUTES MAX_SECONDS #define MAX_HOURS 12

#define MAX_COUNT 121

/* define the global variables */ int hrs,mts,sec;

172Chapter 4 Small 8-Bit Systems int count;

main(void)

{

count=0; /* start count at zero */ TCST.RT0=OFF; /* 57.3 ms cop timer */ TCST.RT1=OFF; /* 8.192 ms RTI */ TCST.RTIE=ON; /* Turn on the RTI */ TCST.RTIF=OFF; /* Reset interrupt */ TCST.TOF=ON; /* flags */

CLI(); /* turn on interrupt */

FOREVER

{

if(sec>MAX_SECONDS) /* do clock things */

{

sec=0; if(++mts>MAX_MINUTES)

{

mts=0; if(++hrs>MAX_HOURS) hrs=1;

}

}

/* here is where any applications program should be placed. */

}

}

void __TIMER(void) /* routine executed every RTI (8.192 ms) */

{

TCST.RTIF=OFF; /* reset interrupt flag */ if (++count>MAX_COUNT)

{

sec++; /* increment seconds */

count=0;/* reset the count each second */

}

}

Listing 4-4: A Time-of-Day Program Based On The 15-bit Timer.

Timers 173

A few words about a good programming practice: numbers in a program with no defined meaning are called “magic numbers.” You should avoid magic numbers, because a number with no meaning makes life difficult for the program maintenance people. In the pro­ gram above, several numbers are needed. These numbers are given a name by either enum statements or #define statements. Then, in the program, you can see every instance of the use of the number does have a meaning relative to the program. Another advantage to avoiding magic numbers is not too evident in the above program, but it is truly an important advantage. If the program is long and com­ plicated, these numbers might be used many times. Then if a maintenance situation requires the change of the value of a number in the program, it can be changed in one place and a recompilation will correct every instance of the number in the program.

Several global variables are used in this program: hrs, mts, sec, and count. These variables are all changed in the main pro­ gram, but they are available in any other part of the program if needed. For example, the count variable is initialized to zero in the main program and incremented and reset in the interrupt service routine. One point should be noted in this program: the main program has all of the time calculations based on the current contents of sec. The variable sec is incremented each second in the interrupt service rou­ tine. Some programmers would put the complete time service within the interrupt service routine. That is, they would reset sec when it reaches 60, increment mts, and so forth within the interrupt service routine. Either approach will provide the same result, and each takes the same total computer time. It is, however, better to keep the time that the program is controlled by the interrupt service routine at a minimum. Interrupts are disabled when a program is in an interrupt service routine. If there are several competing interrupts, execution of an interrupt service routine prevents other interrupts from being processed. Quickest response to all interrupts will be obtained if all of the interrupt service routines are as short as possible.

Program Organization

A compiled version of this program is listed below. Note that the compiler listing routine prints out the contents of the include file. The memory map #pragmas puts the RAM in page 0 beginning at


174 Chapter 4 Small 8-Bit Systems

0xc0 and the program memory starts at 0x300. Note that the com­ piler places the global variables in 0xc0 through 0xc3, and the executable program begins at 0x300 as one would expect.

The first several instructions clear the count location and set or reset proper bits in the TSCR. The instruction CLI clears the inter­ rupt bit in the status register of the microcontroller. When this bit is cleared, interrupts will detected and processed.

The beginning of the loop defined by the macro command FOR­ EVER is at address 0x30d. This macro causes no code at the beginning of the loop. At the end of the loop, address 0x32b, there is an instruc­ tion BRA 0x30d that causes control of the program to start at the beginning of the loop. That is the total code created by the macro FOREVER. Within this loop, the code created by the compiler is straightforward and not very different from code that would be cre­ ated by a competent assembly language programmer.

#include “hc05j1.h”

0000 #pragma portrw PORTA @ 0x00;

0001 #pragma portrw PORTB @ 0x01;

0003 #pragma portr PORTD @ 0x03;

0004 #pragma portrw DDRA @ 0x04;

0005 #pragma portrw DDRB @ 0x05;

0008 #pragma portrw TCST @ 0x08;

0009 #pragma portrw TCNT @ 0x09;

07F0 #pragma portrw __COPSVS @ 0x7f0;

07F8 #pragma vector __TIMER @ 0x07f8; 07FA #pragma vector __IRQ @ 0x07fa; 07FC #pragma vector __SWI @ 0x07fc ; 07FE #pragma vector __RESET @ 0x07fe;

#pragma has STOP ; #pragma has WAIT ; #pragma has MUL ;

00C0 0040 #pragma memory RAMPAGE0 [64] @ 0xc0;

0300 0400 #pragma memory ROMPROG [1024] @ 0x300;

Timers 175

0000 #define RT0 0

0001 #define RT1 1

0004 #define RTIE 4

0005 #define TOFE 5

0006 #define RTIF 6

0007 #define TOF 7

0001

#define TRUE 1

0000

#define FALSE

0

0001

#define FOREVER while(TRUE)

00C0

00C1 00C2

int hrs,mts,sec;

00C3

int count;

main(void)

{

0300

3F

C3

CLR $C3

count=0;

0302

11

08

BCLR 0,$08 TCST.RT0=0;

0304

13

08

BCLR 1,$08 TCST.RT1=0;

0306

18

08

BSET 4,$08 TCST.RTIE=1;

0308

1D

08

BCLR 6,$08 TCST.RTIF=0;

030A

1F

08

BCLR 7,$08 TCST.TOF=0;

030C

9A

CLI CLI();

FOREVER

{

030D

B6

C2

LDA $C2

if(sec==60)

030F

A1

3C

CMP #$3C

0311

25

18

BCS $032B

{

0313

3F

C2

CLR $C2

sec=0;

0315

3C

C1

INC $C1

if(++mts==60)

0317

B6

C1

LDA $C1

0319

A1

3C

CMP #$3C

031B

26

0E

BNE $032B

{

031D

3F

C1

CLR $C1

mts=0;

031F

3C

C0

INC $C0

if(++hrs==13)

0321

B6

C0

LDA $C0

0323

A1

0D

CMP #$0D