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

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

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

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

Добавлен: 15.06.2025

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

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

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

Chapter 6

Large Microcontrollers

In this chapter we’ll examine the programming of systems em­ ploying large microcontrollers. The realm of the large-microcontroller system is not all that different from the 8-bit systems. One of the main advantages of the use of a high-level language is that it keeps the nasty details of the underlying computer hidden from the pro­ grammer. Usually, the programmer will not see much difference between the code for different types of computers. The fallacy to this idea is that when programming any microcontroller, the program­ mer must know about and use all of the on-board peripherals found on the microcontroller. These peripherals will vary from machine to machine, and how they are accessed will differ from device to de­ vice. In this chapter, however, we are going to see an application where a substantial amount of assembly language is required. The chip that we are going to use here has a Digital Signal Processor section. This processor is accessed through special core chip regis­ ters and the core condition code register. The abstract machine that the compiler creates code for contains no registers. Therefore, the only access to these features is through assembly language. We will show how to create assembly language functions that can be accessed from your C program.

The part that we will use for the 16-bit discussions is the Motorola MC68HC16 family of components. These are similar to the MC68HC11 in many ways, but there are important differences. First, there are some new registers that must be programmed directly to make the MC68HC16 work as desired. (The only register in the MC68HC11 that must receive special assembly instructions is the condition code register.) Also, the MC68HC16 does not have automatic stacking of its registers when an exception occurs, unlike the MC68HC11. There­ fore, all interrupt service routines must begin with code that saves the

287

288 Chapter 6 Large Microcontrollers

status of the machine before the normal interrupt operations can pro­ ceed. We will see a few other differences between these devices, but the main difference is in the way peripherals are handled.

On the MC68HC16, the core processor is called the CPU16. This central computer is interfaced to an internal bus called the inter-modual bus (IMB). The IMB is very much like the bus a hard­ ware designer would put onto an external circuit board. It has address and data busses and all of the necessary control signals to control any peripheral that the microcontroller might have applied. In the stan­ dard device, the MC68HC16Z1, there are five built-in peripherals: the system integration module (SIM), the analog-to-digital converter (ADC), the queued serial peripheral interface module (QSPI), the general-purpose timer module (GPT), and the static random access memory module (SRAM). These modules and others can be added or deleted in future components as the customer needs dictate.

The several internal peripheral modules are each interfaced to the IMB. Each module has a specific set of registers that are located at an address relative to the base address of the module. These base ad­ dresses are set at design time. This is interesting because the same modules are used on the MC68300 series of microcontrollers, which are 32-bit microcontrollers. Therefore, the material presented in this chapter is directly applicable to the MC68300 series of microcontrollers. The only difference that the programmer will see is that the base memory locations of the various peripheral modules are different, but even these differences disappear because of the careful design of the microcontrollers. We therefore can consider this chapter to be on large microcontrollers rather than on the 16-bit systems alone.

The MC68HC16

The MC68HC16 is a truly complicated device. However, for our purposes, it can be divided into its several components, and each component is somewhat as one would expect from a programming standpoint. Therefore, we will examine this family of parts as a col­ lection of modules. Each module is rather straightforward. The core processor, which is known as the CPU16, is indeed a complete and competent microcomputer. The bulk of its complexity is hidden by the fact that the program is written in C. This section contains a brief description of the MC68HC16Z1 microcontroller that can be used to


1 2 3 4 5 6

The MC68HC16 289

aid in writing programs for the component in C. There are several Motorola manuals that are useful adjuncts to this chapter.

Copies of these manuals are all to be found on the attached CD­ ROM. It is recommended that these manuals be reviewed prior to any attempt to write code for this family.

CPU16 Core Processor

The CPU16 represents an attempt to bridge the difference be­ tween the large 8-bit microcontrollers and the high-end components embodied in the MC68300 family. This processor is a 16-bit proces­ sor. As such, its instructions are each 16 bits wide, instructions are read from memory 16 bits at time, and the instructions are processed 16 bits at a time. On this particular part, a 20-bit address bus with additional control allows the program to access two individual onemegabyte address maps. The controls available distinguish between program memory and data memory.

