Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4066
Скачиваний: 1
150 Chapter 4 Small 8-Bit Systems
16-bit machine and its peripheral components are nearly all different from those found on the 8-bit machines. The M68HC16 is a superset of the M68HC11; it will execute M68HC11 code, but the hardware computer extensions and new peripheral components are significant.
To successfully program a microcontroller using a high-level lan guage, the programmer must be able to access various control and status registers in the computer. The program must force the language to place both program and data memory addresses in the proper locations in the memory map. Vectors associated with interrupt service routines, and the service routines themselves, must be handled directly by the program. These tasks are difficult to accomplish with most high-level languages, but C allows access to these things without extensions. However, most C compilers for microcomputers have extensions that allow such special features to be easily treated.
The compiler used in this chapter is called C68051. It was written to support the M68HC05 family of devices. Be forewarned: some M68HC05 microcontroller instructions have no counterpart in the standard C lan guage. Special directives identify unique microcontroller characteristics to the compiler. Listed in Table 4-1 below are nine assembly instructions available to the 68HC05. These instructions have no equivalent C call. They can be accessed as either a single instruction (all uppercase) or as a function call as shown. The function call requires a pair of closed parentheses to follow the name of the instruction.
Function |
Operation |
CLC or CLC() |
clear carry bit |
SEC or SEC() |
set carry bit |
CLI or CLI() |
clear interrupt flag (interrupts on) |
SEI or SEI() |
set interrupt flag (interrupts off) |
NOP or NOP() |
no operation |
RSP or RSP() |
reset stack pointer |
STOP or STOP() |
STOP instruction |
SWI or SWI() |
software interrupt |
WAIT or WAIT() |
WAIT instruction |
Table 4-1: Assembly Codes Directly Callable By C6805
1 Byte Craft Limited, 421 King Street North, Waterloo, Ontario, Canada N2J 4E4
Small 8-Bit Systems 151
A pragma is a C preprocessor command not defined by the lan guage. As such, the compiler writer can use the #pragma command to satisfy a need not specifically identified by the language. C6805 uses pragmas to identify microcontroller-specific characteristics. Table 4-2 contains a list of pragmas used by C6805. The format of a pragma directive here is
#pragma portxx portname @ address
where portxx can be portr, portw, or portrw which shows whether the port is read, write, or both. portname is the name used in the program for the port. The at symbol (@) identifies a memory address. #pragma mor identifies the contents of the masked option register used on field programmable chips. There are some instruc tions that are not found across the whole M68HC05 family. In particular, some devices may not have the MUL, the DIV, the STOP, and the WAIT instruction. The #pragma has a preprocessor call that identifies the instructions from this set in the particular microcontroller.
pragma |
Function |
#pragma portxy |
I/O port definition |
#pragma memory |
RAM/ROM definition |
#pragma mor |
mask option register |
#pragma has |
instruction set options |
#pragma options |
compiler directives |
#pragma vector |
interrupt vector definitions |
Table 4-2: C6805 pragma Directives
This compiler has certain options that can be inserted from the command line or, if needed by the programmer, the #pragma op tions preprocessor command can also be used to set the appropriate compiler options. Finally, the #pragma vector identifies a given function name as an interrupt service routine. When the compiler com piles the name specified, it will place the address of the function into the defined vector location. Another modification in the compiled code will take place when #pragma vector is used. All returns from a function identified by a vector pragma will use the return from in terrupt instruction rather than the usual return from subroutine.
152 Chapter 4 Small 8-Bit Systems
Another useful directive pair is the #asm and the #endasm. The code enclosed in a block that starts with #asm and ends with #endasm must be in standard assembly language. Variables defined in the C program can be used safely.
C can accomplish almost everything that the assembly lan guage program can. You will find that the C6805 compiler will create tight, efficient code that is probably as good as can be written by a competent assembly programmer. There are, however, some items that are absolutely foreign and inaccessible to a compiler. A compiler cre ates code for an abstract machine that does not exist in reality. The usual registers found in the real machine are nonexistent in the abstract machine. For example, it is not possible to access the status register of the microcontroller with compiled code. Usually, status register con tents are not directly important to the conduct of the program. But later we’ll see an example where the ability to manipulate the carry bit of the status register can save many bytes of code. Therefore, it is important to be able to use some assembly code as well as C.
This chapter will concentrate on small 8-bit microcontrollers. Subsystems such as timers, analog-to-digital converters, computer operating properly (COP) timers, etc., found on the 8-bit systems will be outlined and their programming discussed. While the main details of the central processor in the microcontroller are important to the assembly language programmer, they are of little interest to the C programmer. This observation is true at least at the C level. If it becomes necessary to enter an assembly language program for opti mization of code size or other considerations, then the programmer is required to have detailed knowledge of the programming model and the internal architecture of the computer.
Let’s start by discussing important microcontroller peripheral components that you can expect to find. We’ll begin with what is probably the most important single consideration in the selection of a microcontroller to do a job—the device memory. This discussion will be followed by sections on other important peripherals such as timers, analog-to-digital and digital-to-analog converters, serial com munications devices, and simple digital input/output lines.
Microcontroller Memory 153
Microcontroller Memory
Most microcontrollers have memory on-board. The memory is in the form of random access memory (RAM), read-only memory (ROM), erasable programmable read-only memory (EPROM), elec trically erasable read-only memory (EEPROM), and a newer type of EEPROM memory called FLASH. These memory types are discussed in the following paragraphs. The discussion of FLASH memory will be deferred until the chapter on the M68HC08 family.
Random Access Memory (RAM)
In a microcontroller, onboard RAM is static random access memory. It is always volatile—when the power to the microcontroller is removed, the contents of this memory disappear. Sometimes, spe cial provisions are made to deliver power to RAM when the processor is in the “off” state. This provision is called battery backedup RAM, and it is one of the alternative ways that a small amount of important data can be saved when power is removed from the main system.
The requirement for RAM in typical microcontroller applica tions is modest to small. Available RAM is usually limited to a few hundred bytes, and often there will be as little as a few tens of bytes of RAM. In the design of the microcontroller, price is a major con sideration. The total silicon area of the computer die often drives the final price of the component. In most computers, a base page is the first 256 bytes of memory. This page is unique because it requires only 8-bits of address to reach any location. Silicon area needed to construct the address decoding for the upper address bits is not re quired to address base page memory. Therefore, onboard RAM is usually located in the computer base page. There are some other functions that are usually assigned to the base page. Generally, you will find that the amount of RAM is limited to less than 256 bytes.
Read-Only Memory (ROM)
Programs and other data that can never be changed are stored in ROM. ROM is programmed during the manufacture of the chip, and its contents cannot be changed once the microcontroller is delivered to the customer. The ROM program is installed as a mask layer and is called masked ROM.
154 Chapter 4 Small 8-Bit Systems
Most microcontroller applications require more program memory space than RAM space. The smallest microcontroller usually has about 512 bytes of ROM, while the largest can contain as much as 32,000 bytes (32 kilobytes, or 32k) or more. Sometimes, the programmer will find it desirable to have a small amount of ROM that can be accessed from the computer base page. To meet these requirements, the microcontroller designers will place a few bytes of ROM in the base page memory map.
Erasable Programmable Read-Only Memory (EPROM)
EPROM is a form of programmable memory that permits the programmer to change the program contents and, if necessary, re turn and change it later after testing. As the name implies, it is possible to reprogram EPROM. First, this memory must be erased. The eras ing procedure involves allowing ultraviolet light to fall upon the memory area of the die. This high-energy light removes stored charge that is placed on each memory gate during programming.
EPROM programming requires that a higher than normal volt age be applied to the chip, and the code be systematically placed in each memory location. The procedure is slow because the code must be left in place for several milliseconds for each memory location stored. Often, a separate programmer board is used to transfer code from an EPROM to the microcomputer EPROM. These program ming boards can program as many as one to eight parts at a time.
EPROM requires a larger silicon die area than the corresponding amount of ROM. Therefore, it is somewhat more expensive. Also, the window package that allows the EPROM to be erased is expen sive. This additional expense makes it impractical to use normal EPROM for production volumes. The window package EPROM de vices are excellent for development purposes, though. The modestly higher cost of these devices is not a serious impediment to their use in development programs.
The economics of production sets the smallest production vol ume for a masked ROM microcontroller at about one to five thousand units. An alternative to the use of masked ROM at smaller levels of production is called the one-time programmable (OTP) chip. These devices use the standard EPROM technology for their program memo ries. They are programmed in the same manner as EPROM chips.
Microcontroller Memory 155
Their packages, however, have no windows to allow erasure of the program once it is put in place. These devices cost somewhat more than masked ROM, but they are sufficiently less expensive than the EPROM parts to allow economic production of rather small quanti ties. They do have the disadvantage that, once programmed, they can never be used for a different program.
Electrically Erasable Programmable Read-Only Memory (EEPROM)
EEPROM is a technology that uses a memory cell similar to the standard EPROM cell. These cells are somewhat larger than the stan dard EPROM, and are therefore more expensive. It is possible to erase an EEPROM electrically without the high-energy ultraviolet light. EEPROM requires a high voltage in programming and erasing the memory. Some microcontrollers have EEPROM that can be pro grammed without an externally applied high voltage. This programming is accomplished by the use of an onboard charge pump to generate the programming voltage. Such charge pumps are not capable of delivering much current, so the amount of EEPROM that can be programmed from an onboard system is usually limited to a maximum of 512 bytes. This EEPROM is used for the storage of information gathered after the microcontroller has been placed into a system. This memory is not often used for the storage of program.
The smaller block of EEPROM can be programmed with the use of the onboard charge pump, and can be programmed “on the fly” during the normal execution of program. Devices with EEPROM are moderately expensive because EEPROM requires the largest silicon area of any memory technology.
Other Memory Considerations
Not all microcontrollers have enough onboard memory to suf fice in some jobs. In these cases, an expanded bus part can be used. Expanded bus parts allow the programmer to access memory that is external to the microcontroller. None of the small microcontrollers currently provide for expanded bus operation. The larger microcontrollers—large 8-bit, 16-bit, or 32-bit—provide expanded bus. In some instances, they provide no onboard memory at all. As we will see later, pins on a microcontroller are at a premium. An expanded bus operation means that some of the component pins must
156 Chapter 4 Small 8-Bit Systems
be used to access memory and will not be available for other microcontroller features. (Pin usage, bus expansion, and pin multi plexing will be discussed in later sections.) The important consideration at this point is that the limited program memory area usually associated with a microcontroller should not cause serious concern. If the program grows to exceed the available size of onboard memory for a microcontroller family, it is always possible to get a larger microcontroller that can handle any additional memory re quirements. The programming goal, though, is usually to confine the program in the smallest possible program memory space so that the least expensive microcontroller will do the job.
Using Microcontroller Memory
In our discussion on variables in Chapter 1, it was shown that C treats all automatic variables as local to the block in which they are declared. The scope of these variables is the block where they are declared. Since these variables exist only in the block where they are declared, the memory locations dedicated to the storage of these vari ables can be freed when the variables go out of scope. These rules create an ideal situation for storage on the program stack. Memory space is easily created on the stack at the beginning of a block, and it is equally easily destroyed at the close of the block. This operation is exactly what is needed, but it cannot be used in a typical small microcontroller. Most microcontrollers have very limited RAM, and the stack arrangement in them is completely different from that you will find on a large computer. On the M68HC05 family of parts, for example, the chip has a hardware stack and no stack pointer into memory that the compiler writer can access. Therefore, it is imprac tical to even attempt to use the system stack to store local variables. The hardware stack on these chips is used only for storage of the processor status when an interrupt occurs or to store the return ad dress from a jump to a subroutine. The stack pointer is set to its initial value on microcontroller reset, and the occurrence of an inter rupt or a jump to subroutine instruction are the only ways that the stack pointer can be changed.
In the larger machines, the stack pointer is set to a value that points to a memory location. This pointer will be automatically incremented and decremented by the equivalent of stack push or pull
Microcontroller Memory 157
operations. The program can arbitrarily change the stack pointer value so that room for automatic variables can be easily provided or elimi nated. In the small microcontrollers, automatic variables are stored in RAM and their scope is not limited to the block in which they are defined. Their access is limited to their block, however. Consider the following code segment:
main()
{
int i;
.
.
.
}
void able(void)
{
int i;
.
.
.
}
The two occurrences of the variable i in this case will cause no trouble because each i will be given a unique location in RAM and the scoping arrangement will insure that any reference to i in main() will not be confused with the i in able() and vice versa.
An important implication of this change in storage: recursion is no longer available! Only one memory location is available for each variable in the program. When a stack is used to store automatic variables, a function can call itself and a new block is created each time the function is entered. Thus, each time a function calls itself, a new stack frame that contains space for all automatic storage in the function is created. The function can call itself repeatedly as long as there is space on the stack to create new stack frames for the succes sive calls. Without stack space for variable storage, recursion is impossible.
A second limitation that occurs is in the available arguments for function calls. The compiler C6805 for the M68HC05 family de fines an int as an 8-bit number and a long as a 16-bit number.
158 Chapter 4 Small 8-Bit Systems
This definition is not compliant with the ANSI Standard, which re quires that an int be at least 16 bits wide and a long be at least 32 bits. Since the stack cannot be used to pass arguments, they must be passed in either registers or as global variables. If they are passed in registers, only two bytes can be passed. The arguments can be either two ints or one long. Function return values have the same limi tations. Of course, the program can use global variables to pass information to or from a function. A global variable defined external to any function can be accessed by any function in the program.
Most C compilers for the M68HC05 family provide automatic placement of variables in the available RAM of the part. Specific memory addresses are identified to the compiler by the #pragma memory directives. The following code segment shows an example of how the memory is defined within an M68HC05 program:
#pragma memory ROMPAGE0 [48] @ 32; #pragma memory ROMPROG [5888] @ 2048; #pragma memory RAMPAGE0 [176] @ 80; #pragma memory RAMPROG [256] @ 256;
This sequence of code will be used to identify the memory map of the M68HC05B6. This part has 48 bytes of ROM in page zero start ing at address 32. There are 5888 bytes of program ROM starting at address 2048. The 176 bytes of page zero RAM starts at address 80. There are 256 bytes of EEPROM in this part that begin at the address 256. Here we treat EEPROM as program RAM because it is pro grammable and is outside of the base page.
Inclusion of the above code lines will identify the necessary memory locations for the compiler, and further concerns about memory locations should be unnecessary. The compiler will auto matically place the code in the ROMPROG area and the RAM requirements will fall at the starting address identified by RAMPAGE0. Programmers who wish to make use of the ROMPAGE0 memory can do so by a command like
const int table[]={—,—,—,...,—} @ 32;
This instruction will place the specified array of data in the ROMPROG0 area and will start it at the address 32.