C MACRO DEFINITIONS FOR THE MC68HC(7)11E9/E8/E1/E0 INTRODUCTION With more microcontroller users moving to high level languages like C, macro definition files like the one outlined in this document can speed software development efforts. The file reproduced in the following pages is available on the Motorola Freeware AMCU Bulletin Board System at (512) 891-3733. Download and unzip the "hc11e9h.zip" file from the MCU11 directory on the BBS. If you have questions about doing this, please call your local Motorola Field Applications Engineer. The "hc11e9h.zip" file includes this ASCII text copy of the documentation and the actual "hc11e9.h" text file. The "hc11e9.h" file, and others like it, use Motorola's designated register and bit names for each device described. Any user already familiar with MC68HC11 assembly language and architecture (a requirement even for those who think they will only program in C), will readily be able to make use of this file. CONVENTIONS The contents of the actual file will be indented with this ">>" notation. Thus the following lines appear in the "hc11e9.h" file: >> /* (C) MOTOROLA, INC., 1993 >> * >> * FILENAME: hc11e9.h >> * >> * DESCRIPTION: Register and bit macro definitions for the MC68HC11E9, >> * MC68HC711E9, MC68HC11E8, MC68HC11E1, and MC68HC11E0 microcontrollers. >> * >> * CREATED: 11/18/93 >> * >> * NOTE: Your comments, suggestions, and corrections are requested and >> * greatly appreciated. >> */ CONCEPTS, DEVELOPMENT, AND USAGE In C, we can make just about anything an lvalue, that is, something that appears to the left of the equal sign in an assignment expression. We can even use a number as an lvalue. In particular, we would like to use register addresses as lvalues. To do this, we must cast the lvalue as a pointer to a particular data type. For example... (unsigned char *) 0x1000 ...would be an lvalue that points to an unsigned character (an 8 bit unsigned value) at memory location 0x1000 ($1000 for those used to assembly language). In this particular form, however, we cannot yet assign a value to the memory location. To do this, we must dereference the pointer. Dereferencing a pointer specifies the value that is pointed to and not the pointer itself. So, to assign the value 0xFF to memory location 0x1000, we would use the following C assignment expression: *(unsigned char *) 0x1000 = 0xFF; Likewise, if we wish to assign the contents of memory location 0x1000 to the variable A, we would use the following assignment: A = *(unsigned char *) 0x1000; This is all that is really necessary to manipulate the memory mapped registers of the MC68HC11. Unfortunately, *(unsigned char *) 0x1000 is not particularly indicative of the function memory location 0x1000 performs (PORT A on most MC68HC11 devices). The extra typing required to use this memory location can also be a source of minor, but unnecessary compilation errors. A better idea is to use the following line (remember, lines appearing in the "hc11e9.h" file are indented with ">>"): >> #define REGISTER unsigned char Thus to access memory location 0x1000, we can now type: A = *(REGISTER *) 0x1000; This is an improvement, but it would be even better if we could define a register as PORTA or DDRC as we do when programming in assembly. Thus the following line... #define SOMEDEVICE *(REGISTER *)0x1000 ...will allow us to address 0x1000 in a very convenient fashion. For example, we can now type... SOMEDEVICE = 0xFF; ...to assign 0xFF to memory location 0x1000, and we can also type... A = SOMEDEVICE; ...to assign the contents of 0x1000 to the variable A. The MC68HC11 has an INIT register which is used to remap internal RAM and registers to the beginning of any 4K page of memory. Some applications may require register remapping, so it would be convenient if we could make a simple change to the macro definition file to account for this. The following line (part of "hc11e9.h") allows us to do this: >> #define REG_BASE 0x1000 We can thus use the following macro definition to handle register relocation: #define SOMEDEVICE *(REGISTER *)(REG_BASE + 0x00) If we leave REG_BASE as 0x1000, then pointers to the MC68HC11's peripheral registers will be addressed at 0x1000 in our source code. If we decide to remap the registers to 0x4000, we can simply replace 0x1000 in the #define REG_BASE macro with 0x4000. Note that this does not actually modify the MC68HC11's INIT register. This must be done by modifying your C compiler's run-time start up code. Refer to your compiler's documentation before making any such changes. Before proceeding with the rest of the "hc11e9.h" file, we need to understand the use of C's volatile keyword. By specifying a variable as volatile, we tell the C compiler not to optimize expressions using that variable. #define PORT *(REGISTER *)(REG_BASE + 0xA0) void main() { PORT = 0x00; etc... /* PORT is not used until while(PORT) */ while (PORT) { etc... } } In this program fragment, we immediately initialize PORT to 0x00, but we will not reference it again until the while (PORT) expression. Unless PORT were to somehow change, while (PORT) would be false, and code in the braces immediately following would not execute. Some C compilers may view this as unnecessary if PORT never changes, and it is possible these lines could be optimized out of the resulting object code. On the MC68HC11, PORT may point to a bi-directional I/O port whose inputs may change during the course of program execution, thus the while (PORT) expression could actually be true when it is executed. As a precaution, we can designate the PORT pointer as volatile so that the optimizer will not attempt to remove any questionable references to it. We would thus change the #define macro to be... #define PORT *(volatile REGISTER *)(REG_BASE + 0xA0) By doing this, references to PORT will not be optimized. Several registers on the MC68HC11 can change without the intervention of user code. These register include port data registers (PORTC), peripheral status registers (SPSR), peripheral data registers (SCDR, ADR1), flag registers (TFLG1), and timer registers (TCNT, TIC3). We could use the volatile keyword with every register macro definition to simplify matters, but this runs counter to good code documentation. By specifying only those registers which require it as volatile, the resulting code will be better documented. Only registers which can receive data externally or be changed by the processor without user intervention will be declared volatile. Write only registers will be easily recognized because they will lack the volatile declaration. The following macro definitions are used for the registers on the MC68HC11E9, MC68HC711E9, MC68HC11E8, MC68HC11E1, and MC68HC11E0 devices: >> #define PORTA (*(volatile REGISTER *)(REG_BASE + 0x00)) >> #define PIOC (*(volatile REGISTER *)(REG_BASE + 0x02)) >> #define PORTC (*(volatile REGISTER *)(REG_BASE + 0x03)) >> #define PORTB (*(REGISTER *)(REG_BASE + 0x04)) >> #define PORTCL (*(volatile REGISTER *)(REG_BASE + 0x05)) >> #define DDRC (*(REGISTER *)(REG_BASE + 0x07)) >> #define PORTD (*(volatile REGISTER *)(REG_BASE + 0x08)) >> #define DDRD (*(REGISTER *)(REG_BASE + 0x09)) >> #define PORTE (*(volatile REGISTER *)(REG_BASE + 0x0A)) >> #define CFORC (*(REGISTER *)(REG_BASE + 0x0B)) >> #define OC1M (*(REGISTER *)(REG_BASE + 0x0C)) >> #define OC1D (*(REGISTER *)(REG_BASE + 0x0D)) The following registers (TCNT, TICx, and TOCx) are declared as unsigned integers because they are 16 bit registers and should be accessed as such. It is much simpler and clearer to change, for example, the output compare 4 register by using TOC4 = 0x4000, TOC4 = TCNT + 0x20FF, or TOC4 += 0x3200. >> #define TCNT (*(volatile unsigned int *)(REG_BASE + 0x0E)) >> #define TIC1 (*(volatile unsigned int *)(REG_BASE + 0x10)) >> #define TIC2 (*(volatile unsigned int *)(REG_BASE + 0x12)) >> #define TIC3 (*(volatile unsigned int *)(REG_BASE + 0x14)) >> #define TOC1 (*(unsigned int *)(REG_BASE + 0x16)) >> #define TOC2 (*(unsigned int *)(REG_BASE + 0x18)) >> #define TOC3 (*(unsigned int *)(REG_BASE + 0x1A)) >> #define TOC4 (*(unsigned int *)(REG_BASE + 0x1C)) >> #define TI4O5 (*(volatile unsigned int *)(REG_BASE + 0x1E)) >> #define TCTL1 (*(REGISTER *)(REG_BASE + 0x20)) >> #define TCTL2 (*(REGISTER *)(REG_BASE + 0x21)) >> #define TMSK1 (*(REGISTER *)(REG_BASE + 0x22)) >> #define TFLG1 (*(volatile REGISTER *)(REG_BASE + 0x23)) >> #define TMSK2 (*(REGISTER *)(REG_BASE + 0x24)) >> #define TFLG2 (*(volatile REGISTER *)(REG_BASE + 0x25)) >> #define PACTL (*(REGISTER *)(REG_BASE + 0x26)) >> #define PACNT (*(volatile REGISTER *)(REG_BASE + 0x27)) >> #define SPCR (*(REGISTER *)(REG_BASE + 0x28)) >> #define SPSR (*(volatile REGISTER *)(REG_BASE + 0x29)) >> #define SPDR (*(volatile REGISTER *)(REG_BASE + 0x2A)) >> #define BAUD (*(REGISTER *)(REG_BASE + 0x2B)) SCCR1 is declared volatile because it has the R8 bit, the ninth data bit received when SCI mode 1 is used. The remaining bits in this register are write only. >> #define SCCR1 (*(volatile REGISTER *)(REG_BASE + 0x2C)) >> #define SCCR2 (*(REGISTER *)(REG_BASE + 0x2D)) >> #define SCSR (*(volatile REGISTER *)(REG_BASE + 0x2E)) >> #define SCDR (*(volatile REGISTER *)(REG_BASE + 0x2F)) ADCTL is declared volatile because bit 7, the conversion complete flag (CCF), is changed without user intervention. The remaining bits in this register are write only. >> #define ADCTL (*(volatile REGISTER *)(REG_BASE + 0x30)) >> #define ADR1 (*(volatile REGISTER *)(REG_BASE + 0x31)) >> #define ADR2 (*(volatile REGISTER *)(REG_BASE + 0x32)) >> #define ADR3 (*(volatile REGISTER *)(REG_BASE + 0x33)) >> #define ADR4 (*(volatile REGISTER *)(REG_BASE + 0x34)) >> #define BPROT (*(REGISTER *)(REG_BASE + 0x35)) >> #define OPTION (*(REGISTER *)(REG_BASE + 0x39)) >> #define COPRST (*(REGISTER *)(REG_BASE + 0x3A)) >> #define PPROG (*(REGISTER *)(REG_BASE + 0x3B)) >> #define HPRIO (*(REGISTER *)(REG_BASE + 0x3C)) >> #define INIT (*(REGISTER *)(REG_BASE + 0x3D)) >> #define TEST1 (*(REGISTER *)(REG_BASE + 0x3E)) >> #define CONFIG (*(REGISTER *)(REG_BASE + 0x3F)) C also allows us to declare individual bit fields as constants. This allows us to make simple register bit assignments and comparisons. For instance... while (!(SPSR & SPIF)); ...can be used to halt program execution until the SPI status register SPIF bit has set. Likewise, we can use... SPCR = SPIE + SPE + MSTR + CPHA + SPR0; ...to configure the SPI for master operation with interrupts using clock phase 1 and a baud rate of E clock divided by 4. We can also use these constants to clear individual bit fields in the timer flag registers. TFLG1 &= OC3F; This clears output compare flag 3 without affecting the other bits in the TFLG1 register. The following macro definitions are used for the register bit fields on the MC68HC11E9, MC68HC711E9, MC68HC11E8, MC68HC11E1, and MC68HC11E0 devices: >> /* Bit names for general use */ >> #define bit7 0x80 >> #define bit6 0x40 >> #define bit5 0x20 >> #define bit4 0x10 >> #define bit3 0x08 >> #define bit2 0x04 >> #define bit1 0x02 >> #define bit0 0x01 >> /* PORTA bit definitions 0x00 */ >> #define PA7 bit7 >> #define PA6 bit6 >> #define PA5 bit5 >> #define PA4 bit4 >> #define PA3 bit3 >> #define PA2 bit2 >> #define PA1 bit1 >> #define PA0 bit0 >> /* PIOC bit definitions 0x02 */ >> #define STAF bit7 >> #define STAI bit6 >> #define CWOM bit5 >> #define HNDS bit4 >> #define OIN bit3 >> #define PLS bit2 >> #define EGA bit1 >> #define INVB bit0 >> /* PORTC bit definitions 0x03 */ >> #define PC7 bit7 >> #define PC6 bit6 >> #define PC5 bit5 >> #define PC4 bit4 >> #define PC3 bit3 >> #define PC2 bit2 >> #define PC1 bit1 >> #define PC0 bit0 >> /* PORTB bit definitions 0x04 */ >> #define PB7 bit7 >> #define PB6 bit6 >> #define PB5 bit5 >> #define PB4 bit4 >> #define PB3 bit3 >> #define PB2 bit2 >> #define PB1 bit1 >> #define PB0 bit0 >> /* PORTCL bit definitions 0x05 */ >> #define PCL7 bit7 >> #define PCL6 bit6 >> #define PCL5 bit5 >> #define PCL4 bit4 >> #define PCL3 bit3 >> #define PCL2 bit2 >> #define PCL1 bit1 >> #define PCL0 bit0 >> /* DDRC bit definitions 0x07 */ >> #define DDC7 bit7 >> #define DDC6 bit6 >> #define DDC5 bit5 >> #define DDC4 bit4 >> #define DDC3 bit3 >> #define DDC2 bit2 >> #define DDC1 bit1 >> #define DDC0 bit0 >> /* PORTD bit definitions 0x08 */ >> #define PD5 bit5 >> #define PD4 bit4 >> #define PD3 bit3 >> #define PD2 bit2 >> #define PD1 bit1 >> #define PD0 bit0 >> /* DDRD bit definitions 0x09 */ >> #define DDD5 bit5 >> #define DDD4 bit4 >> #define DDD3 bit3 >> #define DDD2 bit2 >> #define DDD1 bit1 >> #define DDD0 bit0 >> /* PORTE bit definitions 0x0A */ >> #define PE7 bit7 >> #define PE6 bit6 >> #define PE5 bit5 >> #define PE4 bit4 >> #define PE3 bit3 >> #define PE2 bit2 >> #define PE1 bit1 >> #define PE0 bit0 >> /* CFORC bit definitions 0x0B */ >> #define FOC1 bit7 >> #define FOC2 bit6 >> #define FOC3 bit5 >> #define FOC4 bit4 >> #define FOC5 bit3 >> /* OC1M bit definitions 0x0C */ >> #define OC1M7 bit7 >> #define OC1M6 bit6 >> #define OC1M5 bit5 >> #define OC1M4 bit4 >> #define OC1M3 bit3 >> /* OC1D bit definitions 0x0D */ >> #define OC1D7 bit7 >> #define OC1D6 bit6 >> #define OC1D5 bit5 >> #define OC1D4 bit4 >> #define OC1D3 bit3 >> /* TCTL1 bit definition 0x20 */ >> #define OM2 bit7 >> #define OL2 bit6 >> #define OM3 bit5 >> #define OL3 bit4 >> #define OM4 bit3 >> #define OL4 bit2 >> #define OM5 bit1 >> #define OL5 bit0 >> /* TCTL2 bit definitions 0x21 */ >> #define EDG4B bit7 >> #define EDG4A bit6 >> #define EDG1B bit5 >> #define EDG1A bit4 >> #define EDG2B bit3 >> #define EDG2A bit2 >> #define EDG3B bit1 >> #define EDG3A bit0 >> /* TMSK1 bit definitions 0x22 */ >> #define OC1I bit7 >> #define OC2I bit6 >> #define OC3I bit5 >> #define OC4I bit4 >> #define I4O5I bit3 >> #define IC1I bit2 >> #define IC2I bit1 >> #define IC3I bit0 >> /* TFLG1 bit definitions 0x23 */ >> #define OC1F bit7 >> #define OC2F bit6 >> #define OC3F bit5 >> #define OC4F bit4 >> #define I4O5F bit3 >> #define IC1F bit2 >> #define IC2F bit1 >> #define IC3F bit0 >> /* TMSK2 bit definitions 0x24 */ >> #define TOI bit7 >> #define RTII bit6 >> #define PAOVI bit5 >> #define PAII bit4 >> #define PR1 bit1 >> #define PR0 bit0 >> /* TFLG2 bit definitions 0x25 */ >> #define TOF bit7 >> #define RTIF bit6 >> #define PAOVF bit5 >> #define PAIF bit4 >> /* PACTL bit definitions 0x26 */ >> #define DDRA7 bit7 >> #define PAEN bit6 >> #define PAMOD bit5 >> #define PEDGE bit4 >> #define DDRA3 bit3 >> #define I4O5 bit2 >> #define RTR1 bit1 >> #define RTR0 bit0 >> /* SPCR bit definitions 0x28 */ >> #define SPIE bit7 >> #define SPE bit6 >> #define DWOM bit5 >> #define MSTR bit4 >> #define CPOL bit3 >> #define CPHA bit2 >> #define SPR1 bit1 >> #define SPR0 bit0 >> /* SPSR bit definitions 0x29 */ >> #define SPIF bit7 >> #define WCOL bit6 >> #define MODF bit4 >> /* BAUD bit definitions 0x2B */ >> #define TCLR bit7 >> #define SCP1 bit5 >> #define SCP0 bit4 >> #define RCKB bit3 >> #define SCR2 bit2 >> #define SCR1 bit1 >> #define SCR0 bit0 >> /* SCCR1 bit definition 0x2C */ >> #define R8 bit7 >> #define T8 bit6 >> #define M bit4 >> #define WAKE bit3 >> /* SCCR2 bit definitions 0x2D */ >> #define TIE bit7 >> #define TCIE bit6 >> #define RIE bit5 >> #define ILIE bit4 >> #define TE bit3 >> #define RE bit2 >> #define RWU bit1 >> #define SBK bit0 >> /* SCSR bit definitions 0x2E */ >> #define TDRE bit7 >> #define TC bit6 >> #define RDRF bit5 >> #define IDLE bit4 >> #define OR bit3 >> #define NF bit2 >> #define FE bit1 >> /* SCDR bit definitions 0x2F */ >> #define R7T7 bit7 >> #define R6T6 bit6 >> #define R5T5 bit5 >> #define R4T4 bit4 >> #define R3T3 bit3 >> #define R2T2 bit2 >> #define R1T1 bit1 >> #define R0T0 bit0 >> /* ADCTL bit definitions 0x30 */ >> #define CCF bit7 >> #define SCAN bit5 >> #define MULT bit4 >> #define CD bit3 >> #define CC bit2 >> #define CB bit1 >> #define CA bit0 >> /* BPROT bit definitions 0x35 */ >> #define PTCON bit4 >> #define BPRT3 bit3 >> #define BPRT2 bit2 >> #define BPRT1 bit1 >> #define BPRT0 bit0 >> /* OPTION bit definitions 0x39 */ >> #define ADPU bit7 >> #define CSEL bit6 >> #define IRQE bit5 >> #define DLY bit4 >> #define CME bit3 >> #define CR1 bit1 >> #define CR0 bit0 >> /* PPROG bit definitions 0x3B */ >> #define ODD bit7 >> #define EVEN bit6 >> #define ELAT bit5 /* MC68HC711E9 only */ >> #define BYTE bit4 >> #define ROW bit3 >> #define ERASE bit2 >> #define EELAT bit1 >> #define EEPGM bit0 >> /* HPRIO bit definitions 0x3C */ >> #define RBOOT bit7 >> #define SMOD bit6 >> #define MDA bit5 >> #define IRVNE bit4 >> #define PSEL3 bit3 >> #define PSEL2 bit2 >> #define PSEL1 bit1 >> #define PSEL0 bit0 >> /* INIT bit definitions 0x3D */ >> #define RAM3 bit7 >> #define RAM2 bit6 >> #define RAM1 bit5 >> #define RAM0 bit4 >> #define REG3 bit3 >> #define REG2 bit2 >> #define REG1 bit1 >> #define REG0 bit0 >> /* TEST1 bit definitions 0x3E */ >> #define TILOP bit7 >> #define OCCR bit5 >> #define CBYP bit4 >> #define DISR bit3 >> #define FCM bit2 >> #define FCOP bit1 >> #define TCON bit0 >> /* CONFIG bit definitions 0x3F */ >> #define NOSEC bit3 >> #define NOCOP bit2 >> #define ROMON bit1 /* MC68HC11E9 and MC68HC11E8 only */ >> #define EPON bit1 /* MC68HC711E9 only */ >> #define EEON bit0 DISCLAIMER: Motorola reserves the right to make changes without further notice to any products herein. Motorola makes no warranty, representation or guarantee regarding the suitability of its products for any particular purpose, nor does Motorola assume any liability arising out of the application or use of any product or circuit, and specifically disclaims any and all liability, including without limitation consequential or incidental damages. "Typical" parameters can and do vary in different applications. All operating parameters, including "Typicals" must be validated for each customer application by customer's technical experts. Motorola does not convey any license under its patent rights nor the rights of others. Motorola products are not designed, intended, or authorized for use as components in systems intended for surgical implant into the body, or other applications intended to support or sustain life, or for any other application in which the failure of the Motorola product could create a situation where personal injury or death may occur. Should Buyer purchase or use Motorola products for any such unintended or unauthorized application, Buyer shall indemnify and hold Motorola and its officers, employees, subsidiaries, affiliates, and distributors harmless against all claims, costs, damages, and expenses, and reasonable attorney fees arising out of, directly or indirectly, any claim of personal injury or death associated with such unintended or unauthorized use, even if such claim alleges that Motorola was negligent regarding the design or manufacture of the part. Motorola is a registered trademark of Motorola, Inc. Motorola, Inc. is an Equal Opportunity/Affirmative Action Employer.