It has been stated several times that the programmer’s model for a microcontroller is not too important when writing code in C. To be able to access all of the features of a CPU16, you will have to use assembly language-based functions. There are certain features that are simply outside the concept of a high-level language. In these cases, it is recommended that functions which access and control these features be written and then you can call these functions from the C program. The main properties of the CPU16 that are not avail­ able to the C programmer are the DSP type registers found in the part. Programming of these registers is the subject of a later section of this chapter.

The 20-bit address space of the CPU16 is a significant deviation from the normal 16-bit address space of the MC68HC11. This change has been handled by the addition of several 4-bit extension registers for those registers that deal specifically with addresses. These regis­ ters are the program counter (PC), the stack pointer (SP), the three

1M68HC16 Family MC68HC16Z1 Users Manual MC68HC16Z1UM/AD

2M68HC16 Family CPU16 Central Processor Unit Reference Manual CPU16RM/AD

3Modular Microcontroller Family GPT General Purpose Timer Reference Manual GPTRM/AD

4Modular Microcontroller Family ADC Analog-to-Digital Converter Reference Manual ADCRM/AD

5Modular Microcontroller Family QSM Queued Serial Module Reference Manual QSMRM/AD

6Modular Microcontroller Family SIM System Integration Module Reference Manual SIMRM/AD

290 Chapter 6 Large Microcontrollers

index registers (IX, IY, and IZ), and the EK register. All these regis­ ters except for the EK are 16 bits wide. The EK register is an extension register that is used with the extended addressing mode. Any ex­ tended address calculation will result in a 16-bit number. The result will be concatenated with the EK register to create a 20-bit address. (The EK register has nothing to do with the E accumulator found in the device.) The extension registers are named PK, SK, XK, etc. The extension registers are usually set during initialization of the device, and there is no need to change them during program operation. The content of an extension register is a page pointer. The pages in this case are each 65536 bytes long. Transition from one page to another is automatic. For example, if an address is calculated for a jmp which will pass program control to code in another page, the proper ad­ dress will be calculated for both base and extension register automatically. The calculation will alter the contents of both the base and the extension register without programmer concern.

The MC68HC16 family currently has the several internal modules mentioned above, and it is planned that future versions of the part will have more modules. This complexity has suggested that the arrange­ ment of the header file for the part be broken into several different files: One file for the main processor, and several different header files, one for each of the peripheral modules in the individual part. This approach is shown in the HEADER/HC16HEADERS directory on the CD-ROM. There you will find a header file named hc16.h along with files named adc.h, gpt.h, sim.h, sram.h, and qsm.h. When writing code for any specific module, you should include the hc16.h file along with the proper files for the peripheral portions needed. The hc16.h file must be included first because there are items defined in this file needed by the other headers.

One major difference between the large and small devices is the way the exception vector table is handled. Recall that, in the MC68HC11 family, all interrupt vectors are placed at the top of memory. With the MC68HC16 family, the vector table is contained within the first 512 bytes of memory. Upon reset, the CPU16 core processor reads the first four words of memory where it must find certain data for the opera­ tion of the program. The address 0 must contain a word whose least significant 12 bits are the contents of the ZK, the SK and the PK reg­ isters when the part comes out of reset. The address 2 contains the initial program counter, and the address 4 must contain the initial stack


The MC68HC16 291

pointer. Finally the address 6 must contain the initial value of the IZ register. Note that the word addresses in the MC68HC16 are always even. In this machine you will find words on even boundaries, bytes anywhere, and long words on even boundaries. These data are loaded into this memory area by the use of a vector routine similar to that seen in Chapter 5 for the MC68HC11. With this approach, all of the regis­ ters needed to begin operation of the basic computer are loaded from memory at reset time. Of course, this operation does not eliminate the need for program initialization; it merely provides a mechanism by which the processor will start accessing memory at the correct ad­ dresses when the device comes out of reset.

Table 6-1 contains a listing of the uses of each entry in the vector table. Note that vectors 0x0 through 0x37 have assigned functions. The vectors 0x38 through 0xff are available for userdefined operations. Note the relationship between the vector number and the vector address. The vector address is always twice the vec-

Table 6-1: Exception Vector Table

Vector

Vector Address

Type of Exception

Number

0

0x0000

Reset—Initial ZK, SK, PK

1

0x0002

Reset—Initial PC

2

0X0004

Reset—Initial SP

3

0X0006

Reset—Initial IZ

4

0X0008

Breakpoint

5

0X000A

Bus Error

6

0X000C

Software Interrupt

7

0X000E

Illegal Instruction

8

0X0010

Division by Zero

9-E

0X0012-0X001C

Unassigned, Reserved

F

0X001E

Uninitialized Interrupt

10

0X0020

Level 0 Autovector

11

0X0022

Level 1 Autovector

12

0X0024

Level 2 Autovector

13

0X0026

Level 3 Autovector

14

0X0028

Level 4 Autovector

15

0X002A

Level 5 Autovector

16

0X002C

Level 6 Autovector

17

0X002E

Level 7 Autovector

18

0X0030

Spurious Interrupt

19-37

0X0032-0X006E

Unassigned, Reserved

38-FF

0X0070-0X01FE

User-defined Interrupts


292 Chapter 6 Large Microcontrollers

tor number. When programming the interrupt vectors in the several peripheral modules of the MC68HC16, the programmer will select the interrupt vector. When it comes time to place the interrupt ser­ vice routine address in the proper memory location, it is to the vector address—NOT the vector number—that this value must be assigned.

When setting up the vector table, a wise programmer will fill all of the possible unused vector addresses with the address of a dummy function that provides an orderly return to the program in the event of an unexpected interrupt. Usually the first 0x18 or 24 vectors should be filled with this address. An example function that can be used for this type of operation is

static @port void _init_vector(void) { }

This program will compile to a single RTI (return from interrupt) that will return the program control to the location when the interrupt occurred. A static function is not used often. When a function is declared static, it can be seen only in the file in which it is defined.

Following is a listing of the routine vector.c. This program is modeled closely after that provided with the Cosmic MC68HC16 C compiler.

extern @far @port void _stext(void);/*startup routine*/ extern @port void OC3_Isr(void); /* ISR address */ static @port void _init_vector(void);

static const struct reset {

@far @port void (*rst)(void); /* reset + code extension */

unsigned short isp; /* initial stack pointer */ unsigned short dpp; /* direct page pointer */

@port void (*vector[252])(void); /* interrupt vectors */

} _reset = {

_stext,

/*

1-start address */

0x03fe,

/*

2-stack pointer */

0x0000,

/*

3-page pointer */

_init_vector, /*

4-Breakpoint */

_init_vector, /*

5-Bus Error */

The MC68HC16 293

_init_vector, * 6-Software Interrupt */ _init_vector, /* 7-Illegal Instruction */ _init_vector, /* 8-Divide by Zero */ _init_vector, /* 9-Reserved */ _init_vector, /* a-Reserved */ _init_vector, /* b-Reserved */ _init_vector, /* c-Reserved */ _init_vector, /* d-Reserved */ _init_vector, /* e-Reserved */

_init_vector, /* f-Uninitialized Interrupt */ _init_vector, /* 10-Reserved */

_init_vector, /* 11-Level 1 Interrupt Autovector */ _init_vector, /* 12-Level 2 Interrupt Autovector */ _init_vector, /* 13-Level 3 Interrupt Autovector */ _init_vector, /* 14-Level 4 Interrupt Autovector */ _init_vector, /* 15-Level 5 Interrupt Autovector */ _init_vector, /* 16-Level 6 Interrupt Autovector */ _init_vector, /* 17-Level 7 Interrupt Autovector */ _init_vector, /* 18-Spurious Interrupt */

/* vectors 0x19-0x37 unassigned, reserved */

0,0,0,0,0,0,0,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

/* put timer at vector 0x46 0x40 from ICR and 6 for OC3 */

0,0,0,0,0,0,OC3_Isr/*OC3_Isrvectorat0x46addressat0x8c*/

};

static @port void _init_vector(void) { }

Listing 6-1: The Vector Initialization Routine vector.c

We have already seen the @port command in Chapter 5. The @far command is unique to the Cosmic compiler. This command notifies the compiler that the pointer associated with the command is not the usual 16-bit pointer. An @far pointer is an extended pointer. This 20-bit pointer will be placed into the first two memory words by the vector.c routine. The rightmost 4 bits in word 0 is the program counter extension PK, so that the placement of the 20-bit