The HCS12/9S12:
An Introduction to Software
and Hardware Interfacing
Second Edition
Han-Way Huang
Minnesota State University • Mankato
Australia • Brazil • Japan • Korea • Mexico • Singapore • Spain • United Kingdom • United States
The HCS12 / 9S12: An Introduction to
Software and Hardware Interfacing,
2nd Edition
Han-Way Huang
Vice President, Career and Professional
Editorial: Dave Garza
Director of Learning Solutions: Sandy Clark
© 2010, 2006 Delmar, Cengage Learning
ALL RIGHTS RESERVED. No part of this work covered by the copyright herein
may be reproduced, transmitted, stored, or used in any form or by any means
graphic, electronic, or mechanical, including but not limited to photocopying,
recording, scanning, digitizing, taping, Web distribution, information networks,
or information storage and retrieval systems, except as permitted under
Section 107 or 108 of the 1976 United States Copyright Act, without the prior
written permission of the publisher.
Acquisitions Editor: Stacy Masucci
Managing Editor: Larry Main
Senior Product Manager: John Fisher
Senior Editorial Assistant: Dawn Daugherty
Vice President, Career and Professional
Marketing: Jennifer McAvey
Executive Marketing Manager:
Deborah S. Yarnell
Senior Marketing Manager: Erin Coffin
Marketing Coordinator: Shanna Gibbs
Production Director: Wendy Troeger
Production Manager: Mark Bernard
Art Director: David Arsenault
Technology Project Manager: Christopher
Catalina
For product information and technology assistance, contact us at
Professional Group Cengage Learning Customer & Sales Support, 1-800-354-9706
For permission to use material from this text or product,
submit all requests online at cengage.com/permissions
Further permissions questions can be emailed to
permissionrequest@cengage.com
Library of Congress Control Number: 2009920892
ISBN-13: 978-1-435-42742-6
ISBN-10: 1-435-42742-4
Delmar
5 Maxwell Drive
Clifton Park, NY 12065-2919
USA
Production Technology Analyst: Thomas Stover
Cengage Learning is a leading provider of customized learning solutions with
office locations around the globe, including Singapore, the United Kingdom,
Australia, Mexico, Brazil, and Japan. Locate your local office at:
international.cengage.com/region
Cengage Learning products are represented in Canada by Nelson Education, Ltd.
For your lifelong learning solutions, visit delmar.cengage.com
Visit our corporate website at cengage.com.
Printed in the United States of America
1 2 3 4 5 6 7 12 11 10 09
Notice to the Reader
Publisher does not warrant or guarantee any of the products described herein or perform any independent
analysis in connection with any of the product information contained herein. Publisher does not assume,
and expressly disclaims, any obligation to obtain and include information other than that provided to it by
the manufacturer. The reader is expressly warned to consider and adopt all safety precautions that might be
indicated by the activities described herein and to avoid all potential hazards. By following the instructions
contained herein, the reader willingly assumes all risks in connection with such instructions. The publisher
makes no representations or warranties of any kind, including but not limited to, the warranties of fitness for
particular purpose or merchantability, nor are any such representations implied with respect to the material set
forth herein, and the publisher takes no responsibility with respect to such material. The publisher shall not be
liable for any special, consequential, or exemplary damages resulting, in whole or part, from the readers’ use of,
or reliance upon, this material.
Contents
Preface
Chapter 1 Introduction to the HCS12 Microcontroller
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
1
Objectives 1
Number System Issue 2
Computer Hardware Organization 2
1.3.1
The Processor 3
1.3.2
Microprocessor 5
1.3.3
Microcontroller 6
1.3.4
Embedded Systems 7
Memory 8
1.4.1
Magnetic Memory 8
1.4.2
Optical Memory 8
1.4.3
Semiconductor Memory 8
1.4.4
Nonvolatile and Volatile Memory 8
1.4.5
Random-Access Memory 8
1.4.6
Read-Only Memory 9
Memory System Operation 10
1.5.1
Read Operation 11
1.5.2
Write Operation 12
Program Execution 12
1.6.1
The Circuit of the Program Counter 12
1.6.2
Where Does the Processor Start to Execute the Program?
1.6.3
Instruction Execution Process 14
1.6.4
Instruction Sequence Example 15
1.6.5
Instruction Execution Process 15
Overview of the HCS12 Microcontroller 21
The HCS12 CPU Registers 22
HCS12 Addressing Modes 24
1.9.1
Inherent Mode 24
1.9.2
Immediate Mode 24
1.9.3
Direct Mode 25
1.9.4
Extended Mode 25
14
iv
Contents
1.10
1.11
1.12
1.13
1.14
1.9.5
Relative Mode 25
1.9.6
Indexed Addressing Modes 26
1.9.7
Indexed Addressing Modes with Constant Offsets 26
1.9.8
Indexed Addressing Mode with Offset in an Accumulator 26
1.9.9
Auto Pre-/Postdecrement/-Increment Indexed Addressing Modes 27
1.9.10
16-Bit Offset Indexed Indirect Mode 28
1.9.11
Accumulator D Indirect Indexed Addressing 28
Addressing More than 64 kB 29
A Sample of HCS12 Instructions 29
1.11.1
The Load and Store Instructions 29
1.11.2
Transfer and Exchange Instructions 30
1.11.3
Move Instructions 32
1.11.4
Add and Subtract Instructions 33
Instruction Queue 34
Summary 35
Exercises 36
Chapter 2 HCS12 Assembly Programming
2.1
2.2
2.3
2.4
2.5
2.6
2.7
39
Objectives 39
Assembly Language Program Structure 40
2.2.1
The Label Field 40
2.2.2
The Operation Field 41
2.2.3
The Operand Field 41
2.2.4
The Comment Field 41
Assembler Directives 42
Software Development Issues 45
Writing Programs to Do Arithmetic 47
2.5.1
Carry/Borrow Flag 49
2.5.2
Multiprecision Addition 50
2.5.3
Subtraction and the C Flag 51
2.5.4
Multiprecision Subtraction 52
2.5.5
Binary-Coded-Decimal (BCD) Addition 53
2.5.6
Multiplication and Division 55
Program Loops 60
2.6.1
Condition Code Register 63
2.6.2
Branch Instructions 63
2.6.3
Compare and Test Instructions 65
2.6.4
Loop Primitive Instructions 66
2.6.5
Implementation of Looping Constructs 66
2.6.6
Decrementing and Incrementing Instructions
2.6.7
Bit Condition Branch Instructions 71
2.6.8
Instructions for Variable Initialization 72
Shift and Rotate Instructions 72
70
v
Contents
2.8
2.9
2.10
2.11
2.12
2.13
Boolean Logic Instructions 79
Bit Test and Manipulate Instruction 80
Program Execution Time 81
The Multiply-and-Accumulate (emacs) Instruction
Summary 84
Exercises 85
83
Chapter 3 Hardware and Software Development Tools
for the HCS12 87
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
3.10
3.11
3.12
Objectives 87
Development Tools for the HCS12 88
3.2.1
Software Development Tools 88
3.2.2
Hardware Development Tools 89
3.2.3
Types of HCS12 Demo Boards 89
The Dragon12-Plus Demo Board 90
The D-Bug12 Monitor 91
3.4.1
The D-Bug12 Operating Modes 92
3.4.2
EVB Mode 93
Using a Demo Board with the D-Bug12 Monitor 94
3.5.1
Starting the MiniIDE 96
3.5.2
Communicating with the Demo Board 96
3.5.3
Using the D-Bug12 Commands 99
3.5.4
Entering an Assembly Program 108
3.5.5
Assembling the Program 108
3.5.6
Downloading the S-Record File onto the Demo Board for Execution 109
3.5.7
Running and Debugging the Program 111
Other D-Bug12 Modes 115
3.6.1
The Pod Mode 115
3.6.2
The Jump-to-EEPROM Mode 115
3.6.3
The Bootloader Mode 116
Tips for Assembly Program Debugging 118
3.7.1
Syntax Errors 118
3.7.2
Logical Errors 118
Using CodeWarrior 123
3.8.1
Building a Software Project Using CodeWarrior 123
3.8.2
Project Setup 124
3.8.3
Source Code Entering 125
3.8.4
Project Build 131
3.8.5
Program Debugging 132
BDM Serial Interface 137
The BDM-Based Debugger 139
Summary 140
Lab Exercises and Assignments 142
vi
Contents
Chapter 4 Advanced Assembly Programming
4.1
4.2
4.3
4.4
4.5
4.6
4.7
4.8
4.9
4.10
4.11
4.12
4.13
4.14
4.15
145
Objectives 145
Introduction 146
Stack 146
What Is a Subroutine? 148
Issues Related to Subroutine Calls 151
4.5.1
Parameter Passing 151
4.5.2
Result Returning 152
4.5.3
Allocation of Local Variables 152
4.5.4
Saving the CPU Registers 152
The Stack Frame 153
4.6.1
Subroutines with Local Variables in Stack 154
4.6.2
Bubble Sort 157
4.6.3
Binary Search Subroutine 161
Mathematical Subroutines 164
4.7.1
Subroutine for Performing Multiple-Byte Division 164
4.7.2
Finding the Square Root 167
4.7.3
Subroutine for Prime Testing 170
Using the D-Bug12 Functions to Perform I/O Operations 173
4.8.1
Calling D-Bug12 Functions from Assembly Language 174
4.8.2
Descriptions of Callable Functions 174
4.8.3
Using the D-Bug12 Functions 181
Subroutines for Creating Time Delay 184
Introduction to Parallel I/O Port and Simple I/O Devices 185
4.10.1
Addressing the I/O Port Register 185
4.10.2
I/O Port Direction Configuration 186
4.10.3
I/O Port Data Register 187
Simple I/O Devices 188
4.11.1
Interfacing with LEDs 188
4.11.2
Interfacing with Seven-Segment Displays 190
4.11.3
Generating a Digital Waveform Using I/O Pins 194
4.11.4
Interfacing with DIP Switches 195
Tips for Program Debugging Involving Subroutine Calls 196
4.12.1
What to Do When the Program Gets Stuck 196
4.12.2
Handling the Stack Variable Access with Care 197
4.12.3
General Debugging Strategy 197
Summary 198
Exercises 200
Lab Exercises and Assignments 202
vii
Contents
Chapter 5 C Language Programming
5.1
5.2
5.3
5.4
5.5
5.6
5.7
5.8
5.9
5.10
205
Objectives 205
Introduction to C 206
Types, Operators, and Expressions 207
5.3.1
Data Types 207
5.3.2
Variable Declarations 207
5.3.3
Constants 207
5.3.4
Arithmetic Operators 208
5.3.5
Bitwise Operators 209
5.3.6
Relational and Logical Operators 210
5.3.7
Precedence of Operators 211
Control Flow 211
5.4.1
If Statement 212
5.4.2
If-Else Statement 212
5.4.3
Multiway Conditional Statement 212
5.4.4
Switch Statement 213
5.4.5
For-Loop Statement 213
5.4.6
While Statement 214
5.4.7
Do-While Statement 214
5.4.8
Goto Statement 214
Input and Output 215
Functions and Program Structure 216
5.6.1
Function Prototype 218
5.6.2
Creating Header Files 219
Pointers, Arrays, Structures, and Unions 219
5.7.1
Pointers and Addresses 219
5.7.2
Arrays 220
5.7.3
Pointers and Arrays 221
5.7.4
Passing Arrays to a Function 221
5.7.5
Initializing Arrays 222
5.7.6
Structures 222
5.7.7
Unions 224
Writing C Programs to Perform Simple I/O 224
Miscellaneous Items 227
5.9.1
Automatic, External, Static, and Volatile 227
5.9.2
Scope Rules 228
5.9.3
Type Casting 229
Using the C Compiler 230
5.10.1
Issue in Accessing Peripheral Registers 230
5.10.2
Peripheral Register Bit Definitions 231
5.10.3
Inline Assembly Instructions 231
viii
Contents
5.11
5.12
5.13
5.14
5.15
5.16
5.17
Using the CodeWarrior IDE to Develop C Programs 231
5.11.1
Entering C Programs in CodeWarrior 231
5.11.2
Creating a New Project 234
5.11.3
Adding Source Code to the Project 238
5.11.4
Building the Project 240
5.11.5
Executing and Debugging the Project with CodeWarrior
Using the ImageCraft C Compiler 243
5.12.1
Starting the ICC12 IDE 244
5.12.2
Creating a New Project 245
5.12.3
Adding Files to the Project 245
5.12.4
Building the Project 246
5.12.5
Executing and Debugging the Program with the
ICC12 IDE 248
Programming Style 252
5.13.1
General Guidelines to Comments 252
5.13.2
Program Documentation 253
5.13.3
Function (Subroutine) Documentation 253
5.13.4
Code Appearance 254
5.13.5
Naming of Variables, Constants, and Functions 255
Tips for C Program Debugging 256
Summary 257
Exercises 258
Lab Exercises and Assignments 259
Chapter 6 Interrupts, Clock Generation, Resets, and
Operation Modes 261
6.1
6.2
6.3
6.4
6.5
Objectives 261
Fundamental Concepts of Interrupts 262
6.2.1
What Is an Interrupt? 262
6.2.2
Why Are Interrupts Used? 263
6.2.3
Interrupt Maskability 263
6.2.4
Interrupt Priority 264
6.2.5
Interrupt Service 264
6.2.6
Interrupt Vector 265
6.2.7
Interrupt Programming 265
6.2.8
Overhead of Interrupts 266
Resets 266
HCS12 Exceptions 267
6.4.1
Maskable Interrupts 267
6.4.2
Nonmaskable Interrupts 270
6.4.3
Interrupts in D-Bug12 EVB Mode 271
Interrupt Programming in C Language 274
6.5.1
Interrupt Programming in CodeWarrior 274
6.5.2
Interrupt Programming in ImageCraft ICC12 275
6.5.3
Interrupt Programming in EGNU IDE 276
241
ix
Contents
6.6
6.7
6.8
6.9
6.10
6.11
6.12
6.13
6.14
Clock and Reset Generation Block (CRG) 277
6.6.1
Modes of CRG Operation 278
6.6.2
CRG Signals 278
6.6.3
The Oscillator (OSC) Block 280
6.6.4
Phase-Locked-Loop (PLL) 280
6.6.5
System Clock Generation 284
6.6.6
Clock Monitor 286
Real-Time Interrupt 287
Computer Operating Properly 292
Low-Power Modes 293
6.9.1
The Wai Instruction 293
6.9.2
The Stop Instruction 294
Resets 294
6.10.1
Power-On Reset 294
6.10.2
External Reset 294
HCS12 Operation Modes 295
6.11.1
Normal Operation Modes 296
6.11.2
Special Operation Modes 296
Summary 297
Exercises 298
Lab Exercises and Assignments 299
Chapter 7 Advanced Parallel I/O
7.1
7.2
7.3
7.4
7.5
7.6
303
Objectives 303
I/O Related Issues 304
I/O Addressing Issue 304
I/O Synchronization 304
7.4.1
Synchronization Issue for Parallel Ports 305
7.4.2
Synchronization Issue for Serial Interface 305
The HCS12 Parallel Ports 305
7.5.1
Port A and Port B 305
7.5.2
Port E 306
7.5.3
Port K 309
7.5.4
Port T 310
7.5.5
Port S 311
7.5.6
Port M 312
7.5.7
Ports H, J, and P 313
7.5.8
Ports AD0 and AD1 315
7.5.9
Port L 315
7.5.10
Ports U, V, and W 316
Electrical Characteristic Considerations for I/O Interfacing
7.6.1
Voltage-Level Compatibility 317
7.6.2
Current Drive Capability 319
7.6.3
Timing Compatibility 322
317
x
Contents
7.7
7.8
7.9
7.10
7.11
7.12
7.13
7.14
7.15
Liquid Crystal Displays (LCDs) 322
The HD44780U LCD Controller 323
7.8.1
Display Data RAM 325
7.8.2
Character Generator ROM (CGROM) 325
7.8.3
Character Generator RAM (CGRAM) 325
7.8.4
Registers 328
7.8.5
Instruction Description 329
7.8.6
Interfacing the HD44780U to the HCS12 Microcontroller
Interfacing Parallel Ports to a Keypad 338
7.9.1
Keypad Scanning 339
7.9.2
Keypad Debouncing 340
7.9.3
ASCII Code Lookup 341
Using the D/A Converter 343
7.10.1
The 8-Bit AD7302 DAC 345
7.10.2
Interfacing the AD7302 with the HCS12 346
Stepper Motor Control 347
7.11.1
Principles of Rotation for the Stepper Motor 347
7.11.2
Stepper Motor Drivers 350
Key Wake-Ups 355
7.12.1
Key-Wake-Up Registers 355
7.12.2
Key-Wake-Up Initialization 355
7.12.3
Considerations for the Key-Wake-Up Application 356
Summary 358
Exercises 359
Lab Exercises and Assignments 360
Chapter 8 Timer Functions
8.1
8.2
8.3
8.4
8.5
8.6
363
Objectives 363
Why Are Timer Functions Important? 364
Standard Timer Module 365
Timer Counter Register 366
8.4.1
Timer System Control Register 1 (TSCR1) 366
8.4.2
Timer System Control Register 2 (TSCR2) 367
8.4.3
Timer Interrupt Flag 2 Register (TFLG2) 368
Input-Capture Function 368
8.5.1
Input-Capture/Output-Compare Selection 368
8.5.2
Pins for Input-Capture 369
8.5.3
Registers Associated with Input-Capture 369
8.5.4
Input-Capture Applications 370
Output-Compare Function 377
8.6.1
Operation of the Output-Compare Function 378
8.6.2
Registers Related to the Output-Compare Function 378
8.6.3
Applications of the Output-Compare Function 379
330
xi
Contents
8.6.4
8.6.5
8.7
8.8
8.9
8.10
8.11
8.12
8.13
8.14
Making Sound Using the Output-Compare Function 386
Using OC7 to Control Multiple Output-Compare
Functions 393
8.6.6
Forced Output-Compare 395
Pulse Accumulator 396
8.7.1
Signal Pins 397
8.7.2
Operation Modes 398
8.7.3
Interrupt Sources 398
8.7.4
Registers Related to Pulse Accumulator 399
8.7.5
Operations of the Enhanced Pulse Accumulators 401
8.7.6
Pulse Accumulator Applications 402
Modulus Down Counter 407
8.8.1
Using the Modulus Down Counter to Generate Periodic Interrupts 409
8.8.2
Using the Modulus Down Counter to Generate Time Delays 409
Enhanced Capture Timer (ECT) Module 410
8.9.1
Enhanced Capture Timer Modes of Operation 411
8.9.2
Why the Enhanced Capture Timer Module? 411
8.9.3
The Operation of the Enhanced Input-Capture Function 412
Pulse-Width Modulation (PWM) Function 416
8.10.1
PWM Clock Select 416
8.10.2
PWM Channel Timers 420
8.10.3
PWM Waveform Properties 421
DC Motor Control 430
8.11.1
Drivers 431
8.11.2
Feedback 431
8.11.3
Electrical Braking 434
Summary 434
Exercises 436
Lab Exercises and Assignments 437
Chapter 9 Serial Communication Interface (SCI)
9.1
9.2
9.3
9.4
9.5
443
Objectives 443
Fundamental Concept of Serial Communications 444
The RS-232 Standard 444
9.3.1
TIA-232E Electrical Specification 444
9.3.2
TIA-232E Functional Specification 445
9.3.3
TIA-232E Mechanical Specification 448
9.3.4
TIA-232E Procedural Specification 449
9.3.5
Data Format 451
9.3.6
Data Transmission Errors 453
9.3.7
Null Modem Connection 453
The HCS12 Serial Communication Interface 453
SCI Baud Rate Generation 454
xii
Contents
9.6
9.7
9.8
9.9
9.10
9.11
9.12
The SCI Operation 455
9.6.1
Character Transmission 456
9.6.2
Break Characters 460
9.6.3
Idle Characters 460
9.6.4
Character Reception 460
9.6.5
Receiver Wake-Up 460
9.6.6
Single-Wire Operation 462
9.6.7
Loop Operation 462
Operation of the SCI in Different MCU Modes 463
Flow Control of USART in Asynchronous Mode 463
Interfacing SCI with TIA-232 464
Summary 469
Exercises 470
Lab Exercises and Assignments 471
Chapter 10 The SPI Function 473
10.1
10.2
10.3
10.4
10.5
10.6
10.7
10.8
10.9
10.10
10.11
Objectives 473
Introduction to the SPI Function 474
10.2.1
SPI Signal Pins 474
Registers Related to the SPI Subsystem 475
SPI Operation 478
10.4.1
Transmission Formats 478
10.4.2
Bidirectional Mode (MOMI or SISO) 480
10.4.3
Mode Fault Error 480
10.4.4
Low-Power Mode Options 481
SPI Circuit Connection 482
Configuration of and Data Transfer in SPI 484
SPI-Compatible Chips 487
The 74HC595 Shift Register 487
The TC72 Digital Thermometer 491
10.9.1
Functioning of TC72 491
10.9.2
Temperature Data Format 491
10.9.3
Serial Bus Interface 492
10.9.4
Internal Register Structure 494
The D/A Converter MCP4922 498
10.10.1 Signal Pins 498
10.10.2 Data Format 499
10.10.3 MCP4922 Output Voltage 499
10.10.4 Format Data to Be Sent to MCP4922 500
10.10.5 Interfacing the MCP4922 with the HCS12 500
Matrix LED Displays 504
10.11.1 The Organization of Matrix LED Displays 504
10.11.2 Colors of Matrix LED Displays 505
xiii
Contents
10.12
10.13
10.14
10.15
10.11.3 Connection Method 505
10.11.4 Dimension of Matrix LED Displays 506
10.11.5 Method of Driving Matrix LED Displays 507
The MAX6952 Matrix LED Display Driver 507
10.12.1 Pin Functions 507
10.12.2 Internal Registers 507
10.12.3 Blinking Operation 515
10.12.4 Choosing Values for RSET and CSET 516
Summary 522
Exercises 523
Lab Exercises and Assignments 527
Chapter 11 Inter-Integrated Circuit (I2C) Interface
11.1
11.2
11.3
11.4
11.5
11.6
11.7
529
Objectives 529
The I2C Protocol 530
11.2.1
Characteristics of I2C Protocol 530
11.2.2
I2C Signal Levels 530
11.2.3
I2C Data Transfer Signal Components 531
11.2.4
Synchronization 533
11.2.5
Arbitration 534
11.2.6
Data Transfer Format 535
11.2.7
7-Bit Addressing 536
11.2.8
10-Bit Addressing 538
An Overview of the HCS12 I2C Module 541
Registers for I2C Operation 542
11.4.1
The I2C Address Register (IBAD) 542
11.4.2
The I2C Control Register (IBCR) 542
11.4.3
The I2C Status Register (IBSR) 543
11.4.4
The I2C Data Register (IBDR) 545
11.4.5
The I2C Frequency Divider Register (IBFD) 545
Programming the I2C Module 549
11.5.1
Generation of the Start Condition 549
11.5.2
I2C Data Transfer in Master Mode 550
11.5.3
I2C Data Transfer in Slave Mode 552
The Serial Real-Time Clock DS1307 552
11.6.1
Signal Functions 553
11.6.2
RTC and RAM Address Map 554
11.6.3
Clock and Calendar 554
11.6.4
The DS1307 Control Register 554
11.6.5
Data Transfer 555
11.6.6
Circuit Connection 556
The Digital Thermometer and Thermostat DS1631A 565
11.7.1
Pin Assignment 566
11.7.2
Functional Description 566
xiv
Contents
11.8
11.9
11.10
11.11
11.7.3
DS1631A Registers 567
11.7.4
The DS1631A Operation 568
11.7.5
DS1631A Command Set 569
11.7.6
I2C Communication with DS1631A 569
Interfacing the Serial EEPROM 24LC08B with I2C 578
11.8.1
Pin Assignment and Block Diagram 578
11.8.2
Device Addressing 578
11.8.3
Write Operation 579
11.8.4
Acknowledge Polling 580
11.8.5
Read Operation 581
11.8.6
Circuit Connection Between the I2C Master and the 24LC08B
Summary 586
Exercises 588
Lab Exercises and Assignments 589
Chapter 12 Analog-to-Digital Converter
12.1
12.2
12.3
12.4
12.5
12.6
12.7
12.8
12.9
12.10
12.11
591
Objectives 591
Basics of A/D Conversion 592
12.2.1
A Data Acquisition System 592
12.2.2
Analog Voltage and Digital Code Characteristic 592
12.2.3
A/D Conversion Algorithms 594
12.2.4
Optimal Voltage Range for A/D Conversion 596
12.2.5
Scaling Circuit 596
12.2.6
Voltage Translation Circuit 597
The HCS12 A/D Converter 598
12.3.1
Signal Pins Related to A/D Converter 600
12.3.2
Registers Associated with A/D Converter 600
The Functioning of the ATD Module 607
12.4.1
Analog Input Multiplexer 607
12.4.2
ATD Module Clock 608
12.4.3
Sample-and-Hold Stage 608
12.4.4
Input Channel Wraparound 608
12.4.5
FIFO Mode 609
12.4.6
External Trigger Source 610
12.4.7
Signed and Unsigned Control 610
12.4.8
ATD Operation Modes 610
Procedure for Performing A/D Conversion 611
Using the Temperature Sensor TC1047A 615
Using the IH-3605 Humidity Sensor 621
Measuring Barometric Pressure 624
Summary 627
Exercises 628
Lab Exercises and Assignments 631
582
xv
Contents
Chapter 13 Controller Area Network (CAN)
13.1
13.2
13.3
13.4
13.5
13.6
13.7
13.8
13.9
13.10
633
Objectives 633
Overview of Controller Area Network 634
13.2.1
Layered Approach in CAN 634
13.2.2
General Characteristics of CAN 634
CAN Messages 636
13.3.1
Data Frame 636
13.3.2
Remote Frame 639
13.3.3
Error Frame 639
13.3.4
Overload Frame 640
13.3.5
Interframe Space 641
13.3.6
Message Filtering 642
13.3.7
Message Validation 642
13.3.8
Bitstream Encoding 642
Error Handling 643
13.4.1
Bit Error 643
13.4.2
Stuff Error 643
13.4.3
CRC Error 643
13.4.4
Form Error 643
13.4.5
Acknowledgement Error 643
13.4.6
Error Signaling 643
Fault Confinement 643
13.5.1
CAN Node Status 6443
13.5.2
Error Counts 644
CAN Message Bit Timing 644
13.6.1
Nominal Bit Time 644
13.6.2
Length of Time Segments 645
Synchronization Issue 645
13.7.1
Resynchronization Jump Width 645
13.7.2
Phase Error of an Edge 646
Overview of the HCS12 CAN Module 646
MSCAN Module Memory Map 648
13.9.1
MSCAN Control Registers 648
13.9.2
MSCAN Message Buffers 658
13.9.3
Transmit Storage Structure 662
13.9.4
Receive Storage Structure 663
13.9.5
Identifier Acceptance Filter 664
13.9.6
MSCAN Clock System 665
13.9.7
MSCAN Interrupt Operation 666
13.9.8
MSCAN Initialization 667
Physical CAN Bus Connection 667
13.10.1 The MCP2551 CAN Transceiver 668
13.10.2 Interfacing the MCP2551 to the HCS12 CAN Devices
670
xvi
Contents
13.11
13.12
13.13
13.14
13.15
13.16
Setting the CAN Timing Parameters 671
MSCAN Configuration 675
Data Transmission and Reception in MSCAN 678
13.13.1 MSCAN Data Transmission Programming 678
13.13.2 MSCAN Data Reception Programming 681
13.13.3 Putting It All Together 683
Summary 687
Exercises 688
Lab Exercises and Assignments 689
Chapter 14 Internal Memory Configuration and
External Memory Expansion 693
14.1
14.2
14.3
14.4
14.5
14.6
14.7
14.8
Objectives 693
Introduction 694
Internal Resource Remapping 694
14.3.1
Register Block Mapping 695
14.3.2
SRAM Mapping 696
14.3.3
EEPROM Mapping 696
14.3.4
Miscellaneous Memory Mapping Control 696
Expanded Memory Mapping 699
On-Chip Flash Memory 701
14.5.1
Flash Memory Map 701
14.5.2
Flash Memory Protection 701
14.5.3
Flash Memory Related Registers 704
14.5.4
Securing the Flash Memory 708
14.5.5
Unsecuring the Microcontroller 708
14.5.6
Configuring the FCLKDIV Register 710
14.5.7
Flash Memory Programming and Erasure Algorithms
The On-Chip EEPROM Memory 716
14.6.1
EEPROM Memory Map 717
14.6.2
EEPROM Associated Registers 718
14.6.3
EEPROM Protection 719
14.6.4
Configuring the ECLKDIV Register 720
14.6.5
Programming and Erasure of EEPROM 720
HCS12 External Memory Interface 725
14.7.1
HCS12 Pins for External Memory Interfacing 725
14.7.2
Waveforms of Bus Signals 727
14.7.3
Bus Transactions 729
14.7.4
Bus Multiplexing 730
14.7.5
The HCS12 Bus Cycles 731
Issues Related to Adding External Memory 733
14.8.1
Memory Space Assignment 734
14.8.2
Address Decoder Design 735
14.8.3
Timing Verification 736
710
xvii
Contents
14.9
14.10
14.11
14.12
14.13
Appendices
755
A
B
C
D
E
F
G
References
Instruction Set Reference 755
Number System Issue 777
Summary of Features of HCS12 Devices 789
Tutorial for Using the AsmIDE 793
Tutorial for Using the EGNU to Develop C Programs 803
Music Note Frequencies 821
Vector Table Template in C for the HCS12 (used in CodeWarrior)
827
Glossary 829
Index 839
Memory Devices 736
14.9.1
The K6R1008C1D 736
14.9.2
The AT28C010 EEPROM 737
Example of External Memory Expansion for the HCS12 744
14.10.1 Memory Space Assignment 746
14.10.2 Address Latch 747
14.10.3 Address Decoder Design 748
14.10.4 Write Control Signals 749
14.10.5 Example HCS12 External Memory Design 749
Summary 753
Exercises 754
Lab Exercise and Assignment 754
825
This page intentionally left blank
Preface to Second Edition
The Freescale HCS12 (also known as 9S12) microcontroller family was initially designed for
automotive applications. The design of the HCS12 combines most features common in major
8-bit and 16-bit microcontrollers.
1. Full-feature timer system. The HCS12 timer system provides input-capture, outputcompare, pulse-width modulation, pulse accumulator, modulus down counter, realtime interrupt, and computer-operate-properly systems.
2. Background debug mode (BDM). The BDM circuit provides a single-wire interface
for accessing the internal resources of the HCS12 and hence allows a low-cost debug
adapter to be constructed.
3. Multiple serial interfaces. The HCS12 supports industrial-standard UART, SPI, I2C,
BDLC, and the CAN bus. The UART allows the HCS12 to interface with the PC
using the popular RS232 protocol. The SPI and I2C allow the HCS12 to interface
with numerous peripheral devices (e.g., LED drivers, LCDs, matrix displays, A/D
converters, D/A converters, real-time clocks, EEPROMs, Ethernet controllers, phaselocked-loops, and so on).
4. In-system programming (ISP) capability. Most HCS12 members provide on-chip flash
memory and allow the software to be upgraded in the system.
5. Fuzzy-logic support. The HCS12 provides a group of instructions to support fuzzylogic operations. These instructions should facilitate the programming of fuzzy-logic
applications in assembly language.
With these features, the HCS12 is very suitable for those who want to learn modern microcontroller interfacing and applications.
Intended Audience
This book is written for three groups of readers.
1. Students in electrical and computer engineering and technology who are taking an
introductory course in microprocessor interfacing and applications. For this group of
readers, this book provides a broad and systematic introduction to microprocessors
and microcontrollers.
2. Students in electrical and computer engineering and technology who are taking an
embedded-system design course. This book pays attention to design methodology,
programming style, and debug strategy in addition to discussing the general HCS12
programming and interfacing and hence should be suitable for an embedded system
design course.
xx
Preface
3. Senior electrical engineering and computer engineering students and working
engineers who want to learn the HCS12 and use it in design projects. For this group
of readers, this book provides numerous more complicated examples to explore the
functions and applications of the HCS12.
Prerequisites
The writing of this book has assumed that the reader has taken a course on digital logic design and has been exposed to high-level language programming. Knowledge of digital logic
will greatly facilitate learning the HSC12. Knowledge of assembly language programming is
not required because one of the goals of this book is to teach the HCS12 assembly language
programming.
Approach
Both assembly and C languages are used to illustrate the programming of the HCS12 microcontroller. Learning about the microcontroller using assembly language may produce an intimate
feel for the functioning of the hardware. However, the programming productivity of assembly
language is low because the programmer needs to implement the program logic at a very low
level. C language has the edge in programming productivity. However, the code generated by
a C compiler is still much larger than its equivalent in assembly language. Many time-critical
applications are still written in assembly language, and many applications mix the use of assembly and C languages.
Organization of the Book
Chapter 1 starts with a discussion of the number system issue. It then presents the hardware
structure of the computer, explains how the computer is started and executes instructions,
elaborates the addressing modes, and discusses the operations of a subset of the HCS12 instructions. Chapter 2 starts with the format of the HCS12 assembly program. It then progresses
to discuss assembler directives, software development methodology, arithmetic programming,
program loops, and the HCS12 instructions.
Chapter 3 gives an overview of the hardware and software development tools and then goes
on to give tutorials on the use of MiniIDE and CodeWarrior. A few tips on debugging assembly
programs are also given in this chapter. Chapter 4 discusses the stack data structure, subroutine
mechanism, software reuse, parallel I/O ports, and simple I/O devices. Many subroutine examples, including bubble sort, binary search, 32-bit division, square root, and prime test, are given.
An example is given to illustrate the top-down design and hierarchical refinement system design methodology. Chapter 5 starts with a summary of the syntax of C language. Examples are
then used to illustrate how to write single-function, multiple-function, and multiple-file C programs. Tutorials on the use of CodeWarrior IDE and ImageCraft ICC12 IDE in entering, compiling, and debugging C programs conclude Chapter 5. The tutorial on using the Embedded GNU
(EGNU) IDE is given in Appendix E.
Chapter 6 introduces the concepts of interrupt and reset. Examples are then used to
illustrate how interrupt programming is done in CodeWarrior, ICC12, and EGNU. Topics such
Preface
xxi
as clock generation, real-time interrupt, computer operating properly (COP), and low-power
modes are also elaborated in this chapter. Chapter 7 discusses I/O ports in detail and spells out
the consideration for electrical compatibility. It then continues to elaborate on the topics of
liquid crystal display (LCD), keypad, stepper motor, and D/A converter. Chapter 8 explores the
operation and applications of the timer system, including input-capture, output-compare, realtime interrupt, pulse accumulator, and pulse-width modulation. Chapter 9 deals with serial
communication interface (SCI). Chapter 10 examines the SPI interface and the applications of
the SPI-compatible peripheral chips.
Chapter 11 introduces the I2C protocol and several peripheral chips with I2C interface.
Chapter 12 discusses the A/D converter and its applications in temperature, humidity, and
barometric pressure measurement. Chapter 13 presents the CAN 2.0 protocol and the HCS12
CAN module. Several examples of the programming of the CAN module are provided. Chapter
14 describes the HCS12 internal SRAM, EEPROM, and flash memory. This chapter also explores issues related to external memory expansion: address space assignment, address decoder
design, and timing anaylsis.
Pedagogical Features
Each chapter starts with a list of objectives. Every subject is presented in a step-by-step manner. Background issues are presented before the specifics related to each HCS12 function are
discussed. Numerous examples are then presented to demonstrate the use of each HCS12 I/O
function. Procedural steps and flowcharts are used to help the reader to understand the program logic in most examples. Each chapter concludes with a summary, numerous exercises,
and lab assignments.
Software Development Tools
MiniIDE, AsmIDE, and CodeWarrior are recommended for the development of assembly programs. MiniIDE and AsmIDE require the user to use a demo board programmed with a resident
monitor, for example, D-Bug12. Users can only download their programs onto SRAM for execution. CodeWarrior allows users to perform source-level debugging to their programs, in which
they can quickly identify and locate the program bugs and be able to resolve the problems.
CodeWarrior allows users to download their programs onto both SRAM and flash memory for
execution. CodeWarrior can work with a demo board programmed with the serial monitor (from
Freescale) or with a BDM debug adapter (P&E’s BDM adapters, Abatron’s BDI adapter, Softec’s
inDART, or TBDML).
When using C language to program the HCS12, the CodeWarrior IDE is recommended for
program entering, compiling, and debugging. The source-level debugging capability provided
by CodeWarrior is a great help to pinpoint and locate program errors. The demo version of the
ICC12 IDE from ImageCraft and the freeware Embedded GNU (EGNU) IDE can also be used.
However, the user will need to use a demo board programmed with a resident debug monitor program such as D-Bug12. Neither of them can program the on-chip flash memory of the
HCS12.
The tutorials for using MiniIDE, AsmIDE, CodeWarrior, ICC12, and EGNU IDEs are provided in this textbook.
xxii
Preface
Demo Boards
Demo boards are the most important hardware tool for learning a microcontroller. There are a
few demo boards available to the reader for experimenting with the HCS12 hardware. The combination of the student project board and an HCS12 MCU module (e.g., 9S12C32, 9S12C128, or
9S12DT256) provided by Freescale is a viable option. More information can be found at www
.freescale.com. The user needs to use CodeWarrior to enter, compile (or assemble), and debug
his or her programs with this option.
Another viable demo board for experimenting with the HCS12 is the Dragon12-Plus made
by Wytec. This demo board packs a lot of features and is very popular in universities. The user
can choose a Dragon12-Plus demo board programmed with the D-Bug12 monitor or the serial
monitor. When programmed with the serial monitor, the user will be able to use the sourcelevel debugging capability provided by CodeWarrior. Most programs in this book are tested using the Dragon12-Plus demo board. Information about the Dragon12-Plus demo board can be
found at www.evbplus.com.
To Instructors
It is unnecessary for instructors to follow strictly the order of chapters of this book in their
teaching. If only assembly language programming is to be taught, then the following order is
recommended:
• Chapters 1 through 4 in that order
• Chapters 6 and 7 in either order
• Chapter 8
• Chapters 9 through 14 in any order
If your microprocessor (or microcontroller) course only covers C language, then the following
order is recommended:
• Sections 1.7 and 1.8
• Subjects related to I/O ports in Chapter 4
• Chapter 5
• Chapters 6 and 7 in either order
• Chapter 8
• Chapters 9 through 14 in any order
If both assembly and C languages are to be taught, then the following order is recommended:
• Chapters 1 through 4 in that order
• Chapter 5
• Chapters 6 and 7 in either order
• Chapter 8
• Chapters 9 through 14 in any order
xxiii
Preface
Complementary Material
The following materials are useful to the learning of the HCS12 and are provided in the complementary CD included with this text:
• Source code of all example programs in the text
• The PDF files of datasheets of the HCS12 and peripheral chips
• The software (including CodeWarrior, demo version of ICC12, and EGNU)
• Utility programs (in assembly and C languages) for time delays, LCD, UART, and SPI
Supplements
A CD dedicated to instructors who adopt this text is also available from the publisher. This
CD contains solutions to all exercise problems and the lecture notes in PowerPoint format.
Professors are encouraged to modify the PowerPoint lecture notes to suit their teaching needs.
ISBN: 1-4354-2743-2
Feedback and Update
The author has tried his best to eliminate errors from this text. However, it is impossible to
eliminate all errors. The solutions in the examples of this book may not be the best either. Error
reports and suggestions are welcomed. Please send them to hanwayh@yahoo.com or han-way
.huang@mnsu.edu.
Acknowledgements
This book would not be possible without the help of a number of people. I would like to thank
the following reviewers for their valuable opinions on how to improve the quality of this book:
Shu-Jen Chen, DeVry University; Norm Grossman, DeVry University; and Abhiman Hande,
Lake Superior State University. I would also like to thank Stacy Masucci, acquisition editor, and John Fisher, product manager, of Cengage Learning for their enthusiastic support
during the preparation of this book and the Cengage staff for their outstanding production
work. I would like to express my heart-felt appreciation to my students and colleagues at the
Department of ECET at Minnesota State University, Mankato, who allowed me to test out
the manuscript.
Finally, I would like to express my thanks to my wife, Su-Jane, and my sons, Craig and
Derek, for their encouragement and support during the entire preparation of this book.
Han-Way Huang
Mankato, Minnesota
July 31, 2008
This page intentionally left blank
1
Introduction to the HCS12
Microcontroller
1.1 Objectives
On the successful completion of this chapter,
you should be able to
• Use the appropriate prefix to represent
numbers in different number bases
• Design the arithmetic logic units (ALUs) that
implement multiple functions
• Design the circuit of the program counter
• Explain the memory technology
• Explain the operation of a memory system
• Explain the instruction execution process
• Use addressing modes to specify operands for
the HCS12 instructions
• Use data transfer, add, and subtract instructions
to perform simple operations
2
Chapter 1 ■ Introduction to the HCS12 Microcontroller
1.2 Number System Issue
Computers were initially designed as a number crunching machine. Due to the on-and-off
nature of electricity, numbers were represented in binary base from the beginning of the electronic computer age. However, we are more used to the decimal number system due to the fact
that it has been used for thousands of years. Therefore, entering numbers to be processed by the
computer and outputting numbers to be viewed by the user would be done in decimal format.
Computers need to perform conversion between binary and decimal representations. The unit
used to represent the on or off state is referred to as a bit.
The number of bits used by a computer to represent a number is usually a multiple
of 8. The computation capacity of a computer is also often expressed using the number
of bits that it can operate on in one operation. For example, there are 8-bit, 16-bit, 32-bit,
and 64-bit computers. Computers need to deal with both signed and unsigned numbers.
Two’s complement method is used to represent negative numbers. A number with its most
significant bit set to 1 is negative. Otherwise it is a nonnegative number. Using two’s complement representation allows the computer to use the same circuit to perform addition
and subtraction.
In the microprocessor and microcontroller development tool’s environment, we often see
the mixed use of different number bases. Binary, decimal, octal, and hexadecimal have all been
used. The binary number system has 2 digits: 0 and 1. The octal number system uses 8 different digits: 0 to 7. The decimal number system uses 10 different digits: 0 to 9. The hexadecimal
number system uses 16 different digits: 0 to 9 and A through F. Since these four different number systems share some common digits, ambiguity is unavoidable. To clarify the ambiguity, a
prefix is added to each number to indicate its base. The prefixes used in the Freescale microcontrollers are listed in Table 1.1. Microprocessors and microcontrollers from other vendors may
use a different method to differentiate number bases.
A brief introduction to the conversion among different number bases is given in Appendix B.
Base
Prefix
Example
Binary
Octal
Decimal
Hexadecimal
(shorthand hex)
%
@
%10001010
@123467
12345678
$392
$
Table 1.1 ■ Prefixes for number bases
1.3 Computer Hardware Organization
A computer consists of hardware and software. The hardware of a computer consists of the
processor, input devices, output devices, and memory.
• Processor. The processor is responsible for performing all of the computational
operations and the coordination of the usage of resources of a computer. A
computer system may consist of one or multiple processors. A processor may
perform general-purpose computations or special-purpose computations, such as
graphics rendering, printing, or network processing.
3
1.3 ■ Computer Hardware Organization
• Input devices. A computer is designed to execute programs that manipulate
certain data. Input devices are needed to enter the program to be executed and the
data to be processed into the computer. There are a wide variety of input devices:
keyboards, keypads, scanners, bar code readers, sensors, and so on.
• Output devices. Whether the user uses the computer to perform computations
or to find information from the Internet or a database, the end results must be
displayed or printed on certain media so that the user can see them. There are
many media and devices that can be used to display information: cathode ray tube
(CRT) displays, flat-panel liquid crystal displays (LCDs), seven-segment displays,
printers, light-emitting diodes (LEDs), and so on.
• Memory devices. Users write programs to tell the computer what to do with the
data at hand. Programs to be executed and data to be processed must be stored in
memory devices so that the processor can readily access them.
1.3.1 The Processor
A processor is also referred to as the central processing unit (CPU). A processor consists of
three major components: arithmetic logic unit, control unit, and registers.
THE ARITHMETIC LOGIC UNIT
The Arithmetic Logic Unit (ALU) performs arithmetic and logic operations requested
by the user’s program. The complexity of the ALU varies from one computer to another.
If the processor designer wants to implement more operations directly in the hardware,
then the ALU will get more complicated. An ALU that implements addition, subtraction,
AND, and OR operations is illustrated in Figure 1.1. In Figure 1.1, all four operations are
A
n
B
n
Opcode
CIN 1
0 1
MUX1
Opcode
00 = ADD
01 = SUB
10 = AND
11 = OR
2
0
1
MUX2
Comparator
(=?)
ci
Y
X
Adder
Inverters
n
01
Sum
00
Carry
Cout
AND
OR
n
10
n
MUX3
01
11
Figure 1.1 ■ An ALU that implements ADD, SUB, AND, and OR operations
n
Result
4
Chapter 1 ■ Introduction to the HCS12 Microcontroller
performed simultaneously by different circuits whereas the opcode tells the multiplexer to
select one of the four units’ outputs as the result. The adder is used to perform addition
and subtraction operations.
The four-operation ALU operates in the following manner:
When opcode 5 00, the adder selects the n-bit A as its X input, the n-bit B as its Y input,
and CIN as its Carry input (ci) and generates SUM and Carry. MUX3 selects SUM to become
Result whereas Carry is connected to Cout directly. For this opcode, the ALU performs the ADD
operation.
When opcode 5 01, the comparator output is 1, the inversion of the B input is selected as
the Y input, and 1 is selected as the ci to the adder. The adder essentially adds the two’s complement of B to A, which is equivalent to performing the SUB operation. Since opcode is 01 and
SUM is connected to both the 00 and 01 inputs, it will be selected and sent to Result. For this
opcode, the ALU performs the SUB operation.
When opcode 5 10, the MUX3 multiplexer selects the value connected to the 10 input and
sends it to Result. Therefore, the ALU performs the AND operation.
When opcode 5 11, the MUX3 multiplexer selects the value connected to the 11 input and
sends it to Result. Therefore, the ALU performs the OR operation.
An ALU that performs more operations can be implemented by expanding the circuit
shown in Figure 1.1.
CONTROL UNIT
From the beginning, the electronic digital computer is designed to execute machine
instructions only. A machine instruction is a combination of 0s and 1s. To simplify the computer hardware design, most computers limit the instruction length to a few choices that are
a multiple of 8 bits. For example, the HCS12 microcontroller from Freescale has instructions
that are 8 bits, 16 bits, 24 bits, 32 bits, 40 bits, and 48 bits. You don’t see instructions that are
13 bits, 29 bits, and so on.
A machine instruction has several fields. A mandatory field for every instruction is opcode,
which tells the ALU what operation to perform. Other fields are optional; when they exist, they
specify the operand(s) to be operated on.
To make the instruction execution time predictable, a clock signal is used to synchronize
and set the pace of instruction execution. A clock signal is also needed to control the access of
registers in the processor and external memory. The clock frequencies of the 8-bit and 16-bit
microcontrollers range from a few MHz to over 100 MHz.
Since a program consists of many machine instructions, there is a need to keep track of
what instruction to execute next. The control unit has a register called program counter (PC)
that serves this function. Whenever the processor fetches an instruction from memory, the program counter will be incremented by the length of that instruction so that it points to the next
instruction. The fetched instruction will be placed in the instruction register (IR), decoded, and
executed. During this process, appropriate control signals will be generated to control the hardware circuit operation.
A program is normally not sequential. The execution order of machine instructions may
be changed due to the need to execute instructions on the basis of the value of a certain
condition or to repeat a group of instructions. This is called program flow control. The
decision to change program flow is often based on certain conditions, for example, whether
the previous instruction caused Carry out to be 1, whether the result of the previous operation is 0, or whether the result of the previous operation is negative. These conditions are
often collected in a status register so that they can be used to make a decision. This type of
program flow change is implemented by a conditional branch instruction (may also be called
1.3 ■ Computer Hardware Organization
5
conditional jump instruction). There is a limit to the distance that the processor can branch
(or jump) conditionally. The branch distance (referred to as branch offset) is from 2128 byte
to 127 bytes for most 8-bit and 16-bit microcontrollers because they use 8 bits to specify
branch offset. In other situations, the programmer wants to force the processor to execute the
instruction in any location within the available memory space. A jump instruction is used for
this purpose. The target of jump may be specified in 16 bits, 32 bits, or 64 bits depending on
the width of the program counter.
It is easy to figure out that writing programs in machine instructions is extremely
difficult. Over the years, assembly language and high-level languages such as FORTRAN,
COBOL, BASIC, C, C11, JAVA, and so on have been invented. Assembly language uses a
mnemonic symbol to represent each machine instruction. The result is that each machine
instruction is represented by an assembly instruction. The programmer can see the assembly
instruction and figure out what operation is going to be performed quickly for most instructions. The assembly language makes programming much easier than in machine language.
However, assembly language is still at a very low level. It is not very productive to write large
and complicated programs in assembly language. Moreover, it needs a translator, called an
assembler, to translate assembly instructions into machine instructions so that they can be
executed by the computer.
High-level language is at a much higher level. Therefore, one statement written in highlevel language may be translated into tens or even hundreds of machine instructions. A program
written in high-level language also needs a translator to translate it into machine instructions
so that it can be executed by the computer. The translator of a high-level programming language is called a compiler. The translation from high-level language to machine language is
often not optimal. Therefore, there are some applications that require very tight performance
controls that are still written in assembly language. It is not unusual to find large programming
projects that are written in both assembly and high-level languages.
Programs written in assembly language or high-level languages are referred to as source
code whereas the outputs of assembler and compiler are called object code.
REGISTERS
A register is a storage location inside the CPU. It is used to hold data and/or a memory
address during the execution of an instruction. Because the register is very close to the CPU,
it can provide fast access to operands for program execution. The number of registers varies
greatly from processor to processor.
A processor may add a special register called an accumulator and include it as one of the
operands for most instructions. The Intel 8051 variants, the Microchip PIC18, and the Freescale
HCS12 microcontroller use this approach. Using the dedicated accumulator as one of the operands can shorten the instruction length. Other processors, for example, Atmel AVR and Microchip PIC32, may include many general-purpose data registers (16 or 32) in the CPU and allow
any data register to be used as any operand of most instructions with two or three operands.
This provides great freedom to the compiler during the program translation process. A processor designed using this approach is considered to be orthogonal.
1.3.2 Microprocessor
The earlier processors may be implemented in one or multiple printed circuit boards. With
the advancement of integrated circuit technology, a complete processor can be implemented in
one integrated circuit (an integrated circuit is often called a chip). A microprocessor is a processor implemented in a single integrated circuit.
6
Chapter 1 ■ Introduction to the HCS12 Microcontroller
In 1968, the first microprocessors Intel 4004 and TI TMS 1000 were introduced. Both the
Intel 4004 and TI TMS 1000 are 4-bit microprocessors. In 1972, Intel introduced the Intel 8008,
which was the first 8-bit microprocessor in the world. Several other 8-bit microprocessors were
introduced after the Intel 8008 including the Intel 8080, Zilog Z80, Motorola 6800, Rockwell
6502, and so on. Microprocessors were quickly used as the controller of many products. Because
of their small size (compared to discrete logic), programmability, ease of use, and low cost,
microprocessors were well received and quickly replaced discrete logic devices.
However, the microprocessor still has a few disadvantages.
1. The microprocessor does not have on-chip memory. The designer needs to add external
memory chips and other glue logic circuit such as decoder and buffer chips to provide
program and data storage.
2. The microprocessor cannot drive the input/output (I/O) devices directly due to the fact
that the microprocessor may not have enough current to drive the I/O devices or the
voltage levels between the microprocessor and I/O devices may be incompatible. This
problem is solved by adding peripheral chips as a buffer between the microcontroller and
the I/O devices. The Intel 8255 parallel interface chip is one of the earliest interface chips.
3. The microprocessor does not have peripheral functions such as parallel I/O ports,
timers, analog-to-digital (A/D) converter, communication interface, and so on. These
functions must be implemented using external chips.
Because of these limitations, a product designed with microprocessors cannot be made as
compact as might be desired. One of the design goals of microcontrollers is to eliminate these
problems.
1.3.3 Microcontroller
A microcontroller (MCU) incorporates the processor and one or more of the following
modules in one very large-scale integrated circuit (VLSI):
• Memory
• Timer functions
• Serial communication interfaces such as the Universal Synchronous Asynchronous
Receiver Transmitter (USART), serial peripheral interface (SPI), interintegrated
circuit (I2C), and controller area network (CAN)
• A/D converter
• Digital-to-analog (D/A) converter
• Direct memory access (DMA) controller
• Parallel I/O interface (equivalent to the function of Intel 8255)
• Memory component interface circuitry
• Software debug support hardware
The discussion of these functions is the subject of this textbook. Since their introduction,
MCUs have been used in almost every application that requires a certain amount of intelligence. They are used as controllers for displays, printers, keyboards, modems, charge card
phones, palm-top computers, and home appliances such as refrigerators, washing machines,
and microwave ovens. They are also used to control the operations of engines and machines in
factories. One of the most important applications of MCUs is probably the automobile control.
Today, a luxurious car may use more than 100 MCUs. Today, most homes have one or more
MCU-controlled consumer electronics appliances.
1.3 ■ Computer Hardware Organization
7
1.3.4 Embedded Systems
An embedded system is a special-purpose computer system designed to perform a dedicated
function. Unlike a general-purpose computer, such as a personal computer, an embedded system performs one or a few predefined tasks, usually with very specific requirements, and often
includes task-specific hardware and mechanical parts not usually found in a general-purpose
computer. Since the system is dedicated to specific tasks, design engineers can optimize it, reducing the size and cost of the product. Embedded systems are often mass produced, benefiting
from economy of scale.
Physically, embedded systems range from portable devices such as digital watches and MP3
players, to large stationary installations like traffic lights, factory controllers, or the systems
that control power plants. In terms of complexity, embedded systems run from simple, with
a single microcontroller chip, to very complex with multiple units, peripherals, and networks
mounted inside a large chassis or enclosure.
Mobile phones or handheld computers share some elements with embedded systems, such
as the operating systems and microprocessors that power them, but are not truly embedded systems themselves because they tend to be more general purpose, allowing different applications
to be loaded and peripherals to be connected.
CHARACTERISTICS OF EMBEDDED SYSTEMS
Embedded systems have the following characteristics:
• Embedded systems are designed to perform some specific task, rather than being a
general-purpose computer for multiple tasks. Some also have real-time performance
constraints that must be met, for reasons such as safety and usability; others may
have low or no performance requirements, allowing the system hardware to be
simplified to reduce costs.
• An embedded system is not always a separate block; very often it is physically built
into the device it is controlling.
• The software written for embedded systems is often called firmware and is stored
in read-only memory or flash memory chips rather than a disk drive. It often runs
with limited computer hardware resources: small or no keyboard or screen and
little memory.
U S E R I N T E R FA C E S
Embedded systems range from having no interface at all—dedicated to only one task—to
full user interfaces similar to desktop operating systems in devices such as personal digital
assistants (PDAs).
A simple embedded system may use buttons for input and use LEDs or small characteronly display for output. A simple menu system may be provided for users to interface with.
A more complex system may use a full graphical screen that has touch sensing or screenedge buttons to provide flexibility while at the same time minimize space. The meaning of the
buttons can change with the screen.
Handheld systems often have a screen with a “joystick button” for a pointing device. The
rise of the World Wide Web has given embedded designers another quite different option: providing a webpage interface over a network connection. This avoids the cost of a sophisticated
display, yet provides complex input and display capabilities when needed, on another computer.
This is successful for remote, permanently installed equipment. In particular, routers take
advantage of this ability.
8
Chapter 1 ■ Introduction to the HCS12 Microcontroller
1.4 Memory
There are three major memory technologies in use today: magnetic, optical, and
semiconductor.
1.4.1 Magnetic Memory
Magnetic drum, magnetic tape, and magnetic hard disk are three major magnetic memory
devices that have been invented. Magnetic drum has long been obsolete, and magnetic tape
is only used for data archival. Currently only magnetic hard disk is still being used in almost
every PC, workstation, server, and mainframe computer. Hard-drive vendors are still vigorously improving the hard-disk density. It doesn’t seem possible that any memory technology
can totally replace the hard disk yet.
1.4.2 Optical Memory
There are two major optical memory technologies in use today: compact disc (CD) and
digital videodisc (DVD). The CD was introduced to the market in 1982 and has several variations.
The most popular single-sided CD has a 12-cm diameter and can hold about 700 MB of data.
The CD-R version of the compact disc can be recorded once whereas the CD-RW disc can be rerecorded many times. A single-sided DVD with 12-cm diameter can hold 4.7 GB of data. There are
several versions of the DVD; among them, DVD-R can be recorded only once whereas DVD-RW
can be re-recorded many times by the end user. There are several possible competing successors to
the current DVD technology. They have single-sided capacities ranging from 15 to 25 GB.
1.4.3 Semiconductor Memory
Semiconductor memory is the dominant memory technology used in embedded systems.
Memory technologies can be classified according to several criteria. Two common criteria are
volatility and read-writability. On the basis of volatility, semiconductor memories are divided
into volatile and nonvolatile memories. On the basis of read-writability, semiconductor memories are divided into random-access memory (RAM) and read-only memory (ROM).
1.4.4 Nonvolatile and Volatile Memory
A memory device is nonvolatile if it does not lose the information stored in it even without
the presence of power. If a memory device cannot retain its stored information in the absence of
power, then it is volatile.
1.4.5 Random-Access Memory
Random-access memory allows the CPU to read from or write to any location within the
chip for roughly the same amount of time. RAM can be volatile or nonvolatile. RAM is also
called read/write memory because it allows the processor to read from and write to it. As long as
the power is on, the microprocessor can write data to a location in the RAM chip and later read
back the same contents. Reading memory is nondestructive. Writing memory is destructive.
When the microprocessor writes data to memory, the old data is written over and destroyed.
There are four types of commercially available RAM technology: dynamic RAM (DRAM),
static RAM (SRAM), magnetoresistive RAM (MRAM), and ferroelectric RAM (FRAM).
DRAMs are memory devices that require periodic refreshes of the stored information. Refresh
is the process of restoring binary data stored in a particular memory location. The dynamic RAM
uses one transistor and one capacitor to store 1 bit of information. The information is stored in
1.4 ■ Memory
9
the capacitor in the form of electric charges. The charges stored in the capacitor will leak away
over time, so periodic refresh operations are needed to maintain the contents in the DRAM. The
time interval over which each memory location of a DRAM chip must be refreshed at least once
in order to maintain its contents is called its refresh period. Refresh periods typically range from a
few milliseconds to over a hundred milliseconds for today’s high-density DRAMs.
SRAMs are designed to store binary information without needing periodic refreshes and
require the use of more complicated circuitry for each bit. Four to six transistors are needed to
store 1 bit of information. As long as power is stable, the information stored in the SRAM will
not be degraded.
MRAMs were first developed by IBM. Several other companies were also involved in the
research, development, and marketing of this technology. MRAMs use a magnetic moment to
store data. A MRAM chip combines a magnetic device with standard silicon-based microelectronics to achieve the combined attributes of nonvolatility, high-speed operation, and unlimited read and write endurance. The first MRAM device from Freescale is the 4-Mbit MR2A16A.
This device is a parallel memory (8 or 16 bits can be accessed in one operation) and has a 35 ns
access time, reported in 2007.
FRAMs use the property of ferroelectric crystal to store data bits. Much of the present
FRAM technology was developed by Ramtron International. The FRAM technology has already
achieved high maturity. Both the serial and parallel versions of FRAM chips are available. Ramtron even incorporates FRAM in some of its 8051 microcontroller products. The fastest access
time of FRAM from Ramtron is 55 ns, reported in 2007. However, the access time of FRAM
may improve in the future.
RAM is mainly used to store dynamic programs or data. A computer user often wants to
run different programs on the same computer, and these programs usually operate on different
sets of data. The programs and data must therefore be loaded into RAM from the hard disk or
other secondary storage, and for this reason they are called dynamic.
1.4.6 Read-Only Memory
ROM is nonvolatile. When power is removed from ROM and then reapplied, the original
data will still be there. As its name implies, ROM data can only be read. If the processor attempts
to write data to a ROM location, ROM will not accept the data, and the data in the addressed
ROM memory location will not be changed. However, this statement is not completely true. For
some ROM technologies (EEPROM and flash memory), the user program can still write data into
the memory by following a special procedure prescribed by the manufacturer. However, it would
take a much longer time to write than to read from the flash memory.
Mask-programmed read-only memory (MROM) is a type of ROM that is programmed when
it is manufactured. The semiconductor manufacturer places binary data in the memory according
to the request of the customer. To be cost-effective, many thousands of MROM memory units,
each consisting of a copy of the same data (or program), must be sold. MROM is the major memory
technology used to hold microcontroller application programs and constant data. Most people
simply refer to MROM as ROM. The design of MROM prevents it from being written into.
Programmable read-only memory (PROM) was invented in 1956 by Wen Tsing Chow. It
is a form of memory where the setting of each bit is locked by a fuse or antifuse. The memory
can be programmed just once after manufacturing by blowing the fuses (using a PROM blower),
which is an irreversible process. Blowing a fuse opens a connection whereas blowing an antifuse
closes a connection (hence the name). Programming is done by applying high-voltage pulses
that are not encountered during normal operation (typically 12 to 21 volts). Fused-based PROM
technology is no longer in use today.
10
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Erasable programmable read-only memory (EPROM) was invented by the Israeli engineer
Dov Frohman in 1971. It is an array of floating-gate transistors individually programmed by an
electronic device that supplies higher voltages than those normally used in electronic circuits.
Programming is achieved via hot carrier injection onto the floating gate. Once programmed, an
EPROM can be erased only by exposing it to strong ultraviolet (UV) light. That UV light usually
has a wavelength of 235 nm for optimum erasure time. EPROMs are easily recognizable by the
transparent fused quartz window in the top of the package, through which the silicon chip can
be seen and which permits UV light to go through during erasing.
As the quartz window is expensive to make, one-time programmable (OTP) chips were
introduced; the only difference is that the EPROM chip is packed in an opaque package, so it
cannot be erased after programming. OTP versions are manufactured for both EPROMs themselves and EPROM-based microcontrollers. However, OTP EPROM (whether separate or part of
a larger chip) is being increasingly replaced by EEPROM for small amounts where the cell cost
isn’t too important and flash memory is used for larger amounts.
A programmed EPROM retains its data for about 10 to 20 years and can be read an unlimited
number of times. The erasing window must be kept covered with a foil label to prevent accidental erasure by sunlight. Old PC basic input/output system (BIOS) chips were often EPROMs,
and the erasing window was often covered with a label containing the BIOS publisher’s name,
the BIOS revision, and a copyright notice.
Electrically erasable programmable read-only memory (EEPROM) was developed in 1983
by George Perlegos at Intel. It was built on earlier EPROM technology, but used a thin gate oxide layer so that the chip could erase its own bits without requiring a UV source. EEPROM is
programmed and erased using the process called field emission (more commonly known in the
industry as Fowler-Nordheim tunneling). EEPROM allows the user to selectively erase a single
location, a row, or the whole chip. This feature requires a complicated programming circuitry.
Because of this, the EEPROM cannot achieve the density of the EPROM technology.
Flash memory was invented by Fujio Masuoka while working for Toshiba in 1984. Flash
memory incorporates the advantages and avoids the drawbacks of EPROM and EEPROM technologies. The flash memory can be erased and reprogrammed in the system without using a
dedicated programmer. It achieves the density of EPROM, but it does not require a window for
erasure. Like EEPROM, flash memory can be programmed and erased electrically. However, it
does not allow individual locations to be erased; the user can only erase a block or the whole
chip. Today, the BIOS programs of many high-performance PCs are stored in flash memory.
Most microcontrollers introduced today use on-chip flash memory as their program memory.
Flash memory chips have also been used in flash disk memory, personal digital assistants,
digital cameras, cell phones, and so on.
1.5 Memory System Operation
A simplified memory system block diagram is shown in Figure 1.2. A memory system may
consist of one or multiple memory chips. Both memory chips and memory systems are organized as an array of memory locations. A memory location may hold any number of bits (most
common numbers are 4 bits, 8 bits, 16 bits, 32 bits, and 64 bits). The memory organization of a
memory chip or a memory system is often indicated by m 3 n; where m specifies the number of
memory locations in the memory chip or memory system and n specifies the number of bits in
each location. Every memory location has two components: contents and address.
A memory location can be used to store data, instruction, and the status of peripheral
devices. The size of memory is measured in bytes; a byte consists of 8 bits. A 4-bit quantity is
11
1.5 ■ Memory System Operation
Microprocessor
Address
Memory system
RD
OE
WR
WE
Data
Figure 1.2 ■ Block diagram of a simplified memory system
called a nibble. A 16-bit quantity is called a word. To simplify the quantification of memory,
the units kilobyte (kB), megabyte (MB), and gigabyte (GB) are often used. The value of k is given
by the following formula:
k 5 210 5 1024
M is given by the following formula:
M 5 k2 5 220 5 1024 3 1024 5 1,048,576
G is given by the following formula:
G 5 k3 5 230 5 1024 3 1024 3 1024 5 1,073,741,824
In this book, we will use the notation [addr] and [reg] to refer to the contents of a memory
location at addr and the contents of register reg, respectively. We will use the notation m[addr] to
refer to the memory location at addr. For example,
[$40]
refers to the contents of the memory location at $40 and [A] refers to the contents of accumulator A. The notation m[$40] â [A] refers to saving the contents of accumulator A in the memory
location at $40. Registers are referred to by their names whereas memory locations are referred
to by their addresses.
The memory chip or memory system can only be accessed (read or written) one location at
a time. This is enforced by implementing a decoder on the memory chip to select one and only
one location to be accessed. There are two types of memory accesses: read and write.
1.5.1 Read Operation
Whenever the processor wants to read a memory, it sends out the address of the location
it intends to read. Since the memory access can be a read or a write, the processor needs to use
a control signal to inform the memory of the type of access. In Figure 1.2, the RD signal from
the processor indicates a read access whereas the WR signal indicates a write operation. The
memory chip also has control signals to control the read or write operation. The OE signal in
Figure 1.2 means output enable and is connected to the RD signal from the processor whereas
the WE signal means write enable and is connected to the WR signal from the processor. For
digital systems, there are three logic states for each signal: high, low, and high impedance
(no current flows). When the OE input to the memory chip is low, the data pins are in a highimpedance state.
The processor uses a set of signals, referred to as address signals, to specify a memory location to access. The number of address signals needed for selecting a memory location is log2 m,
12
Chapter 1 ■ Introduction to the HCS12 Microcontroller
where m is the number of memory locations in the memory. The set of conductor wires that
carry address signals is referred to as the address bus.
The number of conductor wires that carry the data must be equal to the number of bits in
each memory location. The set of conductors that carry the data to be accessed is called the
data bus.
To read a memory location, the processor sends out the address of the memory location to
be accessed and applies a logic 1 (high voltage) to the RD signal and a logic 0 to the WR signal
(this specifies a read operation). In response, the memory system decodes the address input and
enables the specified memory location to send out its contents to the data bus to be read by the
processor.
1.5.2 Write Operation
To write a value to a location of the memory system in Figure 1.2, the processor places the
data to be written on the data bus and places the address of the memory location on the address
bus and applies a logic 1 (high voltage) to the WR signal and a logic 0 to the RD signal (this
specifies a write operation). In response, the memory system uses its address decoder to select a
location and writes the value on the data bus to that location.
The actual memory system design and the signals involved may be different from those in
Figure 1.2 but the concept would be the same. The semiconductor vendors may use RD instead
of RD and WR instead of WR to refer to read and write signals. These types of signals are active
low; that is, when they are low, they are considered to be at logic 1.
1.6 Program Execution
In order to allow the computer to execute the program immediately after the power is
turned on, part of the program must be stored in nonvolatile memory. Some computers placed
the startup program in the nonvolatile memory, which will perform the system initialization.
After the system initialization is completed, it loads additional programs from secondary storage such as hard disk or optical storage into the semiconductor memory (often called main
memory) for execution. Mainframe computers, workstations, and personal computers follow
this approach. After power is turned on, the processor starts to execute the program from the
BIOS, which performs the system initialization. After system initialization is completed, the
processor loads additional programs such as Windows operating system into the main memory
for execution. Other computers, including most embedded systems, place all their programs in
the nonvolatile memory. After power-up, the processor starts to execute the program from the
nonvolatile memory.
The following sections deal with several important issues related to program execution.
1.6.1 The Circuit of the Program Counter
The program counter consists of flip-flops and other additional logic gates. There are several types of flip-flops in use. Among them, the D-type flip-flop is the most popular one. The
circuit of a D-type flip-flop with set and reset capability is shown in Figure 1.3.
In Figure 1.3,
• Depending on the design, the D value may be transferred to Q on either the rising
or the falling edge (but not both edges) of the CLK input.
• The CLK signal is the clock input signal of the D flip-flop.
13
1.6 ■ Program Execution
D
Q
CLK
Set
Reset
Figure 1.3 ■ Block diagram of a D flip-flop with a set and reset
• The Q signals of all the flip-flops of the program counter determine the address of
the next instruction to be fetched.
• The set and reset inputs are active low (low voltage means logic 1) and cannot be
low at the same time. When set is low, the Q signal is forced to 1. When reset is
low, the Q signal is forced to 0.
As described in Section 1.3.1, a microprocessor or microcontroller has instructions to
change the program flow. The design of the program counter circuit must take this into account.
Figure 1.4 shows the block diagram of a program counter of an 8-bit microcontroller that allows
the program counter to be
• Forced to 0
• Incremented by 1
• Incremented by a field in the IR
• Loaded with a jump target
1
Branch
Branch
offset
0 1
MUX1
16
Adder
Qs
Jump
target
16
1 0
MUX2
16
16
Ds
CLK
Clk
Reset
Power
on
Jump
Figure 1.4 ■ A simplified block diagram of the program counter (PC) of an 8-bit microcontroller
14
Chapter 1 ■ Introduction to the HCS12 Microcontroller
For the program counter circuit shown in Figure 1.4,
• Whenever power is turned on to the microcontroller, the program counter is forced
to 0 and the instruction fetch will start from address 0.
• If the instruction being executed is a conditional branch instruction and the branch
condition is true, then the branch signal will be 1, the sum of the current PC and
branch offset will be loaded into the PC, and instruction execution will continue
from that address.
• If the instruction being executed is a jump instruction, then the value Jump target
will be loaded into the PC.
• If the instruction being executed is not a program flow control instruction, then
the PC is simply incremented by 1 after each instruction byte is fetched.
Other microprocessors or microcontrollers may have a different program flow control
scheme and may fetch more instruction bytes in one fetch. In that case the program counter
shown in Figure 1.4 will need to be modified accordingly.
1.6.2 Where Does the Processor Start to Execute the Program?
As discussed earlier in this chapter, the program counter holds the address of the next
instruction to be fetched, so the value of the program counter must be known when power
is turned on. One approach is to force the PC to a fixed value when power is turned on. The
circuit shown in Figure 1.4 forces the PC to 0 whenever power is turned on. Many 8-bit microcontrollers including Microchip PIC, all Intel 8051 variants, and Atmel AVR use this approach
because it is easy to implement.
Another approach is to fetch the program starting address from a fixed (known) memory
location whenever the power is turned on. The Freescale microcontrollers use this approach.
The HCS12 microcontroller from Freescale fetches the program starting address from memory
locations at 0xFFFE and 0xFFFF into PC and then start program execution from there. This
approach is slightly more complicated.
Another way to restart program execution is to apply a reset signal to the processor. All
microprocessors and microcontrollers have a reset pin that allows the user to force the processor to start from scratch. The effect is identical to turning on the power.
1.6.3 Instruction Execution Process
The instruction sets of most commercial processors are irregular and complicated. The
complexity of the instruction set makes it difficult to explain the instruction execution process. In the following, we assume that there is an 8-bit processor X with instruction set shown
in Table 1.2. The opcode of any instruction is 1 byte and is always the first byte of the instruction. The processor X has an 8-bit accumulator A and a 16-bit pointer register ptr. The data
memory and program memory are separate and are each 64 kB in size. The register ptr is used to
point to data memory and supports indirect memory addressing for data memory. The instruction set of the processor X allows the instructions to use an 8-bit address to access the lowest
256 bytes (addresses 0 to 255) of data memory. The processor X can use the 16-bit ptr register to
access any location of the 216 data memory locations.
To facilitate the access of data memory, processor X includes the memory data register (MDR) to hold the data received from data memory and data to be written to the data
memory.
15
1.6 ■ Program Execution
Assembly Instruction
Mnemonic
ld
ld
ld
and
bnz
inc
dbnz
Note:
addr, #val
ptr, #data16
A, @ptr
A, #val
addr, offset
addr
addr, offset
Machine
Code
75 aa xx
90 yyyy
E0
54 xx
70 zz
05 aa
D5 aa zz
Meaning
Load the 8-bit value (val) into memory location at addr.
Load the 16-bit value (data16) into the register ptr.
Load the contents of memory location pointed to by ptr into A.
And the 8-bit value (val) with A and leave the result in A.
Branch to a location that is offset from the next instruction if the value at addr is not zero.
Increment the contents of memory location at addr.
Decrement the contents of memory location at addr and branch if the result is not
zero. The branch distance is offset.
aa: an 8-bit value that represent an 8-bit address.
xx: an 8-bit value.
yyyy: a 16-bit value.
zz: distance of branch from the first byte of the instruction after the branch instruction.
Machine codes are expressed in hex format.
Table 1.2 ■ The instruction set of the processor X
1.6.4 Instruction Sequence Example
Assume that the following instruction sequence is stored in the program memory starting
from address 0 so that it will be executed immediately after a power-on or reset:
loop:
next:
ld
ld
ld
ld
and
bnz
inc
dbnz
0x20,#0
0x21,#20
ptr,#0x2000
A,@ptr
A,#0x03
next
0x20
0x21,loop
; place 0 in data memory located at address 0x20
; place 20 in data memory located at address 0x21
; load 0x2000 into the register ptr
; load the memory contents pointed to by ptr into A
; and the value 0x03 with A and leave the result in A
; branch if the result in A is not 0
; increment the memory location at 0x20 by 1
; decrement the memory location at 0x21 and branch if
; the result is not 0
The corresponding machine code of the given instruction sequence is shown in Table 1.3.
The next section explains the process of instruction execution.
1.6.5 Instruction Execution Process
Processor X executes the instruction sequence given in Table 1.3 as follows:
Instruction ld 0x20,#0 (machine code 75 20 00)
When the processor comes out of a reset or power-on process, the program counter is
forced to 0 and this instruction will be fetched and executed. The execution of this instruction
involves the following steps:
Step 1
The value in the PC (0x0000) is placed on the address bus of the program memory with a
request to read the contents of that location.
16
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Assembly Instruction
Mnemonic
loop:
next:
Note:
ld
ld
ld
ld
and
bnz
inc
dbnz
0x20,#0
0x21,#20
ptr,#0x2000
A,@ptr
A,#0x03
next
0x20
0x21,loop
Address
Machine Code
0x0000
0x0003
0x0006
0x0009
0x000A
0x000C
0x000E
0x0010
75 20 00
75 21 14
90 20 00
E0
54 03
70 02
05 20
D5 21 0A
Comment
02 is the branch offset.
0A is the branch offset.
1. The user uses a label to specify the instruction to branch to and the assembler needs to
figure out the branch offset.
2. The assembler figures out that the label next is 2 bytes away from the inc 0x20
instruction.
3. The assembler figures out that the label loop is 10 (0A) bytes away from the first byte
after the “next: dbnz 0x21,loop” instruction.
Table 1.3 ■ The processor X instruction sequence to be executed
Step 2
The 8-bit value at the location 0x0000 is the instruction opcode 0x75. At the end of this
read cycle, the PC is incremented to 0x0001. The opcode byte 0x75 is fetched. Figure 1.5
shows the opcode read cycle.
Step 3
Control unit recognizes that this version of the ld instruction requires one read cycle
to fetch the direct address and another cycle to read the data operand. These 2 bytes are
stored immediately after the opcode byte. Two more read cycles to program memory are
performed to access the data memory address 0x20 (held in IR) and the value 0x00 (held in
IR). After these two read cycles, the PC is incremented to 0x0003.
Step 4
Control unit places 0x0020 on the data memory address bus and the value 0x00 on the
data memory data bus to perform a write operation. The value 0x00 is to be stored at data
memory location 0x0020, as shown in Figure 1.6.
Instruction ld 0x21,#20 (machine code 75 21 14)
The execution of this instruction is identical to that of the previous instruction. After the
execution of this instruction, the PC is incremented to 0x0006 and the data memory location
0x21 receives the value of 20.
Instruction ld ptr,#0x2000 (machine code 90 20 00)
Step 1
The value in the PC (0x0006) is placed on the program memory address bus with a request
to read the contents of that location.
Step 2
The 8-bit value at the location 0x0006 is the instruction opcode 0x90. At the end of this
read cycle, the PC is incremented to 0x0007. The opcode byte 0x90 is fetched. Figure 1.7
shows the opcode read cycle.
17
1.6 ■ Program Execution
Program memory
address bus
75
20
00
75
21
14
90
0x0000
20
00
E0
CPU
54
03
70
02
05
20
D5
21
0A
Before read
0x75
0x0000
PC
Program memory
data bus
After read
0x0001
PC
Figure 1.5 ■ Instruction 1–opcode read cycle
Data memory
Data memory
address bus
0x20
xx
xx
xx
CPU
Before write
0x0003
PC
0x00
After write
Data memory
data bus
0x0003
xx
xx
xx
xx
xx
xx
xx
PC
Figure 1.6 ■ Instruction 1–data memory write cycle
xx
xx
xx
xx
xx
xx
xx
18
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Program memory
address bus
0x0006
CPU
Before read
0x0006
PC
0x90
Program memory
data bus
After read
0x0007
PC
75
20
00
75
21
14
90
20
00
E0
54
03
70
02
05
20
D5
21
0A
Figure 1.7 ■ Instruction 3–opcode read cycle
Step 3
The control unit recognizes that this instruction requires two more read cycles
to the program memory to fetch the 16-bit value to be placed in the ptr register.
These 2 bytes are stored immediately after the opcode byte. The control unit
continues to perform two more read cycles to the program memory. At the end of
each read cycle, the processor X stores the received byte in the ptr register upper
and lower bytes, respectively. After these two read operations, the PC is incremented
to 0x0009.
Instruction ld A,@ptr (machine code E0)
Step 1
The value in the PC (0x0009) is placed on the program memory address bus with a request
to read the contents of that location.
Step 2
The 8-bit value at the location 0x0009 is the instruction opcode 0xE0. At the end of
this read cycle, the PC is incremented to 0x000A. The opcode byte 0xE0 is fetched.
Step 3
The control unit recognizes that the current instruction requires performing a read
operation to the data memory with the address specified by the ptr register. The processor
places the 16-bit value of the ptr register on the data memory address bus and indicates
this is a read operation.
19
1.6 ■ Program Execution
Step 4
The data memory returns the contents to the processor and the processor places it in
accumulator A. The process is shown in Figure 1.8.
Instruction and A,#0x03 (machine code 54 03)
Step 1
The value in the PC (0x000A) is placed on the program memory address bus with a request
to read the contents of that location.
Step 2
The 8-bit value at the location 0x000A is the instruction opcode 0x54. At the end of this
read cycle, the PC is incremented to 0x000B. The program memory returns the opcode
byte 0x54 to the CPU.
Step 3
The control unit recognizes that the current instruction requires performing a
read operation on the program memory to fetch the operand for the AND operation.
It then places the PC value on the program memory address bus again with a read
request.
Step 4
The program memory returns the value 0x03 to the CPU. The PC is incremented
to 0x000C.
Step 5
The CPU then performs an AND operation on the contents of accumulator A and the
value 0x03 and places the result in A.
Data memory
Data memory
address bus
[ptr]
xx
xx
xx
CPU
[ptr]: contents of ptr
Mem[[ptr]]: contents of memory
location pointed to by ptr
xx
xx
xx
xx
xx
xx
xx
Mem[[ptr]]
Data memory
data bus
Figure 1.8 ■ Instruction 4–data memory and read cycle
xx
xx
xx
xx
xx
xx
xx
20
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Instruction bnz next (machine code 70 02)
Step 1
The value in the PC (0x000C) is placed on the program memory address bus with a request
to read the contents of that location.
Step 2
The 8-bit value at the location 0x000C is the instruction opcode 0x70. At the end of this
read cycle, the PC is incremented to 0x000D. The program memory returns the opcode
byte 0x70 to the CPU.
Step 3
The processor recognizes that this is a conditional branch instruction and it needs to fetch
the branch offset from the program. So it places the PC value (0x000D) on the program
memory address bus with a read request. At the end of this read cycle, the processor
increments the PC to 0x000E.
Step 4
The program memory returns the branch offset 0x02 to the CPU (held in IR). The
CPU checks the contents of accumulator A to determine whether the branch should
be taken. Let’s assume that A contains zero and the branch is not taken. The PC
remains at 0x000E. If A contains a nonzero value, the next instruction will be
skipped.
Instruction inc 0x20 (machine code 05 20)
Step 1
The value in the PC (0x000E) is placed on the program memory address bus with a request
to read the contents of that location.
Step 2
The 8-bit value at the location 0x000E is the instruction opcode 0x05. At the end of this
read cycle, the PC is incremented to 0x000F. The program memory returns the opcode
byte 0x05 to the CPU.
Step 3
The processor recognizes that it needs to increment a data memory location; this requires
it to fetch an 8-bit address from the program memory.
Step 4
The processor places the value in the PC on the program memory address bus with a read
request. At the end of the read cycle, the PC is incremented to 0x0010 and the value 0x20 is
returned to the CPU and is placed in the IR register.
Step 5
The processor places the value 0x20 on the data memory address bus with a request to
read the contents of that location. The data memory returns the value of that memory
location at the end of the read cycle, which will be placed in the MDR.
Step 6
The processor adds 1 to the MDR.
Step 7
The processor places the contents on the data memory data bus and places the value
0x0020 on the data memory address bus, and indicates this is a write cycle. At the
end of the cycle, the value in the MDR is written into the data memory location
at 0x20.
1.7 ■ Overview of the HCS12 Microcontroller
Instruction
21
dbnz 0x21,loop (machine code 5 D5 21 0A)
Step 1
The value in the PC (0x0010) is placed on the program memory address bus with a request
to read the contents of that location.
Step 2
The 8-bit value at the location 0x0010 is the instruction opcode 0xD5. At the end of this
read cycle, the PC is incremented to 0x0011. The program memory returns the opcode
byte 0xD5 to the CPU.
Step 3
The CPU recognizes that it needs to read a data memory address and a branch offset from
the program memory.
Step 4
Processor X performs two more read operations to the program memory. The program
memory returns 0x21 and 0x0A (both are held in IR). At the end of these two read cycles,
the PC is incremented to 0x0013.
Step 5
Processor X places 0x21 on the data memory address bus with a read request. At the end
of the read cycle, the value of the data memory location at 0x21 is returned to the CPU
which will be held in the MDR.
Step 6
Processor X decrements the contents of the MDR. The contents of the MDR are then placed
on the data memory data bus. Processor X also places the address 0x21 on the data memory
address bus with a write request to store the contents of the MDR in data memory.
Step 7
If the value stored in the MDR is not zero, processor X adds 0x0A to the PC and places the
result in the PC (this causes a branch behavior). Otherwise, the PC is not changed.
This section demonstrates the activities that may occur during the execution of a program.
Overall, the operations performed by the processor are dictated by the opcode.
1.7 Overview of the HCS12 Microcontroller
Freescale designed the 68HC12 as an upgrade to the 8-bit 68HC11 microcontroller. However,
Motorola discovered that the performance of the 68HC12 microcontroller was not satisfactory
after it was introduced to the market. The 68HC12 has the highest bus clock speed of 8 MHz. To
be competitive, Freescale revised the design to achieve a bus clock rate of 25 MHz (a few microcontrollers can run at 33 MHz). The revised 68HC12 was referred to as the Star12 family. It was
also named the HCS12 family. The HCS12 MCU has the same instruction set and addressing
modes as does the 68HC12. However, many of the internal designs have been changed.
Automotive and process control applications are the two major target markets of the
HCS12. This is evidenced by the inclusion of such peripheral functions as input capture (IC),
output compare (OC), pulse-width modulation (PWM), controller area network (CAN), and
byte data link control (BDLC). Other peripheral functions such as serial peripheral interface
(SPI), serial communication interface (SCI), and interintegrated circuit (I2C) are also included to
facilitate interconnection with a wide variety of peripheral chips.
Using flash memory to hold application programs has become the trend of microcontroller
design. All HCS12 members incorporate on-chip flash memory to hold programs. Most HCS12
22
Chapter 1 ■ Introduction to the HCS12 Microcontroller
MC
Status of product:
MC = fully qualified
XC = partially qualified
PC = product engineering
KMC = sample pack
KXC = sample pack
9
S12
Dx
256
B
xx
x
Packaging
designator
Memory type
9 = flash
Flash
rev.
Core
type
Temperature range
C = – 40°C to 85°C
V = – 40°C to 105°C
M = – 40°C to 125°C
Approximate
memory
Family
Figure 1.9 ■ Freescale product numbering system for the HCS12
devices also include a certain amount of on-chip SRAM and EEPROM to hold data and/or programs needed in different applications.
Most HCS12 devices have many I/O pins to interface with I/O devices. When on-chip
memory is not adequate, external memory can be added. All HCS12 devices adopt the same
design for the same peripheral function to facilitate the migration from one device (with less
memory or fewer peripheral functions) to another.
The features of all HCS12 devices are shown in Appendix C. All devices with the CAN module
are for automotive applications. The numbering system for the HCS12 is shown in Figure 1.9.
In addition to automotive and control applications, Freescale is also trying to attract users from
other application areas. For example, the MC9S12NE64 was designed for applications that need to
access the Internet whereas the MC9S12UF32 was designed for interfacing with the USB bus.
Software debugging support is an important issue for embedded applications. Freescale has
implemented the Background Debug Mode (BDM) in each HCS12 member. With this BDM
module, the tool developers can design inexpensive software debugging tools for the HCS12.
Most of the HCS12 features and peripheral functions will be discussed in this text.
1.8 The HCS12 CPU Registers
The HCS12 microcontroller has registers for supporting general-purpose operations and
controlling the functioning of peripheral modules. These registers are divided into two categories:
CPU registers and I/O registers. CPU registers are used solely to perform general-purpose operations
such as arithmetic, logic, and program flow control. I/O registers are mainly used to configure the
operations of peripheral functions, to hold data transferred in and out of the peripheral subsystem,
and to record the status of I/O operations. The I/O registers in a microcontroller can further be classified into data, data direction, control, and status registers. These registers are treated as memory
locations when they are accessed. CPU registers do not occupy the HCS12 memory space.
The CPU registers of the HCS12 are shown in Figure 1.10 and are listed next. Some of the
registers are 8 bit and others are 16 bit.
General-purpose accumulators A and B. Both A and B are 8-bit registers. Most arithmetic functions are performed on these two registers. These two accumulators can also be
concatenated to form a single, 16-bit accumulator referred to as the D accumulator.
23
1.8 ■ The HCS12 CPU Registers
7
A
7
0
B
0
8-bit accumulator A and B
or
16-bit double accumulator D
15
D
0
15
X
0
Index register X
15
Y
0
Index register Y
15
SP
0
Stack pointer
15
PC
0
Program counter
S X H I N Z V C Condition code register H
Carry
Overflow
Zero
Negative
I Interrupt mask
Half-carry (from bit 3)
X Interrupt mask
Stop Disable
Figure 1.10 ■ HCS12 CPU registers
Index registers X and Y. These two registers are used mainly in forming operand
addresses during the instruction execution process. However, they are also used in
several arithmetic operations.
Stack pointer (SP). A stack is a last-in-first-out data structure. The HCS12 has a 16-bit
stack pointer which points to the top byte of the stack (shown in Figure 1.11). The stack
grows toward lower addresses. The use of the stack will be discussed in Chapter 4.
Program counter. The 16-bit PC holds the address of the next instruction to be executed.
After the execution of an instruction, the PC is incremented by the number of bytes of
the executed instruction.
Condition code register (CCR). This 8-bit register is used to keep track of the program execution status, control the execution of conditional instructions, and
enable/disable the interrupt handling. The contents of the CCR register are shown
in Figure 1.10. The function of each condition code bit will be explained in later
sections and chapters.
The HCS12 supports the following types of data:
• Bits
• 5-bit signed integers
• 8-bit signed and unsigned integers
• 8-bit, two-digit binary-coded-decimal (BCD) numbers
24
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Lower address
Top element
SP
Higher address
Figure 1.11 ■ HCS12 stack structure
• 9-bit signed integers
• 16-bit signed and unsigned integers
• 16-bit effective addresses
• 32-bit signed and unsigned integers
Negative numbers are represented in two’s complement format. Five-bit and 9-bit signed
integers are formed during addressing mode computations. Sixteen-bit effective addresses
are formed during addressing mode computations. Thirty-two-bit integer dividends are used
by extended division instructions. Extended multiply and extended multiply-and-accumulate
instructions produce 32-bit products.
A multibyte integer (16 bit or 32 bit) is stored in memory from most significant to least
significant bytes, starting from low to higher addresses.
1.9 HCS12 Addressing Modes
An HCS12 instruction consists of 1 or 2 bytes of opcode and 0 to 5 bytes of operand addressing
information. The opcode specifies the operation to be performed and the addressing mode(s) used
to access the operand(s). The addressing mode determines how the CPU accesses registers or
memory locations to be operated on. The HCS12 supports the following address modes:
1.9.1 Inherent Mode
In this mode, the instruction does not use extra bytes to specify the operand. Instructions
using this mode either have no operands or all operands are in internal CPU registers. Therefore
the CPU does not need to access memory in order to fetch operands. For example, the following
two instructions use inherent mode:
nop
inx
; this instruction has no operand
; operand is a CPU register
1.9.2 Immediate Mode
In this mode, the value to be operated on has been included in the instruction itself. An immediate value can be 8 bit or 16 bit depending on the context of the instruction. An immediate
value is preceded by a # character in the assembly instruction. For example,
ldaa
#$55
; A ← $55
1.9 ■ HCS12 Addressing Modes
25
places the hex value $55 in accumulator A when this instruction is executed.
ldx
#$2000
; X ← $2000
places the hex value $2000 in index register X when this instruction is executed.
movw
#$10,$100
; m[$100] ← $00; m[$101] ← $10
stores the hex value $00 and $10 in memory locations at $100 and $101, respectively, when this
instruction is executed. Only an 8-bit value was supplied in this instruction. However, the assembler will generate the 16-bit value $0010 because the CPU expects a 16-bit value when this
instruction is executed.
1.9.3 Direct Mode
This addressing mode is sometimes called zero-page addressing because it is used to access
operands in the address range of $0000 to $00FF. Since these addresses begin with $00, only the
8 low-order bits of the address need to be included in the instruction; this saves program space
and execution time. For example,
ldaa
$20
; A ← [$20]
fetches the contents of the memory location at $0020 and puts it in accumulator A.
ldx
$20
; XH ← [$20], XL ← [$21]
fetches the contents of memory locations at $0020 and $0021 and places them in the upper and
lower bytes (XH and XL), respectively, of the index register X.
1.9.4 Extended Mode
In this addressing mode, the full 16-bit address of memory location to be operated on is provided in the instruction. This addressing mode can be used to access any location in the 64-kB
memory map. For example,
ldaa
$2000
; Α ← [$2000]
copies the contents of the memory location at $2000 into accumulator A.
1.9.5 Relative Mode
The relative addressing mode is used only by branch instructions that may change the
program flow. The distance of the branch (or jump) is referred to as branch offset. Short and
long conditional branch instructions use the relative addressing mode exclusively. Branching
versions of bit manipulation instructions (BRSET and BRCLR) may also use the relative addressing mode to specify the branch target. A short branch instruction consists of an 8-bit opcode
and a signed 8-bit offset contained in the byte that follows the opcode. Long branch instructions
consist of an 8-bit prebyte, an 8-bit opcode, and a signed 16-bit offset contained in 2 bytes that
follow the opcode.
Each conditional branch instruction tests certain status bits in the condition code register.
If the bits are in a specified state, the offset is added to the address of the next instruction to
form an effective address, and execution continues at that address; if the bits are not in the
specified state, execution continues with the instruction next to the branch instruction.
Both 8-bit and 16-bit offsets are signed two’s complement numbers to support branching
forward and backward in memory. The numeric range of the short branch offset values is $80
(2128) to $7F (127). The numeric range of the long branch offset values is $8000 (232768) to
$7FFF (32767). If the offset is zero, the CPU executes the instruction immediately following the
branch instruction, regardless of the test result.
26
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Branch offset is often specified using a label rather than a numeric value due to the difficulty
of calculating the exact value of the offset. For example, in the following instruction segment:
minus .
.
.
bmi
…
minus
; if N (of CCR) 5 1
; PC ← PC 1 branch offset
; else
; PC ← PC
The instruction bmi minus causes the HCS12 to execute the instruction with the label
minus if the N flag of the CCR register is set to 1.
The assembler will calculate the appropriate branch offset when the symbol that represents
the branch target is encountered. Using a symbol to specify the branch target makes the programming task easier and the resultant program more readable.
1.9.6 Indexed Addressing Modes
The indexed addressing mode uses two components to compute the effective address of
an operand or the target of a jump instruction. The first component is called the base address,
which is stored in a base register. The base register can be X, Y, SP, or PC. The second component is called the offset, which is the distance of the target from the base address. The effective
address of the operand or jump target is the sum of these two components.
The offset may be a constant (5 bits, 9 bits, or 16 bits) or the contents of accumulator A, B,
or D. The base register may be pre- or postincremented or pre- or postdecremented. The size of
increment or decrement may be specified by the user and can be from 28 to 18. In addition, the
HCS12 also provides the user one level of indirection. That is, the sum of the contents of the
base register and the offset does not point to the actual operand or the jump target. Instead, it
points to the memory location that holds the address of the actual operand or jump target.
The variations of the indexed addressing mode are described in the following subsections.
1.9.7 Indexed Addressing Modes with Constant Offsets
The syntax of the indexed addressing mode with constant offset is as follows:
n, r
where
n is a 5-bit, 9-bit, or 16-bit constant
r is the base register and can be X, Y, SP, or PC
For example,
ldaa
4,X
; A ← [4 1 [X]]
loads the contents of the memory location with the address equal to the sum and 4 and X into A.
The HCS12 performs the following two operations for the ldd 100,Y instruction:
A ← [100 1 [Y]];
B ← [101 1 [Y]];
1.9.8 Indexed Addressing Mode with Offset in an Accumulator
The syntax of this form of indexed address mode is as follows:
acc, r
where
acc can be A, B, or D
r is the base register and can be X, Y, SP, or PC
27
1.9 ■ HCS12 Addressing Modes
For example,
staa
; m[[B] 1 [X]] ← [A]
B, X
stores the contents of A in the memory location of which the address equals the sum of the
contents of B and X.
For the instruction
ldx
D, SP
the HCS12 performs the following operations:
X ← [[D] 1 [SP]]:[1 1 [D] 1 [SP]]
; 2 bytes are loaded into X
1.9.9 Auto Pre-/Postdecrement/-Increment Indexed Addressing Modes
The syntax and resultant effective address of this mode are shown in Table 1.4. For
the predecrement/preincrement version of this addressing mode, the HCS12 decrements/
increments the specified base register by the specified amount (n in Table 1.4) before
using the contents of the base register as an effective address to access memory. For the
postdecrement/postincrement version of this address mode, the HCS12 uses the contents
of the specified base register as the effective address to access memory and then decrements/increments the specified base register. For example, if index register X contains
$1000, then
staa
2, 2X
; predecrement X
stores the contents of accumulator A in the memory location at $9FE and the new value in X
becomes $9FE.
ldaa
2, 1X
; preincrement X
loads the contents of memory location at $1002 into A and the new value of X is $1002.
sty
2, X2
stores the high and low bytes of Y in memory locations at $1000 and $1001, respectively. After
that, index register X receives the new value of $9FE.
ldaa
4, X1
loads the contents of the memory location at $1000 into A. After that, index register X receives
the new value of $1004.
Syntax
Effective
Address
New Value of
Base Register r
Example
Comment
n, –r
[r] – n
[r] – n
std 2, –SP
Predecrement
n, +r
[r] + n
[r] + n
ldd 2, +SP
Preincrement
n, r–
[r]
[r] – n
std 2, X–
Postdecrement
n, r+
[r]
[r] + n
std 2, Y+
Postincrement
Note:
n = amount of decrement or increment.
r = base register (may be X, Y, or SP).
Table 1.4 ■ Auto predecrement/increment or auto postdecrement/increment indexed modes
28
Chapter 1 ■ Introduction to the HCS12 Microcontroller
1.9.10 16-Bit Offset Indexed Indirect Mode
The syntax of this addressing mode is as follows:
[n, r]
where
n is the 16-bit offset
r is the base register and can be X, Y, SP, or PC
In this mode, the HCS12 fetches the actual effective address from the memory location
with address equal to the sum of the 16-bit offset and the contents of the base register and then
uses that effective address to access the operand. The square brackets distinguish this addressing mode from 16-bit constant offset indexing; for example,
ldaa
[10, X]
In this example, index register X holds the base address of a table of pointers. Assume that
X has an initial value of $1000, and that $2000 is stored at addresses $100A and $100B. The instruction first adds the value 10 to the value in X to form the address $100A. Next, an address
pointer ($2000) is fetched from memory locations at $100A and $100B. Then, the value stored
in $2000 is read and loaded into accumulator A.
1.9.11 Accumulator D Indirect Indexed Addressing
The syntax of this addressing mode is as follows:
[D, r]
where
r is the base register and can be X, Y, SP, or PC
This indexed addressing mode adds the value in accumulator D to the value in the base
index register to form the address of a memory location that contains a pointer to the memory
location affected by the instruction. The instruction operand points not to the memory location
to be acted on but rather to the location of a pointer to the location to be acted on. The square
brackets distinguish this addressing mode from accumulator D offset indexing. For example,
the following instruction sequence implements a computed GOTO statement:
jmp
[D, PC]
dc.w target1
; the keyword dc.w reserves 2 bytes to hold the
dc.w target2
; value of the symbol that follows
dc.w target3
;
“
...
target1 . . .
.
.
target2 . . .
.
.
target3 . . .
.
.
In this instruction segment, the names (also called labels) target1, target2, and target3 are
labels that represent the addresses of the memory locations that the jmp instruction may jump
to. The names GO1, GO2, and GO3 are also labels. They represent the memory locations that
hold the values of the labels target1, target2, and target3, respectively.
GO1
GO2
GO3
1.11 ■ A Sample of HCS12 Instructions
29
The values beginning at GO1 are addresses of potential destinations of the jump instructions. At the time the jmp [D, PC] instruction is executed, the PC points to the address GO1
and D holds one of the values $0000, $0002, or $0004 (determined by the program some time
before the jmp). Assume that the value in D is $0002. The jmp instruction adds the values in D
and PC to form the address of GO2 and jumps to target2. The locations of target1 through target3 are known at the time of program assembly, but the destination of the jmp depends on the
value in D computed during program execution.
1.10 Addressing More than 64 kB
The HCS12 devices incorporate hardware that supports addressing a larger memory space
than the standard 64 kB. The expanded memory system is accessed by using the bank-switching scheme. The HCS12 treats the 16 kB of memory space from $8000 to $BFFF as a program
memory window. The HCS12 has an 8-bit program page register (PPAGE), which allows up to
256 16-kB program memory pages to be switched into and out of the program memory window.
This provides up to 4 MB of paged program memory space.
1.11 A Sample of HCS12 Instructions
It would be very helpful to learn a small set of HCS12 instructions that are used most often
before we formally learn HCS12 assembly language programming. In the following, we will
examine data movement, addition, and subtraction instructions. The HCS12 provides a large
group of data movement instructions. Some of them may transfer data between a CPU register
and a memory location. Some of them may transfer or exchange data between two registers.
Others may transfer data from one memory location to another memory location.
1.11.1 The Load and Store Instructions
The load instruction copies the contents of a memory location or places an immediate
value into an accumulator or a register. Memory contents are not changed. Store instructions copy the contents of a CPU register into a memory location. The contents of the
accumulator or CPU register are not changed. Store instructions automatically update the
N and Z flags in the condition code register (CCR). Table 1.5 is a summary of load and store
instructions.
There are restrictions on the addressing modes that can be used in a load and a store instruction:
• For the load instruction, all except for the relative addressing mode can be used to select
the memory location or value to be loaded into an accumulator or a CPU register.
• For the store instruction, all except for the relative and immediate addressing modes
can be used to select the memory location to store the contents of a CPU register.
For example, the following instruction loads the contents of the memory location pointed to by
index register X into accumulator A:
ldaa
0,X
The following instruction loads the contents of the memory location at $1004 into accumulator B:
ldab
$1004
The following instruction stores the contents of accumulator A in the memory location at $20:
staa
$20
30
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Load Instructions
Mnemonic
Function
Operation
LDAA <opr>
LDAB <opr>
LDD <opr>
LDS <opr>
LDX <opr>
LDY <opr>
LEAS <opr>
LEAX <opr>
LEAY <opr>
Load A
Load B
Load D
Load SP
Load index register X
Load index register Y
Load effective address into SP
Load effective address into X
Load efective address into Y
A ← [opr]
B ← [opr]
A:B ← [opr]:[opr+1]
SP ← [opr]:[opr+1]
X ← [opr]:[opr+1]
Y ← [opr]:[opr+1]
SP ← effective address
X ← effective address
Y ← effective address
Mnemonic
Function
Operation
STAA <opr>
STAB <opr>
STD <opr>
STS <opr>
STX <opr>
STY <opr>
Store A in a memory location
Store B in a memory location
Store D in a memory location
Store SP in a memory location
Store X in a memory location
Store Y in a memory location
m[opr] ← [A]
m[opr] ← [B]
m[opr]:m[opr+1] ← [A]:[B]
m[opr]:m[opr+1] ← [SP]
m[opr]:m[opr+1] ← [X]
m[opr]:m[opr+1] ← [Y]
Store Instructions
Table 1.5 ■ Load and store instructions
The following instruction stores the contents of index register X in memory locations at $8000
and $8001:
stx
$8000
When dealing with a complex data structure such as a record, we often use an index
register or the stack pointer to point to the beginning of the data structure and use the indexed
addressing mode to access the elements of the data structure. For example, a record contains
the following four fields:
• ID number (unit none, size 4 bytes)
• Height (unit inch, size 1 byte)
• Weight (unit pound, size 2 bytes)
• Age (unit year, size 1 byte)
Suppose this record is stored in memory starting at $6000. Then we can use the following instruction sequence to access the weight field:
ldx
ldd
#$6000
5, X
; set X to point to the beginning of data structure
; copy weight into D
1.11.2 Transfer and Exchange Instructions
A summary of transfer and exchange instructions is displayed in Table 1.6. Transfer instructions
copy the contents of a register or accumulator into another register or accumulator. Source
content is not changed by the operation. TFR is a universal transfer instruction, but other
31
1.11 ■ A Sample of HCS12 Instructions
mnemonics are accepted for compatibility with the MC68HC11. The TAB and TBA instructions affect the N, Z, and V condition code bits. The TFR instruction does not affect the
condition code bits.
It is possible to transfer from a smaller register to a larger one or vice versa. When transferring from a smaller register to a larger one, the smaller register is signed-extended to 16-bit and
then assigned to the larger register. When transferring from a larger register to a smaller one, the
smaller register receives the value of the lower half of the larger register. For example,
tfr
tfr
A,X
X,B
; A is signed-extended to 16 bits and then assigned to X
; B← X[7:0], B receives bits 7 to 0 of X
Exchange instructions (exg r1, r2) exchange the contents of pairs of registers or accumulators.
For example,
exg
A, B
exchanges the contents of accumulator A and B.
exg
D,X
exchanges the contents of double accumulator D and index register X.
The r1 register does not need to have the same size as r2. If r1 has a larger size than r2 does,
then r2 will be 0-extended to 16 bits and loaded into r1 whereas r2 will receive the lower half of r1.
For example,
exg
X,A
; X ← $00:[A], A ← X[7:0]
Transfer Instructions
Mnemonic
TAB
TAP
TBA
TFR
TPA
TSX
TSY
TXS
TYS
Function
Operation
Transfer A to B
Transfer A to CCR
Transfer B to A
Transfer register to register
Transfer CCR to A
Transfer SP to X
Transfer SP to Y
Transfer X to SP
Transfer Y to SP
B ← [A]
CCR ← [A]
A ← [B]
A, B, CCR, D, X, Y, or SP ← [A, B, CCR, D, X, Y, or SP]
A ← [CCR]
X ← [SP]
Y ← [SP]
SP ← [X]
SP ← [Y]
Exchange Instructions
Mnemonic
Function
EXG
XGDX
XGDY
Exchange register to register
Exchange D with X
Exchange D with Y
Operation
[A, B, CCR, D, X, Y, or SP] ⇔ [A, B, CCR, D, X, Y, or SP]
[D] ⇔ [X]
[D] ⇔ [Y]
Sign Extension Instructions
Mnemonic
SEX
Operation
Function
Sign extend 8-bit operand
X, Y, or SP ← [A, B, CCR]
Table 1.6 ■ Transfer and exchange instructions
32
Chapter 1 ■ Introduction to the HCS12 Microcontroller
If the r2 register is larger in size than r1, then r1 will be 0-extended to 16 bits and loaded
into r2 whereas r1 will receive the lower half of r2. For example,
exg
; A ← Y[7:0], Y ← $00:[A]
A,Y
The sex instruction is a special case of the universal transfer instruction that is used to
sign-extend 8-bit two’s complement numbers so that they can be used in 16-bit operations. The
8-bit number is copied from accumulator A, accumulator B, or the condition code register to
accumulator D, index register X, index register Y, or the stack pointer. All the bits in the upper
byte of the 16-bit result are given the value of the most significant bit of the 8-bit number. For
example,
sex
A,X
copies the contents of accumulator A to the lower byte of X and duplicates bit 7 of A to every
bit of the upper byte of X.
sex
B,Y
copies the contents of accumulator B to the lower byte of Y and duplicates bit 7 of B to every bit
of the upper byte of Y.
Transfer instructions allow operands to be placed in the right register so that the desired
operation can be performed. For example, if we want to compute the squared value of accumulator A, we can use the following instruction sequence:
; B ← [A]
; A:B ← [A] × [B]
tab
mul
Applications of other transfer and exchange instructions will be discussed in Chapters 2
and 4.
1.11.3 Move Instructions
A summary of move instructions is listed in Table 1.7. These instructions move data bytes
or words from a source (M,M111) to a destination (M,M112) in memory. Six combinations
of immediate, extended, and indexed addressing are allowed to specify source and destination
addresses (IMM ⇒ EXT, IMM ⇒ IDX, EXT ⇒ EXT, EXT ⇒ IDX, IDX ⇒ EXT, IDX ⇒ IDX).
Move instructions allow the user to transfer data from memory to memory or from I/O registers to memory and vice versa.
For example, the following instruction copies the contents of the memory location at $1000
to the memory location at $2000:
movb
$1000, $2000
The following instruction copies the 16-bit word pointed to by X to the memory location
pointed to by Y:
movw
0,X, 0,Y
Transfer Instructions
Mnemonic
Function
Operation
MOVB <src>, <dest>
MOVW <src>, <dest>
Move byte (8-bit)
Move word (16-bit)
dest ← [src]
dest ← [src]
Table 1.7 ■ Move instructions
33
1.11 ■ A Sample of HCS12 Instructions
Add Instructions
Mnemonic
Function
Operation
ABA
Add B to A
A ← [A] + [B]
ABX
Add B to X
X ← [X] + [B]
ABY
Add B to Y
Y ← [Y] + [B]
ADCA <opr>
Add with carry to A
A ← [A] + [opr] + C
ADCB <opr>
Add with carry to B
B ← [B] + [opr] + C
ADDA <opr>
Add without carry to A
A ← [A] + [opr]
ADDB <opr>
Add without carry to B
B ← [B] + [opr]
ADDD <opr>
Add without carry to D
D ← [D] + [opr]
Subtract Instructions
Mnemonic
Function
Operation
SBA
Subtract B from A
A ← [A] – [B]
SBCA <opr>
Subtract with borrow from A
A ← [A] – [opr] – C
SBCB
Subtract with borrow from B
B ← [B] – [opr] – C
SUBA <opr>
Subtract memory from A
A ← [A] – [opr]
SUBB <opr>
Subtract memory from B
B ← [B] – [opr]
SUBD <opr>
Subtract memory from D
D ← [D] – [opr]
Table 1.8 ■ Add and subtract instructions
1.11.4 Add and Subtract Instructions
Add and subtract instructions allow the HCS12 to perform fundamental arithmetic operations. A summary of add and subtract instructions is in Table 1.8. The <opr> field in Table 1.8
is specified using one of the legal addressing modes. All except inherent and relative modes are
legal addressing modes for these two groups of instructions.
Example 1.1
▼
Write an instruction sequence to add 3 to the memory locations at $10 and $15.
Solution: A memory location cannot be the destination of an ADD instruction. Therefore, we
need to copy the memory content into an accumulator, add 3 to it, and then store the sum back
to the same memory location.
ldaa
adda
staa
ldaa
adda
staa
$10
#3
$10
$15
#3
$15
; copy the contents of memory location at $10 to A
; add 3 to A
; store the sum back to memory location at $10
; copy the contents of memory location at $15 to A
; add 3 to A
; store the sum back to memory location at $15
▲
34
Chapter 1 ■ Introduction to the HCS12 Microcontroller
Example 1.2
▼
Write an instruction sequence to add the byte pointed to by index register X and the following byte and place the sum at the memory location pointed to by index register Y.
Solution: The byte pointed to by index register X and the following byte can be accessed by using
the indexed addressing mode.
ldaa
adda
staa
0,X
1,X
0,Y
; put the byte pointed to by X in A
; add the following byte to A
; store the sum at the location pointed to by Y
▲
Example 1.3
▼
Write an instruction sequence to add the numbers stored at $1000 and $1001 and store the
sum at $1004.
Solution: To add these two numbers, we need to put one of them in an accumulator.
ldaa
adda
staa
$1000 ; copy the number stored in memory location at $1000 to A
$1001 ; add the second number to A
$1004 ; save the sum at memory location at $1004
▲
Example 1.4
▼
Write an instruction sequence to swap the 2 bytes at $100 and $200.
Solution: To swap the 2 bytes, we need to make a copy of one of the 2 bytes and then the swapping can proceed.
ldaa
movb
staa
$100
$200,$100
$200
; make a copy of m[$100] in A
; store [$200] in m[$100]
; store the original [$100] in m[$200]
▲
1.12 Instruction Queue
The HCS12 uses a three-stage instruction queue to facilitate instruction fetching and
increase execution speed. Queue logic prefetches program information and positions it for
sequential execution, one instruction at a time. The relationship between bus cycles and
execution cycles is straightforward and facilitates tracking and debugging.
There are three 16-bit stages in the instruction queue. Instructions enter the queue at stage
1 and roll out after stage 3. Each byte in the queue is selectable. An opcode-prediction algorithm
determines the location of the next opcode in the instruction queue.
Each instruction refills the queue by fetching the same number of bytes that the instruction uses. Program information is fetched in aligned 16-bit words. Each program fetch indicates
1.13 ■ Summary
35
that 2 bytes need to be replaced in the instruction queue. Each optional fetch indicates that
only 1 byte needs to be replaced. For example, an instruction composed of 5 bytes does two program fetches and one optional fetch. If the first byte of the 5-byte instruction was even aligned,
the optional fetch is converted into a free cycle. If the first byte was odd aligned, the optional
fetch is executed as a program fetch.
Two external pins, IPIPE[1:0], provide time-multiplexed information about instruction
execution and data movement in the queue. Decoding and using the IPIPE[1:0] signals are
discussed in Chapter 14.
The content of queue stage 1 advances to stage 2, stage 2 advances to stage 3, and stage 1 is
loaded with a word of program information from the data bus.
1.13 Summary
The invention of the microprocessor in 1968 resulted in a revolution in the electronics
industry. The first microprocessor, the Intel 4004, incorporated a simplified CPU into an integrated circuit. Following the introduction of the 4-bit 4004, Intel introduced the 8-bit 8008,
8080, and 8085 over three years. The 8085 was a big success because of its programmability. Through this programmability, many products could be designed and constructed. Other
companies joined in the design and manufacturing of microprocessors, Zilog, Motorola, and
Rockwell being among the more successful.
The earliest microprocessors still needed peripheral chips to interface with I/O devices
such as seven-segment displays, printers, timers, and so on. Memory chips were also needed
to hold the application program and dynamic data. Because of this, the products designed with
microprocessors could not be made as small as desired. Then came the introduction of microcontrollers, which incorporated the CPU, some amount of memory, and peripheral functions
such as parallel I/O ports, timer, and serial interface functions onto one chip. The development
of microcontrollers has had the following impacts:
• I/O interfacing is greatly simplified.
• External memory is no longer needed for many applications.
• System design time is greatly shortened.
A microcontroller is not designed to build a desktop computer. Instead, it is used as the
controller of many products. End users of these products do not care what microcontrollers are
used in their appliances; they only care about the functionality of the product. A product that
uses a certain microcontroller as a controller and has this characteristic is called an embedded
system. Cell phones, automobiles, cable modems, HDTVs, and home security systems are wellknown embedded systems.
Over the last 20 years, we have clearly seen that a microcontroller needs to incorporate
some or all of the following peripheral functions in order to be useful:
• Timer module that incorporates input capture, output compare, real-time interrupt,
and counting capability
• Pulse-width modulation function for easy waveform generation
• Analog-to-digital converter
• Digital-to-analog converter
• Temperature sensor
• Direct memory access controller
36
Chapter 1 ■ Introduction to the HCS12 Microcontroller
• Parallel I/O interface
• Serial I/O interface such as UART, SPI, I2C, and CAN
• Memory component interface circuitry
The HCS12 from Freescale incorporates most of these peripheral modules and the CPU in one
VLSI chip.
Memory is where software programs and data are stored. Semiconductor memory chips can
be classified into two major categories: random-access memory and read-only memory. RAM
technology includes DRAM, FRAM, MRAM, and SRAM. MROM, PROM, EPROM, EEPROM,
and flash memory are read-only memories.
Programs are known as software. A program is a set of instructions that the computer
hardware can execute. In the past, system designers mainly used assembly language to write
microcontroller application software. The nature of assembly language forces an assembly programmer to work on the program logic at a relatively low level. This hampers programming
productivity. In the last 15 years, more and more people have turned to high-level language to
improve their programming productivity. C is the most widely used language for embedded
system programming.
Although system designers use assembly or high-level language to write their programs,
the microcontroller can only execute machine instructions. Programs written in assembly or
high-level language must be translated into machine instructions before they can be executed.
The program that performs the translation work is called an assembler or compiler depending
on the language to be translated.
A machine instruction consists of opcode and addressing information that specifies the
operands. Addressing information is also called addressing mode. The HCS12 implements a
rich instruction set along with many addressing modes for specifying operands. This chapter
examines the functions of a few groups of instructions. Examples were used to explore the
implementation of simple operations using these instructions.
The execution of an instruction may take several clock cycles. Because the HCS12 does not
access memory in every clock cycle, it performs instruction prefetch to speed up the instruction execution. A two-word (16-bit word) instruction prefetch queue and a 16-bit buffer are
added to hold the prefetched instructions.
1.14 Exercises
E1.1 What is a processor?
E1.2 What is a microprocessor? What is a microcomputer?
E1.3 What makes a microcontroller different from a microprocessor?
E1.4 How many bits can the HCS12 CPU manipulate in one operation?
E1.5 How many different memory locations can the HCS12 access without the expanded
memory?
E1.6 Why must every computer have some amount of nonvolatile memory?
E1.7 Why must every computer have some amount of volatile memory?
E1.8 What is source code? What is object code?
E1.9 Convert 5K, 8K, and 13K to decimal representation.
E1.10 Write an instruction sequence to swap the contents of memory locations at $1000 and
$1001.
E1.11 Write an instruction sequence to add 10 to memory locations at $1000 and $1001,
respectively.
1.14 ■ Exercises
37
E1.12 Write an instruction sequence to set the contents of memory locations at $1000, $1010,
and $1020 to 10, 11, and 12, respectively.
E1.13 Write an instruction sequence to perform the operations equivalent to those performed
by the following high-level language statements:
I 5 11;
J 5 33;
K 5 I 1 J 2 5;
Assume that variables I, J, and K are located at $1000, $1005, and $1010, respectively.
E1.14 Write an instruction sequence to subtract the number stored at $1010 from that stored at
$1000 and store the difference at $1005.
E1.15 Write an instruction sequence to add the contents of accumulator B to the 16-bit word
stored at memory locations $1000 and $1001. Treat the value stored in B as a signed number.
E1.16 Write an instruction sequence to copy 4 bytes starting from $1000 to $1100,$1103.
E1.17 Write an instruction sequence to subtract the contents of accumulator B from the 16-bit
word at $1000,$1001 and store the difference at $1100,$1101. Treat the value stored in B as a
signed value.
E1.18 Write an instruction sequence to swap the 16-bit word stored at $1000,$1001 with the
16-bit word stored at $1100,$1101.
E1.19 Give an instruction that can store the contents of accumulator D at the memory location
with an address larger than the contents of X by 8.
E1.20 Give an instruction that can store the contents of index register Y at the memory location with an address smaller than the contents of X by 10.
This page intentionally left blank
2
HCS12 Assembly
Programming
2.1 Objectives
After completing this chapter, you should be
able to
• Use assembler directives to allocate memory
blocks, define constants, and create a message
to be output
• Write assembly programs to perform simple
arithmetic operations
• Write program loops to perform repetitive
operations
• Use program loops to create time delays
• Use Boolean and bit manipulation instructions
to perform bit field manipulation
40
Chapter 2 ■ HCS12 Assembly Programming
2.2 Assembly Language Program Structure
An assembly language program consists of a sequence of statements that tells the computer to
perform the desired operations. From a global point of view, an HCS12 assembly program consists
of three sections. In some cases, these sections can be mixed to provide better algorithm design.
1. Assembler directives. Assembler directives instruct the assembler how to process
subsequent assembly language instructions. Directives also provide a way to define
program constants and reserve space for dynamic variables. Some directives may also
set a location counter.
2. Assembly language instructions. These instructions are HCS12 instructions. Some
instructions are defined with labels.
3. Comments. There are two types of comments in an assembly program. The first
type is used to explain the function of a single instruction or directive. The second
type explains the function of a group of instructions or directives or a whole routine.
Adding comments improves the readability of a program.
Each line of an HCS12 assembly program, excluding certain special constructs, is made up of
four distinct fields. Some of the fields may be empty. The order of these fields is
1. Label
2. Operation
3. Operand
4. Comment
2.2.1 The Label Field
Labels are symbols defined by the user to identify memory locations in the programs and
data areas of the assembly module. For most instructions and assembler directives, the label is
optional. The rules for forming a label are as follows:
• The label field can begin in column one or in any column if it is terminated by a
colon (:). It must begin with a letter (A–Z, a–z), and the letter can be followed by
letters, digits, or special symbols. Some assemblers permit special symbols to be used.
For example, the assembler from IAR Inc. allows a symbol to start with a question
mark (?), a character (@), and an underscore (_) in addition to letters. Digits and the
dollar ($) character can also be used after the first character in the IAR assembler.
• Most assemblers restrict the number of characters in a label name. The as12
assembler reference manual does not mention the limit. The IAR assembler allows
a user-defined symbol to have up to 255 characters.
• The as12 assembler from several companies allows a label to be terminated by a
colon (:).
Example 2.1 Examples of Valid and Invalid Labels
▼
The following instructions contain valid labels:
begin
print:
ldaa
jsr
jmp
#10
hexout
begin
; label begins in column 1
; label is terminated by a colon
; instruction references the label begin
2.2 ■ Assembly Language Program Structure
41
The following instructions contain invalid labels:
here is
loop
adda
deca
#5
; a space is included in the label
; label begins at column 2
▲
2.2.2 The Operation Field
This field specifies an assembler instruction, a directive, or a macro call. Assembler instructions or directives are case insensitive. The operation field must not start in column
one. If a label is present, the opcode or directive must be separated from the label field by at
least one space. If there is no label, the operation field must be at least one space from the left
margin.
Example 2.2 Examples of Operation Fields
▼
true
adda
#$02
; adda is the instruction mnemonic
equ
1
; the equ directive occupies the operation field
▲
2.2.3 The Operand Field
If an operand field is present, it follows the operation field and is separated from the operation field by at least one space. The operand field is composed of one or more operands separated
by commas, followed by optional space or tab characters. The operand field is used to supply
arguments to the assembler instruction, directive, or macro that has been used in the operation
field. The following instructions include the operand field:
TCNT
TC0
equ
equ
$0084
$0090
; the value $0084 is the operand field
; the value $0090 is the operand field
2.2.4 The Comment Field
The comment field is optional and is added mainly for documentation purposes. The comment field is ignored by the assembler. Here are the rules for comments.
• Any line beginning with an * is a comment.
• Any line beginning with a semicolon (;) is a comment. In this book, we use a “;” to
start a comment.
• You must have a “;” prefixing any comment on a line with mnemonics.
Examples of comments are shown in the following instructions:
; this program computes the square root of N 8-bit integers.
org
$1000
; set the location counter to $1000
dec
lp_cnt
; decrement the loop count
In this chapter, we use the Freescale Freeware cross assembler as12 as the standard to explain every aspect of assembly programming. The as12 assembler contained in the MiniIDE
from Mgtek is compatible with the as12 freeware from Freescale.
42
Chapter 2 ■ HCS12 Assembly Programming
2.3 Assembler Directives
Assembler directives look just like instructions in an assembly language program, but they
tell the assembler to do something other than create the machine code for an instruction. The
available assembler directives vary with the assembler. Interested readers should refer to the
user’s manual of the specific assembler for details.
We discuss assembler directives supported by the as12 in detail here. In the following discussion, statements enclosed in square brackets [ ] are optional. All directives and assembly
instructions can be in either upper- or lowercase.
end
The end directive is used to end a program to be processed by the assembler. In general, an
assembly program looks like this:
(your program)
end
The end directive indicates the logical end of the source program. Any statement following the
end directive is ignored. A warning message will occur if the end directive is missing from the
source code; however, the program will still be assembled correctly.
org (origin)
The assembler uses a location counter to keep track of the memory location where the
next machine code byte should be placed. If the programmer wants to force the program or data
array to start from a certain memory location, then the org directive can be used. For example,
the statement
org
$1000
forces the location counter to be set to $1000.
The org directive is mainly used to force a data table or a segment of instructions to start
with a certain address. As a general rule, this directive should be used as infrequently as possible. Using too many orgs will make your program less reusable.
db (define byte), dc.b (define constant byte), fcb (form constant byte)
These three directives define the value of a byte or bytes that will be placed at a given
memory location. The db (or dc.b or fcb) directive assigns the value of the expression to the
memory location pointed to by the location counter. Then the location counter is incremented.
Multiple bytes can be defined at a time by using commas to separate the arguments. For example, the statement
array
db
$11,$22,$33,$44,$55
initializes 5 bytes in memory to
$11
$22
$33
$44
$55
and the assembler will use array as the symbolic address of the first byte whose initial value is
$11. The program can also force these 5 bytes to a certain address by adding the org directive.
For example, the sequence
array
org
db
$1000
$11,$22,$33,$44,$55
2.3 ■ Assembler Directives
43
initializes the contents of memory locations at $1000, $1001, $1002, $1003, and $1004 to $11,
$22, $33, $44, and $55, respectively.
dw (define word), dc.w (define constant word), fdb (form double bytes)
These three directives define the value of a word or words that will be placed at a given address. The value can be specified by an integer or an expression. For example, the statement
vect_tab
dw
$1234, $5678
initializes the two words starting from the current location counter to $1234 and $5678, respectively. After this statement, the location counter will be incremented by 4.
fcc (form constant character)
This directive allows us to define a string of characters (a message). The first character
in the string is used as the delimiter. The last character must be the same as the first character because it will be used as the delimiter. The delimiter must not appear in the string. The
space character cannot be used as the delimiter. Each character is encoded by its corresponding
American Standard Code for Information Interchange (ASCII) code. For example, the statement
alpha
fcc
“def”
will generate the following values in memory:
$64
$65
$66
and the assembler will use the label alpha to refer to the address of the first letter, which is
stored as the byte $64. A character string to be output to the LCD display is often defined using
this directive.
fill (fill memory)
This directive allows a user to fill a certain number of memory locations with a given
value. The syntax of this directive is as follows:
fill
value, count
where the number of bytes to be filled is indicated by count and the value to be filled is indicated by value. For example, the statement
spaceLine
fill
$20, 40
will fill 40 bytes with the value of $20 starting from the memory location referred to by the
label spaceLine.
ds (define storage), rmb (reserve memory byte), ds.b (define storage bytes)
Each of these three directives reserves a number of bytes given as the arguments to the
directive. The location counter will be incremented by the number that follows the directive
mnemonic. For example, the statement
buffer
ds
100
reserves 100 bytes in memory starting from the location represented by the label buffer. After
this directive, the location counter will be incremented by 100. The content(s) of the reserved
memory location(s) is(are) not defined.
ds.w (define storage word), rmw (reserve memory word)
Each of these directives increments the location counter by the value indicated in the number-of-words argument multiplied by 2. In other words, if ds.w evaluates to k, then the location counter is advanced by 2k. These directives are often used with a label. For example, the
statement
44
Chapter 2 ■ HCS12 Assembly Programming
dbuf
ds.w
20
reserves 40 bytes starting from the memory location represented by the label dbuf. None of
these 40 bytes is initialized.
equ (equate)
This directive assigns a value to a label. Using equ to define constants will make a program
more readable. For example, the statement
loop_cnt
equ
40
informs the assembler that whenever the symbol loop_cnt is encountered, it should be replaced
with the value of 40.
loc
This directive increments and produces an internal counter used in conjunction with the
backward tick mark (`). By using the loc directive and the ` mark, you can write a program segment like the following example without thinking up new labels:
loop`
loop`
loc
ldaa
deca
bne
loc
brclr
#2
loop`
0,x $55 loop`
This code segment will work perfectly fine because the first loop label will be seen as loop001,
whereas the second loop label will be seen as loop002. The assembler really sees this:
loop001
loop002
loc
ldaa
deca
bne
loc
brclr
#2
loop001
0,x $55 loop002
You can also set the loc directive with a valid expression or number by putting that expression
or number in the operand field. The resultant number will be used to increment the suffix to
the label.
macro, endm
Macro is a name assigned to a group of instructions or directives. There are situations in
which the same sequence of instructions needs to be included in several places. This sequence
of instructions may operate on different parameters. By placing this sequence of instructions in
a macro, the sequence need be typed only once. The macro capability not only makes the programmer more productive but also makes the program more readable. The freeware MiniIDE
supports macro directives. However, the freeware AsmIDE does not.
The keyword macro starts a new macro definition, whereas the keyword endm ends the
macro definition. For example, a macro may be defined for the HCS12 as follows:
sumOf3
macro
ldaa
adda
adda
endm
arg1,arg2,arg3
arg1
arg2
arg3
2.4 ■ Software Development Issues
45
If you want to add the values of three memory locations at $1000, $1001, and $1002 and leave
the sum in accumulator A, you can use the following statement to invoke the previously defined macro:
sumOf3 $1000,$1001,$1002
When processing this macro call, the assembler will insert the following instructions in the
user program:
ldaa
adda
adda
$1000
$1001
$1002
2.4 Software Development Issues
A complete discussion of issues involved in software development is beyond the scope of this
text. However, we do need to take a serious look at some software development issues because
embedded system designers must spend a significant amount of time on software development.
Software development starts with problem definition. The problem presented by the application must be fully understood before any program can be written. At the problem definition
stage, the most critical thing is to get the programmer and the end user to agree on what needs to
be done. To achieve this, asking questions is very important. For complex and expensive applications, a formal, written definition of the problem is formulated and agreed on by all parties.
Once the problem is known, the programmer can begin to lay out an overall plan of how
to solve the problem. The plan is also called an algorithm. Informally, an algorithm is any
well-defined computational procedure that takes some value, or a set of values, as input and
produces some value, or a set of values, as output. An algorithm is thus a sequence of computational steps that transforms the input into the output. We can also view an algorithm as a tool
for solving a well-specified computational problem. The statement of the problem specifies in
general terms the desired input/output relationship. The algorithm describes a specific computational procedure for achieving that input/output relationship.
An algorithm is expressed in pseudocode which is very much like C or PASCAL. What
separates pseudocode from “real” code is that in pseudocode, we employ whatever expressive
method is most clear and concise to specify a given algorithm. Sometimes, the clearest method
is English, so do not be surprised if you come across an English phrase or sentence embedded
within a section of “real” code.
An algorithm provides not only the overall plan for solving the problem but also documentation to the software to be developed. In the rest of this book, all algorithms will be presented
in the following format:
Step 1
...
Step 2
...
An earlier alternative for providing the overall plan for solving software problems was the
use of flowcharts. A flowchart shows the way a program operates. It illustrates the logic flow
of the program. Therefore, flowcharts can be a valuable aid in visualizing programs. Flowcharts
are not only used in computer programming; they are also used in many other fields, such as
business and construction planning.
46
Chapter 2 ■ HCS12 Assembly Programming
The flowchart symbols used in this book are shown in Figure 2.1. The terminal symbol is
used at the beginning and the end of each program. When it is used at the beginning of a program, the word Start is written inside it. When it is used at the end of a program, it contains the
word Stop.
The process box indicates what must be done at this point in the program execution. The
operation specified by the process box could be shifting the contents of one general-purpose register to a peripheral register, decrementing a loop count, and so on.
The input/output box is used to represent data that are either read or displayed by the
computer.
The decision box contains a question that can be answered either yes or no. A decision box
has two exits, also marked yes or no. The computer will take one action if the answer is yes and
will take a different action if the answer is no.
The on-page connector indicates that the flowchart continues elsewhere on the same page.
The place where it is continued will have the same label as the on-page connector.
The off-page connector indicates that the flowchart continues on another page. To determine where the flowchart continues, one needs to look at the following pages of the flowchart
to find the matching off-page connector.
Normal flow on a flowchart is from top to bottom and from left to right. Any line that
does not follow this normal flow should have an arrowhead on it. When the program gets complicated, the flowchart that documents the logic flow of the program also becomes difficult to
follow. This is the limitation of the flowchart. In this book, we will mix both the flowchart and
the algorithm procedure to describe the solution to a problem.
After the programmer is satisfied with the algorithm or the flowchart, it is converted to
source code in one of the assembly or high-level languages. Each statement in the algorithm
(or each block of the flowchart) will be converted into one or multiple assembly instructions
A
Terminal
Process
Subroutine
Input or
output
B
Off-page connector
Decision
Yes
A
On-page connector
No
Figure 2.1 ■ Flowchart symbols used in this book
47
2.5 ■ Writing Programs to Do Arithmetic
or high-level language statements. If an algorithmic step (or a block in the flowchart) requires
many assembly instructions or high-level language statements to implement, then it might
be beneficial either to convert this step (or block) into a subroutine and just call the subroutine or to further divide the algorithmic step (or flowchart block) into smaller steps (or
blocks) so that it can be coded with just a few assembly instructions or high-level language
statements.
The next major step is testing the program. Testing a program means testing for anomalies. The first test is for normal inputs that are always expected. If the result is what is expected, then the borderline inputs are tested. The maximum and minimum values of the
input are tested. When the program passes this test, then illegal input values are tested. If the
algorithm includes several branches, then enough values must be used to exercise all the possible branches. This is to make sure that the program will operate correctly under all possible
circumstances.
In the rest of this book, most of the problems are well defined. Therefore, our focus is on
how to design the algorithm that solves the specified problem as well as how to convert the
algorithm into source code.
2.5 Writing Programs to Do Arithmetic
In this section, we use small programs that perform simple computations to demonstrate
how a program is written.
Example 2.3
▼
Write a program to add the numbers stored at memory locations $1000, $1001, and $1002
and store the sum at memory location $1010.
Solution: This problem can be solved by the following steps:
Step 1
Load the contents of the memory location at $1000 into accumulator A.
Step 2
Add the contents of the memory location at $1001 into accumulator A.
Step 3
Add the contents of the memory location at $1002 into accumulator A.
Step 4
Store the contents of accumulator A at memory location $1010.
These steps can be translated into the as12 assembly program as follows:
org
ldaa
adda
adda
staa
end
$1500
$1000
$1001
$1002
$1010
; starting address of the program
; A ⇐ [$1000]
; A ⇐ [A] 1 [$1001]
; A ⇐ [A] 1 [$1002]
; $1010 ⇐ [A]
▲
48
Chapter 2 ■ HCS12 Assembly Programming
Start
A ← [$1001]
A ← [A] + [$1002]
A ← [A] − [$1005]
$1010 ← [A]
Stop
Figure 2.2 ■ Logic flow of program 2.4
Example 2.4
▼
Write a program to subtract the contents of the memory location at $1005 from the sum of
the memory locations at $1000 and $1002 and store the result at the memory location $1010.
Solution: The logic flow of this program is illustrated in Figure 2.2. The assembly program is as
follows:
org
ldaa
adda
suba
staa
end
$1500
$1000
$1002
$1005
$1010
; starting address of the program
; A ⇐ [$1000]
; A ⇐ [A] 1 [$1002]
; A ⇐ [A] 2 [$1005]
; $1010 ⇐ [A]
▲
Example 2.5
▼
Write a program to subtract 5 from four memory locations at $1000, $1001, $1002, and
$1003.
Solution: In the HCS12, a memory location cannot be the destination of an ADD or SUB instruction. Therefore, three steps must be followed to add or subtract a number to or from a
memory location.
Step 1
Load the memory contents into an accumulator.
Step 2
Add (or subtract) the number to (from) the accumulator.
49
2.5 ■ Writing Programs to Do Arithmetic
Step 3
Store the result at the specified memory location.
The program is as follows:
org
ldaa
suba
staa
ldaa
suba
staa
ldaa
suba
staa
ldaa
suba
staa
end
$1500
$1000
#5
$1000
$1001
#5
$1001
$1002
#5
$1002
$1003
#5
$1003
; A ⇐ [$1000]
; A ⇐ [A] 2 5
; $1000 ⇐ [A]
; A ⇐ [$1001]
; A ⇐ [A] 2 5
; $1001 ⇐ [A]
; A ⇐ [$1002]
; A ⇐ [A] 2 5
; $1002 ⇐ [A]
; A ⇐ [$1003]
; A ⇐ [A] 2 5
; $1003 ⇐ [A]
▲
Example 2.6
▼
Write a program to add two 16-bit numbers that are stored at $1000,$1001 and $1002,$1003
and store the sum at $1010,$1011.
Solution: This program is very straightforward.
org
ldd
addd
std
end
$1500
$1000
$1002
$1010
; place the 16-bit number at $1000,$1001 in D
; add the 16-bit number at $1002,$1003 to D
; save the sum at $1010,$1011
▲
2.5.1 Carry/Borrow Flag
The HCS12 can add and subtract either 8-bit or 16-bit numbers and place the result in
either 8-bit accumulators, A or B, or the double accumulator D. The 8-bit number stored in accumulator B can also be added to index register X or Y. However, programs can also be written
to add numbers larger than 16 bits. Arithmetic performed in a 16-bit microprocessor/microcontroller on numbers that are larger than 16 bits is called multiprecision arithmetic. Multiprecision arithmetic makes use of the carry flag (C flag) of the condition code register (CCR).
Bit 0 of the CCR register is the C flag. It can be thought of as a temporary 9th bit that is appended to any 8-bit register or 17th bit that is appended to any 16-bit register. The C flag allows
us to write programs to add and subtract hex numbers that are larger than 16 bits. For example,
consider the following two instructions:
ldd
addd
#$8645
#$9978
These two instructions add the numbers $8645 and $9978.
50
Chapter 2 ■ HCS12 Assembly Programming
$ 86 4 5
+ $9978
$11 F B D
The result is $11FBD, a 17-bit number, which is too large to fit into the 16-bit double
accumulator D. When the HCS12 executes these two instructions, the lower 16 bits of the
answer, $1FBD, are placed in double accumulator D. This part of the answer is called sum.
The leftmost bit is called a carry. A carry of 1 following an addition instruction sets the C flag
of the CCR register to 1. A carry of 0 following an addition clears the C flag to 0. This applies
to both 8-bit and 16-bit additions for the HCS12. For example, execution of the following two
instructions
ldd
addd
#$1245
#$4581
will clear the C flag to 0 because the carry resulting from this addition is 0. In summary,
• If the addition produces a carry of 1, the carry flag is set to 1.
• If the addition produces a carry of 0, the carry flag is cleared to 0.
2.5.2 Multiprecision Addition
For a 16-bit microcontroller like the HCS12, multiprecision addition is the addition of
numbers that are larger than 16 bits. To add the hex number $1A598183 to $76548290, the
HCS12 has to perform multiprecision addition.
1
1 1
$ 1A 5 9 81 8 3
+ $ 7 6 5 4 82 9 0
$ 9 0 A E 04 1 3
Multiprecision addition is performed 1 byte at a time, beginning with the least significant byte.
The HCS12 does allow us to add 16-bit numbers at a time because it has the addd instruction. The following two instructions can be used to add the least significant 16-bit numbers
together:
ldd
addd
#$8183
#$8290
Since the sum of the most significant digit is greater than 16, it generates a carry that must
be added to the next more significant digit, causing the C flag to be set to 1. The contents of
double accumulator D must be saved before the higher bytes are added. Here the 2 bytes are
saved at $1002,$1003.
std
$1002
When the second most significant bytes are added, the carry from the lower byte must be added
in order to obtain the correct sum. In other words, we need an “add with carry” instruction.
There are two versions of this instruction: the adca instruction for accumulator A and the adcb
instruction for accumulator B. The instructions for adding the second significant bytes are
ldaa
adca
#$59
#$54
We need also to save the second most significant byte of the result at $1001 with the following
instruction:
staa
$1001
51
2.5 ■ Writing Programs to Do Arithmetic
The most significant bytes can be added using similar instructions, and the complete program
with comments appears as follows:
ldd
addd
std
ldaa
adca
staa
ldaa
adca
staa
end
#$8183
#$8290
$1002
#$59
#$54
$1001
#$1A
#$76
$1000
; D ⇐ $8183
; D ⇐ [D] 1 $8290
; $10022$1003 ⇐ [D]
; A ⇐ $59
; A ⇐ [A] 1 $54 1 C
; $1001 ⇐ [A]
; A ⇐ $1A
; A ⇐ [A] 1 $76 1 C
; $1000 ⇐ [A]
Note that the load and store instructions do not affect the value of the C flag (otherwise, the
program would not work). The HCS12 does not have a 16-bit instruction with the carry flag as
an operand. Whenever the carry needs to be added, we must use the 8-bit instruction adca or
adcb. This is shown in the previous program.
Example 2.7
▼
Write a program to add two 4-byte numbers that are stored at $1000,$1003 and $1004,$1007
and store the sum at $1010,$1013.
Solution: The addition should start from the least significant byte and proceed to the most significant byte. The program is as follows:
org
ldd
addd
std
ldaa
adca
staa
ldaa
adca
staa
end
$1500
$1002
$1006
$1012
$1001
$1005
$1011
$1000
$1004
$1010
; starting address of the program
; D ⇐ [$1002,$1003]
; D ⇐ [D] 1 [$1006,$1007]
; $1012,$1013 ⇐ [D]
; A ⇐ [$1001]
; A ⇐ [A] 1 [$1005] 1 C
; $1011 ⇐ [A]
; A ⇐ [$1000]
; A ⇐ [A] 1 [$1004] 1 C
; $1010 ⇐ [A]
▲
2.5.3 Subtraction and the C Flag
The C flag also enables the HCS12 to borrow from the high byte to the low byte during a
multiprecision subtraction. Consider the following subtraction problem:
$39
− $74
We are attempting to subtract a larger number from a smaller one. Subtracting $4 from $9 is not
a problem.
$39
− $74
5
52
Chapter 2 ■ HCS12 Assembly Programming
Now we need to subtract $7 from $3. To do this, we need to borrow from somewhere. The
HCS12 borrows from the C flag, thus setting the C flag. When we borrow from the next higher
digit of a hex number, the borrow has a value of decimal 16. After the borrow from the C flag,
the problem can be completed.
$39
− $74
$C5
When the HCS12 executes a subtract instruction, it always borrows from the C flag. The borrow is either 1 or 0. The C flag operates as follows during a subtraction:
• If the HCS12 borrows a 1 from the C flag during a subtraction, the C flag is set to 1.
• If the HCS12 borrows a 0 from the C flag during a subtraction, the C flag is set to 0.
2.5.4 Multiprecision Subtraction
For a 16-bit microcontroller, multiprecision subtraction is the subtraction of numbers that
are larger than 16 bits. To subtract the hex number $16753284 from $98765432, the HCS12 has
to perform a multiprecision subtraction.
$9 8 7 6 5 4 3 2
− $1 6 7 5 7 2 8 4
Like multiprecision addition, multiprecision subtraction is performed 1 byte at a time, beginning with the least significant byte. The HCS12 does allow us to subtract 2 bytes at a time
because it has the subd instruction. The following two instructions can be used to subtract the
least significant 2 bytes of the subtrahend from the minuend:
ldd
subd
#$5432
#$7284
Since a larger number is subtracted from a smaller one, there is a need to borrow from the
higher byte, causing the C flag to be set to 1. The contents of double accumulator D should be
saved before the higher bytes are subtracted. Let’s save these 2 bytes at $1002,$1003.
std
$1002
When the second most significant bytes are subtracted, the borrow 1 has to be subtracted from
the second most significant byte of the result. In other words, we need a “subtract with borrow”
instruction. There is such an instruction, but it is called subtract with carry. There are two
versions: the sbca instruction for accumulator A and the sbcb instruction for accumulator B.
The instructions to subtract the second most significant bytes are
ldaa
sbca
#$76
#$75
We also need to save the second most significant byte of the result at $1001 with the following
instruction:
staa
$1001
The most significant bytes can be subtracted using similar instructions, and the complete
program with comments is as follows:
org
ldd
subd
std
$1500
#$5432
#$7284
$1002
; starting address of the program
; D ⇐ $5432
; D ⇐ [D] 2 $7284
; $1002,$1003 ⇐ [D]
53
2.5 ■ Writing Programs to Do Arithmetic
ldaa
sbca
staa
ldaa
sbca
staa
end
#$76
#$75
$1001
#$98
#$16
$1000
; A ⇐ $76
; A ⇐ [A] 2 $75 — C
; $1001 ⇐ [A]
; A ⇐ $98
; A ⇐ [A] 2 $16 — C
; $1000 ⇐ [A]
Example 2.8
▼
Write a program to subtract the hex number stored at $1004,$1007 from the hex number
stored at $1000,$1003 and save the difference at $1010,$1013.
Solution: We will perform the subtraction from the least significant byte toward the most significant byte as follows:
org
ldd
subd
std
ldaa
sbca
staa
ldaa
sbca
staa
end
$1500
$1002
$1006
$1012
$1001
$1005
$1011
$1000
$1004
$1010
; starting address of the program
; D ⇐ [$1002 2 $1003]
; D ⇐ [D] 2 [$1006 2 $1007]
; $1012 2 $1013 ⇐ [D]
; A ⇐ [$1001]
; A ⇐ [A] 2 [$1005] 2 C
; $1011 ⇐ [A]
; A ⇐ [$1000]
; A ⇐ [A] 2 [$1004] 2 C
; $1010 ⇐ [A]
▲
2.5.5 Binary-Coded-Decimal (BCD) Addition
Although virtually all computers work internally with binary numbers, the input and
output equipment generally uses decimal numbers. Since most logic circuits only accept twovalued signals, the decimal numbers must be coded in terms of binary signals. In the simplest
form of binary code, each decimal digit is represented by its binary equivalent. For example,
2538 is represented by
0010 0101 0011 1000
This representation is called a binary coded decimal (BCD). If the BCD format is used, it must
be preserved during arithmetic processing.
The principal advantage of the BCD encoding method is the simplicity of input/output
conversion; its major disadvantage is the complexity of arithmetic processing. The choice between binary and BCD depends on the type of problems the system will be handling.
The HCS12 microcontroller can add only binary numbers, not decimal numbers. The following instruction sequence appears to cause the HCS12 to add the decimal numbers 25 and 31
and store the sum at the memory location $1000:
ldaa
adda
staa
#$25
#$31
$1000
54
Chapter 2 ■ HCS12 Assembly Programming
This instruction sequence performs the following addition:
$25
+$31
+ $56
When the HCS12 executes this instruction sequence, it adds the numbers according to the rules
of binary addition and produces the sum $56. This is the correct BCD answer, because the
result represents the decimal sum of 25 1 31 5 56. In this example, the HCS12 gives the appearance of performing decimal addition. However, a problem occurs when the HCS12 adds
two BCD digits and generates a sum greater than 9. Then the sum is incorrect in the decimal
number system, as the following three examples illustrate:
$18
+$47
$5F
$35
+$47
$7C
$19
+$47
$60
The answers to the first two problems are obviously wrong in the decimal number system because
the hex digits F and C are not between 0 and 9. The answer to the third example appears to contain
valid BCD digits, but in the decimal system 19 plus 47 equals 66, not 60; this example involves a
carry from the lower nibble to the higher nibble.
In summary, a sum in the BCD format is incorrect if it is greater than $9 or if there is a
carry to the next-higher nibble. Incorrect BCD sums can be adjusted by adding $6 to them. To
correct the examples,
1. Add $6 to every sum digit greater than 9.
2. Add $6 to every sum digit that had a carry of 1 to the next higher digit.
Here are the problems with their sums adjusted.
$18
+ $47
$5F
+$ 6
$65
$35
+$47
$7C
+$ 6
$8 2
$19
+ $47
$60
+$ 6
$66
The fifth bit of the condition code register is the half-carry, or H flag. A carry from the lower
nibble to the higher nibble during the addition operation is a half-carry. A half-carry of 1 during
addition sets the H flag to 1, and a half-carry of 0 during addition clears it to 0. If there is a carry
from the high nibble during addition, the C flag is set to 1; this indicates that the high nibble is
incorrect. $6 must be added to the high nibble to adjust it to the correct BCD sum.
Fortunately, we don’t need to write instructions to detect the illegal BCD sum following
a BCD addition. The HCS12 provides a decimal adjust accumulator A instruction, daa, which
takes care of all these detailed detection and correction operations. The daa instruction monitors the sums of BCD additions and the C and H flags and automatically adds $6 to any nibble
that requires it. The rules for using the daa instruction are
1. The daa instruction can only be used for BCD addition. It does not work for subtraction
or hex arithmetic.
2. The daa instruction must be used immediately after one of the three instructions that
leave their sum in accumulator A. (These three instructions are adda, adca, and aba.)
3. The numbers added must be legal BCD numbers to begin with.
55
2.5 ■ Writing Programs to Do Arithmetic
Example 2.9
▼
Write an instruction sequence to add the BCD numbers stored at memory locations $1000
and $1001 and store the sum at $1010.
Solution:
ldaa
adda
daa
staa
$1000
$1001
; load the first BCD number in A
; perform addition
; decimal adjust the sum in A
; save the sum
$1010
Multiple-byte BCD numbers can be added and the correct result can be obtained by executing the daa instruction immediately after the addition of each byte.
▲
2.5.6 Multiplication and Division
The HCS12 provides three multiply and five divide instructions. A brief description of
these instructions is shown in Table 2.1.
The emul instruction multiplies the 16-bit unsigned integers stored in accumulator D and
index register Y and leaves the product in these two registers. The upper 16 bits of the product
are in Y, whereas the lower 16 bits are in D.
The mul instruction multiplies the 8-bit unsigned integer in accumulator A by the 8-bit unsigned integer in accumulator B to obtain a 16-bit unsigned result in double accumulator D. The
upper byte of the product is in accumulator A, whereas the lower byte of the product is in B.
Mnemonic
Function
Operation
emul
Unsigned 16 by 16 multiply
(D) × (Y) → Y:D
emuls
Signed 16 by 16 multiply
(D) × (Y) → Y:D
mul
Unsigned 8 by 8 multiply
ediv
Unsigned 32 by 16 divide
(A) × (B) → A:B
(Y:D) ÷ (X)
Quotient → Y
Remainder → D
edivs
Signed 32 by 16 divide
fdiv
16 by 16 fractional divide
(D) ÷ (X) → X
Remainder → D
idiv
Unsigned 16 by 16 integer
divide
(D) ÷ (X) → X
Remainder → D
idivs
Signed 16 by 16 integer
divide
(D) ÷ (X) → X
Remainder → D
(Y:D) ÷ (X)
Quotient → Y
Remainder → D
Table 2.1 ■ Summary of 68HC12 multiply and divide instructions
56
Chapter 2 ■ HCS12 Assembly Programming
The ediv instruction performs an unsigned 32-bit by 16-bit division. The dividend is the
register pair Y and D with Y as the upper 16 bits of the dividend. Index register X is the divisor.
After division, the quotient and the remainder are placed in Y and D, respectively.
The edivs instruction performs a signed 32-bit by 16-bit division using the same operands
as the ediv instruction. After division, the quotient and the remainder are placed in Y and D,
respectively.
The fdiv instruction divides an unsigned 16-bit dividend in double accumulator D by an unsigned 16-bit divisor in index register X, producing an unsigned 16-bit quotient in X, and an unsigned 16-bit remainder in D. The dividend must be less than the divisor. The radix point of the
quotient is to the left of bit 15. In the case of overflow (the denominator is less than or equal to the
nominator) or division by zero, the quotient is set to $FFFF and the remainder is indeterminate.
The idiv instruction divides an unsigned 16-bit dividend in double accumulator D by the
unsigned 16-bit divisor in index register X, producing an unsigned 16-bit quotient in X and an
unsigned 16-bit remainder in D. If both the divisor and the dividend are assumed to have radix points in the same positions (to the right of bit 0), the radix point of the quotient is to the
right of bit 0. In the case of division by zero, the quotient is set to $FFFF and the remainder is
indeterminate.
The idivs instruction divides the signed 16-bit dividend in double accumulator D by the
signed 16-bit divisor in index register X, producing a signed 16-bit quotient in X and a signed
16-bit remainder in D. If division by zero is attempted, the values in D and X are not changed,
but the values of the N, Z, and V status bits are undefined.
Example 2.10
▼
Write an instruction sequence to multiply the contents of index register X and double accumulator D and store the product at memory locations $1000,$1003.
Solution: There is no instruction to multiply the contents of double accumulator D and index
register X. However, we can transfer the contents of index register X to index register Y and
execute the emul instruction. If index register Y holds useful information, then we need to save
it before the data transfer.
sty
tfr
emul
sty
std
ldy
$1010
x,y
$1000
$1002
$1010
; save Y in a temporary location
; transfer the contents of X to Y
; perform the multiplication
; save the upper 16 bits of the product
; save the lower 16 bits of the product
; restore the value of Y
▲
Example 2.11
▼
Write an instruction sequence to divide the signed 16-bit number stored at memory locations $1005,$1006 by the 16-bit signed number stored at memory locations $1020,$1021 and
store the quotient and remainder at $1030,$1031 and $1032,$1033, respectively.
Solution: Before we can perform the division, we need to place the dividend and divisor in D and
X, respectively.
2.5 ■ Writing Programs to Do Arithmetic
ldd
ldx
idivs
stx
std
$1005
$1020
$1030
$1032
; place the dividend in D
; place the divisor in X
; perform the signed division
; save the quotient
; save the remainder
57
▲
Because most arithmetic operations can be performed only on accumulators, we need to
transfer the contents of index register X to D so that further division on the quotient can be
performed. The HCS12 provides two exchange instructions in addition to the TFR instruction
for this purpose.
• The xgdx instruction exchanges the contents of accumulator D and index register X.
• The xgdy instruction exchanges the contents of accumulator D and index register Y.
The HCS12 provides instructions for performing unsigned 8-bit by 8-bit and both signed
and unsigned 16-bit by 16-bit multiplications. Since the HCS12 is a 16-bit microcontroller, we
expect that it will be used to perform complicated operations in many sophisticated applications. Performing 32-bit by 32-bit multiplication will be one of them.
Since there is no 32-bit by 32-bit multiplication instruction, we have to break a 32-bit number into two 16-bit halves and use the 16-bit by 16-bit multiply instruction to synthesize the
operation. Assume that M and N are the multiplicand and the multiplier, respectively. These
two numbers can be broken down as follows:
M 5 MHML
N 5 NHNL
where MH and NH are the upper 16 bits and ML and NL are the lower 16 bits of M and N, respectively. Four 16-bit by 16-bit multiplications are performed, and then their partial products are
added together, as shown in Figure 2.3.
The procedure is as follows:
Step 1
Allocate 8 bytes to hold the product. Assume these 8 bytes are located at P, P11, . . . , and
P17.
Step 2
Compute the partial product MLNL (in Y:D) and save it at locations P14,P17.
Step 3
Compute the partial product MHNH (in Y:D) and save it at locations P,P13.
Step 4
Compute the partial product MHNL (in Y:D) and add it to memory locations P12,P15.
The C flag may be set to 1 after this addition.
Step 5
Add the C flag to memory location P11 using the adca (or adcb) instruction. This addition
may also set the C flag to 1. So, again, add the C flag to memory location P.
Step 6
Generate the partial product MLNH (in Y:D) and add it to memory locations P12,P15.
The carry flag may be set to 1, so add the C flag to memory location P11 and then add it
to memory location P.
58
Chapter 2 ■ HCS12 Assembly Programming
16-bit
16-bit
16-bit
16-bit
Upper half Lower half Partial product MLNL
Upper half Lower half
Partial product MHNL
Upper half Lower half
Partial product MLNH
Partial product MHNH
Upper half Lower half
Address
P , P+1 P+2 , P+3 P+4 , P+5 P+6 , P+7 Final product M × N
msb
lsb
Note: msb stands for most significant byte and lsb for least significant byte
Figure 2.3 ■ Unsigned 32-bit by 32-bit multiplication
Example 2.12
▼
Write a program to multiply the 32-bit unsigned integers stored at M,M13 and N,N13,
respectively, and store the product at memory locations P,P17.
Solution: The following program is a direct translation of the previous multiplication
algorithm:
org
$1000
rmb
4
; multiplicand
rmb
4
; multiplier
rmb
8
; product
org
$1500
……………….........
; some other instructions
ldd
M12
; place ML in D
ldy
N12
; place NL in Y
emul
; compute MLNL
sty
P14
; save the upper 16 bits of the partial product MLNL
std
P16
; save the lower 16 bits of the partial product MLNL
ldd
M
; place MH in D
ldy
N
; place NH in Y
emul
; compute MHNH
sty
P
; save the upper 16 bits of the partial product MHNH
std
P12
; save the lower 16 bits of the partial product MHNH
ldd
M
; place MH in D
ldy
N12
; place NL in Y
emul
; compute MHNL
; the following seven instructions add MHNL to memory locations P12,P15
addd
P14
; add the lower half of MHNL to P14,P15
std
P14
;
“
M
N
P
59
2.5 ■ Writing Programs to Do Arithmetic
tfr
y,d
; transfer Y to D
adcb
P13
stab
P13
adca
P12
staa
P12
; the following six instructions propagate carry to the most significant byte
ldaa
P11
adca
#0
; add C flag to location P11
staa
P11
;
“
ldaa
P
adca
#0
; add C flag to location P
staa
P
; the following three instructions compute MLNH
ldd
M12
; place ML in D
ldy
N
; place NH in Y
emul
; compute MLNH
; the following seven instructions add MLNH to memory locations P12,P15
addd
P14
; add the lower half of MLNH to P14,P15
std
P14
;
“
tfr
y,d
; transfer Y to D
adcb
P13
stab
P13
adca
P12
staa
P12
; the following six instructions propagate carry to the most significant byte
ldaa
P11
adca
#0
; add C flag to location P11
staa
P11
ldaa
P
adca
#0
; add C flag to location P
staa
P
end
▲
Example 2.13
▼
Write a program to convert the 16-bit binary number stored at $1000,$1001 to BCD format
and store the result at $1010,$1014. Convert each BCD digit into its ASCII code and store it in
1 byte.
Solution: A binary number can be converted to BCD format using repeated division by 10. The
largest 16-bit binary number corresponds to the 5-digit decimal number 65,535. The first division by 10 computes the least significant digit and should be stored in the memory location
$1014, the second division-by-10 operation computes the 10s digit, and so on. The ASCII code
of a BCD digit can be obtained by adding $30 to each BCD digit. The program is as follows:
data
org
fdb
org
$1000
12345
$1010
; place a number for testing
60
Chapter 2 ■ HCS12 Assembly Programming
result
rmb
org
ldd
ldy
ldx
idiv
addb
stab
xgdx
ldx
idiv
addb
stab
xgdx
ldx
idiv
addb
stab
xgdx
ldx
idiv
5
$1500
data
#result
#10
addb
stab
xgdx
addb
stab
end
#$30
1,Y
#$30
4,Y
; reserve 5 bytes to store the result
; make a copy of the number to be converted
; divide the number by 10
;
“
; convert to ASCII code
; save the least significant digit
; swap the quotient to D
#10
#$30
3,Y
; convert to ASCII code
; save the second-least significant digit
#10
#$30
2,Y
; save the middle digit
#10
; separate the most significant and second-most
; significant digits
#$30
0,Y
; save the second-most significant digit
; swap the most significant digit to B
; convert to ASCII code
; save the most significant digit
▲
2.6 Program Loops
Many applications require repetitive operations. We can write programs to tell the computer to perform the same operation over and over. A finite loop is a sequence of instructions
that will be executed by the computer for a finite number of times; an endless loop is a sequence of instructions that the computer will execute forever.
There are four major loop constructs.
D O S TAT E M E N T S F O R E V E R
This is an endless loop in which statement S is repeated forever. In some applications, we
might add the statement “If C then exit” to leave the infinite loop. An infinite loop is shown in
Figure 2.4.
FOR i 5 N1 TO N2 DO S OR FOR i 5 N2 DOWNTO N1 DO S
Here, the variable i is the loop counter, which keeps track of the current iteration of the loop.
The loop counter can be incremented (the first case) or decremented (the second case). Statement S is repeated n2 2 n1 1 1 times. The value of n2 is assumed to be no smaller than that of n1.
61
2.6 ■ Program Loops
S
Figure 2.4 ■ An infinite loop
If there is concern that the relationship n1 ≤ n2 may not hold, then it must be checked at the
beginning of the loop. Four steps are required to implement a FOR loop.
Step 1
Initialize the loop counter and other variables.
Step 2
Compare the loop counter with the limit to see if it is within bounds. If it is, then perform
the specified operations. Otherwise, exit the loop.
Step 3
Increment (or decrement) the loop counter.
Step 4
Go to step 2.
A For loop is illustrated in Figure 2.5.
WHILE C DO S
Whenever a While construct is executed, the logical expression C is evaluated first. If it
yields a false value, statement S will not be executed. The action of a While construct is illustrated in Figure 2.6. Four steps are required to implement a While loop.
i ← n1
i ≤ n2 ?
i ← n2
No
i ≥ n1 ?
Yes
Yes
S
S
i←i+1
i←i–1
(a) For i = n1 to n2 DO S
No
(b) For i = n2 down to n1DO S
Figure 2.5 ■ For looping construct
62
Chapter 2 ■ HCS12 Assembly Programming
Step 1
Initialize the logical expression C.
Step 2
Evaluate the logical expression C.
Step 3
Perform the specified operations if the logical expression C evaluates to true. Update the
logical expression C and go to step 2. (Note: The logical expression C may be updated by
external conditions or by an interrupt service routine.)
Step 4
Exit the loop.
True
C
S
False
Figure 2.6 ■ The While … Do looping construct
R E P E AT S U N T I L C
Statement S is first executed then the logical expression C is evaluated. If C is false, the
next statement will be executed. Otherwise, statement S will be executed again. The action
of this construct is illustrated in Figure 2.7. Statement S will be executed at least once. Three
steps are required to implement this construct.
Step 1
Initialize the logical expression C.
Step 2
Execute statement S.
initialize C
S
True
C
False
Figure 2.7 ■ The Repeat … Until looping construct
63
2.6 ■ Program Loops
Step 3
Go to step 2 if the logical expression C evaluates to true. Otherwise, exit.
To implement one of the looping constructs, we need to use the unconditional branch or one
of the conditional instructions. When executing conditional branch instructions, the HCS12
checks the condition flags in the CCR register.
2.6.1 Condition Code Register
The contents of the condition code register are shown in Figure 2.8. The shaded characters
are condition flags that reflect the status of an operation. The meanings of these condition flags
are as follows:
7
6
5
4
3
2
1
0
S
X
H
I
N
Z
V
C
Figure 2.8 ■ Condition code register
• C: the carry flag. Whenever a carry is generated as the result of an operation, this
flag will be set to 1. Otherwise, it will be cleared to 0.
• V: the overflow flag. Whenever the result of a two’s complement arithmetic
operation is out of range, this flag will be set to 1. Otherwise, it will be set to 0.
The V flag is set to 1 when the carry from the most significant bit and the second
most significant bit differ as the result of an arithmetic operation.
• Z: the zero flag. Whenever the result of an operation is zero, this flag will be set to
1. Otherwise, it will be set to 0.
• N: the negative flag. Whenever the most significant bit of the result of an operation
is 1, this flag will be set to 1. Otherwise, it will be set to 0. This flag indicates that
the result of an operation is negative.
• H: the half-carry flag. Whenever there is a carry from the lower four bits to the
upper four bits as the result of an operation, this flag will be set to 1. Otherwise, it
will be set to 0.
2.6.2 Branch Instructions
Branch instructions cause program flow to change when specific conditions exist.
The HCS12 has three kinds of branch instructions, short branches, long branches, and bitconditional branches.
Branch instructions can also be classified by the type of condition that must be satisfied in
order for a branch to be taken. Some instructions belong to more than one category.
• Unary (unconditional) branch instructions always execute.
• Simple branches are taken when a specific bit in the CCR register is in a specific
state as a result of a previous operation.
• Unsigned branches are taken when a comparison or a test of unsigned quantities
results in a specific combination of condition code register bits.
• Signed branches are taken when a comparison or a test of signed quantities results
in a specific combination of condition code register bits.
64
Chapter 2 ■ HCS12 Assembly Programming
When a short-branch instruction is executed, a signed 8-bit offset is added to the value in
the program counter when a specified condition is met. Program execution continues at the
new address. The numeric range of the short-branch offset value is $80 (2128) to $7F (127) from
the address of the instruction immediately following the branch instruction. A summary of the
short-branch instructions is in Table 2.2.
When a long-branch instruction is executed, a signed 16-bit offset is added to the value in
the program counter when a specified condition is met. Program execution continues at the
Unary Branches
Mnemonic
bra rel8 or lbra rel16
brn rel8 or lbrn rel16
Function
Equation or Operation
1=1
1=0
Branch always
Branch never
Simple Branches
Mnemonic
Function
Equation or Operation
bcc rel8 or lbcc rel16
bcs rel8 or lbcs rel16
Branch if carry clear
Branch if carry set
C=0
C=1
beq rel8 or lbeq rel16
bmi rel8 or lbmi rel16
Branch if equal
Branch if minus
Z=1
N=1
bne rel8 or lbne rel16
Branch if not equal
Z=0
bpl rel8 or lbpl rel16
bvc rel8 or lbvc rel16
Branch if plus
Branch if overflow clear
N=0
V=0
bvs rel8 or lbvs rel16
Branch if overflow set
V=1
Unsigned Branches
Mnemonic
Function
Equation or Operation
C+Z=0
bhi rel8 or lbhi rel16
Branch if higher
bhs rel8 or lbhs rel16
blo rel8 or lblo rel16
Branch if higher or same
Branch if lower
C=0
C=1
bls rel8 or lbls rel16
Branch if lower or same
C+Z=1
Signed Branches
Mnemonic
Function
Equation or Operation
N⊕V=0
bge rel8 or lbge rel16
Branch if greater than or equal
bgt rel8 or lbgt rel16
Branch if greater than
Z + (N ⊕ V) = 0
ble rel8 or lble rel16
blt rel8 or lblt rel16
Branch if less than or equal
Branch if less than
Z + (N ⊕ V) = 1
N⊕V=1
Note: 1. Each row contains two branch instructions that are separated by the word or.
2. The instruction to the left of or is a short branch with 8-bit offset.
3. The instruction to the right of or is a long branch with 16-bit offset.
Table 2.2 ■ Summary of short and long branch instructions
65
2.6 ■ Program Loops
new address. Long-branch instructions are used when large displacements between decisionmaking steps are necessary.
The numeric range of long-branch offset values is $8000 (232,768) to $7FFF (32,767) from
the instruction immediately after the branch instruction. This permits branching from any location in the standard 64-kB address map to any other location in the map. A summary of the
long-branch instructions is in Table 2.2.
Although there are many possibilities in writing a program loop, the following one is a
common format:
loop:
.
.
.
Bcc (or LBcc) loop
where cc is one of the condition codes (CC, CS, EQ, MI, NE, PL, VC, VS, HI, HS, LO, LS, GE,
GT, LS, and LT). Usually, there will be a comparison or arithmetic instruction to set up the condition code for use by the conditional branch instruction. Unsigned branch instructions treat
the numbers compared previously as nonnegative numbers. Signed branch instructions treat
the numbers compared previously as signed numbers.
2.6.3 Compare and Test Instructions
The HCS12 has a set of compare instructions that are dedicated to the setting of condition
flags. The compare and test instructions perform subtraction between a pair of registers or between a register and a memory location. The result is not stored, but condition codes are set by
the operation. In the HCS12, most instructions update condition code flags automatically, so it
is often unnecessary to include a separate test or compare instruction. Table 2.3 is a summary
of compare and test instructions.
Compare Instructions
Mnemonic
cba
Function
Operation
Compare A to B
(A) − (B)
cmpa <opr>
cmpb <opr>
Compare A to memory
Compare B to memory
(A) − (M)
(B) − (M)
cpd <opr>
cps <opr>
Compare D to memory
Compare SP to memory
(D) − (M:M+1)
(SP) − (M:M+1)
cpx <opr>
cpy <opr>
Compare X to memory
Compare Y to memory
(X) − (M:M+1)
(Y) − (M:M+1)
Test Instructions
Mnemonic
tst <opr>
tsta
tstb
Function
Operation
Test memory for zero or minus
Test A for zero or minus
(M) − $00
(A) − $00
Test B for zero or minus
(B) − $00
Note: <opr> represents an immediate value or a memory location and can be specified by
using the immediate, direct, extended, and indexed addressing modes.
Table 2.3 ■ Summary of compare and test instructions
66
Chapter 2 ■ HCS12 Assembly Programming
2.6.4 Loop Primitive Instructions
A lot of the program loops are implemented by incrementing or decrementing a loop count.
The branch is taken when either the loop count is equal to zero or not equal to zero, depending
on the applications. The HCS12 provides a set of loop primitive instructions for implementing
this type of looping mechanism. These instructions test a counter value in a register or accumulator (A, B, D, X, Y, or SP) for zero or nonzero value as a branch condition. There are predecrement, preincrement, and test-only versions of these instructions.
The range of the branch is from $80 (2128) to $7F (127) from the instruction immediately following the loop primitive instruction. Table 2.4 shows a summary of the loop primitive instructions.
Mnemonic
Function
dbeq cntr, rel
Decrement counter and branch if = 0
(counter = A, B, D, X, Y, or SP)
counter ← (counter) − 1
If (counter) = 0, then branch;
else continue to next instruction.
dbne cntr, rel
Decrement counter and branch if ≠ 0
(counter = A, B, D, X, Y, or SP)
counter ← (counter) − 1
If (counter) ≠ 0, then branch;
else continue to next instruction.
ibeq cntr, rel
Increment counter and branch if = 0
(counter = A, B, D, X, Y, or SP)
counter ← (counter) + 1
If (counter) = 0, then branch;
else continue to next instruction.
ibne cntr, rel
Increment counter and branch if ≠ 0
(counter = A, B, D, X, Y, or SP)
counter ← (counter) + 1
If (counter) ≠ 0, then branch;
else continue to next instruction.
tbeq cntr, rel
Test counter and branch if = 0
(counter = A, B, D, X, Y, or SP)
If (counter) = 0, then branch;
else continue to next instruction.
tbne cntr, rel
Test counter and branch if ≠ 0
(counter = A, B, D, X, Y, or SP)
If (counter) ≠ 0, then branch;
else continue to next instruction.
Note:
Equation or Operation
1. cntr is the loop counter and can be accumulator A, B, or D and register X, Y, or SP.
2. rel is the relative branch offset and is usually a label.
Table 2.4 ■ Summary of loop primitive instructions
2.6.5 Implementation of Looping Constructs
The statement “for i 5 n1 to n2 do S” can be implemented as follows:
n1
n2
i
loop
equ
equ
…
ds.b
…
movb
ldaa
cmpa
bgt
…
…
inc
xx
yy
; start index (a nonnegative number)
; end index (a positive number)
1
#n1,i
i
#n2
next
i
; initialize loop index i
; check loop index
;
“
; if all iterations have been performed, then exit
; perform the loop operation
;
“
; increment loop index
67
2.6 ■ Program Loops
bra
…
next
loop
; go back to the loop body
The implementation of that statement “for i 5 n2 to n1 do S” can be modified from the
previous instruction sequence as follows:
n1
n2
equ
equ
…
ds.b
…
movb
ldaa
cmpa
blt
…
…
dec
bra
…
i
loop
next
xx
yy
; start index (a nonnegative number)
; end index (a positive number)
1
#n2,i
i
#n1
next
i
loop
; initialize loop index i
; check loop index
;
“
; if all iterations have been performed, then exit
; perform the loop operation
;
“
; decrement loop index
; go back to the loop body
Like the for loop, the while-loop construct also checks the condition at the start of the
loop, and its implementation is similar with the following exceptions:
• The condition to be checked may be an external event instead of a variable.
• Updating the condition may be done by an external event such as an interrupt or
the change of an input signal.
Assume that the CPU will keep performing a certain operation as long as the variable icount
is not zero and icount is decremented by the interrupt service routine (discussed in Chapter 6).
Then the following instruction sequence implements a loop using the while-loop construct:
N
equ
…
ds.b
…
movb
ldaa
cmpa
beq
…
…
bra
…
icount
wloop
next
xx
1
#N, icount
#0
icount
next
; performed loop operation
wloop
The “repeat S until C” looping construction is used more often to perform some operation a
certain number of times. The following instruction sequence will perform a certain operation
N times:
N
loop
equ
…
…
ldy
…
…
dbeq
xx
; define the constant N
#N
; use Y to hold loop count
; perform operations
Y,loop
; is loop count decremented to 0 yet?
68
Chapter 2 ■ HCS12 Assembly Programming
Example 2.14
▼
Write a program to add an array of N 8-bit numbers and store the sum at memory locations
$1000,$1001. Use the “for i 5 n1 to n2 do” looping construct.
Solution: We will use variable i as the array index. This variable can also be used to keep track
of the current iteration being performed. We will use a 2-byte variable sum to hold the sum of
array elements. The logic flow of the program is illustrated in Figure 2.9. The program is a direct translation of the flowchart in Figure 2.9.
Start
i←0
sum ← 0
Yes
i = N?
Stop
No
sum ← sum + array[i]
i←i+1
Figure 2.9 ■ Logic flow of example 2.14
N
sum
i
loop
equ
org
rmb
rmb
org
ldaa
staa
staa
staa
ldab
cmpb
beq
ldx
abx
ldab
ldy
aby
sty
inc
bra
20
$1000
2
1
$1500
#0
i
sum
sum11
i
#N
done
#array
0,x
sum
sum
i
loop
; array count
; starting address of on-chip SRAM
; array sum
; array index
; starting address of the program
; initialize loop (array) index to 0
; initialize sum to 0
;
“
; is i 5 N?
; if done, then branch
; use index register X as a pointer to the array
; compute the address of array[i]
; place array[i] in B
; place sum in Y
; compute sum <- sum 1 array[i]
; update sum
; increment the loop count by 1
69
2.6 ■ Program Loops
done
swi
; return to D-Bug12 monitor
; the array is defined in the following statement
array
db
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
end
▲
It is a common mistake for an assembly language programmer to forget about updating the
variable in memory. For example, we will not get the correct value for sum if we did not add the
instruction sty sum in the program of Example 2.14.
Loop primitive instructions are especially suitable for implementing the “repeat S until C“
looping construct, as demonstrated in the following example:
Example 2.15
▼
Write a program to find the maximum element from an array of N 8-bit elements using the
“repeat S until C” looping construct.
Solution: We will use the variable i as the array index and also as the loop count. The variable max_val
will be used to hold the array maximum. The logic flow of the program is shown in Figure 2.10.
The program is as follows:
Start
max_val ← array[0]
i←N−1
max_val < array[i] ?
Yes
max_val ← array[i]
i←i−1
No
i = 0?
Yes
Stop
Figure 2.10 ■ Logic flow of example 2.15
No
70
Chapter 2 ■ HCS12 Assembly Programming
N
equ
org
ds.b
org
movb
ldx
ldab
ldaa
cmpa
bge
movb
dex
dbne
bra
db
end
arrmax
loop
chk_end
forever
array
20
$1000
1
$1500
array,arrmax
#array1N21
#N21
arrmax
0,x
chk_end
0,x,arrmax
; array count
; starting address of on-chip SRAM
; memory location to hold array max
; starting address of program
; set array[0] as the temporary array max
; start from the end of the array
; use B to hold variable i and initialize it to N21
; compare arrmax with array[i]
; no update if max_val is larger
; update arrmax
; move the array pointer
; decrement the loop count, branch if not zero yet
b,loop
forever
1,3,5,6,19,41,53,28,13,42,76,14,20,54,64,74,29,33,41,45
▲
2.6.6 Decrementing and Incrementing Instructions
We often need to add 1 to or subtract 1 from a variable in our program. Although we can
use one of the add or sub instructions to achieve this, it would be more efficient to use a single
instruction. The HCS12 has a few instructions for us to increment or decrement a variable by 1.
A summary of decrement and increment instructions is listed in Table 2.5.
Decrement Instructions
Mnemonic
Function
Operation
dec <opr>
Decrement memory by 1
M ← [M] − 1
deca
decb
Decrement A by 1
Decrement B by 1
A ← [A] − 1
B ← [B] − 1
des
dex
Decrement SP by 1
Decrement X by 1
SP ← [SP] − 1
X ← [X] − 1
dey
Decrement Y by 1
Y ← [Y] − 1
Increment Instructions
Mnemonic
Function
Operation
inc <opr>
Increment memory by 1
M ← [M] + 1
inca
incb
Increment A by 1
Increment B by 1
A ← [A] + 1
B ← [B] + 1
ins
inx
Increment SP by 1
Increment X by 1
SP ← [SP] + 1
X ← [X] + 1
iny
Increment Y by 1
Y ← [Y] + 1
Note: <opr> can be specified using direct, extended, or indexed addressing
modes.
Table 2.5 ■ Summary of decrement and increment instructions
71
2.6 ■ Program Loops
Example 2.16
▼
Use an appropriate increment or decrement instruction to replace the following instruction
sequence:
ldaa
adda
staa
i
#1
i
Solution: These three instructions can be replaced by the following instruction:
inc
i
▲
2.6.7 Bit Condition Branch Instructions
In certain applications, one needs to make branch decisions on the basis of the value of a
few bits. The HCS12 provides two special conditional branch instructions for this purpose. The
syntax of the first special conditional branch instruction is
brclr opr, msk, rel
where
opr
specifies the memory location to be checked and can be specified using direct, extended, and all
indexed addressing modes.
msk
is an 8-bit mask that specifies the bits of the memory location to be checked. The bits to be checked
correspond to those bit positions that are 1s in the mask.
rel
is the branch offset and is specified in 8-bit relative mode.
This instruction tells the HCS12 to perform bitwise logical AND on the contents of the
specified memory location and the mask supplied with the instruction, then branch if the result is 0. For example, for the instruction sequence
here
brclr
ldd
$66,$80,here
$70
the HCS12 will continue to execute the first instruction if the most significant bit of the memory location at $66 is 0. Otherwise, the next instruction will be executed.
The syntax of the second special conditional branch instruction is
brset opr, msk, rel
where
opr
specifies the memory location to be checked and can be specified using direct, extended, and all
indexed addressing modes.
msk
is an 8-bit mask that specifies the bits of the memory location to be checked. The bits to be checked
correspond to those bit positions that are 1s in the mask.
rel
is the branch offset and is specified in 8-bit relative mode.
This instruction tells the HCS12 to perform the logical AND of the contents of the specified memory location inverted and the mask supplied with the instruction, then branch if the
result is 0 (this occurs only when all bits corresponding to 1s in the mask byte are 1s in the
tested byte). For example, for the instruction sequence
72
Chapter 2 ■ HCS12 Assembly Programming
loop
inc
count
…
brset
…
$66,$E0,loop
the branch will be taken if the most significant 3 bits of the memory location at $66 are all 1s.
Example 2.17
▼
Write a program to count the number of elements that are divisible by 4 in an array of N
8-bit numbers. Use the “repeat S until C” looping construct.
Solution: The lowest 2 bits of a number divisible by 4 are 00. By checking the lowest 2 bits of a
number, we can determine if a number is divisible by 4. The program is as follows:
N
total
loop
yes
chkend
forever
array
equ
org
rmb
20
$1000
1
org
clr
ldx
ldab
brclr
bra
inc
inx
dbne
bra
db
end
$1500
total
#array
#N
0,x,$03,yes
chkend
total
; starting address of the program
; initialize total to 0
; use index register X as the array pointer
; use accumulator B as the loop count
; add 1 to the total
; move the array pointer
b,loop
forever
2,3,4,8,12,13,19,24,33,32,20,18,53,52,80,82,90,94,100,102
▲
2.6.8 Instructions for Variable Initialization
We often need to initialize a variable to 0 when writing a program. The HCS12 has three
instructions for this purpose. They are
clr
opr
where opr is a memory location specified using the extended mode and all indexed addressing
(direct and indirect) modes. The memory location is initialized to 0 by this instruction.
clra
Accumulator A is cleared to 0 by this instruction.
clrb
Accumulator B is cleared to 0 by this instruction.
2.7 Shift and Rotate Instructions
Shift and rotate instructions are useful for bit field manipulation. They can be used to
speed up the integer multiply and divide operations if one of the operands is a power of 2.
73
2.7 ■ Shift and Rotate Instructions
A shift/rotate instruction shifts/rotates the operand by 1 bit. The HCS12 has shift instructions
that can operate on accumulators A, B, and D or a memory location. A memory operand must
be specified using the extended or indexed (direct or indirect) addressing modes. A summary of
shift and rotate instructions is shown in Table 2.6.
Logical Shift Instructions
Mnemonic
Function
Operation
lsl <opr>
lsla
lslb
Logical shift left memory
Logical shift left A
Logical shift left B
lsld
Logical shift left D
0
C
b0
b7
0
C
lsr <opr>
lsra
lsrb
Logical shift right memory
Logical shift right A
Logical shift right B
lsrd
Logical shift right D
b7
b0 b7
A
B
b0
0
b0 C
b7
0
b7
A
b0
b7
B
b0
C
Arithmetic Shift Instructions
Mnemonic
Function
Operation
asl <opr>
asla
aslb
Arithmetic shift left memory
Arithmetic shift left A
Arithmetic shift left B
asld
Arithmetic shift left D
0
C
0
C
asr <opr>
asra
asrb
b0
b7
b7
A
Arithmetic shift right memory
Arithmetic shift right A
Arithmetic shift right B
b0
b7
B
b0
b7
C
Rotate Instructions
Mnemonic
Function
rol <opr>
rola
rolb
Rotate left memory thru carry
Rotate left A through carry
Rotate left B through carry
ror <opr>
rora
rorb
Rotate right memory thru carry
Rotate right A through carry
Rotate right B through carry
Table 2.6 ■ Summary of shift and rotate instructions
Operation
C
b7
b7
b0
b0
C
b0
74
Chapter 2 ■ HCS12 Assembly Programming
Example 2.18
▼
What are the values of accumulator A and the C flag after executing the asla instruction
assuming that originally A contains $95 and the C flag is 1?
Solution: The operation of this instruction is shown in Figure 2.11a.
C flag
1
Accumulator A
1
0
0
1
0
1
0
1
0
0
1
0
1
0
1
0
0
Figure 2.11a ■ Operation of the asla instruction
The result is shown in Figure 2.11b.
Original Value
New Value
[A] = 10010101
C=1
[A] = 00101010
C=1
Figure 2.11b ■ Execution result of the asla instruction
▲
Example 2.19
▼
What are the new values of the memory location at $1000 and the C flag after executing the
instruction asr $1000? Assume that the memory location $1000 originally contains the value of
$ED and the C flag is 0.
Solution: The operation of this instruction is shown in Figure 2.12a.
1
1
1
0
1
1
0
1
C flag
Memory location
$1000
1
1
1
1
0
1
1
0
Figure 2.12a ■ Operation of the asr $1000 instruction
1
75
2.7 ■ Shift and Rotate Instructions
The result is shown in Figure 2.12b.
Original value
New value
[$1000] = 11101101
C=0
[$1000] = 11110110
C=1
Figure 2.12b ■ Result of the asr $1000 instruction
▲
Example 2.20
▼
What are the new values of the memory location at $1000 and the C flag after executing the
instruction lsr $1000 assuming that the memory location $1000 originally contains $E7 and the
C flag is 1?
Solution: The operation of this instruction is illustrated in Figure 2.13a.
1
1
1
0
0
1
1
1
Memory location
$1000
0
C flag
0
1
1
1
0
0
1
1
1
Figure 2.13a ■ Operation of the lsr $1000 instruction
The result is shown in Figure 2.13b.
Original value
New value
[$1000] = 11100111
C=1
[$1000] = 01110011
C=1
Figure 2.13b ■ Execution result of lsr $1000
▲
Example 2.21
▼
What are the new values of accumulator B and the C flag after executing the rolb instruction assuming that the original value of B is $BD and the C flag is 1?
Solution: The operation of this instruction is illustrated in Figure 2.14a.
76
Chapter 2 ■ HCS12 Assembly Programming
1
0
1
1
1
1
0
1
1
C flag
Accumulator B
0
1
1
1
1
0
1
1
1
Figure 2.14a ■ Operation of the rolb instruction
The result is shown in Figure 2.14b.
Original value
New value
[B] = 10111101
C=1
[B] = 01111011
C=1
Figure 2.14b ■ Execution result of rolb
▲
Example 2.22
▼
What are the values of accumulator A and the C flag after executing the instruction rora
assuming that the original value of A is $BE and C 5 1?
Solution: The operation of this instruction is illustrated in Figure 2.15a.
1
1
0
1
1
1
1
1
0
0
1
1
0
1
1
1
1
1
C flag
Figure 2.15a ■ Operation of the rora instruction
accumulator A
77
2.7 ■ Shift and Rotate Instructions
The result is shown in Figure 2.15b.
Original value
[A] = 10111110
C=1
New value
[A] = 11011111
C=1
Figure 2.15b ■ Execution result of rora
▲
Example 2.23
▼
Write a program to count the number of 0s contained in memory locations $1000,$1001
and save the result at memory location $1005.
Solution: The logical shift-right instruction is available for double accumulator D. We can load
this 16-bit value into D and shift it to the right 16 times or until it becomes 0. The algorithm of
this program is as follows:
Step 1
Initialize the loop count to 16 and the zero count to 0.
Step 2
Place the 16-bit value in D.
Step 3
Shift D to the right one place.
Step 4
If the C flag is 0, increment the zero count by 1.
Step 5
Decrement the loop count by 1.
Step 6
If the loop count is 0, then stop. Otherwise, go to step 3.
The program is as follows:
zero_cnt
lp_cnt
again
org
dc.w
rmb
rmb
org
clr
movb
ldd
lsrd
bcs
inc
$1000
$2355
1
1
$1500
zero_cnt
#16,lp_cnt
$1000
chk_end
zero_cnt
; initialize the zero count to 0
; initialize the loop count to 16
; place the 16-bit number in D
; branch if the lsb is 1
78
Chapter 2 ■ HCS12 Assembly Programming
chk_end
forever
dec
bne
bra
end
lp_cnt
again
forever
; have we tested all 16 bits yet?
▲
Sometimes we need to shift a number larger than 16 bits. However, the HCS12 does not
have an instruction that does this. Suppose the number has k bytes and the most significant byte
is located at loc. The remaining k 2 1 bytes are located at loc 1 1, loc 1 2, . . . , loc 1 k 2 1, as
shown in Figure 2.16.
loc
loc + 1
loc + k – 1
...
msb
lsb
Figure 2.16 ■ k bytes to be shifted
The logical shift-one-bit-to-the-right operation is shown in Figure 2.17.
loc
loc + 1
loc + k – 1
0
msb
lsb
Figure 2.17 ■ Shift-one-bit-to-the-right operation
As shown in Figure 2.17,
• Bit 7 of each byte will receive bit 0 of the byte on its immediate left with the
exception of the most significant byte, which will receive a 0.
• Each byte will be shifted to the right by one bit. Bit 0 of the least significant byte
will be shifted out and lost.
The operation can therefore be implemented as follows:
Step 1
Shift the byte at loc to the right one place (using the lsr <opr> instruction).
Step 2
Rotate the byte at loc 1 1 to the right one place (using the ror <opr> instruction).
Step 3
Repeat step 2 for the remaining bytes.
By repeating this procedure, the given k-byte number can be shifted to the right as many bits as
desired. The operation to shift a multibyte number to the left should start from the least significant byte and rotate the remaining bytes toward the most significant byte.
79
2.8 ■ Boolean Logic Instructions
Example 2.24
▼
Write a program to shift the 32-bit number stored at $1000,$1003 to the right four places.
Solution: The most significant to the least significant bytes are stored at $1000,$1003. The following instruction sequence implements the algorithm that we just described:
again
ldab
ldx
lsr
ror
ror
ror
dbne
#4
#$1000
0,x
1,x
2,x
3,x
b,again
; set up the loop count
▲
2.8 Boolean Logic Instructions
When dealing with input and output port pins, we often need to change the values of a few
bits. For these types of applications, the Boolean logic instructions come in handy. A summary
of the HCS12 Boolean logic instructions is given in Table 2.7.
The operand opr can be specified using all except the relative addressing modes. Usually,
we would use the and instruction to clear one or a few bits and use the or instruction to set one
Mnemonic
Function
anda <opr>
andb <opr>
andcc <opr>
AND A with memory
AND B with memory
AND CCR with memory (clear CCR bits)
eora <opr>
eorb <opr>
oraa <opr>
orab <opr>
orcc <opr>
clc
cli
clv
com <opr>
coma
comb
neg <opr>
nega
negb
Exclusive OR A with memory
Exclusive OR B with memory
OR A with memory
OR B with memory
OR CCR with memory
Clear C bit in CCR
Clear I bit in CCR
Clear V bit in CCR
One's complement memory
One's complement A
One's complement B
Two's complement memory
Two's complement A
Two's complement B
Table 2.7 ■ Summary of Boolean logic instructions
Operation
A ← (A) • (M)
B ← (B) • (M)
CCR ← (CCR) • (M)
A ← (A) ⊕ (M)
B ← (B) ⊕ (M)
A ← (A) + (M)
B ← (B) + (M)
CCR ← (CCR) + (M)
C←0
I←0
V←0
M ← $FF − (M)
A ← $FF − (A)
B ← $FF − (B)
M ← $00 − (M)
A ← $00 − (A)
B ← $00 − (B)
80
Chapter 2 ■ HCS12 Assembly Programming
or a few bits. The exclusive or instruction can be used to toggle (change from 0 to 1 and from 1
to 0) one or a few bits. For example, the instruction sequence
ldaa
anda
staa
$56
#$0F
$56
clears the upper four pins of the I/O port located at $56. The instruction sequence
ldaa
oraa
staa
$56
#$01
$56
sets the bit 0 of the I/O port at $56. The instruction sequence
ldaa
eora
staa
$56
#$0F
$56
toggles the lower 4 bits of the I/O port at $56. The instructions (coma and comb) that perform
one’s complementing can be used if all of the port pins need to be toggled.
2.9 Bit Test and Manipulate Instruction
These instructions use a mask value to test or change the value of individual bits in an accumulator or in a memory location. The instructions bita and bitb provide a convenient means
of testing bits without altering the value of either operand. Table 2.8 shows a summary of bit
test and manipulation instructions.
Mnemonic
bclr <opr>2, msk8
bita <opr>1
bitb <opr>1
bset <opr>2, msk82
Note:
Function
Operation
Clear bits in memory
Bit test A
Bit test B
Set bits in memory
M ← (M) • (mm)
(A) • (M)
(B) • (M)
M ← (M) + (mm)
1. <opr> can be specified using all except relative addressing modes
for bita and bitb.
2. <opr> can be specified using direct, extended, and indexed
(exclude indirect) addressing modes.
3. msk8 is an 8-bit value.
Table 2.8 ■ Summary of bit test and manipulation instructions
For example, the instruction
bclr
0,x,$81
clears the most significant and the least significant bits of the memory location pointed to by
index register X. The instruction
bita
#$44
81
2.10 ■ Program Execution Time
tests bit six and bit two of accumulator A and updates the Z and N flags of the CCR register accordingly. The V flag in CCR register is cleared. The instruction
bitb
#$22
tests bit five and bit one of accumulator B and updates the Z and N flags of CCR register accordingly. The V flag in the CCR register is cleared. The instruction
bset
0,y,$33
sets bits five, four, one, and zero of the memory location pointed to by index register Y.
2.10 Program Execution Time
The HCS12 uses the bus clock (we will call it the E-clock from now on) signal as a timing
reference. The generation of the E-clock is described in Chapter 6. The execution times of instructions are also measured in E cycles. The execution time of each instruction can be found
in the column “Access Detail” in Appendix A. The number of letters in that column indicates
the number of E cycles that a specific instruction takes to complete the execution. For example,
the Access Detail column of the pula instruction contains three letters, ufo, which indicates
that the pula instruction takes three E cycles to complete.
There are many applications that require the generation of time delays. Program loops are
often used to create a certain amount of delay unless the time delay needs to be very accurate.
The creation of a time delay involves two steps.
1. Select a sequence of instructions that takes a certain amount of time to execute.
2. Repeat the instruction sequence for the appropriate number of times.
For example, the following instruction sequence takes 40 E-clock cycles to execute:
loop
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
nop
nop
dbne
; 2 E cycles
; 3 E cycles
x,loop
; 1 E cycle
; 1 E cycle
; 3 E cycles
82
Chapter 2 ■ HCS12 Assembly Programming
Example 2.25
▼
Write an instruction sequence to create a 100-ms time delay for a demo board with a
24-MHz bus clock.
Solution: In order to create a 100-ms time delay, we need to repeat the preceding instruction
sequence 60,000 times [100 ms ÷ (40 ÷ 24,000,000) μs 5 60,000]. The following instruction
sequence will create the desired delay:
loop
ldx
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
nop
nop
dbne
#60000
x,loop
; 2 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 1 E cycle
; 1 E cycle
; 3 E cycles
▲
Example 2.26
▼
Write an instruction sequence to create a delay of 10 sec.
Solution: The instruction sequence in Example 2.25 can only create a delay slightly longer than
100 ms. In order to create a longer time delay, we need to use a two-layer loop. For example, the
following instruction sequence will create a 10-sec delay:
ldab
out_loop
ldx
inner_loop psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
#100
#60000
; 1 E cycle
; 2 E cycles
2.11 ■ The Multiply-and-Accumulate (emacs) Instruction
psha
pula
psha
pula
psha
pula
nop
nop
dbne
dbne
x,inner_loop
b,out_loop
; 3 E cycles
83
▲
The time delay created by using program loops is not accurate. Some overhead is required to set
up the loop count. For example, the one-layer loop has a 2-E-cycle overhead while the two-layer
loop has much more overhead.
overhead 5 1 E cycle (caused by the ldab #100 instruction)
1 100 3 2 E cycles (caused by the out_loop
ldx #60000 instruction)
1 100 3 3 E cycles (caused by the dbne b, out_loop instruction)
5 501 E cycles 5 20.875 μs (at 24-MHz E-clock)
To reduce the overhead, one can adjust the number to be placed in index register X. For example, by placing 59999 in X, one can create a delay of 9.99985 sec with the previous program,
which is closer to 10 sec.
2.11 The Multiply-and-Accumulate (emacs) Instruction
The multiply-and-accumulate (emacs) instruction multiplies two 16-bit operands stored in
memory and adds the 32-bit result to a third memory location (32 bit). EMACS can be used to
implement simple digital filters, defuzzification routines, and any operation that involves the
evaluation of linear polynomial functions.
When the emacs instruction is executed, the first source operand is fetched from a location
pointed to by index register X, and the second source operand is fetched from a location pointed
to by register Y. Before the instruction is executed, the X and Y index registers must contain
values that point to the most significant bytes of the source operands. The most significant byte
of the 32-bit result is specified by an extended address supplied with the instruction.
Example 2.27
▼
Write an assembly program to compute the following expression using the emacs instruction:
a3y1b
where a, y, and b are in memory locations $1000, $1002, and $1004, respectively. Leave the result in memory location $1010,1013.
Solution: The value of b should be stored in memory locations $1012,$1013, and index registers
X and Y should be set up to point to memory locations $1000 and $1002. We also need to store
the constant in the destination. The following program performs the desired computation:
aa
xx
org
dc.w
dc.w
$1000
10
7
84
Chapter 2 ■ HCS12 Assembly Programming
bb
dc.w
org
result ds.w
org
ldx
ldy
movw
movw
emacs
swi
end
6
$1010
2
$1500
#aa
#xx
bb,result12
#0,result
result
; set X to point to constant aa
; set Y to point to variable xx
; store b in the result
;
“
; perform the multiplication and accumulation
▲
Many applications (for example, digital filtering) involve the evaluation of polynomials.
The emacs instruction can be used to compute the value of a polynomial. Before utilizing
this instruction, we must transform the polynomial to a form that can take advantage of this
instruction. The polynomial
a xn 1 a xn21 1 … 1 a x 1 a
n
n21
1
0
can be transformed into the format
(x(x(…(a x 1 a ) 1 a ) 1 … a ) 1 a )
n
n21
n22
1
0
The expression enclosed by each pair of parentheses in the polynomial requires the computation of a multiplication followed by an addition and hence can be evaluated using the
emacs instruction. The actual computation is quite straightforward and is left as an exercise
problem.
2.12 Summary
An assembly language program consists of three major parts: assembler directives, assembly language instructions, and comments. A statement of an assembly language program
consists of four fields: label, operation code, operand, and comment. Assembly directives supported by the freeware as12 were all discussed in this chapter.
The HCS12 instructions were explained category by category. Simple program examples
were used to demonstrate the applications of different instructions. The HCS12 is a 16-bit microcontroller. Therefore, it can perform 16-bit arithmetic. Numbers greater than 16 bits must
be manipulated using multiprecision arithmetic.
Microcontrollers are designed to perform repetitive operations. Repetitive operations are
implemented by program loops. There are two types of program loops: infinite loops and finite
loops. There are four major variants of the looping constructs.
• Do statement S forever
• For i 5 n1 to n2 do S or For i 5 n2 downto n1 do S
• While C do S
• Repeat S until C
In general, the implementation of program loops requires
• The initialization of loop counter (or condition)
• Performing the specified operation
85
2.13 ■ Exercises
• Comparing the loop count with the loop limit (or evaluating the condition)
• Making a decision regarding whether the program loop should be continued
The HCS12 provides instructions to support the initialization of the loop counter, decrementing (or incrementing) the loop counter, and making a decision whether looping should be
continued.
The shifting and rotating instructions are useful for bit field operations. Integer multiplication
by a power of 2 and division by a power of 2 can be sped up by using the shifting instructions.
The HCS12 also provides many Boolean logical instructions that can be very useful for setting, clearing, and toggling the I/O port pins.
2.13 Exercises
E2.1 Find the valid and invalid labels in the following statements, and explain why the invalid
labels are invalid:
column 1
a. ABC
b. lp+:
c. too:
d. not_true
e. star+=
decb
adda
mul
nega
ldaa
f.
dec
too_big
#1
#10
count
E2.2 Identify the four fields of the following instructions:
a.
bne
not_done
b. loop
brclr
0,x,$01,loop
; wait until the least significant bit is set
c. here:
dec
lp_cnt
; decrement the variable lp_cnt
E2.3 Write a sequence of assembler directives to reserve 10 bytes starting from $1000.
E2.4 Write a sequence of assembler directives to build a table of ASCII codes of lowercase letters a–z. The table should start from memory location $2000.
E2.5 Write a sequence of assembler directives to store the message “Welcome to the robot demonstration!” starting from the memory location at $2050.
E2.6 Write an instruction sequence to add the two 24-bit numbers stored at $1010,$1012 and
$1013,$1015 and save the sum at $1100,$1102.
E2.7 Write an instruction sequence to subtract the 6-byte number stored at $1000,$1005 from
the 6-byte number stored at $1010,$1015 and save the result at $1020,$1025.
E2.8 Write a sequence of instructions to add the BCD numbers stored at $1000 and $1001 and
store the sum at $1003.
E2.9 Write an instruction sequence to add the 4-digit BCD numbers stored at $1000,$1001 and
$1002,$1003 and store the sum at $1010,1011.
E2.10 Write a program to compute the average of an array of N 8-bit numbers and store the result
at $1000. The array is stored at memory locations starting from $1010. N is no larger than 255.
E2.11 Write a program to multiply two 3-byte numbers that are stored at $1000,$1002 and
$1003,$1005 and save the product at $1010,$1015.
E2.12 Write a program to compute the average of the square of all elements of an array with 32
8-bit unsigned numbers. The array is stored at $1000,$101F. Store the result at $1020,$1021.
86
Chapter 2 ■ HCS12 Assembly Programming
E2.13 Write a program to count the number of even elements of an array of N 16-bit elements.
The array is stored at memory locations starting from $1010. N is no larger than 255.
E2.14 Write an instruction sequence to shift the 32-bit number to the left four places. The 32bit number is located at $1000,$1003.
E2.15 Write a program to count the number of elements in an array that are smaller than 16.
The array is stored at memory locations starting from $1010. The array has N 8-bit unsigned
elements.
E2.16 Write an instruction sequence to swap the upper four bits and the lower four bits of accumulator A (swap bit 7 with bit 3, bit 6 with bit 2, and so on).
E2.17 Write a program to count the number of elements in an array whose bits 3, 4, and 7 are
0s. The array has N 8-bit elements and is stored in memory locations starting from $1000.
E2.18 Write an instruction sequence to set bits 3, 2, 1, and 0 of memory location at $1000 to 1
and leave the upper 4 bits of the same location unchanged.
E2.19 Find the values of condition flags N, Z, V, and C in the CCR register after the execution
of each of the following instructions, given that [A] 5 $50 and the condition flags are N 5 0,
Z 5 1, V 5 0, and C 5 1.
a. suba #40
b. tsta
c. adda #$50
d. lsra
e. rola
f. lsla
E2.20 Find the values of condition flags N, Z, V, and C in the CCR register after executing each
of the following instructions independently, given that [A] 5 $00 and the initial condition codes
are N 5 0, C 5 0, Z 5 1, and V 5 0.
a. tsta
b. adda #$40
c. suba #$78
d. lsla
e. rola
f. adda #$CF
E2.21 Write an instruction sequence to toggle the odd-number bits and clear the even-number
bits of memory location at $66.
E2.22 Write a program to shift the 8-byte number located at $1000,$1007 to the left four
places.
E2.23 Write a program to shift the 6-byte number located at $1010,$1015 to the right three
places.
E2.24 Write a program to create a time delay of 100 sec by using program loops, assuming that
the frequency of the bus clock is 24 MHz.
E2.25 Write a program to create a time delay of 5 sec using program loops, assuming that the
frequency of the bus clock is 24 MHz.
E2.26 Write a program to evaluate
a2x2 1 a1x 1 a0
Plug in values to verify that your program is correct.
E2.27 Write a program to evaluate
a3x3 1 a2x2 1 a1x 1 a0
Plug in values to verify that your program is correct.
3
Hardware and Software
Development Tools for the
HCS12
3.1 Objectives
After completing this chapter, you should be
able to
• Explain the differences among different
HCS12 members
• Know the peripheral functions available at
different HCS12 members
• Understand the types of hardware and
software development tools available
• Explain the functions of a source-level
debugger
• Use the D-Bug12 commands to view and
change the contents of memory locations
and CPU registers
• Use the D-Bug12 commands to set
breakpoints and trace program execution
on the demo board
• Use the MiniIDE program to enter,
assemble, and download programs onto
the demo board for execution
• Use the D-Bug12 bootloader mode to
upgrade D-Bug12 monitor and program
application code into flash memory
• Use CodeWarrior to enter, assemble, and
debug HCS12 assembly programs
• Understand the function of the background
debug module
88
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
3.2 Development Tools for the HCS12
Microcontroller development tools can be divided into two categories: hardware and
software.
3.2.1 Software Development Tools
A user needs a good text editor to enter his or her program. After the program is entered,
the user needs an assembler or a compiler to convert the program into machine code for execution. A complicated program may consist of multiple computer files. A linker will be needed to
resolve the variable cross-reference and memory allocation issue.
Before the user has the hardware to execute the program, a software simulator can be
used to verify whether the program is logically correct. A simulator allows the user to execute
microcontroller programs without having the actual hardware. It uses the computer memory to
represent microcontroller registers and memory locations. The simulator interprets each microcontroller instruction by performing the operation required by the instruction and then saves
the execution results in the computer memory. The simulator also allows the user to set the
contents of memory locations and registers before the simulation run starts.
After the software has been assembled or compiled without syntax errors, the user may
want to transfer the resultant machine code to the target hardware for execution. This will
require a communication program (for example, the HyperTerminal bundled with Windows
operating system) or even a hardware programmer.
When the software becomes complicated, the program management issue becomes complicated. The common approach is to use project as a unit to manage the software development
process.
The source-level debugger is a program that allows the user to find problems in her or his
code at the high-level-language (such as C) or assembly-language level. A debugger may have
the option to run the program on the target hardware or using a simulator. Like a simulator, a
debugger can display the contents of registers and memory (internal and external) and program
code in separate windows. With a debugger, all debugging activities are done at the source
level. The user can see the value change of a variable after a statement has been executed. The
user can also set a breakpoint at a statement in a high-level language. However, a source-level
debugger requires a lot of computation. A source-level debugger needs to communicate with
the monitor program on the demo board in order to display the contents of CPU registers and
memory locations, set or delete breakpoints, trace program execution, and so on. Since the
monitor programs on different evaluation boards may not be the same, a source-level debugger
may be used only with one type of demo board. The BDM mode of the HCS12 (and other microcontrollers) offers an alternative for implementing the source-level debugger.
The software vendors often integrate a text editor, an assembler and/or compiler, a
linker, a simulator, a source-level debugger, and a project manager into a package so that
the user can switch from one tool to another without exiting any of these programs. This
integrated software environment is called an integrated development environment (IDE).
The complexity of IDEs varies significantly. For example, the freeware MiniIDE consists of an
assembler, a linker, a project manager, and a terminal program for the PC to communicate
with the demo board. Using the MiniIDE, the user can enter and assemble his or her assembly program and download it onto a HCS12 demo board (this HCS12 demo board must have
the D-Bug12 monitor program) for execution. After downloading the machine code onto the
demo board, the user can then use commands supported by the D-Bug12 monitor to perform
debug activities.
3.2 ■ Development Tools for the HCS12
89
The CodeWarrior from Freescale is an IDE that consists of a text editor, an assembler, a linker,
a C compiler, a project manager, a simulator, device drivers, and a source-level debugger. Both
the MiniIDE and CodeWarrior will be used in this text for developing and testing programs. The
AsmIDE written by Eric Engler is similar to MiniIDE in function.
3.2.2 Hardware Development Tools
For learning a microcontroller, the most important hardware tool is the microcontroller
demo board. Tools such as function generators and oscilloscopes will be helpful for testing a
user’s program. Hardware tools such as in-circuit emulators and logic analyzers will be very
useful in helping users in debugging their software. However, these are expensive debugging
tools and hence we will not discuss them in this text.
The background debug module (BDM) of the HCS12 microcontroller allows the user to
trace instruction execution on the target hardware (a HCS12 microcontroller) from a PC or
another HCS12-based demo board running appropriate software. When performing debug activities in the BDM mode, the PC or the host HCS12 communicates with the target HCS12 via the
BDM serial interface. This approach allows debug activities to be performed less intrusively.
3.2.3 Types of HCS12 Demo Boards
On the basis of the supporting debug environment, a demo board can be classified into the
following three categories:
DEMO BOARD WITH THE D-BUG12 MONITOR
An HCS12 demo board may have its microcontroller programmed with the D-Bug12 monitor to support debug activities. The D-Bug12 monitor occupies the flash memory and supports
a set of debug commands. The user can use these commands to display and modify register
and memory contents, set breakpoints at certain memory locations (breakpoints can only be
set in SRAM), step through the program, and download the program onto the demo board for
execution. Using this approach, the user can only download his or her program onto the on-chip
SRAM of the HCS12. For learning assembly language programs, this type of demo board provides a simple environment for testing and debugging user programs. Both the MiniIDE and the
AsmIDE may work with this type of demo board.
DEMO BOARD WITH THE SERIAL MONITOR
An HCS12 demo board may also choose to have its microcontroller programmed with the
serial monitor from Freescale. The serial monitor occupies 2 kB of flash memory and allows
the user program to employ the remaining memory space. The CodeWarrior IDE from Freescale
may communicate with the serial monitor and allow the user to carry out all debug activities.
DEMO BOARD WITH THE BDM ADAPTOR
This type of demo board relies on the HCS12 background debug module to carry out debug
activities. The user runs an appropriate program on the PC to communicate with the HCS12
via a BDM adaptor to perform debug activities. The BDM adaptor may be a separate hardware
kit or a part of the demo board. There is a public domain BDM-based debugging interface called
Turbo BDM Light (TBDML) that every user can add to her or his demo board or make into a
separate debug adaptor. The TBDML is supported by the CodeWarrior IDE and provides a nice
debugging environment. CodeWarrior also supports a few other BDM adaptors including P&E
micro’s BDM adaptor. To use this approach to debug programs, the demo board must have a
BDM interface circuit.
90
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
A demo board can be switched from one category to another. For example, a demo board
programmed with the D-Bug12 monitor can be reprogrammed with the serial monitor and allow
the user to employ CodeWarrior to develop software. If a demo board has the BDM interface
circuit (a 6-pin connector), then the user can also use a BDM adaptor and CodeWarrior (or other
IDE) to develop the software. If the user prefers to employ the D-Bug12 monitor, he or she can
use a BDM adaptor to program the D-Bug12 monitor into the microcontroller of the demo board
and start to use the D-Bug12 commands to debug the software.
It is obvious that different people will have different opinions on the choice of which HCS
demo board is best for learning the HCS12 microcontroller. Some educators prefer a demo board
to be a bare kit but allow the user to connect a wide variety of chips and I/O devices to the
demo board. Other educators may prefer a demo board to have a lot of features and also allow
the user to connect many other I/O devices and peripheral chips to the demo board. However,
all educators would agree that the demo board must be affordable. For the demo board to have
many features and still be affordable, in this text we use the Dragon12-Plus demo board from
Wytec to test and debug all the programs. The Dragon12-Plus and its predecessors have been
very popular in universities.
3.3 The Dragon12-Plus Demo Board
A photo of the Dragon12-Plus demo board is shown in Figure 3.1. In addition to the
9S12DG256B MCU, the Dragon12-Plus demo board has the following features:
• Dual RS232 communication ports
• DS1307 real-time clock (RTC) with backup battery included for testing I2C interface
• CAN port
• A 10-bit dual-channel DAC LTC1661
• Four robot servo controllers with terminal block for external 5 V
• Four 7-segment displays
Figure 3.1 ■ Dragon12-Plus demo board
3.4 ■ The D-Bug12 Monitor
91
• Eight LEDs
• Eight-position dual inline package (DIP) switch
• Four push button switches
• 5-V regulator with DC jack and terminal block for external 9-V battery input
• A speaker to be driven by timer, or DAC or PWM signal for alarm or music
applications
• A dual H-bridge motor driver with motor feedback or rotary encoder interface for
controlling two DC motors or one Stepper motor
• An IR transceiver with an on-board 38-kHz oscillator
• A BDM-in connector to be connected with a BDM from multiple vendors for
debugging
• An opto-coupler output
• A logic probe with LED indicator
• An abort switch for stopping program when program is hung in a dead loop
• A mode switch for selecting four operating modes: EVB, Jump-to-EEPROM, BDM
POD, and Bootloader
• A 4 3 4 keypad
• A form-C relay output rated at 3 A/30 V or 1 A/125 V
• An X-Y-Z accelerometer interface or GP2-D12 distance-measuring sensor interface
• A potentiometer trimmer pot for analog input
• A temperature sensor
• A communication port for a video graphics array (VGA) camera with built-in Joint
Photographic Experts Group (JPEG) compression (camera is optional.)
• A light sensor
• A low-battery detection circuit
• Female and male headers provide access to all I/O pins of the MC9S12DG256
Additional product information about the Dragon12-Plus demo board can be found on
the website at http://www.evbplus.com. Wytec also provides the miniDragon-Plus2 demo
board, which is a bare kit that allows the user to add her or his circuit. This demo board has
fewer features than the Dragon12-Plus demo board. The photo of this demo board is shown in
Figure 3.2.
3.4 The D-Bug12 Monitor
The D-Bug12 is a monitor program designed for the HCS12 microcontrollers. Version 4 of
the D-Bug12 supports the following devices:
• MC9S12Dx256 (x 5 G, P, or T)
• MC9S12A256
• MC9S12Dx128 (x 5 G, P, or T)
• MC9S12H256
• MC9S12A128
92
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.2 ■ Wytec MiniDragon demo board
This monitor is used in several HCS12 demo boards from several companies. It facilitates
the writing, evaluation, and debugging of user programs.
Version 4.x.x of the D-Bug12 requires a host terminal program that supports XON/XOFF
software handshaking for proper operation. The HyperTerminal program that comes with Windows 2000 and Windows XP can work with the D-Bug12. In addition, the terminal programs
bundled with MiniIDE, AsmIDE, and EmbeddedGNU IDE can all work with the D-Bug12 monitor. The default baud rate out of reset is 9600. However, the baud rate can be set to a higher
value if a higher communication speed is desired.
3.4.1 The D-Bug12 Operating Modes
The D-Bug12 monitor has four operating modes. When the D-Bug12 monitor first starts
(at power-up or reset), it reads the logic levels on the PAD0 and PAD1 pins to enter different operating modes. The operating modes for the four logic level combinations are listed in
Table 3.1.
93
3.4 ■ The D-Bug12 Monitor
PAD1
PAD0
Operating Mode
0
0
1
1
0
1
0
1
D-Bug12; EVB
Jump to internal EEPROM
D-Bug12; POD
SerialBootloader
Table 3.1 ■ D-Bug12 operating modes
3.4.2 EVB Mode
The EVB mode is the most important mode for beginners to learn the HCS12 microcontroller using a demo board. In this mode, the monitor operates as a ROM resident monitor/
debugger executing from the on-chip flash memory. Although this mode provides a good environment for testing new algorithms or conducting performance benchmarks, it does have a few
limitations. In this mode, the flash memory, 1024 bytes of on-chip SRAM, and one of the SCI
serial ports are not available to the developer. The portion of the SRAM that may be used by the
user program begins at $1000 and ends at $3BFF. D-Bug12 uses the remainder of the SRAM that
begins at $3C00 and ends at $3FFF. User programs can also be loaded onto the on-chip EEPROM
for execution. The conceptual operating model for this mode is illustrated in Figure 3.3.
HCS12 demo board
Low-level
interface
routines
Target
system
D-Bug12
PC running
a terminal
program
User
Figure 3.3 ■ EVB mode conceptual model
Suppose we have run a terminal program on the PC and selected the EVB mode; then either
powering on the demo board or pressing the reset button brings out the sign-on message shown
in Figure 3.4 on the terminal screen. The D-Bug12 monitor displays the ASCII greater than
character (.), indicating that it is ready to accept a command. When issuing a command that
causes a program to run from the internal RAM or EEPROM, D-Bug12 will place the terminal
cursor on a blank line, where it will remain until control is returned to D-Bug12. If a running
program fails to return to D-Bug12, pressing the EVB’s reset button causes the running program
to halt execution and initiate the D-Bug12 initialization sequence. Using this method to regain
control of an executing program fails to report any information to the programmer on why or
how the program may have failed.
D-Bug12 4.0.0b32
Copyright 1996–2005 Motorola Semiconductor
For Commands type "Help"
>
Figure 3.4 ■ D-Bug12 EVB mode sign on message
94
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Alternatively, if an optional, normally open switch is wired to the XIRQ pin, pressing it generates an XIRQ interrupt and causes the running program to halt execution and return control
back to D-Bug12 where the CPU register contents are displayed. The Dragon12-Plus demo
board implements this option by providing the abort switch.
As with all ROM-based monitors, D-Bug12 utilizes some of the on-chip resources to perform its debugging functions. The D-Bug12’s memory maps when running on 256-kB and 128-kB
devices are detailed in Table 3.2. For the HCS12 devices with 256-kB flash memory, there are
11 kB of SRAM available for the development of application programs. For devices with 128-kB
flash memory, only 7 kB is available for program development. The current implementation
of D-Bug12 does not allow any of the on-chip flash memory to be utilized by application code
when running in EVB mode.
Note that even though the HCS12Dx256 parts contain 4 kB of EEPROM, only the upper
3 kB are visible, as the lower 1 kB is overlaid with the I/O registers. Table 3.2 shows only
the 64-kB memory map. Most of the D-Bug12 code occupies the on-chip paged flash memory
beginning on page $38.
Address Range
$0000,$03FF
$0400,$0FFF
$1000,$3BFF
$3C00,$3FFF
$4000,$EE7F
$EE80,$EEBF
$EEC0,$EEFF
$EF00,$EF8B
$EF8C,$EFFF
$F000,$FFFF
Description
I/O registers
On-chip EEPROM
On-chip SRAM (available to user)
On-chip SRAM (D-Bug12)
D-Bug12 code
User-accessible function table
Customization data
D-Bug12 startup code
Secondary reset/interrupt table
Bootloader
Table 3.2a ■ D-Bug12 memory map for HCS12Dx256
Address Range
$0000,$03FF
$0800,$0FFF
$2000,$3BFF
$3C00,$3FFF
$4000,$EE7F
$EE80,$EEBF
$EEC0,$EEFF
$EF00,$EF8B
$EF8C,$EFFF
$F000,$FFFF
Description
I/O registers
On-chip EEPROM
On-chip SRAM (available to user)
On-chip SRAM (D-Bug12)
D-Bug12 code
User accessible function table
Customization data
D-Bug12 startup code
Secondary reset/interrupt table
Bootloader
Table 3.2b ■ D-Bug12 memory map for HCS12Dx128
D-Bug12 supports a set of commands that can be used for program development on the
demo board. A summary of the command set is given in Table 3.3. One can request the D-Bug12
monitor to show the complete command set by typing help at the D-Bug12 monitor prompt.
3.5 Using a Demo Board with the D-Bug12 Monitor
We need at least the following software programs in order to develop assembly programs to
be downloaded onto a demo board for execution:
1. A text editor
2. An HCS12 cross assembler
3. A terminal program
We prefer using an IDE program in developing assembly programs. Both the AsmIDE by Eric
Engler and the MiniIDE from Mgtek are well-designed IDEs for developing assembly programs for
the HCS12 and 68HC11 microcontrollers. These two freeware IDEs allow us to enter, assemble,
95
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
Command
ALTCLK
ASM
BAUD ,baudrate. [;t]
BDMBD
BF ,startAddress.,EndAddress. [,data.]
BR [,Address.]
BULK
CALL [<address.]
DEVICE [see description]
EEBASE <Address.
FBULK
FLOAD <AddressOffset.
FSERASE
G [<Address.]
GT <Address.
HELP
LOAD [<AddressOffset.]
MD <StartAddress. [<EndAddress.]
MDW <StartAddress. [<EndAddress.]
MM <Address. [<data.]
MMW <address. [<data.]
MOVE <StartAddress. <EndAddress.
<DestAddress.
NOBR [<Address. <Address....]
PCALL
RD
REGBASE
RESET
RM
SO
STOP
T [<count.]
TCONFIG
UPLOAD <StartAddress.<EndAddress.
USEHBR
VER
VERF [<AddressOffset.]
<Register Name.<Register Value.
Register Names:
CCR Status Bits:
Description
Specify an alternate BDM communication rate
Single line assembler/disassembler
Set communications rate for the terminal
Enter the BDM command debugger
Fill memory with data
Set/Display breakpoints
Erase entire on-chip EEPROM contents
Execute a user subroutine; return to D-Bug12 when
finished
Select/define a new target MCU device
Inform D-Bug12 of the Target’s EEPROM base address
Erase the target processor’s on-chip flash EEPROM
Program the target processor’s on-chip Flash EEPROM
from S-records
Erase one or more sectors of target flash EEPROM
Go-begin execution of user program
Go-Till–set a temporary breakpoint and begin execution of
user program
Display D-Bug12 command set and command syntax
Load user program in S-record
Memory display–display memory contents in hex
bytes/ASCII format
Memory display word–display memory contents in hex
words/ASCII format
Memory modify–interactively examine/change memory
contents
Memory modify word–interactively examine/change
memory contents
Move a block of memory
Remove individual user breakpoints
Execute a user subroutine in expanded memory, return to
D-Bug12 when finished
Register display–display the CPU registers
Inform D-Bug12 of the target I/O register’s base address
Reset the target CPU
Register modify–interactively examine/change CPU
register contents
Step over subroutine calls
Stop execution of user code on the target processor and
place it in background mode
Trace ,count. instructions
Configure target before erasing or programming
S-record memory display
Use hardware breakpoints
Display the running version of D-Bug12
Verify S-records against memory contents
Set register contents
PC, SP, X, Y, A, B, D, PP
S, XM, H, IM, N, Z, V, C
Table 3.3 ■ D-Bug12 command-set summary
96
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
and download the S-record file onto a demo board for execution without quitting any one of
them. AsmIDE can be downloaded from the website at http://www.geocities.com/englere_geo;
MiniIDE can be downloaded from the website at http://www.mgtek.com/miniide. The tutorial
for using the MiniIDE is given in the next section; the tutorial for using the AsmIDE is given in
Appendix E.
3.5.1 Starting the MiniIDE
The MiniIDE can be started by clicking on its icon. The startup screen should be similar to
that in Figure 3.5.
Figure 3.5 ■ MiniIDE startup screen
3.5.2 Communicating with the Demo Board
To communicate with the demo board using the D-Bug12 monitor, press Terminal menu
and select Show Terminal Window and Connected. After this, the lower half becomes the terminal window as shown in Figure 3.6. Press the reset button of the demo board and the screen
will change to Figure 3.7.
One can then enter D-Bug12 commands in the terminal window to display registers and
memory contents, modify memory and register contents, set breakpoints, trace instruction execution, and download the program onto the demo board for execution.
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
Figure 3.6 ■ Screen to show terminal window and connect to demo board
Figure 3.7 ■ MiniIDE terminal window with D-Big12 command prompt
97
98
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
If the D-Bug12 command prompt does not appear on the screen, the most likely three
causes are
1. The demo board is not powered up.
2. The RS232 cable connection is wrong.
3. The baud rate of the terminal program does not match that of the demo board.
Make sure that the demo board has been powered up and also make sure that the RS232
cable is connected to the right connector (if the demo board has two RS232 connectors). If the DBug12 prompt still does not appear, then check the baud rate setting. Select the Terminal menu
and unselected Connected and click on Options. The Options dialog will appear as shown in
Figure 3.8. The settings shown in Figure 3.8 are acceptable. Another option that the user needs
to set is Tools. Click on Tools in the dialog box and make sure that asm12.exe is selected as the
tool for assembling HCS12 programs as shown in Figure 3.9. The user may also want to set the
options in the General category. The settings in the General category are shown in Figure 3.10.
The default settings for the options of the assembler are acceptable.
Figure 3.8 ■ Setting MiniIDE communication parameters
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
99
Figure 3.9 ■ Setting tools option for MiniIDE
3.5.3 Using the D-Bug12 Commands
The D-Bug12 commands are provided to help the program-debugging process. Some of these
commands can be used to set and display the contents of memory locations. In this section, character strings entered by the user are in boldface and optional fields are enclosed in brackets [ ].
In the following section, the syntax of a command is presented and then examples are given.
bf *StartAddress+ *EndAddress. [*Data+]
The bf command is used to fill a block of memory locations with the same value. For example, the following command clears the internal memory locations from $1000 to $10FF to zero:
+bf 1000 1FFF 0
The data field is optional. If we did not specify the data to be filled in, then zero would be
entered in the specified memory locations.
md *StartAddress+ [*EndAddress+]
The md (memory display) command is used to display memory contents. This command
displays memory contents as both hexadecimal bytes and ASCII characters, 16 bytes on each
line. The ,StartAddress. parameter must be supplied; the ,EndAddress. parameter is optional. When the ,EndAddress. parameter is not specified, a single line is displayed.
100
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.10 ■ MiniIDE options in the general category
The number supplied as the ,StartAddress. parameter is rounded down to the next-lower
multiple of 16; the number supplied as the ,EndAddress. parameter is rounded up to the
next-higher multiple of 16 minus 1. This causes each line to display memory in the range of
$xxx0 to $xxxF. For example, if $1005 is entered as the start address and $1020 as the ending address, then the actual memory range displayed would be $1000 through $102F. This command
allows the user to examine the program execution result.
Example 3.1
▼
+md 1000
1000 00 00 00 00 - 00 00 00 00 - 00 00 00 00 - 00 00 00 00
............
+md 1005 1020
1000 00 00 00 00 - 00 00 00 00 - 00 00 00 00 - 00 00 00 00
1010 00 00 00 00 - 00 00 00 00 - 00 00 00 00 - 00 00 00 00
1020 00 00 00 00 - 00 00 00 00 - 00 00 00 00 - 00 00 00 00
............
............
............
▲
101
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
mdw *StartAddress+ [*EndAddress+]
The mdw (memory display words) command displays the contents of memory locations
as hexadecimal words and ASCII characters, 16 bytes on each line. The ,StartAddress. parameter must be supplied; the ,EndAddress. parameter is optional. When the ,EndAddress.
parameter is not supplied, a single line is displayed.
The number supplied as the ,StartAddress. parameter is rounded down to the next lower
multiple of 16, while the number supplied as the ,EndAddress. parameter is rounded up to
the next higher multiple of 16 minus 1. This causes each line to display memory in the range of
$xxx0 through $xxxF.
Example 3.2
▼
+mdw 1000
1000
FC08 00CD - 0900 CE00 - 0A18 10CB - 306B 44B7
. . . . . . . . . . . . 0kD.
+mdw 1000 1020
1000
1010
1020
.
FC08 00CD - 0900 CE00 - 0A18 10CB - 306B 44B7
C5CE 000A - 1810 CB30 - 6B43 B7C5 - CE00 0A18
10CB 306B - 42B7 C5CE - 000A 1810 - CB30 6B41
. . . . . . . . . . . . 0kD.
. . . . . . . 0kC . . . . . .
. . 0kB . . . . . . . . 0kA
▲
mm *Address+ [*Data+]
The mm (memory modify) command allows us to examine and modify the contents of memory locations 1 byte at a time. If the 8-bit data parameter is present on the command line, the
byte at memory location ,Address. is replaced with ,Data. and the command is terminated.
If no optional data is provided, then D-Bug12 enters the interactive memory modify mode. In the
interactive mode, each byte is displayed on a separate line following the address of data. Once the
memory modify command has been entered, single-character subcommands are used for the modification and verification of memory contents. These subcommands have the following format:
[,Data.]
[,Data.]
[,Data.]
[,Data.]
,CR.
,/. or ,5.
,^. or ,2.
,..
Optionally update current location and display the next location.
Optionally update current location and redisplay the same location.
Optionally update current location and display the previous location.
Optionally update current location and exit Memory Modify.
With the exception of the carriage return (CR), the subcommand must be separated from
any entered data with at least a one-space character. If an invalid subcommand character is entered, an appropriate error message is issued and the contents of the current memory location
are redisplayed.
Example 3.3
▼
In this example, each line is terminated with a carriage-return character. However, the carriage return character is nondisplayable and hence is not shown.
.mm 1000
1000 00
1001 00 FF
1002 00 ^
1001 FF
102
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
1002 00
1003 00 55 /
1003 55 .
.
▲
mmw *Address+ [*Data+]
The mmw (memory modify, word) command allows the contents of memory to be
examined and/or modified as 16-bit hex data. If the 16-bit data is present on the command
line, the word at memory location ,Address. is replaced with ,Data. and the command
is terminated. If not, D-Bug12 enters the interactive memory modify mode. In the interactive mode, each word is displayed on a separate line following the address of data. Once the
memory modify command has been entered, single-character subcommands are used for the
modification and verification of memory contents. These subcommands have the following
format:
[,Data.]
[,Data.]
[,Data.]
[,Data.]
,CR.
,/. or ,5.
,^. or ,2.
,..
Optionally update current location and display the next location.
Optionally update current location and redisplay the current location.
Optionally update current location and display the previous location.
Optionally update current location and exit memory modify.
With the exception of the carriage return (CR), the subcommand must be separated from
any entered data with at least a one-space character. If an invalid subcommand character is
entered, an appropriate error message is issued and the contents of the current memory location
are redisplayed.
Example 3.4
▼
In this example, each line is terminated with a carriage-return character. However, the
carriage-return character is nondisplayable and hence is not shown.
.mmw 1100
1100 00F0
1102 AA55 0008
1104 0000 ^
1102 0008 aabb
1104 0000
1106 0000 .
.
▲
move *StartAddress+ *EndAddress+ *DestAddress+
The move (move memory block) command is used to move a block of memory from one
location to another, 1 byte at a time. Addresses are specified in 16-bit hex values. The number
of bytes moved is one more than ,EndAddress. 2 ,StartAddress.. The block of memory
beginning at the destination address may overlap the memory block defined by ,StartAddress.
and ,EndAddress.. One of the uses of the move command might be to copy a program from
RAM into the on-chip EEPROM.
103
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
Example 3.5
▼
+move 1000 10ff 1100
.
▲
rd
The rd (register display) command is used to display the HCS12 CPU registers, including
the ppage register.
Example 3.6
▼
+rd
PP PC
38 1521
xx: 1521
.
SP
3C00
9C42
X
2014
Y
0000
CPD
D 5 A:B
6E:14
$0042
CCR 5 SXHI
1001
NZVC
0100
▲
rm
The rm (register modify) command is used to examine and/or modify the contents of the
CPU12 registers interactively. As each register and its contents are displayed, D-Bug12 allows
the user to enter a new value for the register in hex. If modification of the displayed register
is not desired, entering a carriage return will cause the next CPU register and its contents
to be displayed on the next line. When the last of the CPU registers has been examined and/
or modified, the rm command displays the first register, giving the user an opportunity to
make additional modifications to the CPU register contents. Typing a period as the first nonspace character on the line will exit the interactive mode of the register modify command and
return to the D-Bug12 command prompt. The registers are displayed in the following order,
one register per line: PC, SP, X, Y, A, B, CCR. The PPAGE register (represented by PP) is not
displayed.
Example 3.7
▼
+rm
PC50000 1500
SP50A00
IX50000 0100
IY50000
A500
B500 ff
CCR590 d1
PC51500 .
.
▲
104
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
pc *RegisterName+ *RegisterValue+
The pc command allows one to change the value of any CPU register (PC, SP, X, Y, A, B, D,
CCR). Each of the fields in the CCR may be modified by using the bit names shown in Table 3.4.
CCR Bit Name
Description
Legal Values
S
H
N
Z
V
C
IM
XM
STOP enable
Half carry
Negative flag
Zero flag
Two’s complement overflow flag
Carry flag
IRQ interrupt mask
XIRQ interrupt mask
0 or 1
0 or 1
0 or 1
0 or 1
0 or 1
0 or 1
0 or 1
0 or 1
Table 3.4 ■ Condition code register bits
Example 3.8
▼
+pc 2000
PC
2000
+x 800
PC
2000
+c 0
PC
2000
+z 1
PC
2000
+d 2010
PC
2000
.
SP
0A00
X
0100
Y
0000
D 5 A:B
00:FF
CCR 5 SXHI
1101
NZVC
0001
SP
0A00
X
0800
Y
0000
D 5 A:B
00:FF
CCR 5 SXHI
1101
NZVC
0001
SP
0A00
X
0800
Y
0000
D 5 A:B
00:FF
CCR 5 SXHI
1101
NZVC
0000
SP
0A00
X
0800
Y
0000
D 5 A:B
00:FF
CCR 5 SXHI
1101
NZVC
0100
SP
0A00
X
0800
Y
0000
D 5 A:B
20:10
CCR 5 SXHI
1101
NZVC
0100
▲
asm *Address+
The asm command invokes the one-line assembler/disassembler. It allows memory contents to be viewed and altered using assembly-language mnemonics. Each entered source line
is translated into object code and placed into memory at the time of entry. When displaying
memory contents, each instruction is disassembled into its source mnemonic form and displayed along with the hex object code and any instruction operands.
Assembly mnemonics and operands may be entered in any mix of upper- and lowercase
letters. Any number of spaces may appear between the assembler prompt and the instruction
mnemonic or between the instruction mnemonic and the operand. Numeric values appearing
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
105
in the operand field are interpreted as signed decimal numbers. Placing a $ in front of any number will cause the number to be interpreted as a hex number.
When an instruction is disassembled and displayed, the D-Bug12 prompt is displayed
following the disassembled instruction. If a carriage return is the first nonspace character
entered following the prompt, the next instruction in memory is disassembled and displayed on
the next line. If an HCS12 instruction is entered following the prompt, the entered instruction
is assembled and placed into memory. The line containing the new entry is erased and the new
instruction is disassembled and displayed on the same line. The next instruction location is
then disassembled and displayed on the screen.
When entering branch instructions, the number placed in the operand field should be the
absolute destination address of the instruction. The assembler calculates the two’s complement
offset of the branch and places the offset in memory with the instruction.
The assembly/disassembly process may be terminated by entering a period as the first nonspace character following the assembler prompt.
The following example displays the assembly instructions from memory location $2000
until $2011. The carriage-return character is entered at the + prompt of each line and the period
character is entered at the last line.
+asm 2000
2000
FC0800
2003
CD0900
2006
CE000A
2009
1810
200B
CB30
200D
6B44
200F
B7C5
2011
CE000A
.
LDD
LDY
LDX
IDIV
ADDB
STAB
XGDX
LDX
$0800
#$0900
#$000A
#$30
4,Y
#$000A
.
.
.
.
.
.
.
..
The following example enters a short program that consists of three instructions starting
from the memory location at $1500:
+asm 1500
1500
FC0800
1503
F30802
1506
7C0900
1509
E78C
.
LDD
ADDD
STD
TST
$0800
$0802
$0900
12,SP
..
br [*Address+ …]
The br (breakpoint set) command sets a breakpoint at a specified address to display any previously set breakpoints. The function of a breakpoint is to halt user program execution when
the program reaches the breakpoint address. When a breakpoint address is encountered, DBug12 disassembles the instruction at the breakpoint address, prints the CPU register contents,
and waits for a D-Bug12 command to be entered by the user.
Breakpoints are set by typing the breakpoint command followed by one or more breakpoint
addresses. Entering the breakpoint command without any breakpoint addresses will display all
the currently set breakpoints. A maximum of two user breakpoints may be set at one time.
Whenever the user program is not working correctly and the user suspects that the instruction
at a certain memory location is incorrect, he or she can set a breakpoint at that location and
check the execution result by looking at the contents of CPU registers or memory locations.
106
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Example 3.9
▼
+br
1020
Breakpoints:
1020
Breakpoint Table Full
.
1040
1040
1050
▲
nobr [*Address+ *Address+]
The nobr command removes one or more previously entered breakpoints. If the nobr command is entered without any argument, all user breakpoints are removed from the breakpoint
table.
Example 3.10
▼
+br
2000
Breakpoints:
2000
Breakpoint Table Full
+nobr
2000
Breakpoints:
2010
+
2010
2010
2020
2040
1090
▲
g [*Address+]
The g command is used to begin execution of user code in real time. Before beginning the
execution of user code, any breakpoints that were set with the br command are placed in memory. Execution of the user program continues until a user breakpoint is encountered, a CPU
exception occurs, the stop or reset command is entered, or the EVB’s reset switch is pressed.
When the user code halts for any of these reasons and control is returned to D-Bug12, a message
is displayed explaining the reason for user program termination. In addition, D-Bug12 disassembles the instruction at the current PC address, prints the CPU register contents, and waits
for the next D-Bug12 command to be entered by the user. If the starting address is not supplied
in the command line parameter, program execution will begin at the address defined by the current value of the program counter.
Example 3.11
▼
+g 1500
User Bkpt Encountered
PP PC
SP
X
38 150C 3C00
7B48
xx: 150C 911E
.
Y
0000
CMPA
D 5 A:B
03:E8
$001E
CCR 5 SXHI
1001
NZVC
0001
▲
gt *Address+
The gt (go till) command is similar to the g command except that a temporary breakpoint
is placed at the address supplied at the command line. Any breakpoints that were set by the use
107
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
of the br command are not placed in the user code before program execution begins. Program
execution begins at the address defined by the current value of the program counter. When user
code reaches the temporary breakpoint and control is returned to D-Bug12, a message is displayed explaining the reason for user program termination. In addition, D-Bug12 disassembles
the instruction at the current PC address, prints the CPU register contents, and waits for a command to be entered by the user.
Example 3.12
▼
+pc 1500
PP PC
SP
X
Y
38 1500 3C00
1000
1002
xx: 1500 CF1500
LDS
+gt 1540
Temporary Breakpoint Encountered
PP PC
SP
X
Y
38 1510 1500
1000
1002
xx: 1510 3B
PSHD
.
D 5 A:B
00:00
#$1500
CCR 5 SXHI
1001
NZVC
0101
D 5 A:B
1E:00
CCR 5 SXHI
1001
NZVC
0000
▲
t [*count+]
The t (trace) command is used to execute one or several user program instructions beginning at the current program counter location. As each instruction is executed, the CPU register
contents and the next instruction to be executed are displayed. A single instruction may be
executed by entering the trace command immediately followed by a carriage return.
Because of the method used to execute a single instruction, branch instructions (Bcc, LBcc,
BRSET, BRCLR, DBEQ/NE, IBEQ/NE, and TBEQ/NE) that contain an offset that branches back
to the instruction opcode do not execute. D-Bug12 appears to become stuck at the branch instruction and does not execute the instruction even if the condition for the branch instruction
is satisfied. This limitation can be overcome by using the gt command to set a temporary breakpoint at the instruction following the branch instruction.
Example 3.13
▼
+pc 1500
PP PC
SP
38 1500 1500
xx: 1500 CF1500
+t
PP PC
SP
38 1503 1500
xx: 1503 CE1000
+t 2
PP PC
SP
38 1506 1500
xx: 1506 34
X
1000
Y
1002
LDS
D 5 A:B
1E:00
#$1500
CCR 5 SXHI
1001
NZVC
0000
X
1000
Y
1002
LDX
D 5 A:B
1E:00
#$1000
CCR 5 SXHI
1001
NZVC
0000
X
1000
Y
1002
PSHX
D 5 A:B
1E:00
CCR 5 SXHI
1001
NZVC
0000
108
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
PP PC
SP
38 1507 14FE
xx: 1507 861E
.
X
1000
Y
1002
LDAA
D 5 A:B
1E:00
#$1E
CCR 5 SXHI
1001
NZVC
0000
▲
The first command in this example sets the program counter to $1500 so that the user knows
where the program execution starts. This command is normally needed when tracing a program.
call [*Address+]
The call command is used to execute a subroutine and returns to the D-Bug12 monitor program
when the final rts instruction of the subroutine is executed. All CPU registers contain the values at
the time the final rts instruction was executed, with the exception of the program counter. The program counter contains the starting address of the subroutine. If a subroutine address is not supplied
on the command line, the current value of the program counter is used as the starting address.
No user breakpoints are placed in memory before execution is transferred to the subroutine.
If the called subroutine modifies the value of the stack pointer during its execution, it must restore the stack pointer’s original value before executing the rts instruction. This restriction is
required because a return address is placed on the user’s stack that returns to D-Bug12 when the
final rts of the subroutine is executed. Obviously, any subroutine must obey this restriction in
order to execute properly.
Example 3.14
▼
+call 1600
Subroutine Call Returned
pp PC
SP
X
38 1600 0A00
0032
xx: 1600 FC1000
Y
0900
LDD
D 5 A:B
00:31
$1000
CCR 5 SXHI
1001
NZVC
0000
.
▲
This command is useful for testing a subroutine without writing a testing program.
3.5.4 Entering an Assembly Program
When a program is first created, one needs to open a new file to hold it. To do that, one
presses the File menu from the MiniIDE window and selects New, as shown in Figure 3.11.
After one selects the New command from the menu, an empty screen will appear allowing the
user to enter a new program. The user can now start to enter a new program.
The editing window (upper half of the IDE window) may be too small and should be adjusted. After adjusting the window size, the user enters the program that converts a hex number
into BCD digits as in Example 2.13. This program performs the repeated divide-by-10 operation
to the given number and adds hex number $30 to each remainder to convert to its corresponding ASCII code. The result is shown in Figure 3.12.
3.5.5 Assembling the Program
To assemble an assembly program, press the Build menu and select Build eg02_13.asm, as
shown in Figure 3.13. If the program is assembled successfully, the status window will display
the corresponding message, as shown in Figure 3.14.
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
109
Figure 3.11 ■ Select New from the File menu to create a new file
After the program is assembled successfully, we are ready to run and debug the program.
The output of the assembly process is an S-record file eg2_13.s19. The file name has a suffix
s19. The S-record format is a common file format defined by Freescale to allow tools from different vendors to work on the same project.
3.5.6 Downloading the S-Record File onto the Demo Board for Execution
The D-Bug12 provides the following command for the user to download the S-record file
onto the demo board:
load [*AddressOffset+]
The load command is used to load S-record objects into memory from an external device.
The ,AddressOffset., if supplied, is added to the load address of each S-record before its data
bytes are placed in memory. Providing an address offset other than zero allows an object code or
data to be loaded into memory at a location other than that for which it was assembled. During
the loading process, the S-record data is not echoed to the control console. However, for each
10 S-records that are successfully loaded, an ASCII asterisk character (*) is sent to the control
console. When an S-record file has been successfully loaded, the D-Bug12 prompt reappears on
the screen.
The load command is terminated when D-Bug12 receives an S9 end-of-file record. If the
object being loaded does not contain an S9 record, D-Bug12 does not output its prompt and
continues to wait for the end-of-file record. Pressing the reset switch returns D-Bug12 to its
command line prompt.
110
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.12 ■ Enter the program to convert hex number to BCD ASCII digits
Figure 3.13 ■ Prepare to assemble the program eg213.asm
111
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
Figure 3.14 ■ Message window (in the middle) shows that previous assembling is successful
Example 3.15
▼
+load
*
.
▲
This example downloads a file that is equal to or shorter than 10 S-records and hence only
one * character is displayed on the screen. In addition to entering the load command followed by a
carriage return, one also needs to go back to the IDE window to specify the file to be downloaded.
An example of selecting the Download File command in MiniIDE is shown in Figure 3.15.
After the Download File command is selected, a popup dialog box, as shown in Figure 3.16,
will appear. This dialog box allows one to specify the file to be downloaded. Click on the Open
button on the popup window and the file will be transferred to the demo board.
3.5.7 Running and Debugging the Program
We need to go back to the terminal window to run and debug the program. The screen after
the program download would look like Figure 3.17.
Before running the program, we should verify that the test data is downloaded correctly
into the memory. In this program, the test data is the decimal number 12,345, which corresponds to the hex number $3039. One can use the command md 1000 to verify it. The contents
of the memory locations $1000 and $1001 displayed by the D-Bug12 monitor should be $3039.
Otherwise, some error might have occurred.
Also make sure that the program has been downloaded onto the right memory area. This
can be verified by using the asm 1500 command (the eg2_13 program starts at $1500) and pressing the Enter key several times. The first few lines should look as follows:
+asm 1500
xx:1500
FC1000
LDD
$1000
.
112
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.15 ■ Prepare to download a .s19 file
Figure 3.16 ■ Select an S-record file to be downloaded
xx:1503
xx:1506
xx:1509
xx:150B
xx:150D
CD1010
CE000A
1810
CB30
6B44
LDY
LDX
IDIV
ADDB
STAB
#$1010
#$000A
#$30
4,Y
.
.
.
.
.
After making sure that the program has been downloaded correctly, execute the program
without setting any breakpoint and check the result to see if it is correct. The screen should
look like Figure 3.18.
3.5 ■ Using a Demo Board with the D-Bug12 Monitor
113
Figure 3.17 ■ Screen after downloading the program eg2_13.s19 onto the demo board
The second line of the terminal window that contains the statement User Bkpt Encountered
is caused by the swi (software interrupt) instruction. The swi instruction caused the program
control to be returned to the D-Bug12 monitor. This instruction is often used as the last instruction of a program to be executed on a demo board with the D-Bug12 monitor. The last line of
the terminal window displays the contents of the memory locations $1010 to $101F. The first 5
bytes represent the ASCII codes of 1, 2, 3, 4, and 5. Therefore, the program executes correctly.
If the program does not work correctly, we can set breakpoints at locations we suspect or
we can even trace the execution of some instructions to find out the error. Suppose the execution result (we take out the first xgdx instruction) looks as follows:
+md 1010
1010 30 30 35 33 - 35 2D 2D 2D - 2D 2D 2D 2D - 2D 2D 2D 2D 00535--------------.
The first four digits were found to be incorrect. Since this program is short, we can trace
through it. One approach is the following:
Step 1
Trace through the first six instructions and check to see if the quotient (in index register X) and
the memory contents at $1014 are correct. The last seven lines of the terminal window are
PP PC
SP
X
Y
D 5 A:B
CCR 5 SXHI NZVC
38 150F 3C00
04D2 1010
00:35
1001 0000
xx: 150F CE000A
LDX
#$000A
.md 1010
1010 30 30 35 33 - 35 2D 2D 2D - 2D 2D 2D 2D - 2D 2D 2D 2D 00535--------------.
114
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.18 ■ Screen after running the eg2_13 program and displaying the result
These six lines tell us that
• The index register contains hex value $04D2 (equal to decimal 1234) and is the
correct quotient.
• The memory location at $1014 contains hex value $35 and is the ASCII code of 5.
• In the next division, the number to be divided by 10 will be 1234.
• The next instruction to be executed is ldx #$0A.
Step 2
Trace the next two instructions. Oops! We find one error. The double accumulator D does
not contain the value 1234 (it contains $35 instead) before the idiv instruction is executed.
We forgot to swap the value in index register X with double accumulator D before
performing the second division!
+t
PP PC
38 1512
xx: 1512
+t
PP PC
38 1514
xx: 1514
SP
3C00
1810
X
000A
Y
1010
IDIV
D 5 A:B
00:35
CCR 5 SXHI
1001
NZVC
0000
SP
3C00
CB30
X
0005
ADDB
Y
1010
#$30
D 5 A:B
00:03
CCR 5 SXHI
1001
NZVC
0000
.
Step 3
Fix the error by inserting the xgdx instruction before the ldx #$0a instruction and rerun
the program. After rerunning the program, check the contents at $1000 to $1004 again.
The last few lines on the screen of the terminal window would be as follows:
115
3.6 ■ Other D-Bug12 Modes
+load
*
+g 1500
User Bkpt Encountered
PP PC
SP
X
Y
D 5 A:B
CCR 5 SXHI NZVC
38 1537 3C00
0032
1010
00:31
1001 0000
xx: 1537 2D2D
BLT
$1566
+md 1010
1010 31 32 33 34 - 35 2D 2D 2D - 2D 2D 2D 2D - 2D 2D 2D 2D 12345--------------.
The memory locations from $1000 to $1004 contain correct values. So, we have fixed the error.
Most debug sessions are similar to this. Of course, longer programs will take a longer time
and require more commands to try different things before we can fix the bugs.
3.6 Other D-Bug12 Modes
The other three D-Bug12 modes are not used as often as the EVB mode.
3.6.1 The Pod Mode
The pod mode is intended to use the demo board as a BDM host to control a target board.
The arrangement is shown in Figure 3.19. This mode will be discussed in Section 3.9.5.
Your demo board
User
Terminal
(or PC)
Low-level
D-Bug12 interface
routine
Background debug
command
HCS12
microcontroller
Target
system
Figure 3.19 ■ D-Bug’s POD mode conceptual model
3.6.2 The Jump-to-EEPROM Mode
The Jump-to-EEPROM mode allows a small program to be executed from the on-chip
EEPROM whenever the EVB is powered up or reset. When running on a demo board with an
MC9S12Dx256, MC9S12H256, or MC9S12A256 MCU, the bootloader startup code jumps directly to address $0400 without performing any initialization of the CPU registers or peripherals. When running on a demo board with an MC9S12Dx128 or MC9S12A128 MCU, the
EEPROM is not visible at its default address of $0000 because it is overlaid by the on-chip
SRAM. In this case, the SRAM is relocated to $2000 and the bootloader start code jumps directly to address $0400 without performing any additional initialization of the CPU registers or
peripherals. This mode provides a convenient way to execute a program in a stand-alone manner without having to erase and program the on-chip flash using the bootloader. Program code
and data can be programmed into the EEPROM using the D-Bug12’s load command.
116
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
3.6.3 The Bootloader Mode
The on-chip flash memory includes a boot block area from $F000 to $FFFF containing an
S-record bootloader program. The bootloader can be used to erase and reprogram the remainder
of on-chip flash memory or erase the on-chip byte-erasable EEPROM. The bootloader utilizes
the on-chip SCI for communications and does not require any special programming software on
the host computer. The only host software required is a simple terminal program that is capable
of communicating at 9600 to 115,200 baud and supports XON/XOFF handshaking.
The bootloader mode is mainly used to program the D-Bug12 monitor into the flash memory or download a user’s fully debugged code into the D-Bug12 portion of flash memory. The
latter allows the board to be operated in EVB mode or restart user code every time the board is
turned on or reset.
When the user code is programmed into the D-Bug12 portion of flash memory, it wipes out
the D-Bug12 monitor. We can restore it anytime because bootloader is not erased. We cannot
erase the bootloader in bootloader mode. The bootloader can only be erased by a BDM kit via
the BDM port.
Invoking the bootloader causes the prompt shown in Figure 3.20 to be displayed on the host
terminal’s screen.
D-Bug12 Bootloader v1.0.0
a) Erase Flash
b) Program Flash
c) Set Baud Rate
d) Erase EEPROM
?
Figure 3.20 ■ Serial bootloader prompt
ERASE FLASH COMMAND
This command is selected by typing a on the terminal after the ? character in Figure 3.20.
This command causes a bulk erase of all four 64-kB flash memory arrays except for the 4-kB boot
block in the upper 64-kB array where the S-record bootloader resides. After the erase operation
is completed, a verify operation is performed to ensure that all locations were properly erased.
If the erase operation is successful, the bootloader’s prompt is redisplayed. If any locations were
found to contain a value other than $FF, an error message is displayed on the screen and the
bootloader’s prompt is redisplayed. If the HCS12 MCU will not erase after one or two attempts,
the device may be damaged.
PROGRAM FLASH COMMAND
The bootloader uses interrupt-driven, buffered serial I/O in conjunction with XON/XOFF
software handshaking to control the flow of S-record data from the host computer during
the programming process. This allows the bootloader to continue receiving S-record data from
the host computer while the data from the previously received S-record is programmed into the
3.6 ■ Other D-Bug12 Modes
117
flash memory. The terminal program must support XON/XOFF handshaking to properly reprogram the HCS12’s flash memory.
Typing a lowercase b on the keyboard causes the bootloader to enter the programming
mode and wait for S-records to be sent from the host computer. The bootloader will continue
to receive and process S-records until it receives an S8 or S9 end-of-file record. If the object file
being sent to the bootloader does not contain an S8 or S9 record, the bootloader will not return
its prompt and will continue to wait for the end-of-file record. Pressing the reset button on the
demo board will cause the bootloader to return to its prompt.
If a flash memory location does not program properly, an error message is displayed on the
terminal screen and the bootloader’s prompt is redisplayed. If the device does not program correctly after one or two attempts, the device may be damaged or an S-record with a load address
outside the range of the available on-chip flash may have been received. The S-record data must
have load addresses ranging from $C0000 to $FFFFF. This address represents the upper 256 kB
of the 1-MB address space of the MC9S12DP256.
S E T - B A U D - R AT E C O M M A N D
The default baud rate of 9600 is too slow for programming a large S-record file. The user
has the option to select a higher baud rate in programming the on-chip flash memory. The SetBaud-Rate command allows the bootloader communication rate to be set to one of four standard baud rates.
Typing a lowercase c on the terminal keyboard causes the prompt shown in Figure 3.21 to
be displayed on the monitor screen. Entering 1 through 4 on the keyboard will select the associated baud rate and issue a secondary prompt indicating that the terminal baud rate should also
be changed. After changing the terminal baud rate, pressing the Enter key will return to the
main bootloader prompt (in Figure 3.20).
1) 9600
2) 38400
3) 57600
4) 115200
?3
Change Terminal BR, Press Return
Figure 3.21 ■ Change baud rate (both the demo board and terminal program)
RELOADING D-BUG12
Freescale adds new features and enhancements to the D-Bug12 monitor frequently. It may
be desirable to update to the latest version. A .zip distribution file containing the current version of D-Bug12, including the bootloader and the reference manual, can be downloaded from
Freescale’s website.
LOADING USER PROGRAMS INTO FLASH MEMORY
A demo board preloaded with the D-Bug12 monitor can also be reprogrammed with the
user application to prototype an embedded application. When using the board in this manner,
118
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
the user-supplied code may occupy all of the on-chip flash memory except the address range
from $F000 to $FFFF in the fixed flash memory page that begins at $C000. To begin execution
of the user’s application program, both the PAD0 and PAD1 pins must have jumpers placed in
the 0, or off, position. This will cause the bootloader startup code to jump to the address in the
alternate reset vector at $EFFE. When the user code is programmed into flash, an address must
be placed in the reset vector position ($EFFE) of the alternate interrupt vector table.
3.7 Tips for Assembly Program Debugging
Assembly program errors can be classified into two categories:
• Syntax errors
• Logical errors
3.7.1 Syntax Errors
Syntax errors are common for beginners. Syntax errors can be divided into following
categories:
• Misspelling of instruction mnemonics. This type of error will be highlighted by the
assembler and can be fixed easily.
• Starting the assembly program from column 1. If a line of the assembly program
has a label, then this does not cause any error. However, if a line of the assembly
program does not have a label, then the instruction mnemonic is treated as a label
by the assembler, whereas the operand (represented in one of the addressing modes)
is treated as the instruction mnemonics. This will always cause the undefined
mnemonic error!
• Missing operands. Depending on what is missing, the error message will vary. For
example, for the instruction
brclr
PORTA,$04
the resultant error message is “Missing target address.” For the instruction
ldaa
the assembler outputs the message “Inherent addressing illegal.”
As time goes on and you gain experience and also memorize the instruction mnemonics
better, this type of error will reduce and even disappear.
3.7.2 Logical Errors
Beginners make many logical errors. The most common ones are as follows:
U S I N G E X T E N D E D ( O R D I R E C T ) M O D E I N S T E A D O F I M M E D I AT E M O D E
This error is very common for beginners. The following assembly program is written to
compute the sum of an array of N 8-bit elements:
N
array
sum
equ
org
dc.b
dc.b
ds.w
org
20
$1000
2,4,6,8,10,12,14,16,18,20
22,24,26,28,30,32,34,36,38,40
1
$1500
; array count
119
3.7 ■ Tips for Assembly Program Debugging
loop
ldx
movw
ldy
ldab
sex
addd
std
dbne
swi
end
array
0,sum
N
1,x1
B,D
sum
sum
y,loop
; place the starting address of array in X
; initialize sum to 0
; initialize loop count to N
; place one number in B and move array pointer
; sign-extend the 8-bit number to 16-bit
; add to sum
; update the sum
; add all numbers to sum yet?
; return to monitor
At first look, this program appears to be fine and should work. After assembling the program, we download it onto the demo board and get the following screen output:
.load
....
done
.
It is a good idea to make sure that the program has been downloaded onto the demo board
by using the asm command.
+asm 1500
xx:1500
xx:1503
xx:1509
xx:150B
xx:150D
xx:150F
xx:1512
xx:1515
xx:1518
FE1000
180400001014
DD14
E630
B714
F31014
7C1014
0436F3
3F
LDX
MOVW
LDY
LDAB
SEX
ADDD
STD
DBNE
SWI
$1000
$0000,$1014
$0014
1,X1
B,D
$1014
$1014
Y,$150B
.
.
.
.
.
.
.
.
.
Indeed, the program is downloaded correctly. The next thing to check is to make sure that
the array data is downloaded correctly by using the md command.
+md 1000 1010
1000 02 04 06 08 - 0A 0C 0E 10 - 12 14 16 18 - 1A 1C 1E 20 . . . . . . . . . . . . . . .
1010 22 24 26 28 - 00 00 B9 A9 - 2A CA FA DB - AC DA 18 97 “$&(. . . .*. . . . . . .
.
Again, we are convinced that the data array has been downloaded correctly. The array is
stored at locations from $1000 to $1013. The variable sum occupies the memory locations from
$1014 to $1015. Since the program has not been run yet, these 2 bytes contain 0s.
The next thing to do is to run the program. This program should run to the swi instruction
and stop. The screen should look as follows:
+g 1500
User Bkpt Encountered
PP PC
SP
X
38 1519 3C00
0213
xx: 1519 88F4
.
Y
0000
EORA
D 5 A:B
FF:07
#$F4
CCR 5 SXHI
1001
NZVC
1000
120
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Checking the resultant sum, we can see that it ($FF07) is incorrect. The correct answer
should be $1A4 (or 420). The sum can be found by using the md command as follows:
+md 1010
1010 22 24 26 28 - FF 07 B9 A9 - 2A CA FA DB - AC DA 18 97 “$&(. . . .*. . . . . . .
.
Since this program is short, we can trace through it. To trace this program, we set the program counter value to the start of the program ($1500) as follows:
+pc 1500
PP PC
SP
38 1500 3C00
xx: 1500 FE1000
.
X
0213
CCR 5 SXHI
1001
NZVC
1000
Y
D 5 A:B
CCR 5 SXHI
0000
FF:07
1001
MOVW $0000,$1014
NZVC
0000
Y
0000
LDX
D 5 A:B
FF:07
$1000
Trace one instruction at a time.
+t 1
PP PC
SP
X
38 1503 3C00
0204
xx: 1503 180400001014
.
The executed instruction is ldx array. The purpose of this instruction is to place the starting address
of the array into X. After the execution of this instruction, the value of X should change to $1000.
However, the instruction trace shows otherwise. This is because of the incorrect use of the addressing
mode. Change the instruction to ldx #array, rerun the program, and the sum is still incorrect.
+md 1010
1010 22 24 26 28 - FF F0 B9 A9 - 2A CA FA DB - AC DA 18 97 “$&(. . . .*. . . . . . .
.
Trace the program again. This time we trace up to the second instruction and examine the
contents of sum.
+t 2
PP PC
SP
X
38 1503 3C00
1000
xx:1503 180400001014
Y
D 5 A:B
CCR 5 SXHI
0000
FF:F0
1001
MOVW $0000,$1014
PP PC
SP
38 1509 3C00
xx:1509 DD14
Y
0000
$0014
X
1000
LDY
D 5 A:B
FF:F0
CCR 5 SXHI
1001
NZVC
0000
NZVC
0000
+md 1010
1010 22 24 26 28 - FF 00 B9 A9 - 2A CA FA DB - AC DA 18 97 “$&(. . . .*. . . . . . .
.
At this point, the value of sum should be changed to 0 because the second instruction intends to initialize it to 0. Again, the addressing mode is wrong. The correct instruction should
be movw #0,sum. This is not easy to figure out by looking at the program. Rerun the program
and display the contents of sum.
+load
*
+g 1500
121
3.7 ■ Tips for Assembly Program Debugging
User Bkpt Encountered
PP PC
SP
X
Y
D 5 A:B
CCR 5 SXHI NZVC
38 1519 3C00
100F
0000
00:F0
1001 0000
xx: 1519 88F4
EORA
#$F4
+md 1010
1010 22 24 26 28 - 00 F0 B9 A9 - 2A CA FA DB - AC DA 18 97 “$&(. . . .*. . . . . . .
.
The value of sum is still incorrect. Again trace the program up to the third instruction.
+pc 1500
PP PC
SP
X
38 1500 3C00
100F
xx: 1500 CE1000
+t 3
PP PC
SP
X
38 1503 3C00
1000
xx: 1503 180300001014
CCR 5 SXHI
1001
NZVC
0000
Y
D 5 A:B
CCR 5 SXHI
0000
00:F0
1001
MOVW #$0000,$1014
NZVC
0000
Y
0000
LDX
D 5 A:B
00:F0
#$1000
PP PC
SP
38 1509 3C00
xx: 1509 DD14
X
1000
Y
0000
LDY
D 5 A:B
00:F0
$0014
CCR 5 SXHI
1001
NZVC
0000
PP PC
SP
38 150B 3C00
xx: 150B E630
.
X
1000
Y
000F
LDAB
D 5 A:B
00:F0
1,X1
CCR 5 SXHI
1001
NZVC
0000
The third instruction intends to load the array count N into index register Y. Y should
receive the value of N, which is 20. The instruction trace shows that Y receives 15 ($F) instead
of 20. Again, this is due to the incorrect addressing mode. Change the instruction to ldy #N,
assemble, download, rerun the program, and display the contents of sum.
+g 1500
User Bkpt Encountered
PP PC
SP
X
Y
D 5 A:B
CCR 5 SXHI NZVC
38 151A 3C00
1014
0000
01:A4
1001 0000
xx: 151A F421BD
ANDB
$21BD
+md 1010
1010 22 24 26 28 - 01 A4 B9 A9 - 2A CA FA DB - AC DA 18 97 “$&(. . . .*. . . . . . .
.
This time the value of sum is $1A4 (420) and is correct.
M I S M AT C H O F O P E R A N D S I Z E
This is another common mistake made by beginners. For example, some people might
write the previous program as follows:
N
array
equ
org
dc.b
dc.b
20
$1000
2,4,6,8,10,12,14,16,18,20
22,24,26,28,30,32,34,36,38,40
; array count
122
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
sum
loop
ds.w
org
ldx
movw
ldy
ldd
addd
std
dbne
swi
end
1
$1500
#array
#0,sum
#N
1,x1
sum
sum
y,loop
; place the starting address of array in X
; initialize sum to 0
; initialize loop count to N
; place one number in D and move array pointer
; add to sum
; update the sum
; add all numbers to sum yet?
; return to monitor
At the first run of this program, sum ($A61F) is also incorrect.
+md 1010
1010 22 24 26 28 - A6 1F B9 A9 - 2A CA FA DB - AC DA 18 97 “$&(. . . .*. . . . . . .
.
Again, the error can be found by tracing. This time the error will not be found until the
instruction loop ldd 1,x1 is traced.
+pc 1500
PP PC
SP
X
38 1500 3C00
1014
xx: 1500 CE1000
+t
PP PC
SP
X
38 1503 3C00
1000
xx: 1503 180300001014
+t
PP PC
SP
X
38 1509 3C00
1000
xx: 1509 CD0014
+t
PP PC
SP
X
38 150C 3C00
1000
xx: 150C EC30
+t
PP PC
SP
X
38 150E 3C00
1001
xx: 150E F31014
.
CCR 5 SXHI
1001
NZVC
1000
Y
D 5 A:B
CCR 5 SXHI
0000
A6:1F
1001
MOVW #$0000,$1014
NZVC
0000
Y
0000
LDX
D 5 A:B
A6:1F
#$1000
Y
0000
LDY
D 5 A:B
A6:1F
#$0014
CCR 5 SXHI
1001
NZVC
0000
Y
0014
LDD
D 5 A:B
A6:1F
1,X1
CCR 5 SXHI
1001
NZVC
0000
Y
0014
ADDD
D 5 A:B
02:04
$1014
CCR 5 SXHI
1001
NZVC
0000
We expect this instruction to place the first array element ($02) in D. Instead, it places
$0204 in D. This is obviously wrong! The error is due to the fact that the ldd instruction loads
a word (2 bytes) instead of 1 byte into D. At this point, we should be able to figure out the right
instruction to use and fix the problem.
I N A P P R O P R I AT E U S E O F I N D E X A D D R E S S I N G M O D E
When the index addressing mode is used to step through an array, one needs to increment or
decrement the index register to reach the next element. Depending on the size of the element,
3.8 ■ Using CodeWarrior
123
one needs to increment or decrement the index register by 1, 2, or some other value. When such
errors occur, the program execution result will be incorrect. This type of error can be discovered
by checking the computation result of the first two elements.
S TA C K F R A M E E R R O R S
This type of error will be discussed in Chapter 4.
INCORRECT ALGORITHM
An incorrect algorithm can never result in a correct program. This type of problem cannot be
fixed by tracing the program. After program tracing fails to fix the problem, we must reexamine
the algorithm to see if it is incorrect.
3.8 Using CodeWarrior
CodeWarrior is an IDE designed to support the software development for all microcontroller
products manufactured by Freescale. CodeWarrior allows the user to debug his or her software
using the following three approaches:
1. Running the program using the simulator
2. Running the program on the target hardware programmed with the serial monitor
(The HCS12 MCU is programmed with the serial monitor.)
3. Running the program on the target hardware connected to a BDM-based debug adapter
CodeWarrior has a built-in simulator that can be used by the user to debug her or his software. CodeWarrior can support software debugging via the serial monitor. Using this approach,
the user needs to connect the demo board to the COM port of the PC using a serial cable. At the
time of this writing, CodeWarrior can work with the following BDM-based adaptors to debug
users’ software:
1. P&E Multilink/CyclonePro BDM adaptor
2. TBDML adaptor
3. Abatron BDI adaptor
4. Softec’s inDART debugger
A BDM adaptor can also be part of the demo board. For example, the Freescale student
project board includes the P&E Multilink BDM adaptor. By plugging in different HCS12
microcontroller kits, the user can perform software debugging for several different HCS12
microcontrollers. Freescale publishes the public domain TBDML interface to be used by anyone interested in building her or his BDM debugger kit.
3.8.1 Building a Software Project Using CodeWarrior
CodeWarrior can be started by clicking on its icon. The startup screen is shown in Figure 3.22.
CodeWarrior displays Tip of the Day whenever it starts up. The user can get rid of Tip of the Day
by clicking on the Close button.
CodeWarrior uses project as the unit for managing the software development task. The
software development process under CodeWarrior involves four stages.
Stage 1
Project setup
124
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.22 ■ CodeWarrior startup screen with Tip of the Day
Stage 2
Source code entering
Stage 3
Code compiling or assembling
Stage 4
Code debugging
3.8.2 Project Setup
Project setup involves the following steps:
Step 1
Create a new project by pressing the File menu and selecting New. A popup dialog box will
appear to allow you to enter the project name as shown in Figure 3.23.
Step 2
Enter the project name and the project directory and then click on OK. A project wizard as
shown in Figure 3.24 will appear.
Step 3
Select the HCS12 device (select MC9S12DG256B for Dragon12-Plus demo board) and then
click on Next.
Step 4
Select the set of languages to be supported initially as shown in Figure 3.25. Choose
Assembly for this tutorial and then click on Next.
3.8 ■ Using CodeWarrior
125
Figure 3.23 ■ Screen for creating a new project under CodeWarrior IDE
Step 5
Select absolute or relocatable assembly as shown in Figure 3.26. Absolute assembly fixes
the memory location for the assembly program. It does not need linker to make memory
assignment. Choose absolute assembly for this tutorial and then click on Next.
Step 6
Choose the connection method to the target hardware. You can choose only one
method or multiple methods. Choosing multiple methods allows you to switch
from one method to another. The screen in Figure 3.27 selects three connection
methods. Click Finish to complete the project setup. The resultant screen is shown
in Figure 3.28.
3.8.3 Source Code Entering
During the project creation process, CodeWarrior also creates the required files for the project and puts them under different directory names (Sources, Prm, Libraries, Debugger Project
File, and Debugger Cmd Files). The user can display the file names under these directories by
clicking on the 1 character to their left as shown in Figure 3.29. Figure 3.30 shows the files created under these directories.
It would be helpful for the first-time user to browse through the files readme.txt and tips.txt.
A file can be opened by double-clicking on it using the left mouse button.
126
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.24 ■ CodeWarrior project wizard
To enter a program source file into the project, the user can modify the main.asm program
or enter his or her program with a different name and add it to the project. Using the second
approach, the user must remove the main.asm file from the project.
In this section, we use the following assembly program to illustrate the project build and
debug process:
ARCNT
sum
average
icnt
start
addloop
include
equ
org
ds.w
ds.w
ds.b
org
movw
movb
ldx
ldd
addd
std
dec
bne
“c:\miniide\hcs12.inc”
20
$1000
1
1
1
$1500
#0,sum
#ARCNT,icnt
#array
sum
2,x1
sum
icnt
addloop
; initialize loop count
; use X as a pointer to the array
; add array[i] to sum and move pointer
; “
; “
; decrement loop count
; not done yet?
127
3.8 ■ Using CodeWarrior
array
ldd
ldx
idiv
stx
bra
dc.w
dc.w
org
dc.w
end
sum
#ARCNT
average
$
11,12,13,14,15,16,17,18,19,20
21,22,23,24,25,26,27,28,29,30
$FFFE
start
; compute array average by dividing array count into
; array sum
;
“
; save the average
; stay here forever
; set up reset vector
Figure 3.25 ■ Select language to be supported under CodeWarrior Wizard
The user can open main.asm by double clicking on it and then selecting the whole contents
and replacing them with the previous program.
There are a few things that the user needs to know about when entering a program to be
run by CodeWarrior.
• The need to set up reset vector. The reset vector is the address of the first
instruction to be executed after the power is turned on or a reset. The reset vector
must be stored at $FFFE. The following instruction sequence sets up the reset
vector accordingly:
org
$FFFE
dc.w
start
; start is the label of the first instruction of the program
128
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.26 ■ Select absolute or a relocatable assembly
• The need to set up system clock. The HCS12 includes an on-chip phase-lockedloop (PLL) circuit that allows the user to use a low-frequency crystal oscillator
(e.g., 4 MHz or 8 MHz) to generate a higher-frequency E-clock signal. To achieve
this, the user will need to write and call a subroutine similar to the following to
multiply the frequency (this has been done in the D-Bug12 monitor):
; ********************************************************************************
; The following subroutine enables PLL and uses an external oscillator to generate the system
; clock. Set the system clock (E-clock) to 24 MHz from a 4-MHz external crystal oscillator.
; ********************************************************************************
SetClk
;
movb
movb
movb
movb
brclr
bset
rts
#0,REFDV
#$05,SYNR
#$02,SYNR
#$60,PLLCTL
CRGFLG,LOCK,*
CLKSEL,PLLSEL
; set SYSCLK to 24 MHz from a 4 MHz oscillator
;“
; use this value if external oscillator is 8 MHz
; turn on PLL, select high-bandwidth filter
; wait until PLL lock into target frequency
; use PLL to derive system clock
The functioning and programming of the HCS12 on-chip phase-locked-loop are discussed
in Chapter 6. To call this subroutine to set up the system clock, add the following instruction
to your program:
jsr
SetClk
3.8 ■ Using CodeWarrior
Figure 3.27 ■ Screen for choosing the connection method to the target
Figure 3.28 ■ CodeWarrior screen after project setup
129
130
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.29 ■ Display files under each directory by clicking the ‘+’ character to the left of the
directory name
Figure 3.30 ■ Files created by CodeWarrior Wizard during the project-creating phase
To delete a file from the project, press the right mouse button on the file to be removed
and select Remove from the popup menu as shown in Figure 3.31. A file can be added into the
project by pressing the right mouse button on Sources and selecting the file name from the directory that contains the file.
3.8 ■ Using CodeWarrior
131
Figure 3.31 ■ Remove a file from the project
3.8.4 Project Build
A project can be built by pressing the Project menu and selecting Make or simply by pressing the F7 function key on the keyboard. This is shown in Figure 3.32. If there is no syntax error
in the program, CodeWarrior won’t display anything. However, if there are some errors in the
program, CodeWarrior will display the error messages. One example is shown in Figure 3.33.
The user can double-click on the message, and CodeWarrior will guide the user to the error location to fix it. There is no syntax error in this example program.
Figure 3.32 ■ Action to build a project
132
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.33 ■ CodeWarrior display error messages when there are errors in the program
3.8.5 Program Debugging
This phase is entered by selecting Debug from the Project menu or pressing the F5 function
key of the keyboard. The initial response of CodeWarrior varies with the connection methods.
INITIAL RESPONSE UNDER SERIAL MONITOR
Suppose the user has a Dragon12-Plus demo board (or other demo board) programmed with
the serial monitor; by pressing the function key F5, the debugger screen will look like that in
Figure 3.34. The user needs to select the COM port before she or he can proceed. After selecting
the correct COM port and clicking on OK, the debugger screen will change to that shown in Figure 3.35. This screen indicates that the debugger hasn’t been able to successfully reset the MCU
yet. Press the reset button indicated in Figure 3.35, and the screen should change to that shown in
Figure 3.37.
I N I T I A L R E S P O N S E U N D E R T H E P & E M U LT I L I N K
Suppose the user uses a P&E Multilink or a CyclonePro debugger to work with the
Dragon12-Plus demo board; the initial debugger screen after pressing the F5 function will look
like that in Figure 3.36. This screen asks the user whether it is OK to erase the flash memory
and download the program onto the memory. Click on OK and the screen will change to that
shown in Figure 3.37.
D E B U G G E R S C R E E N L AY O U T
The CodeWarrior screen contains seven windows.
• Source window. This window holds the source code of the project.
• Data window. This window displays all the program variables (sum, average,
icnt) declared by assembler directives. The values of each program variable will be
updated after each breakpoint is reached.
• Command window. The user can enter commands in this window to ask the
debugger to execute.
• Assembly window. This window contains the program as shown in the Source
window but it also displays the line number of each instruction.
3.8 ■ Using CodeWarrior
133
Figure 3.34 ■ CodeWarrior debugger initial popup dialog
Figure 3.35 ■ CodeWarrior debugger startup screen and commonly used debug command buttons
134
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.36 ■ Initial CodeWarrior debugger response screen under P&E Multilink debugger
• Register window. This window displays the value of each CPU register. The value
of each CPU register will be updated after the MCU stops at each breakpoint.
• Procedure window. This window displays all the procedures defined in the program.
• Memory window. This window displays the contents of every memory location
in the hardware. When the program contains one or multiple arrays, the user will
need to use this window to examine the values of arrays.
EXAMPLE OF A CODEWARRIOR DEBUG SESSION
The user can resize the seven windows within the CodeWarrior debugger screen or even
close some of them. We will get rid of the command window and resize the source window to
display the whole program. A debug session may be similar to the following description:
Single step a few instructions to find out whether the program variables are initialized
correctly.
• Click on the Step over button once. The value of sum is changed to 0 in the data
window. This is correct.
• Click on the Step over button one more time. The value of icnt is changed to 20.
This is also correct.
• Click on the Step over button one more time. This time the value of the index register
is changed to 1529 (hex). This is also correct if we examine the assembly window.
3.8 ■ Using CodeWarrior
135
Figure 3.37 ■ The CodeWarrior debugger screen after downloading program onto the memory
After executing the first three instructions, the debugger window is changed as shown in
Figure 3.38.
Use the Run to Cursor command to find out the program execution result. For example, we
can move the mouse cursor on the bne addloop instruction and press the right mouse button. This
will bring up a popup window (shown in Figure 3.39) that displays all the debug commands that
the user can apply. Select the Run to Cursor command by clicking on it. After this, the value of
sum is changed to 11 whereas the value of icnt is changed to 19. Both are correct. Now move the
mouse cursor to the next instruction (ldd sum) and then press the right mouse button and select
the Run to Cursor command. This time, the value of sum is changed to 410 whereas the value of
icnt is changed to 0. Both are also correct. Now move the mouse cursor to the bra $ instruction
and then execute the Run to Cursor command again. This time, the value of the variable average
is changed to 20 which is the quotient of 410 divided by 20 (the fraction is truncated because the
CPU is performing integer divide). This is also correct. We have verified that the program is correct
(shown in Figure 3.40).
O T H E R C O M M O N LY U S E D D E B U G G E R C O M M A N D S
Set Program Counter
Sometimes the user may need to execute an instruction sequence starting from a certain
instruction. This command allows the user to do just that. The user can set the program
counter to the address of any instruction by taking the following actions:
1. Place the mouse cursor at the instruction at which the program counter is to be set in
the Source window.
2. Press the right mouse button to bring up the debugger command menu.
3. Select the Set Program Counter command.
136
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.38 ■ CodeWarrior debugger screen after executing the first three instructions
The instruction to which the program counter is pointing at is darkened as shown in
Figure 3.41. Executing this command will also bring the instruction pointed to by the program
counter to the first line in the assembly window.
Set Breakpoint
To set a breakpoint at an instruction in the Source window, take the following actions:
1. Place the mouse cursor at the instruction in the source window.
2. Press the right mouse button to bring up the debugger command menu.
3. Select the Set Breakpoint command.
After these, a right-pointing red arrow will appear to the left of the instruction at the breakpoint as shown in Figure 3.41. A breakpoint can be disabled. A disabled breakpoint will not
cause the program to stop. A disabled breakpoint can be re-enabled.
Show Location
Sometimes the user wants to find out the address of an instruction or a memory location
that contains data; she or he can use this command to move that location to the first line in
3.9 ■ BDM Serial Interface
137
Figure 3.39 ■ CodeWarrior debugger debugging commands
the assembly window. To show the location of a line in the source window, take the following
actions:
1. Place the mouse cursor at the instruction in the source window.
2. Press the right mouse button to bring up the debugger command menu.
3. Select the Show Location command.
3.9 BDM Serial Interface
The BDM module communicates with external devices via the BKGD pin. During reset,
this pin is a mode-select input, which selects between the normal and special modes of operation. After reset, this pin becomes the dedicated serial interface pin for the BDM.
The timing of the BDM serial interface is controlled by the target clock. The target clock is
programmable and can be either the bus clock or the oscillator clock. The BDM serial interface
138
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
Figure 3.40 ■ CodeWarrior debugger screen after the whole program executed
Figure 3.41 ■ CodeWarrior debugger source window after setting program counter
and a breakpoint
139
3.10 ■ The BDM-Based Debugger
uses a clocking scheme in which the external host generates a falling edge on the BKGD pin to
indicate the start of each bit time. This falling edge is sent for every bit whether data is transmitted or received. Data is transferred most significant bit first at 16 target clock cycles per bit. The
interface times out if 512 clock cycles occur between falling edges from the development host.
The BDM module implements a set of hardware and software commands that allows the
external debug adapter to access CPU registers and memory locations to facilitate software debug
activities. For hardware Read commands, the development host must wait for 150 clock cycles after
sending the address before attempting to obtain the read data. For hardware Write commands, the
host must wait for 150 clock cycles after sending data to be written before attempting to send a new
command. For firmware Read commands, the external host should wait for 44 clock cycles before
attempting to obtain the read data. For firmware Write commands, the external host must wait for
32 clock cycles after sending data to be written before attempting to send a new command.
The detailed timing of the BDM serial interface and the BDM commands are beyond the
scope of this text. Interested readers should refer to the Background Debug Module Guide published by Freescale.
3.10 The BDM-Based Debugger
The block diagram of a BDM-based development system is shown in Figure 3.42. The BDM pod
is connected to the target board via a BDM cable. To promote the portability of tools that use BDM
mode, Freescale defines a 6-pin connector to be installed on the target board, shown in Figure 3.43.
Host PC
Target Board
Cable
BDM pod
BDM cable
6-pin BDM connector
Figure 3.42 ■ BDM development system
BKGD
1
2
GND
NC
3
4
RESET
VFP
5
6
VDD
Figure 3.43 ■ BDM tool connector
140
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
The BDM pod can be connected to the PC or workstation via the serial, parallel, or USB
port. There must be a software program running on the PC or workstation to send commands
to the POD to perform the appropriate debug operations. This PC-based software should have a
good user interface and allow the user to perform at least the following operations:
• Do basic configuration
• Download the program to the target board for execution
• Set breakpoints
• Display and change the values of the CPU registers, I/O registers, and memory
locations
• Display the source code
• Set up a watch list to display the values of selected program variables
• Trace the program execution to the breakpoint
The PC sends the intended operation of the user to the POD and the POD sends the corresponding BDM commands to the target board to perform the desired operations. A source-level
debugger using this approach need not be expensive. A BDM-based source-level debugger can be
integrated with an IDE, which allows the user to perform program editing, assembling or compiling, and source-level debugging in the same environment. CodeWarrior supports the BDM-based
debug adapters from several vendors. The BDM pod can also be part of the target board.
3.11 Summary
The HCS12 microcontroller was developed as an upgrade for the 8-bit 68HC11 microcontroller family. All members implement the same architecture; that is, they have the same instruction set and addressing modes, but differ in the number of peripheral functions that they
implement.
All HCS12 members use the same design in each peripheral function; this simplifies the
migration from one device to another. All of the HCS12 members implement a 10-bit A/D converter. The HCS12 microcontroller implements a complicated timer system.
• The input-capture (IC) function latches the arrival time of a signal edge. This
capability allows the user to measure the frequency, the period, and the duty cycle
of an unknown signal.
• The output compare (OC) function allows the user to make a copy of the main
timer, add a delay to this copy, and store the sum to an output compare register.
This capability can be used to create a delay, generate a digital waveform, trigger an
action on a signal pin, and so on.
• The pulse accumulation function allows one to count the events that arrived
within an interval.
• The pulse-width modulation (PWM) module is used to generate a digital waveform
of a certain frequency with a duty cycle ranging from 0 to 100 percent. This
capability is useful in DC motor control.
The HCS12 microcontroller also provides a wide variety of serial interface functions to appeal to different applications.
3.11 ■ Summary
141
• The serial communication interface (SCI) function supports the EIA232
standard. A microcontroller demo board would use this interface to communicate
with a PC.
• The serial peripheral interface (SPI) function is a synchronous interface that
requires a clock signal to synchronize the data transfer between two devices. This
interface is mainly used to interface with peripheral chips such as shift registers,
seven-segment displays and LCD drivers, A/D and D/A converters, SRAM and
EEPROM, phase-locked-loop chips, and so on. All of these devices must have the
SPI interface.
• The inter-integrated circuit (I2C) is a serial interface standard proposed by Phillips.
This interface standard allows microcontrollers and peripheral devices to exchange
data.
• The byte data link communication (BDLC) module was proposed for low-speed
data communication in automotive applications. It provides access to an external
serial communication multiplexed bus that operates according to the SAE J1850
protocol.
• The controller area network (CAN) was proposed to be used mainly as a vehicle
serial data bus to provide reliable operation in the EMI environment and achieve
the high bandwidth required in that environment.
The MC9S12DG256-based demo board Dragon12-Plus from Wytec is recommended for
learning the HCS12 microcontrollers.
Software development tools include the text editor, the terminal program, the cross assembler, the cross compiler, the simulator, the source-level debugger, and the integrated development environment (IDE). A sophisticated IDE should contain a text editor, a terminal program,
a cross compiler, a cross assembler, and a source-level debugger. It allows the user to perform all
the development work without leaving any program.
In this text, we use the freeware from Mgtek and CodeWarrior from Freescale to enter and
test all the assembly programs. Tutorials on how to use MiniIDE and CodeWarrior IDE are provided. The debugging activities on a demo board are facilitated by the command set provided by
the D-Bug12 monitor.
The D-Bug12 monitor has four modes.
• EVB mode. Most users use this mode to perform development and evaluation
work.
• POD mode. This mode allows one to use the demo board to debug another HCS12
target board.
• Jump-to-EEPROM mode. This mode allows the user to run the application
programmed into the on-chip EEPROM after power-up or reset.
• Bootloader mode. This mode allows the user to update the D-Bug12 monitor or
program the application code into the flash memory out of reset or after being
powered on.
The only freeware source-level debugger is the limited version of CodeWarrior from
Freescale. CodeWarrior requires a demo board to be programmed with the 2-kB serial monitor
from Freescale or connected to a BDM-based debug adapter.
142
Chapter 3 ■ Hardware and Software Development Tools for the HCS12
3.12 Lab Exercises and Assignments
L3.1 Turn on the PC and start the MiniIDE (or AsmIDE) program to connect to a demo board
with the D-Bug12 monitor. Perform the following operations:
a. Enter a command to set the contents of the memory locations from $1000 to
$10FF to 0.
b. Display the contents of the memory locations from $1000 to $10FF.
c. Set the contents of the memory locations $1000,$1003 to 1, 2, 3, and 4, respectively.
d. Verify that the contents of memory locations $1000,$1003 have been set correctly.
L3.2 Enter monitor commands to display the breakpoints. Set new breakpoints at memory
locations $1520 and $1550. Delete breakpoints at $1550, and redisplay the breakpoints.
L3.3 Enter commands to place $10 and $0 in accumulators A and B.
L3.4 Use appropriate D-Bug12 commands to perform the following operations:
a. Set the contents of the memory location at $1000 to 3.
b. Set the contents of the memory location at $1001 to 4, and redisplay the location
$1000.
c. Set the contents of the memory location at $1002 to 5, and redisplay the same
location.
d. Set the contents of the memory location at $1003 to 6, and return to the D-Bug12
command prompt.
L3.5 Invoke the one-line assembler to enter the following instructions to the demo board: starting from address $1500, trace through the program, and examine the contents of the memory
locations at $1000 and $1001.
ldd
std
ldab
incb
ldx
abx
stx
cmpb
bne
swi
#$0000
$1000
#$00
$1000
$1000
#$14
$1508
L3.6 Use the text editor of MiniIDE (or AsmIDE) to enter the following assembly program as a
file with the filename lab03_6.asm:
org
sum rmb
arcnt rmb
org
ldaa
staa
ldx
clr
$1000
1
1
$1500
#20
arcnt
#array
sum
3.12 ■ Lab Exercises and Assignments
again
next
array
ldaa
lsra
bcs
ldaa
adda
staa
inx
dec
bne
swi
org
fcb
end
143
0,X
next
sum
0,X
sum
arcnt
again
$2000
1,3,5,7,2,4,6,8,9,11,13,10,12,14,15,17,19,16,18,20
After entering the program, perform the following operations:
a. Assemble the program.
b. Download the S-record file (filename lab03_6.s19) to the demo board.
c. Display the contents of memory locations from $1000 to $100F and $2000 to $202F.
d. Execute the program.
e. Display the contents of the memory location at $1000.
This program adds all even numbers in the given array and stores the sum at $1000.
L3.7 Write a program to count the number of elements in an array that are divisible by 8. The
array has 30 8-bit elements and is stored immediately after your program. Use the Repeat S
until C looping construct. Leave the result at $1000. If you have a demo board programmed
with the serial monitor, then use CodeWarrior to enter and debug the your program.
L3.8 Write a program to swap the last element of an array with the first element, second element with the second-to-last element, and so on. The array has 30 8-bit elements.
L3.9 Write a program to find the greatest common divisor (gcd) of two 16-bit numbers stored at
$1000,$1001 and $1002,$1003, and store the result at $1010,$1011. When testing the program,
enter two 16-bit numbers manually before running the program. The most efficient method for
finding the greatest common divisor is the Euclidean method. This algorithm can be found on the
website at www.fact-index.com/e/eu/euclidean_algorithm.html.
This page intentionally left blank
4
Advanced Assembly
Programming
4.1 Objectives
After completing this chapter, you should
be able to
• Explain the reason why the subroutine
is useful
• Explain the issues involved in a subroutine call
• Write subroutines to perform arithmetic and string processing operations
• Access parameters stored in the stack
and manipulate stack data structure
• Make subroutine calls
• Perform terminal I/O by calling
D-Bug12 library functions
• Configure an I/O pin for input or for
output
• Use HCS12 I/O ports to interface
with simple I/O devices such as
LEDs, seven-segment displays, and
DIP switches
• Use an HCS12 I/O pin to generate
• Perform binary-to-ASCII string and
waveforms and make sounds
ASCII string-to-binary conversion
• Write subroutines to create time
• Write subroutines to perform certain
functions
delays that are a multiple of certain
base values
146
Chapter 4 ■ Advanced Assembly Programming
4.2 Introduction
It is common that the same sequence of instructions need to be executed in several places of
the program. The user may define macros as a solution to this issue. However, using the macro
call will duplicate the same sequence of instructions in places where the macro is invoked
and cause the program size to be bloated. Fortunately, all microprocessors and microcontrollers
provide the subroutine mechanism in which the program can invoke the same sequence of
instructions in many places of the program without duplicating it.
A subroutine is often written to perform operations on the basis of inputs provided by the
caller. The inputs provided by the caller are called incoming parameters. The caller of the subroutine often expects the subroutine to return certain results to it.
A subroutine call instruction causes the program control flow to change. When calling a
subroutine, the processor loads the starting address of the subroutine into the program counter
and then the CPU starts to execute the subroutine instructions. When the processor finishes
execution of the subroutine, it should return to the instruction immediately after the instruction that makes the subroutine call. This is achieved by executing a return instruction.
The subroutine call and return instructions work together to make the subroutine mechanism work. The subroutine call instruction saves the return address in the stack data structure
while at the same time changing the program control flow to the start of the subroutine. The return instruction fetches the return address from the stack and places it in the program counter
and hence returns the program control back to the instruction immediately after the subroutine
call instruction.
The subroutine mechanism has great impact on the program development methodology. It is
mentioned in Chapter 2 that the most popular software development methodology is top-down
design with hierarchical refinement. The subroutine mechanism makes this approach possible.
Reusable macros and subroutines should be made into files and included in programs that
need them. This approach can increase programmers’ productivity. Subroutines are especially
convenient for this approach. One of the objectives of this text is to promote software reuse and
writing reusable software.
4.3 Stack
A stack is a data structure from which elements can be accessed from only its top. The
processor can add a new element to the stack by performing a push operation and remove an
element by performing a pull (or pop) operation. Physically, a stack can grow from a high address
toward lower addresses or from a low address toward higher addresses. Depending on the processor, the stack pointer can point to the top element of the stack or to the byte immediately
above the top element of the stack. As shown in Figure 4.1, the HCS12 stack grows from a high
address toward lower addresses and has a 16-bit stack pointer (SP) that points to the top byte
of the stack. The memory space available for use by the stack is limited in a computer system.
There is always a danger of stack overflow and stack underflow. Stack overflow is a situation
in which the processor pushes data into the stack too many times so that the SP points to a
location outside the area allocated to the stack. Stack underflow is a situation in which the
processor pulls data from the stack too many times so that the SP points to an area below the
stack bottom. We must check the stack overflow and underflow in order to make sure that
the program won’t crash. Of course, this checking adds overhead to the stack access.
The HCS12 provides instructions for pushing and pulling all CPU registers except the
stack pointer. A push instruction writes data from the source to the stack after decrementing
147
4.3 ■ Stack
Low address
Top element
SP
High address Bottom element
Figure 4.1 ■ Diagram of the HCS12 stack
the stack pointer. There are six push instructions: psha, pshb, pshc, pshd, pshx, and pshy.
A pull instruction loads data from the top of the stack to a register and then increments the
stack pointer. There are also six pull instructions: pula, pulb, pulc, puld, pulx, and puly. These
instructions have equivalent store and load instructions combined with predecrement and
postincrement index addressing modes, as shown in Table 4.1.
Mnemonic
Function
Equivalent Instruction
psha
pshb
pshc
pshd
pshx
pshy
pula
pulb
pulc
puld
pulx
puly
push A into the stack
push B into the stack
push CCR into the stack
push D into stack
push X into the stack
push Y into the stack
pull A from the stack
pull B from the stack
pull CCR from the stack
pull D from the stack
pull X from the stack
pull Y from the stack
staa 1, 2SP
stab 1, 2SP
none
std 2, 2SP
stx 2, 2SP
sty 2, 2SP
ldaa 1, SP1
ldab 1, SP1
none
ldd 2, SP1
ldx 2, SP1
ldy 2, SP1
Table 4.1 ■ HCS12 push and pull instructions and their
equivalent load and store instructions
Sometimes the programmer may need to push a value into the stack directly. He or she
may use one of the following instructions to do that:
movb
movw
#val1, 1, 2SP
#val2, 2, 2SP
; push the 8-bit val1 into the stack
; push the 16-bit val2 into the stack
Example 4.1
▼
Assuming that we have the following instruction sequence to be executed by the HCS12,
what would be the contents of the stack after the execution of these instructions?
lds
ldaa
psha
#$1500
#$20
148
Chapter 4 ■ Advanced Assembly Programming
ldab
pshb
ldx
pshx
#40
#0
Solution: The first instruction initializes the stack pointer to $1500. The second and the third
instructions together push the 8-bit value $20 into the stack. The fourth and fifth instructions push the 8-bit value 40 (hex $28) into the stack. The sixth and seventh instructions
push the value 0 to the top 2 bytes of the stack. The contents of the HCS12 stack are shown
in Figure 4.2.
SP (= $14FC)
0
0
$28
$20
Figure 4.2 ■ The contents of the HCS12 stack
▲
One of the main uses of the stack is saving the return address for a subroutine call. Before
the stack can be used, we need to set up the stack pointer. Since the stack can only be implemented in the RAM, we must make sure not to violate this requirement. Because the stack is a
last-in-first-out data structure, it can be used to reverse a data structure.
4.4 What Is a Subroutine?
A subroutine is a sequence of instructions that can be called from many different places in a program. A key issue in a subroutine call is to make sure that the program execution returns to the point
immediately after the subroutine call (this address is called the return address) when the subroutine
completes its execution. This is normally achieved by saving and retrieving the return address in and
from the stack. The program flow change involved in a subroutine call is illustrated in Figure 4.3.
Caller
<call> subroutine_x
subroutine_x
<return>
Figure 4.3 ■ Program flow during a subroutine call
149
4.4 ■ What Is a Subroutine?
The HCS12 has dedicated instructions for making the subroutine call. The subroutine call
instruction saves the return address, which is the address of the instruction immediately following the subroutine call instruction, in the system stack. When completing the computation
task, the subroutine will return to the instruction immediately following the instruction that
makes the subroutine call. This is achieved by executing a return instruction, which will retrieve the return address from the stack and transfer the CPU control to it.
The HCS12 provides three instructions (bsr, jsr, and call) for making subroutine calls and
two instructions for returning from a subroutine. The syntax and operations performed by these
instructions are as follows:
• bsr <opr>: bsr stands for branch subroutine. This instruction requires the user to
use the relative addressing mode to specify the subroutine to be called. When
this instruction is executed, the stack pointer is decremented by 2 and the return
address is saved in the stack; then the offset in the instruction is added to the
current PC value and instruction execution is continued from there. In assembly
language, the relative address is specified by using a label, and the assembler will
figure out the relative offset and place it in the program memory. For example, the
following instruction calls the subroutine bubble:
bsr
bubble
• jsr <opr>: jsr stands for jump subroutine. The user can use direct, extended,
indexed, and indexed indirect addressing modes to specify the subroutine to be
called. The subroutine can be located anywhere within 64 kB. As with the bsr
instruction, the HCS12 first saves the return address in the stack and then jumps
to execute the subroutine. Examples of the jsr instruction are as follows:
jsr
jsr
jsr
$ff
sq_root
0,x
; call the subroutine located at $ff
; call the subroutine sq_root
; call a subroutine pointed to by index register X
• call <opr>: This instruction is designed to work with expanded memory (larger than
64 kB) supported by some HCS12 members. Members with expanded memory treat
the 16-kB memory space from $8000 to $BFFF as a program memory window. An
8-bit program page register (PPAGE) is added to select one of the 256 16-kB program
memory pages to be accessed. To support subroutine calls in expanded memory, the
call instruction pushes the current value of the PPAGE register along with the return
address onto the stack and then transfers program control to the subroutine (3 bytes
are pushed into the stack). The <opr> field in the call instruction specifies the page
number and the starting address of the subroutine within that page. The new page
number will be loaded into the PPAGE register when the call instruction is executed.
Extended, indexed, and indexed indirect addressing modes can be used to specify the
subroutine address within a page. Writing assembly programs to be run in expanded
memory requires an assembler that supports this feature.
• rts: rts stands for return from subroutine. This instruction pops the 16-bit value
from the stack onto the program counter and increments the stack pointer by 2.
Program execution continues at the address restored from the stack. This
instruction is used with the jsr and bsr instructions.
• rtc: rtc stands for return from call. This instruction terminates subroutines in
expanded memory invoked by the call instruction. The program page register and
the return address are restored from the stack; program execution continues at the
restored address. For code compatibility, call and rtc are also executed correctly by
150
Chapter 4 ■ Advanced Assembly Programming
the HCS12 members that do not have expanded memory capability, that is, with
memory capacity no larger than 64 kB.
Example 4.2
▼
Write a subroutine to convert an unsigned 16-bit binary number into an ASCII string that
represents the decimal value of the original binary number. The resultant string must be terminated by a NULL character (the ASCII code of a NULL character is 0). For example, the binary
value 0101,1011,1010,00002 (or 2345610) will be converted to $32 $33 $34 $35 $36 $00. The
16-bit number to be converted is held in D, and the pointer (address) to the buffer to hold the
resultant string is stored in Y.
Solution: The individual decimal digit can be separated by repeatedly performing divide by 10
to the given number and adding $30 to each remainder. Let ptr, quo, and rem represent the
pointer to the buffer to hold the resultant string, the quotient after the divide operation, and the
remainder of the divide operation, respectively. The repeated divide-by-10 operation will separate the least significant digit first and the more significant digits later; we cannot save them
directly in the buffer pointed to by ptr. The solution is to push these digits into the stack in the
order that they are separated and then pop them out and save them in the buffer. The following
algorithm implements this idea:
Step 1
Push a 0 into the stack; quo ← number to be converted; the zero (NULL character) pushed
into the stack is used to tell the program to stop popping from the stack.
Step 2
rem ← quo % 10; quo ← quo 4 10.
Step 3
Push the sum of $30 and rem into the stack.
Step 4
If (quo 55 0) go to step 6.
Step 5
Go to step 2.
Step 6
Pull a byte from the stack. Save the byte in the memory location pointed to by ptr.
Step 7
ptr ← ptr 1 1;
Step 8
If the byte pulled out in step 6 is NULL, then stop; else go to Step 6.
The subroutine that implements this algorithm is as follows:
bin2dec
divloop
pshx
movb
ldx
idiv
addb
pshb
#0, 1, 2SP
#10
#$30
; save X in stack
; push NULL character into stack
; divide the number by 10
; “
; convert the remainder to ASCII code
; push it into the stack
▲
4.5 ■ Issues Related to Subroutine Calls
revloop
done
xgdx
cpd
beq
bra
pula
staa
cmpa
beq
bra
pulx
rts
#0
revloop
divloop
1, y+
#0
done
revloop
151
; swap quotient to D
; if quotient is 0, then prepare to pull out
; the decimal digit characters
; quotient is not 0, continue to perform divide-by-10
; save ASCII string in the buffer
; reach the NULL pushed previously?
; if yes, then done
; continue to pup
; restore the index register X
Before calling this subroutine, the caller must place the number to be converted in D and
place the starting address of the buffer to hold the resultant string in Y. The following instruction sequence is needed to call the above subroutine to convert the value 23,456 to an ASCII
string:
ldd
ldy
jsr
#23456
#buffer
bin2dec
; use Y as the pointer to the buffer that holds the string
This subroutine can be modified to convert a signed 16-bit number into the ASCII string
that represents its decimal value. A negative value would have a minus sign (2) in the front after the conversion. The modification to the subroutine to convert signed numbers is minor and
hence will be left as an exercise problem.
This example points out several issues involved in the subroutine call.
• Passing parameters
• Returning results
• Saving registers used in the subroutine
A more complicated subroutine may have variables that are local to the subroutine but
invisible to the caller. These variables are referred to as local variables. Examples of local variables include loop indices and temporal results. These variables do not exist if the subroutine is
not entered. The next section deals with all of these issues.
4.5 Issues Related to Subroutine Calls
The program unit that makes the subroutine call is referred to as a caller, and the subroutine called by other program units is referred to as a callee. As was pointed out in the previous section, there are four major issues in a subroutine call. These issues are explored in the
following subsections:
4.5.1 Parameter Passing
The caller usually wants the subroutine to perform a computation using the parameters
passed to it. The caller may use the following methods to pass parameters to the subroutine:
• Use registers. In this method, parameters are placed in CPU registers before the
subroutine is called. This method is very convenient when there are only a few
parameters to be passed.
152
Chapter 4 ■ Advanced Assembly Programming
• Use the stack. In this method, parameters are pushed into the stack before the
subroutine is called. The stack must be cleaned up after the computation is
completed. This can be done by either the caller or the callee.
• Use the global memory. Global memory is accessible to both the caller and the
callee. As long as the caller places parameters in global memory before it calls the
subroutine, the callee will be able to access them.
4.5.2 Result Returning
The result of a computation performed by the subroutine can be returned to the caller using three methods:
• Use registers. This method is most convenient when there are only a few bytes to
be returned to the caller.
• Use the stack. The caller creates a hole of a certain size in the stack before making
the subroutine call. The callee places the computation result in the hole before
returning to the caller.
• Use global memory. The callee simply places the value in the global memory and
the caller will be able to access them.
4.5.3 Allocation of Local Variables
In addition to the parameters passed to it, a subroutine may need memory locations to hold
temporary variables and results. Temporary variables are called local variables because they
only exist when the subroutine is entered. Local variables are always allocated in the stack so
that they are not accessible to any other program units.
Although there are several methods for allocating local variables, the most efficient one
is using the leas instruction. This instruction loads the stack pointer with an effective address
specified by the program. The effective address can be any indexed addressing mode except an
indirect address. For example, to allocate 10 bytes, we can use the indexed addressing mode
with SP as the base register and 210 as the offset:
leas
210, sp
; allocate 10 bytes in the stack
This instruction simply subtracts 10 from the SP and puts the difference back to the SP.
The general format for allocating space to local variables is
leas
2n,sp
; allocate n bytes in the stack
where n is the number of bytes to be allocated.
Before the subroutine returns to the caller, the space allocated to local variables must be
deallocated. Deallocation is the reverse of allocation and can be achieved by the following
instruction:
leas
n,sp
; deallocate n bytes from the stack
4.5.4 Saving the CPU Registers
A subroutine may use CPU registers to hold local variables to perform certain operations.
Sometimes, the subroutine needs to perform some operations that involve certain CPU registers. For example, the emul and emuls instructions use register D and Y whereas the ediv and the
edivs instructions use registers D, X, and Y. However, these CPU registers may also be used by
153
4.6 ■ The Stack Frame
the caller of the subroutine. If the values of these CPU registers were not preserved during a
subroutine call, the execution result of the caller cannot be correct. Either the caller or the callee may be responsible for saving these registers. However, it is more efficient for the subroutine to do the saving because the same subroutine may be called by many callers. CPU registers
are usually saved at the entrance of the subroutine before they are modified.
Only those registers that are not used for passing incoming parameters and returning results need to be saved. The subroutine should restore the values of the saved registers immediately before returning to the caller. The saving and restoring must be done in the reverse order.
For example, if the subroutine saves D, X, and Y in the following order:
pshd
pshx
pshy
then it should have the following instruction sequence to restore them before returning to the
caller:
puly
pulx
puld
4.6 The Stack Frame
The stack is used heavily during a subroutine call: The caller may pass parameters to the
callee, and the callee may need to save registers and allocate local variables in the stack. The
region in the stack that holds incoming parameters, return addresses, saved registers, and local
variables is referred to as the stack frame. Some microprocessors have a dedicated register for
managing the stack frame; the register is referred to as the frame pointer. The HCS12, however,
does not have a register dedicated to the function of the frame pointer. Since the stack frame
is created during a subroutine call, it is also called the activation record of the subroutine. The
stack frame exists as long as the subroutine is not exited. The structure of a stack frame is
shown in Figure 4.4.
The reason for having a dedicated frame pointer is that the stack pointer may change during the lifetime of a subroutine. Once the stack pointer changes value, there can be problems in
accessing the variables stored in the stack frame. The frame pointer is added to point to a fixed
Local variables
SP
Saved registers
Return address
Incoming parameters
Figure 4.4 ■ Structure of the HCS12 stack frame
154
Chapter 4 ■ Advanced Assembly Programming
location in the stack and can avoid this problem. Since the HCS12 does not have a dedicated
frame pointer, we will not use the term frame pointer in the following discussion.
Example 4.3
▼
Draw the stack frame for the following program segment after the last leas 210,sp instruction is executed:
sub_xyz
ldd
pshd
ldx
pshx
jsr
…
pshd
pshx
psy
leas
#$1234
#$4000
sub_xyz
210, sp
…
Solution: The caller pushes two 16-bit words into the stack. The subroutine sub_xyz saves three
16-bit registers in the stack and allocates 10 bytes in the stack. The resultant stack frame is
shown in Figure 4.5.
▲
4.6.1 Subroutines with Local Variables in Stack
When a subroutine has very few local variables, the subroutine can use CPU registers to
hold them (need to save these registers in the stack). However, when there are not enough
CPU registers to hold local registers, the subroutine will need to assign local variables to
the stack.
SP
10 bytes for local
variables
[Y]
$4000
$1234
Return address
$4000
$1234
Figure 4.5 ■ Stack frame of Example 4.5
155
4.6 ■ The Stack Frame
Example 4.4
▼
Write a subroutine that can convert a BCD ASCII string to a binary number and leave the result
in double accumulator D. The ASCII string represents a number in the range of 2215,215 21. A
pointer to the string is passed to this subroutine in X.
Solution: The subroutine needs to perform error checking. If there is any illegal character, the
error flag will be set to 1. The subroutine will return the error flag to the caller using the CY
flag in the CCR register. Let in_ptr, sign, error, and number represent the pointer to the BCD
string, sign flag, error flag, and the number represented by the BCD string. The algorithm of the
subroutine is as follows:
Step 1
sign ← 0; error ← 0; number ← 0;
Step 2
If the character pointed to by in_ptr is the minus sign, then
sign ← 1; in_ptr ← in_ptr 1 1
Step 3
If the character pointed to by in_ptr is the NULL character,
then go to step 4.
else if the character is not a BCD digit (i.e., m[in_ptr] > $39 or m[in_ptr] < $30), then:
error ← 1;
go to step 4;
else:
number ← number × 10 + m[in_ptr] 2 $30;
in_ptr ← in_ptr 1 1;
go to step 3;
Step 4
If sign 5 1 and error 5 0, then
number ← two’s complement of number;
else
stop;
In this example, the subroutine allocates local variables in the stack as shown in
Figure 4.6.
1
2
4
5
dummy
pdVal
val
sign
err
Y
Ret_addr
SP
Present digit value
The binary value of the BCD string
Sign of the number
Error flag
Figure 4.6 ■ Stack frame of Example 4.4
156
Chapter 4 ■ Advanced Assembly Programming
The conversion subroutine and its test program are as follows:
minus
dummy
pdVal
val
sign
err
locVar
StrBuf
result
start
#include “c:\miniide\hcs12.inc”
equ
$2D
; ASCII code of minus sign
equ
0
; offset of dummy to hold a 0
equ
1
; offset of present digit value from SP in stack
equ
2
; offset of the 2-byte binary value of the BCD string
; from SP in stack
equ
4
; offset of the sign from SP in stack
equ
5
; offset of error flag from SP in stack
equ
6
org
$1000
dc.b
“29889”,0 ; input ASCII to be converted
ds.w
1
org
$1500
lds
#$1500
ldx
#StrBuf
jsr
bcd2bin
std
result
swi
; *********************************************************************************
; This subroutine converts a BCD string into its equivalent binary value and also uses a CY
; flag to indicate error condition. The CY flag is set to 1 to indicate error.
; *********************************************************************************
bcd2bin pshy
leas
2locVar,SP
; allocate 4 bytes for local variables
movw
#0,val,SP
; initialize accumulated value to 0
movb
#0,dummy,SP
movb
#0,sign,SP
; initialize sign to positive
movb
#0,err,SP
; clear error flag initially
ldaa
0,x
; check the first character
cmpa
#minus
; is the first character a minus sign?
bne
GEZero
; branch if not minus
movb
#1,sign,SP
; set the sign to 1
inx
; move the pointer
GEZero ldab
1,x1
; is the current character a NULL character?
lbeq
done
; yes, we reach the end of the string
cmpb
#$30
; is the character not between 0 to 9?
blo
inErr
; ''
cmpb
#$39
; ''
bhi
inErr
; ''
subb
#$30
; convert to the BCD digit value
stab
pdVal,SP
; save the current digit value
ldd
val,SP
; get the accumulated value
ldy
#10
emul
; perform 16-bit by 16-bit multiplication
addd
dummy,SP
; add the current digit value
std
val,SP
; save the sum
bra
GEZero
inErr
movb
#1,err,SP
; set the error flag to indicate error
bra
chkout
157
4.6 ■ The Stack Frame
done
chkout
clrErr
dealloc
;
;
ldaa
beq
ldd
subd
addd
std
ldaa
beq
sec
bra
clc
ldd
leas
puly
rts
org
dc.w
end
sign,SP
chkout
#$FFFF
val,SP
#1
val,SP
err,SP
clrErr
; check to see if the original number is negative
; convert to two’s complement format
; if the number is negative
; ''
; ''
; check the error flag
; go to clear CY flag before return
; clear the C flag
dealloc
; set the C flag
val,SP
locVar,SP
$FFFE
start
; deallocate local variables
; restore Y
; needed under CodeWarrior
; ''
▲
4.6.2 Bubble Sort
Sorting is among the most common ingredients of programming and many sorting
methods are being used. Sorting makes many efficient search methods possible. The bubble
sort is a simple, widely known, but inefficient, sorting method. Many other more efficient
sorting methods require the use of recursive subroutine calls, which fall outside the scope of
this book.
The basic idea underlying the bubble sort is to go through the array or file sequentially
several times. Each iteration consists of comparing each element in the array or file with its
successor (x[i] with x[i 1 1]) and interchanging the two elements if they are not in proper order
(either ascending or descending). Consider the following array:
157 13 35 9 98 810 120 54 10 30
Suppose we want to sort this array in ascending order. The following comparisons are made
in the first iteration:
x[0] with x[1] (157 and 13) interchange
x[1] with x[2] (157 with 35) interchange
x[2] with x[3] (157 with 9) interchange
x[3] with x[4] (157 with 98) interchange
x[4] with x[5] (157 with 810) no interchange
x[5] with x[6] (810 with 120) interchange
x[6] with x[7] (810 with 54) interchange
x[7] with x[8] (810 with 10) interchange
x[8] with x[9] (810 with 30) interchange
Thus, after the first iteration, the array is in the order
13 35 9 98 157 120 54 10 30 810
Notice that after this first iteration, the largest element (in this case 810) is in its proper
position within the array. In general, x[n 2 i] will be in its proper position after iteration i. The
158
Chapter 4 ■ Advanced Assembly Programming
method is called the bubble sort because each number slowly bubbles up to its proper position.
After the second iteration the array is in the order
13 9 35 98 120 54 10 30 157 810
Notice that 157 is now in the second-highest position. Since each iteration places a new
element into its proper position, an array or a file of n elements requires no more than n 2 1
iterations. The complete set of iterations is as follows:
Iteration
0 (original array) 157 13 35 9 98 810 120 54 10 30
1
13 35 9 98 157 120 54 10 30 810
2
13 9 35 98 120 54 10 30 157 810
3
9 13 35 98 54 10 30 120 157 810
4
9 13 35 54 10 30 98 120 157 810
5
9 13 35 10 30 54 98 120 157 810
6
9 13 10 30 35 54 98 120 157 810
7
9 10 13 30 35 54 98 120 157 810
8
9 10 13 30 35 54 98 120 157 810
9
9 10 13 30 35 54 98 120 157 810
There are some obvious improvements to the foregoing method.
• First, since all elements in positions greater than or equal to n 2 i are already
in proper position after iteration i, they need not be considered in succeeding
iterations. Thus, in the first iteration n 2 1 comparisons are made, on the second
iteration n 2 2 comparisons are made, and on the (n 2 1)th iteration, only one
comparison is made (between x[0] and x[1]). Therefore, the process is sped up as it
proceeds through successive iterations.
• Second, although we have shown that n 2 1 iterations are sufficient to sort an array
or a file of size n, in the preceding sample array of 10 elements, the array was sorted
after the seventh iteration, making the last two iterations unnecessary. To eliminate
unnecessary iterations, we must be able to detect the fact that the array is already
sorted. An array is sorted if no swaps are made in an iteration. By keeping a record of
whether any swaps are made in a given iteration, it can be determined whether any
further iteration is necessary. The logic flow of the bubble sort algorithm is illustrated
in Figure 4.7. The following example implements the bubble sort as a subroutine:
Example 4.5
▼
Write a subroutine to implement the bubble sort algorithm and a sequence of instructions
along with a set of test data for testing this subroutine. Use an array that consists of n 8-bit
unsigned integers for testing purposes.
Solution: The bubble sort subroutine has several local variables.
• buf. buffer space for swapping adjacent elements
• inOrder. flag to indicate whether the array is in order after an iteration
• inner. number of comparisons remaining to be performed in the current iteration
• iteration. number of iterations remaining to be performed
The stack frame of the bubble sort subroutine is shown in Figure 4.8.
arr
arcnt
buf
equ
equ
equ
13
12
3
; distance of the variable arrayX from stack top
; distance of the variable arcnt from stack top
; distance of local variable buf from stack top
159
4.6 ■ The Stack Frame
Start
iteration ← N − 1
in_order ← 1
inner ← iteration
i←0
No
array[i] > array[i+1]?
Yes
swap array[i] & array[i+1]
in_order ← 0
inner ← inner − 1
i←i+1
No
inner = 0?
Yes
Yes
in_order = 1?
no
Iteration ← iteration − 1
No
iteration = 0?
Yes
Stop
Figure 4.7 ■ Logic flow of bubble sort
SP+1
SP+2
SP+3
SP+12
SP+13
iteration
inner
inOrder
buf
X
Y
A
B
return address
N
arrayX
SP
Figure 4.8 ■ Stack frame for bubble sort
160
Chapter 4 ■ Advanced Assembly Programming
inOrder
inner
iteration
true
false
n
local
arrayX
equ
equ
equ
equ
equ
equ
equ
org
dc.b
dc.b
2
; distance of local variable inOrder from stack top
1
; distance of local variable inner from stack top
0
; distance of local variable iteration from stack top
1
0
30
; array count
4
; number of bytes used by local variables
$1000
3,29,10,98,54,9,100,104,200,92,87,48,27,22,71
1,62,67,83,89,101,190,187,167,134,121,20,31,34,54
org
$1500
lds
#$1500
; initialize stack pointer
ldx
#arrayX
pshx
ldaa
#n
psha
jsr
bubble
leas
3,sp
; deallocate space used by outgoing parameters
;
bra
$
; uncomment this instruction under CodeWarrior
swi
; break to D-Bug12 monitor
; *********************************************************************************
; The following subroutine implements the bubble sort with the array address and array count
; pushed into the stack.
; *********************************************************************************
bubble pshd
pshy
pshx
leas
-local,sp
; allocate space for local variables
ldaa
arcnt,sp
; compute the number of iterations to be performed
deca
; ''
staa
iteration,sp ; ''
ploop
ldaa
#true
; set array inOrder flag to true before any iteration
staa
inOrder,sp
; ''
ldx
arr,sp
; use index register X as the array pointer
ldaa
iteration,sp ; initialize inner loop count for each iteration
staa
inner,sp
; ''
cloop
ldaa
0,x
; compare two adjacent elements
cmpa
1,x
; ''
bls
looptst
; the following five instructions swap the two adjacent elements
staa
buf,sp
; swap two adjacent elements
ldaa
1,x
; ''
staa
0,x
; ''
ldaa
buf,sp
; ''
staa
1,x
; ''
ldaa
#false
; reset the inOrder flag
staa
inOrder,sp
; ''
looptst inx
dec
inner,sp
start
4.6 ■ The Stack Frame
161
bne
cloop
tst
inOrder,sp
; test array inOrder flag after each iteration
bne
done
dec
iteration,sp
bne
ploop
; the following instruction deallocates local variables
done
leas
local,sp
; deallocate local variables
pulx
puly
puld
rts
;
org
$FFFE
; uncomment for CodeWarrior
;
dc.w
start
; uncomment for CodeWarrior
end
▲
4.6.3 Binary Search Subroutine
Searching is another frequently performed operation. When an array is not sorted, the
search program may need to compare every element of the array. When an array or a file needs
to be searched frequently, it would be more efficient for the programmer to sort the array and
apply a more efficient search method to find the desired element from the array or file.
The binary search algorithm is one of the most efficient search algorithms in use. Suppose
the sorted array (in ascending order) has n elements and is stored at memory locations starting
at the label arr. Let max and min represent the highest and lowest range of array indices to be
searched, and the variable mean represent the average of max and min. The idea of a binary
search algorithm is to divide the sorted array into three parts.
• The upper half. the portion of the array with indices ranging from mean 1 1 to max
• The middle element. the element with index equal to mean
• The lower half. the portion of the array with indices ranging from min to mean 21
The binary search algorithm compares the key with the middle element and takes one of
the following actions on the basis of the comparison result:
• If the key equals the middle element, then stop.
• If the key is larger than the middle element, then the key can be found only in the
upper half of the array. The search will be continued in the upper half.
• If the key is smaller than the middle element, then the key can be found only in
the lower half of the array. The search will be continued in the lower half.
The binary search algorithm can be formulated as follows:
Step 1
Initialize variables max and min to n 2 1 and 0, respectively.
Step 2
If max , min, then stop. No element matches the key.
Step 3
Let mean 5 (max 1 min)/2.
Step 4
If key equals arr[mean], then key is found in the array; exit.
162
Chapter 4 ■ Advanced Assembly Programming
Step 5
If key , arr[mean], then set max to mean 2 1 and go to step 2.
Step 6
If key . arr[mean], then set min to mean 1 1 and go to step 2.
The algorithm works for arrays sorted in ascending order, but can be modified to work for arrays
sorted in descending order.
Example 4.6
▼
Write a subroutine to implement the binary search algorithm and a sequence of instructions to test it. Use an array of n 8-bit elements for implementation.
Solution: In this example, we will use the stack to pass parameters (base address of the array,
array count, and search key) and also allocate local variables in the stack. The stack frame is
shown in Figure 4.9.
mean
min
max
X
B
ret_address
key
arrCnt
arrBase
SP
SP+1
SP+2
SP+8
SP+9
SP+10
Figure 4.9 ■ Stack frame for binary search
n
srch
mean
min
max
key
arrCnt
arrBas
locvar
result
equ
equ
equ
equ
equ
equ
equ
equ
equ
org
ds.b
org
lds
movw
movb
movb
jsr
leas
staa
30
; array count
69
; key to be searched
0
; stack offset for local variable mean
1
; stack offset for local variable min
2
; stack offset for local variable max
8
; stack offset for local variable key
9
; stack offset for local variable arrCnt
10
; stack offset for local variable arrBas
3
; number of bytes for local variables
$1000
1
; search result
$1500
#$1500
#arr,2,2SP ; pass array base address
#n,1,2SP
; pass array count
#srch,1,2SP ; pass key for search
binsearch
4,SP
; deallocate space used in passing parameters
result
163
4.6 ■ The Stack Frame
;
bra
swi
$
; uncomment this instruction for CodeWarrior
; *********************************************************************************
; The following subroutine uses the binary search algorithm to search an array of
; 8-bit elements to find if there is any element that matches the supplied key. The array
; base address, array count, and key are passed in the stack.
; *********************************************************************************
binSearch pshx
pshb
leas
movb
ldaa
deca
staa
ldx
loop
ldab
cmpb
lbhi
addb
lsrb
stab
ldaa
cmpa
beq
bhi
ldaa
inca
staa
bra
searchLO ldaa
deca
staa
bra
found
ldaa
bra
notfound ldaa
exit
leas
pulb
pulx
rts
arr
db
db
db
;
org
;
dc.w
end
2locVar,SP
#0,min,SP
arrCnt,SP
max,SP
arrBas,SP
min,SP
max,SP
notfound
max,SP
mean,SP
b,x
key,SP
found
searchLO
mean,SP
min,SP
loop
mean,SP
max,SP
loop
#1
exit
#0
locVar,SP
; save X in the stack
; save B in the stack
; allocate space for locVar variables
; initialize min to 0
; initialize max to arCnt 2 1
; ''
; ''
; use X as the pointer to the array
; is search over yet?
; ''
; if min > max, then not found (unsigned comparison)
; compute mean
; ''
; save mean
; get a copy of the element arr[mean]
; compare key with array[mean]
; found it?
; continue to search in lower half
; prepare to search in upper half
; ''
; ''
; set up indices range for searching in the
; lower half
; ''
1,3,6,9,11,20,30,45,48,60
61,63,64,65,67,69,72,74,76,79
80,83,85,88,90,110,113,114,120,123
$FFFE
; uncomment this line for CodeWarrior
start
; uncomment this line for CodeWarrior
▲
164
Chapter 4 ■ Advanced Assembly Programming
4.7 Mathematical Subroutines
Mathematical computation is one of the most important application areas for the microcontroller. Several examples will be used to illustrate parameter passing, local variable allocation, and result returning.
4.7.1 Subroutine for Performing Multiple-Byte Division
The HCS12 provides instructions for 16-bit by 16-bit signed and unsigned divisions and
also for 32-bit by 16-bit signed and unsigned divisions. However, the HCS12 does not have
instructions for performing higher-precision divisions; say, for example, 32-bit by 32-bit division.
We will need to write subroutines to synthesize such operations.
The most popular method for performing high-precision divide operations is the repeated
subtraction method. The conceptual hardware for implementing this algorithm is shown in
Figure 4.10.
m sb
C
lsb
R register
Q register
32-bit
32-bit
Write R
Shift left
Set lsb
Controller
ALU
P register
32-bit
Figure 4.10 ■ Conceptual hardware for implementing the repeated subtraction method
Step 1
icnt ← n; R ← 0; Q ← dividend; P ← divisor.
Step 2
Shift the register pair (R, Q) 1 bit left.
Step 3
Subtract the register P from the register R; put the result back to R if the result is
nonnegative.
Step 4
If the result of step 2 is negative, then set the least significant bit of Q to 0. Otherwise, set
the least significant bit of Q to 1.
Step 5
icnt ← icnt 2 1.
Step 6
If (icnt 55 0), then stop;
else go to step 2.
165
4.7 ■ Mathematical Subroutines
Example 4.7
▼
Write a subroutine that implements the division algorithm using the repeated subtraction
method for a 32-bit unsigned dividend and divisor. The caller of this subroutine will pass the
dividend and divisor in the stack and will allocate space in the stack for this subroutine to
return the quotient and remainder. Also, write an instruction sequence to test this subroutine.
Solution: The stack frame of this subroutine is shown in Figure 4.11.
buf
SP+4
i
SP+5
R
SP+9
SP
Q
[y]
[x]
[D]
return address
SP+21
divisor
SP+25
dividend
SP+29
quotient
SP+33
remainder
Figure 4.11 ■ Stack frame of Example 4.7
The divide subroutine and its test program are as follows:
buf
i
R
Q
divisor
dividend
quo
rem
locVar
dvdendHI
dvdendLO
dvsorHI
dvsorLO
quotient
remain
start
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
0
4
5
9
21
25
29
33
13
$42
$4c15
$0
$64
; distance of buf from the top of the stack
; distance of i from the top of the stack
; distance of R from the top of the stack
; distance of Q from the top of the stack
; distance of divisor from the top of the stack
; distance of dividend from the top of the stack
; distance of quo from the top of the stack
; distance of rem from the top of the stack
; number of bytes for local variables
; dividend to be tested
; ''
; divisor to be tested
; ''
org
ds.b
ds.b
org
lds
leas
$1000
4
4
$1500
#$1500
28,SP
; memory locations to hold the quo
; memory locations to hold the remainder
; starting address of the program
; initialize stack pointer
; make a hole of 8 bytes to hold the result
166
Chapter 4 ■ Advanced Assembly Programming
ldd
#dvdendLO
pshd
ldd
#dvdendHI
pshd
ldd
#dvsorLO
pshd
ldd
#dvsorHI
pshd
jsr
div32
; call the divide subroutine
; the following instruction deallocates the stack space used by the divisor and dividend
leas
8,SP
; the following four instructions get the quotient from the stack
puld
std
quotient
puld
std
quotient 1 2
; the following four instructions get the remainder from the stack
puld
std
remain
puld
std
remain 1 2
;
bra
$
; uncomment this instruction for CodeWarrior
swi
; *********************************************************************************
; The following subroutine divides an unsigned 32-bit integer by another unsigned
; 32-bit integer
; *********************************************************************************
div32
loop
pshd
pshx
pshy
leas
ldd
std
std
ldd
std
ldd
std
ldaa
staa
lsl
rol
rol
rol
rol
rol
rol
rol
2locVar,sp
; allocate space for local variables
#0
R,sp
; initialize register R to 0
R 1 2,sp
; ''
dividend,sp
; place dividend in register Q
Q,sp
; ''
dividend 1 2,sp ; ''
Q+2,sp
; ''
#32
; initialize loop count
i,sp
; ''
Q 1 3,sp
; shift register pair Q and R to the left
Q 1 2,sp
; by 1 bit
Q 1 1,sp
; ''
Q,sp
; ''
R 1 3,sp
; ''
R 1 2,sp
; ''
R 1 1,sp
; ''
R,sp
; ''
4.7 ■ Mathematical Subroutines
; the following eight instructions subtract the divisor from register R
ldd
R 1 2,sp
subd
divisor 1 2,sp
std
buf 1 2,sp
ldaa
R 1 1,sp
sbca
divisor 1 1,sp
staa
buf 1 1,sp
ldaa
R,sp
sbca
divisor,sp
bcs
smaller
; the following six instructions store the difference back to R register
staa
R,sp
ldaa
buf 1 1,sp
staa
R 1 1,sp
ldd
buf 1 2,sp
std
R 1 2,sp
bset
Q 1 3,sp,$01 ; set the least significant bit of Q register to 1
bra
looptest
smaller
bclr
Q 1 3,sp,$01 ; set the least significant bit of Q register to 0
looptest
dec
i,sp
lbne
loop
; the following four instructions copy the remainder into the hole in the stack
ldd
R,sp
std
rem,sp
ldd
R 1 2,sp
std
rem 1 2,sp
; the following four instructions copy the quotient into the hole in the stack
ldd
Q,sp
std
quo,sp
ldd
Q 1 2,sp
std
quo 1 2,sp
leas
locVar,sp
; deallocate local variables
puly
pulx
puld
rts
;
org
$FFFE
; uncomment this line for CodeWarrior
;
dc.w
start
; uncomment this line for CodeWarrior
end
167
▲
4.7.2 Finding the Square Root
There are several methods available for finding the square root of a number q. One of the
methods is based on successive approximation shown in Figure 4.12. The square root of a 2nbit number is n bits long. The successive approximation method computes the square root of a
32-bit integer in the following manner:
Step 1
sar ← 0; mask ← $8000; lpcnt ← 16; temp ← 0
168
Chapter 4 ■ Advanced Assembly Programming
Step 2
temp ← sar OR mask
Step 3
If ((temp ** 2) > num) sar ← temp;
Step 4
mask ← mask srl 1 (shift right logically one place);
Step 5
lpcnt ← lpcnt 2 1
Step 6
If (lpcnt 55 0) stop; else go to step 2.
Start
SAR[n − 1, . . . , 0]← 0
i←n−1
SAR[i] ← 1
i←i−1
SAR * SAR > num?
Yes
SAR[i] ← 0
No
No
i = 0?
Yes
Stop
Figure 4.12 ■ Successive-approximation method for finding square root
Example 4.8
▼
Write a subroutine to implement the square root algorithm. This subroutine must be
able to find the square root of a 32-bit unsigned integer. The parameter q (for which we want
to find the square root) is pushed into the stack and the square root is returned in double
accumulator D.
Solution: Four local variables are needed for implementing the square root algorithm.
• mask. set a bit in the SAR register to be 1
• sar. successive approximation register
• temp. the value of (mask OR sar)
• lpcnt. number of loop iterations to be performed
The stack frame of this algorithm is shown in Figure 4.13.
169
4.7 ■ Mathematical Subroutines
mask
SP
sar
SP+2
temp
SP+4
lpcnt
SP+6
y
x
return address
q_hi
SP+13
q_lo
SP+15
Figure 4.13 ■ Stack frame of Example 4.8
The subroutine for finding the square root of a 32-bit integer and its testing program is as follows:
mask
sar
temp
lpcnt
q_hi
q_lo
testHi
testLo
locVar
sqroot
start
;
#include
equ
equ
equ
equ
equ
equ
equ
equ
equ
org
ds.w
“c:\miniide\hcs12.inc”
0
; stack offset of the variable mask from SP
2
; stack offset of the variable sar from SP
4
; stack offset of the variable temp from SP
6
; stack offset of 1-byte loop count from SP
13
; stack offset of the upper and lower halves of the
15
; number Q we want to find the square root from SP
$00
; upper half of the test number (q)
$7BC4
; lower half of the test number (q)
7
$1000
1
; square root
org
lds
movw
movw
jsr
std
leas
bra
swi
$1500
#$1500
#testLo,2,2SP
#testHi,2,2SP
findsqr
sqroot
4,SP
$
; push testHi into stack
; push testLo into stack
; save the returned square root
; deallocate the space used in passing parameters
; uncomment this line for CodeWarrior
; *********************************************************************************
; The following subroutine computes the closest integer square root for the incoming
; unsigned integer pushed in the stack (q_hi and q_lo).
; *********************************************************************************
findsqr
pshx
pshy
leas
movw
movw
2locVar,SP
; allocate space for local variables
#0,sar,SP
; initialize sar to 0
#$8000,mask,SP ; initialize mask to $8000
170
Chapter 4 ■ Advanced Assembly Programming
iloop
nextb
;
;
movb
ldd
oraa
orab
std
tfr
emul
cpy
bhi
cpd
bhi
ldd
std
lsr
ror
dec
bne
ldd
leas
puly
pulx
rts
org
dc.w
end
#16,lpcnt,SP
mask,SP
sar,SP
sar 1 1,SP
temp,SP
D,Y
q_hi,SP
nextb
q_lo,SP
nextb
temp,SP
sar,SP
mask,SP
mask 1 1,SP
lpcnt,SP
iloop
sar,SP
locVar,SP
$FFFE
start
; initialize loop count to 16
; get the mask
; set a bit in sar to 1
; ''
; save a copy of your guess
; compute sar * sar
; ''
; is our guess correct?
; ''
; ''
; ''
; yes, our guess is correct
; so, transfer temp to sar
; shift mask to the right one place
; ''
; put sar in D as the approximated square root
; deallocate local variables
; uncomment this line for CodeWarrior
; uncomment this line for CodeWarrior
▲
This subroutine will find the exact square root if the given number has one. If the given
number does not have an exact integer square root, then the number returned by the subroutine
may not be the closest approximation. The algorithm in Figure 4.12 will compute a sar that
satisfies the relationship sar * sar , q. This may have the undesirable effect that the last sar
value may not be as close to the real square root as is the value of sar 1 1. However, this can be
fixed easily by comparing the following two expressions:
1. (sar 1 1)2 2 q
2. q 2 sar2
If the first expression is smaller, then (sar 1 1) is a better choice. Otherwise, we should
choose sar as the approximation to the real square root. This will be left as an exercise problem.
We will make the subroutine findsqr into a file (findsqr.asm) so that it can be reused in the next
example.
4.7.3 Subroutine for Prime Testing
Testing whether a number is a prime may not be extremely useful for embedded applications. However, it can serve as a good example for software reuse.
The most efficient method for testing whether a number is a prime is to divide the number
by all the prime numbers from 2 to its integral square root. Since the prime numbers from 2
to the square root of the given number are not available, we will be satisfied with dividing the
given number by all the integers from 2 to its integral square root.
4.7 ■ Mathematical Subroutines
171
The algorithm for testing whether a number is a prime is as follows:
Step 1
Let num, i, and isprime represent the number to be tested, the loop index, and the flag to
indicate if num is a prime.
Step 2
isprime ← 0; tlimit ← square root of num;
Step 3
for i 5 2 to tlimit do
if ((num % i) 55 0) then return;
isprime ← 1;
return;
The following example implements this algorithm:
Example 4.9
▼
Write a subroutine that can test whether an unsigned integer is a prime number.
Solution: The subroutine that determines whether a number is a prime and its test program is
as follows:
test_hi
test_lo
equ
$0638
; number to be tested for prime
equ
$F227
; ''
org
$1000
isprime
ds.b
1
org
$1500
start
lds
#$1500
; set up stack pointer
movw
#test_lo,2,2SP ; push the lower half of the test number
movw
#test_hi,2,2SP ; push the upper half of the test number
jsr
PrimeTest
staa
isprime
;
bra
$
; uncomment this line for CodeWarrior
swi
; *********************************************************************************
; The following subroutine tests whether an integer is a prime. The number to be tested
; is 32-bit and is pushed into the stack with the lower half first. The result is returned in A
; and will be 1 if the number is a prime.
; *********************************************************************************
ii
equ
0
; stack offset from SP of loop index
tlimit
equ
2
; stack offset from SP of test limit
pNumHi
equ
10
; stack offset from SP of upper half of test number
pNumLo
equ
12
; stack offset from SP of lower half of test number
pLocal
equ
4
; number of bytes used by local variables
; *********************************************************************************
PrimeTest
pshx
pshy
leas
2pLocal,SP
; allocate space for local variables
ldaa
pNumLo 1 1,SP ; check if the number is even (if bit 0 is 0)
anda
#$01
; ''
beq
nonPRI
; ''
172
Chapter 4 ■ Advanced Assembly Programming
testPR
divLoop
isPRI
nonPRI
exitPT
;
;
ldd
pNumHi,SP
cpd
#0
bne
testPR
; upper half nonzero, then enter normal test
ldd
pNumLo,SP
; if upper half is 0, then test lower half
cpd
#0
; is lower half equal to 0?
beq
nonPri
; 0 is not a prime
cpd
#1
; is lower half equal to 1
beq
nonPri
; 1 is not a prime
ldd
pNumLo,SP
; find the square root of Num
ldx
pNumHi,SP
; ''
pshd
; ''
pshx
; ''
jsr
findsqr
; ''
leas
4,SP
; deallocate space for passing parameters
std
tlimit,SP
; save returned value as the prime test limit
movw
#2,ii,SP
; initialize test divide number to 3
ldd
ii,SP
cpd
tlimit,SP
; has test divided all numbers up to tlimit?
bhi
isPRI
; the number is prime
ldd
pNumLo,SP
; divide Num by ii
ldx
pNumHi,SP
; ''
ldy
ii,SP
; ''
leas
28,SP
; ''
pshd
; '' (push pNumLo)
pshx
; '' (push pNumHi)
pshy
; '' (push ii)
movw
#0,2,2SP
; '' (push 0 to the stack)
jsr
div32
; '' (call the divide subroutine)
leas
14,SP
; deallocate the space used by outgoing parameters
puld
; get the lower 2 bytes of the remainder
cpd
#0
; is remainder equal to 0?
beq
nonPRI
; If remainder equals 0, then Num is not a prime
ldd
ii,SP
; test divide the next higher integer
addd
#1
; ''
std
ii,SP
; ''
bra
divLoop
ldaa
#1
bra
exitPT
ldaa
#0
leas
pLocal,SP
puly
pulx
rts
#include “c:\miniide\findsqr.asm”
#include “c:\miniide\div32.asm”
org
$FFFE
; uncomment this line for CodeWarrior
dc.w
start
; uncomment this line for CodeWarrior
end
▲
173
4.8 ■ Using the D-Bug12 Functions to Perform I/O Operations
4.8 Using the D-Bug12 Functions to Perform I/O Operations
The D-Bug12 monitor provides a few subroutines to support I/O operations. One can utilize these I/O routines to facilitate program developments on a demo board that contains the
D-Bug12 monitor. A summary of these routines is in Table 4.2.
These user-accessible routines are written in C. All except the first parameter are passed to
the user-callable functions on the stack. Parameters must be pushed onto the stack in the reverse order (right to left) so that they are listed in the function declaration. The first parameter
is passed to the function in accumulator D. If a function has only a single parameter, then the
parameter is passed in accumulator D. Parameters of type char must be converted to an integer
(16-bit). Parameters of type char will occupy the lower-order byte (higher address) of a word
pushed onto the stack or accumulator B if the parameter is passed in D.
Parameters pushed onto the stack before the function is called remain on the stack when the
function returns. It is the responsibility of the caller to remove passed parameters from the stack.
All 8- and 16-bit function values are returned in accumulator D. A value of type char returned in accumulator D is located in the 8-bit accumulator B. A Boolean function returns a
zero value for false and a nonzero value for true.
None of the CPU register contents, except the stack pointer, are preserved by the called
functions. If any of the register values need to be preserved, they should be pushed onto the stack
before any of the parameters have been pushed and restored after deallocating the parameters.
Subroutine
Function
Pointer
Address
far main ()
Start of D-Bug12
$EE80
getchar ()
Get a character from SCI0 or SCI1
$EE84
putchar ()
Send a character out to SCI0 or SCI1
$EE86
printf ()
Formatted string output-translates binary values to string
$EE88
far GetCmdLine ()
Get a line of input from the user
$EE8A
far sscanhex()
Convert ASCII hex string to a binary integer
$EE8E
isxdigit ()
Check if a character (in B) is a hex digit
$EE92
toupper()
Convert lowercase characters to uppercase
$EE94
isalpha ()
Check if a character is alphabetic
$EE96
strlen ()
Returns the length of a NULL-terminated string
$EE98
strcpy ()
Copy a NULL-terminated string
$EE9A
far out2hex ()
Output 8-bit number as two ASCII hex characters
$EE9C
far out4hex ()
Output a 16-bit number as four ASCII hex characters
$EEAO
SetUserVector ()
Set up a vector to a user’s interrupt service routine
$EEA4
far WriteEEByte()
Write a byte to the on-chip EEPROM memory
$EEA6
far EraseEE ()
Bulk erase the on-chip EEPROM memory
$EEAA
far ReadMem ()
Read data from the HCS12 memory map
$EEAE
far WriteMem ()
Write data to the HCS12 memory map
$EEB2
Table 4.2 ■ D-Bug12 monitor (version 4.x.x) routines
174
Chapter 4 ■ Advanced Assembly Programming
4.8.1 Calling D-Bug12 Functions from Assembly Language
Calling the functions from assembly language is a simple matter of pushing the parameters
onto the stack in the proper order and loading the first or only function parameter into accumulator D. The function can then be called with a jsr instruction. The code following the jsr instruction
should remove any parameters pushed onto the stack. If a single parameter was pushed onto the
stack, a simple pulx or puly instruction is one of the most efficient ways to remove the parameter
from the stack. If two or more parameters are pushed onto the stack, the leas instruction is the
most efficient way to remove the parameters. Any of the CPU registers that were saved on the
stack before the function parameters should be restored with a corresponding pull instruction.
For example, the WriteEEByte() function has two parameters: The first parameter is the address of
the memory location to which the data is to be written; the second is the data itself. An example of
the instruction sequence to call this function to write the value #$55 into EEPROM is as follows:
WriteEEByte
equ
.
.
ldd
pshd
ldd
jsr
leas
beq
.
.
$EEA6
#$55
; write $55 to EEPROM
#EEAddress
[WriteEEByte,pcr]
2,sp
EEWError
; EEPROM address to write data
; call the routine
; remove the parameter from stack
; 0 return value means error
The addressing mode used in the jsr instruction of this example is a form of indexed indirect
addressing that uses the program counter as an index register. The PCR mnemonic used in place of
an index register name stands for program counter relative addressing. In reality, the HCS12 does
not support PCR. Instead, the PCR mnemonic is used to instruct the assembler to calculate an
offset to the address specified by the label WriteEEByte. The offset is calculated by subtracting the
value of the PC at the address of the first object code byte of the next instruction (in this example,
leas 2,sp) from the address supplied in the indexed offset field (WriteEEByte). When the jsr instruction
is executed, the opposite occurs. The HCS12 adds the value of the PC at the first object code byte of
the next instruction to the offset embedded in the instruction object code. The indirect addressing,
indicated by the square brackets, specifies that the address calculated as the sum of the index register (in this case the PC) and the 16-bit offset contains a pointer to the destination of the jsr.
The MiniIDE software supports this syntax. However, if you are using an assembler that
does not support program-counter-relative-indexed addressing, the following two-instruction
sequence can be used:
ldx
jsr
WriteEEByte
0,x
; load the address of WriteEEByte()
; call the subroutine
If the name of a library function is preceded by the keyword far, then it is located in the
expanded memory and must be called by using the call instruction. Other library functions can
be called by executing the jsr instruction.
4.8.2 Descriptions of Callable Functions
For each of the callable functions, the prototype declaration and the pointer address (where
the starting address of the function is stored) are listed.
void far main (void);
Pointer address:
$EE80
4.8 ■ Using the D-Bug12 Functions to Perform I/O Operations
175
This function simply restarts the D-Bug12 monitor, which will reinitialize all of D-Bug12’s
internal tables and variables. Any previously set breakpoints will be lost. This function will not
be useful to user application development.
int getchar (void);
Pointer address:
$EE84
This function retrieves a single character from the control terminal SCI. If an unread character is not available in the receive data register when this function is called, it will wait until
one is received. Because the character is returned as an integer, the 8-bit character is placed in
accumulator B.
Adding the following instruction sequence in your program will read a character from the
SCI0 port:
getchar
equ
…
jsr
…
$EE84
[getchar,PCR]
int putchar(int);
Pointer address:
$EE86
This function outputs a single character to the control terminal SCI. If the control SCI’s
transmit data register is full when the function is called, putchar() will wait until the transmit
data register is empty before sending the character. No buffering of characters is provided.
Putchar() returns the character that was sent. However, it does not detect any error conditions
that may occur in the process and therefore will never return EOF (end of file). Adding the following instruction sequence in your program will output the character A to the control SCI
(when the program is running on a demo board, the character A will be displayed on the monitor screen):
putchar
equ
…
ldd
jsr
…
$EE86
#’A’
[putchar,PCR]
int printf(char *format, . . .);
Pointer address: $EE88
This function is used to convert, format, and print its arguments on the standard output (the output device could be the monitor screen, printer, LCD, etc.) under the control of
the format string pointed to by format. It returns the number of characters that were sent
to standard output (sent through serial port SCI0). All except floating-point data types are
supported.
The format string can contain two basic types of objects: ASCII characters that are copied directly from the format string to the display device, and conversion specifications that
cause succeeding printf() arguments to be converted, formatted, and sent to the display device.
Each conversion specification begins with a percent sign (%) and ends with a single conversion
character. Optional formatting characters may appear between the percent sign and end with a
single conversion character in the following order:
[2] [<FieldWidth>] [.] [<Precision>] [h | l]
These optional formatting characters are explained in Table 4.3.
The FieldWidth, or Precision, field may contain an asterisk (*) character instead of a number.
The asterisk will cause the value of the argument in the argument list to be used instead.
176
Chapter 4 ■ Advanced Assembly Programming
Character
Description
2 (minus sign)
Left justifies the converted argument.
FieldWidth
Integer number that specifies the minimum field width for the converted argument.
The argument will be displayed in a field at least this wide. The displayed
argument will be padded on the left or right if necessary.
. (period)
Separates the field width from the precision.
Precision
Integer number that specifies the maximum number of characters to display from
a string or the minimum number of digits for an integer.
h
To have an integer displayed as a short.
l (letter ell)
To have an integer displayed as a long.
Table 4.3 ■ Optional formatting characters
The formatting characters supported by the printf() function are listed in Table 4.4. If the conversion character following the percent sign is not one of the formatting characters shown in
this table or the characters shown in Table 4.3, the behavior of the printf() function is undefined.
The printf() function can be used to print a message. One example is as follows:
CR
LF
printf
equ
equ
equ
…
ldd
jsr
…
db
prompt
$0D
$0A
$EE88
#prompt
[printf,PCR]
“Flight simulation”,CR,LF,0
This instruction sequence will cause the message Flight simulation to be displayed, and
the cursor will be moved to the beginning of the next line.
Character
Argument Type; Displayed As
d, i
int; signed decimal number
o
int; unsigned octal number (without a leading zero)
x
int; unsigned hex number using abcdef for 10, . . . , 15
X
int; unsigned hex number using ABCDEF for 10, . . . , 15
u
int; unsigned decimal number
c
int; single character
s
char *; display from the string until a ‘\0’ (NULL)
p
void *; pointer (implementation-dependent representation)
%
no argument is converted; print a %
Table 4.4 ■ Printf() conversion characters
4.8 ■ Using the D-Bug12 Functions to Perform I/O Operations
177
Suppose labels m, n, and gcd represent three memory locations and the memory locations
(2 bytes) starting with the label gcd hold the greatest common divisor of two numbers stored at
memory locations starting with labels m and n. By adding the following instruction sequence,
the relationship among m, n, and gcd can be displayed on the PC monitor screen when the program is executed on a demo board with D-Bug12 monitor:
CR
LF
printf
prompt db
equ
$0D
equ
$0A
equ
$EE88
…
ldd
gcd
pshd
ldd
n
pshd
ldd
m
pshd
ldd
#prompt
jsr
[printf,PCR]
leas
6,sp
…
“The greatest common divisor of %d and %d is %d”,CR,LF,0
…
This example is probably not easy to follow. In reality, this example is equivalent to the
following C statement:
printf(“The greatest common divisor of %d and %d is %d\n”, m, n, gcd);
This function call has four parameters: the first parameter is a pointer to the formatting
string and should be placed in D (done by the ldd #prompt instruction). The other parameters (m,
n, and gcd) should be pushed into the stack in the order from right to left. There are three formatting characters corresponding to these three variables to be output.
int far GetCmdLine(char *CmdLineStr, int CmdLineLen);
Pointer address:
$EE8A
This function is used to obtain a line of input from the user. GetCmdLine() accepts input from
the user one character at a time by calling getchar(). As each character is received it is echoed
back to the user terminal by calling putchar() and placed in the character array pointed to by
CmdLineStr. A maximum of CmdLineLen 2 1 characters may be entered. Only printable ASCII
characters are accepted as input with the exception of the ASCII backspace character ($08) and
the ASCII carriage return ($0D). All other nonprintable ASCII characters are ignored by the
function.
The ASCII backspace character ($08) is used by the GetCmdLine() function to delete the
previously received character from the command line buffer. When GetCmdLine() receives
the backspace character, it will echo the backspace to the terminal, print the ASCII space
character ($20), and then send a second backspace character to the terminal device. At the
same time, the character is deleted from the command line buffer. If a backspace character is received when there are no characters in CmdLineStr, the backspace character is
ignored.
The reception of an ASCII carriage return character ($0D) terminates the reception of characters from the user. The carriage return is not placed in the command line buffer. Instead, a
NULL character ($00) is placed in the next available buffer location.
178
Chapter 4 ■ Advanced Assembly Programming
Before returning, all the entered characters are converted to uppercase. GetCmdLine()
always returns an error code of noErr.
We can use this function to request the user to enter a string from the keyboard when running programs on a demo board with the D-Bug12 monitor. Usually, the program would output
a message so that the user knows when to enter the string. The following instruction sequence
will ask the user to enter a string from the keyboard:
printf
GetCmdLine
cmdlinelen
CR
LF
equ
equ
equ
equ
equ
prompt
db
…
ds.b
…
ldd
jsr
ldd
pshd
ldd
call
puld
inbuf
$EE88
$EE8A
100
$0D
$0A
…
“Please enter a string: “,CR,LF,0
100
#prompt
[printf,PCR]
#cmdlinelen
#inbuf
[GetCmdLine,PCR]
; output a prompt to remind the user to
; enter a string
; push the CmdLineLen
;
“
; read a string from the keyboard
; clean up the stack
char * far sscanhex(char *HexStr, unsigned int *BinNum);
Pointer address:
$EE8E
The sscanhex() function is used to convert a hex string to a binary number. The hex string
pointed to by HexStr may contain any number of ASCII hex characters. However, the converted
value must be no greater than $FFFF. The string must be terminated by either a space or a
NULL character.
Suppose one has entered an ASCII string that represents a number, then the following
instruction sequence will convert it into a hex number (or binary number):
sscanhex
HexStr
BinNum
equ
…
ds.b
ds.b
…
ldd
pshd
ldd
call
leas
…
int isxdigit(int c);
Pointer address:
$EE8E
10
2
; input buffer to hold a hex string
; to hold the converted number
#BinNum
#HexStr
[sscanhex,PCR]
2,sp
; deallocates space used by outgoing parameters
$EE92
The isxdigit() function tests the character passed in c for membership in the character set
[0..9, a..f, A..F]. If the character c is in this set, the function returns a nonzero (true) value.
4.8 ■ Using the D-Bug12 Functions to Perform I/O Operations
179
Otherwise, a value of zero is returned. The following instruction sequence illustrates the use
of this function:
isxdigit
c_buf
equ
…
ds.b
…
clra
ldab
jsr
$EE92
20
c_buf
[isxdigit,PCR]
; buffer that holds data to be validated
; clear accumulator A
; get one character
…
int toupper(int c);
Pointer address:
$EE94
If c is a lowercase character, toupper() will return the corresponding uppercase letter. If the
character is in uppercase, it simply returns c. The following instruction utilizes this function to
convert a character contained in B to uppercase:
toupper
c_buf
equ
…
ds.b
…
ldab
clra
jsr
…
int isalpha(int c);
Pointer address:
$EE94
20
c_buf
; buffer that contains string to be converted to
; uppercase
; get one character to convert
[toupper,PCR]
$EE96
This function tests the character passed in c for membership in the character set [a..z, A..Z].
If the character c is in this set, the function returns a nonzero value. Otherwise, it returns a
zero. This function would also be useful for validating an input string. The following instruction sequence illustrates the use of this function:
isalpha
c_buf
equ
…
ds.b
…
ldab
clra
jsr
…
$EE96
20
c_buf
[isalpha,PCR]
; check whether the character in B is alphabetic
unsigned int strlen(const char *cs);
Pointer address:
$EE98
The strlen() function returns the length of the string pointed to by cs. The following instruction sequence counts the number of characters contained in the string pointed to by cs:
strlen
cs
equ
…
db
…
$ EE98
“…..”
180
Chapter 4 ■ Advanced Assembly Programming
ldd
jsr
…
#cs
[strlen,PCR]
char *strcpy(char *s1, char *s2);
Pointer address:
$EE9A
This function copies the string pointed to by s2 into the string pointed to by s1 and returns
a pointer to s1. The following instruction sequence copies the string pointed to by s1 to the
memory location pointed to by s2:
strcpy
s1
s2
equ
…
db
ds.b
…
ldd
pshd
ldd
jsr
leas
…
$EE9A
“……”
…
#s2
#s1
[strcpy,PCR]
2,sp
void far out2hex(unsigned int num);
Pointer address:
$EE9C
This function displays the lower byte of num on the terminal screen as two hex characters.
The upper byte of num is ignored. The function out2hex() simply calls printf() with a format string
of “%2.2X.”
The following instruction sequence outputs the number in accumulator B as two hex digits
to the terminal screen:
out2hex
data
equ
…
ds.b
…
ldab
clra
call
…
$EE9C
20
data
[out2hex,PCR]
void far out4hex(unsigned int num);
Pointer address:
$EEA0
This function displays num on the control terminal as four hex characters. The function
out4hex() simply calls printf() with a format string of “%4.4X.” The following instruction sequence
outputs the 16-bit number stored at memory location num as four hex digits:
out4hex
num
equ
…
db
…
ldd
call
…
$EEA0
…
num
[out4hex,PCR]
4.8 ■ Using the D-Bug12 Functions to Perform I/O Operations
181
int SetUserVector(int VectNum, Address UserAddress);
Pointer address:
$EEA4
This function will be discussed in Chapter 6.
Boolean far WriteEEByte (Address EEAddress, Byte EEData);
Pointer address:
$EEA6
The WriteEEByte() function provides a mechanism to program individual bytes of the onchip EEPROM. It does not perform any range checking on the passed EEAddress. A user’s
program can determine the start address and size of the on-chip EEPROM array by examining the data contained in the custom data area fields CustData.EEBase and CustData.
EESize.
A byte-erase operation is performed before the programming operation and a verification
operation is performed after the programming operation. If the EEPROM data does not match
EEData, false (0 value) is returned by the function.
Int far EraseEE(void);
Pointer address:
$EEAA
This function performs a bulk erase operation to the on-chip EEPROM without having
to manipulate the EEPROM programming control registers. After the bulk erase operation is performed, the memory range described by CustData.EEBase and CustData.EESize
is checked for erasure. If any of the bytes does not contain 0xFF, a nonzero error code is
returned.
int far ReadMem(Address StartAddress, Byte *MemDataP, unsigned int NumBytes);
Pointer address:
$EEAE
This function is used internally by D-Bug12 for all memory read access.
int WriteMem(Address StartAddress, Byte *MemDataP, unsigned int NumBytes);
Pointer address:
$EEB2
The WriteMem() function is used internally by D-Bug12 for all memory write accesses. If
a byte is written to the memory range described by CustData.EEBase and CustData.EESize,
WriteMem() calls the WriteEEByte() function to program the data into the on-chip EEPROM
memory. A nonzero error code is returned if a problem occurs while writing to target
memory.
4.8.3 Using the D-Bug12 Functions
A useful program usually consists of several functions. An example is given in this
section.
Example 4.10
▼
Write a program that invokes appropriate functions to find the prime number between 1000
and 2000. Output eight prime numbers in one line. To do this, we need to
1. Invoke the PrimeTest subroutine to test if an integer is a prime. The PrimeTest
subroutine will in turn call subroutines FindSqr and div32.
2. Invoke the printf() function to output the prime number.
3. Write a loop to test all the integers between 1000 and 2000.
182
Chapter 4 ■ Advanced Assembly Programming
Solution: The logic structure of the program is as follows:
Step 1
Output the message “The prime numbers between 1000 and 2000 are as follows:”.
Step 2
For every number between 1000 and 2000, do the following:
1. Call the PrimeTest() subroutine to see if it is a prime.
2. Output the number (call printf()) if it is a prime.
3. If there are already eight prime numbers in the current line, then also output a carry
return.
The assembly program is as follows:
CR
LF
equ
equ
$0D
$0A
upper
lower
printf
equ
equ
equ
2000
1000
$EE88
org
out_buf ds.b
PRIcnt ds.b
k
ds.b
tmp
ds.b
org
start
ldx
stx
pshx
ldx
stx
pshx
ldd
jsr
leas
clr
again ldd
cpd
bhi
pshd
ldd
pshd
jsr
leas
tsta
beq
inc
ldd
pshd
ldd
jsr
leas
$1000
10
1
2
2
$1500
#upper
tmp
#lower
k
; upper limit for testing prime
; lower limit for testing prime
; location where the address of printf() is stored
; prime number count
; initialize k to 1000 for prime testing
#form0
[printf,PCR]
4,sp
PRIcnt
k
#upper
Pstop
; stop when k is greater than upper
#0
primetest
4,sp
; test if k is prime
; deallocate space used by outgoing parameters
next_k
PRIcnt
k
; test next integer if k is not prime
; increment the prime count
#form1
[printf,PCR] ; output k
2,sp
183
4.8 ■ Using the D-Bug12 Functions to Perform I/O Operations
ldaa
PRIcnt
cmpa
#8
; are there eight prime numbers in the current line?
blo
next_k
; output a CR, LF if there are already eight prime numbers in the current line
ldd
#form2
jsr
[printf,PCR]
clr
PRIcnt
next_k ldx
k
inx
stx
k
lbra
again
; stop bra
$
; uncomment this line for CodeWarrior
Pstop swi
; comment this line for CodeWarrior
#include “c:\miniide\primetest.asm”
form0 db
CR,LF,”The prime numbers between %d and %d are as follows: “
db
CR,LF,CR,LF,0
form1 db
'' %d '',0
form2 db
'' '',CR,LF,0
;
org
$FFFE
; uncomment this line for CodeWarrior
;
dc.w
start
; uncomment this line for CodeWarrior
end
The program execution output should look like the following:
>load
**
>g 1500
The prime numbers between 1000 and 2000 are as follows:
1009
1051
1103
1171
1229
1289
1327
1427
1471
1523
1579
1621
1697
1753
1823
1879
1951
1013
1061
1109
1181
1231
1291
1361
1429
1481
1531
1583
1627
1699
1759
1831
1889
1973
PP PC
30 1560
xx:1560
>
SP
1500
34
1019
1063
1117
1187
1237
1297
1367
1433
1483
1543
1597
1637
1709
1777
1847
1901
1979
X
07D1
1021
1069
1123
1193
1249
1301
1373
1439
1487
1549
1601
1657
1721
1783
1861
1907
1987
Y
15DE
PSHX
1031
1087
1129
1201
1259
1303
1381
1447
1489
1553
1607
1663
1723
1787
1867
1913
1993
D = A:B
07:D1
1033
1091
1151
1213
1277
1307
1399
1451
1493
1559
1609
1667
1733
1789
1871
1931
1997
1039
1093
1153
1217
1279
1319
1409
1453
1499
1567
1613
1669
1741
1801
1873
1933
1999
1049
1097
1163
1223
1283
1321
1423
1459
1511
1571
1619
1693
1747
1811
1877
1949
User Bkpt Encountered
CCR = SXHI NZVC
1001 0000
▲
184
Chapter 4 ■ Advanced Assembly Programming
4.9 Subroutines for Creating Time Delay
We have learned how to create a time delay using program loops. These program loops can
be converted into subroutines so that they can be called from any place in a program.
Example 4.11
▼
Write a subroutine that can create a time delay of 100 ms.
Solution: By adding an rts instruction to the end of the instruction sequence of Example 2.25, a
subroutine that creates a delay of 100 ms can be created.
delay100ms
pshx
ldx
iloop
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
psha
pula
nop
nop
dbne
pulx
rts
#60000
x,iloop
; 2 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 1 E cycle
; 1 E cycle
; 3 E cycles
This subroutine will be more useful if it is made parameterized; that is, the delay time can
be set to be a multiple of 100 ms instead of a fixed value. The following subroutine can generate
a time delay that is a multiple of 100 ms with the multiple passed in Y:
delayby100ms
pshx
eloop3 ldx
iloop3 psha
pula
psha
pula
psha
pula
psha
pula
psha
#60000
; 2 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
4.10 ■ Introduction to Parallel I/O Port and Simple I/O Devices
pula
psha
pula
psha
pula
nop
nop
dbne
dbne
pulx
rts
x,iloop3
y,eloop3
185
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 2 E cycles
; 3 E cycles
; 1 E cycle
; 1 E cycle
; 3 E cycles
; 3 E cycles
▲
The subroutines that can generate a delay that is a multiple of an other value (for example,
1 ms or 10 ms) can be created in the same manner. The delay.asm file in the complementary CD
provides the following delay subroutines that the user can call to generate delays that are a
multiple of 50 μs, 1 ms, 10 ms, and 100 ms with the multiple also passed in Y:
• delayby50μs
• delayby1ms
• delayby10ms
• delayby100ms
4.10 Introduction to Parallel I/O Port and Simple I/O Devices
So far we have considered how to write general-purpose assembly programs which do not
involve any microcontroller peripheral functions. Dealing with peripheral functions and I/O
devices will add great fun to the learning of assembly language programming. The purpose of
this section is to introduce the parallel I/O port, simple I/O devices, and their applications.
An HCS12 device may have from 48 to 144 signal pins arranged in 3 to 12 I/O ports. Most
signal pins serve multiple functions. When a pin is not used by any peripheral function, it can
be used for general-purpose input/output such as driving an LED or a switch. An I/O port consists of a set of I/O pins and the registers required to control its operations. The I/O pins associated with an I/O port may be configured for input or output and can be used to output values to
LEDs or LCDs or input values from DIP switches or keypad directly.
The pin assignment of the HCS12 D subfamily is shown in Figure 4.14. Most I/O ports have
eight pins although a few do not. A summary of the HCS12 I/O ports is listed in Table 4.5.
4.10.1 Addressing the I/O Port Register
To perform input and output, the CPU sends data to or from a register instead of dealing
with I/O pins. Each register is assigned an address. The user accesses a register by specifying the
address assigned to it. For example, the Port A data register is assigned to address 0. The following instruction will output the value $35 to Port A:
movb
#$35,0
; address 0 is Port A data register
However, it is quite difficult to read the program that performs I/O operations that refers
to the address of a port data register. Instead, the user can use an equ directive to make the
instruction more readable:
PTA
equ
movb
0
#$35,PTA
186
Chapter 4 ■ Advanced Assembly Programming
The HCS12 assembler collects all the equate directives into an include file called hcs12.inc
so that the user can access any peripheral register by referring to an appropriate symbolic name.
In order to use symbolic names to access peripheral registers, the user should add the following
statement to his or her program, preferably as the first line of the program:
#include “c:\...\hcs12.inc” ; specify the directory that contains the hcs12.inc file
Although different assemblers may provide different include files, this author has prepared
an include file (hcs12.inc) in the complementary CD that enables the user to access peripheral
registers and their bits by referring to their names.
4.10.2 I/O Port Direction Configuration
Each I/O port has a data direction register (DDRx, x is the port name) that allows the user
to configure data transfer direction. Setting a bit in the DDRx register to 1 configures the associated pin for output whereas setting a bit in DDRx to 0 configures the associated pin for input.
For example, the instruction
movb
#$FF,DDRA
; configure Port A for output
configures Port A for output.
movb
#0,DDRA
; configure Port A for input
configures Port A for input.
The user can configure a few pins of an I/O port for input and the remaining pins for output. For example, the instruction
movb
#$55,DDRB
configures the even-numbered pins of Port B for output and the odd-numbered pins for input.
Port Name
No. of Pins
Pin Name
A
8
PA7,PA0
B
8
PB7,PB0
E
8
PE7,PE0
H
8
PH7,PH0
J
4
PJ7,PJ0
K
7
PK4,PK0
M
8
PM7,PM0
P
8
PP7,PP0
S
8
PS3,PS0
T
8
PT7,PT0
PAD1, PAD0
16
PAD15,PAD0
L
8
PL7,PL0
U
8
PU7,PU0
V
8
PV7,PV0
W
8
PW7,PW0
Table 4.5 ■ Number of pins available in each parallel port
4.10 ■ Introduction to Parallel I/O Port and Simple I/O Devices
187
Figure 4.14 ■ HCS12 D family pin assignment
4.10.3 I/O Port Data Register
When outputting data, write the value to the port data register. When inputting data, read
from the port data register. For example, the following instruction outputs $12 to Port B:
movb
#$12,PTB
188
Chapter 4 ■ Advanced Assembly Programming
Port Name
Data Register Name
A
PORTA1
B
PORTB1
E
PORTE1
K
PORTK1
H
PTH
J
PTJ
M
PTM
P
PTP
S
PTS
T
PTT
PAD1, PADO
PORTADO, PORTAD1
L
PTL2
U
PTU2
V
PTV2
W
PTW2
Note: 1. PORTA, PORTB, PORTE, and PORTK are also
referred to as PTA, PTB, PTE, and PTK in the hcs12.inc
and hcs12.h files
2. Port L, U, V, and W are available in H-family devices only
Table 4.6 ■ HCS12 parallel I/O port data register names
The following instruction reads a value from Port E and places the value in accumulator A:
ldaa
PTE
The HCS12 port data register names are listed in Table 4.6.
4.11 Simple I/O Devices
Many embedded systems only require simple output devices such as switches, lightemitting diodes, keypads, and seven-segment displays.
4.11.1 Interfacing with LEDs
The LED is often used to indicate the system operation mode, whether power is turned on,
whether system operation is normal, and so forth. An LED can illuminate when it is forward
biased and has sufficient current flowing through it. The current required to light an LED may
range from a few to more than 10 mA. The forward voltage drop across the LED can range from
about 1.6 V to more than 2.2 V.
Figure 4.15 suggests three methods for interfacing with LEDs. Methods (a) and (b) are recommended for use with LEDs that need only 1 or 2 mA to produce enough brightness. The circuit (c) is
recommended for use with LEDs that need larger current to light. Resistors R1, R2, and R3 are referred
to as current-limiting resistors because they set the magnitude of the current flowing through the
LED. The current-limiting resistor for circuits (a) and (b) should be larger (between 1.5 kΩ to 2 kΩ).
189
4.11 ■ Simple I/O Devices
Port
pin
VCC
R1
R2
Port
pin
Port
pin
(a) Positive direct drive
(b) Inverse direct drive
74HC04
R3
(c) Buffered drive
Figure 4.15 ■ An LED connected to a CMOS inverter through a current-limiting resistor
Example 4.12
▼
Use the HCS12 Port B to drive eight LEDs. Light each of them for half a second in turn and
repeat, assuming that the HCS12 has a 24-MHz E-clock. When the Port J pin 1 is low, the LEDs
are enabled to light. This circuit is used in the Dragon12 demo board.
Solution: The circuit for driving eight LEDs using Port B is shown in Figure 4.16.
To turn on the LED driven by pins PB7,PB0 one at a time, we output the values $80, $40, . . . ,
and $01 to Port B in turn. The procedure to achieve the desired LED display pattern is as follows:
Step 1
Place the values $80, $40, . . . , and $01 in a table. Use the index register X to point to the
start of this table. Pull the PJ1 pin low to enable LEDs to light.
Step 2
Output the value pointed to by X to Port B. Increment the pointer X.
Step 3
Wait for half a second.
HCS12
1.5 kΩ
PB7
PB6
PB5
PB4
PB3
PB2
PB1
PB0
PJ1
Figure 4.16 ■ Circuit connection for Example 4.12
190
Chapter 4 ■ Advanced Assembly Programming
Step 4
If X points to the end of the table, reset X to point to the start of the table.
Step 5
Go to step 2.
The assembly program that implements this algorithm is as follows:
lpcnt
start
forever
led_lp
led_tab
;
;
#include “C:\miniide\hcs12.inc”
org
$1000
ds.b
1
org
$1500
movb
#$FF,DDRB
; configure port B for output
bset
DDRJ,$02
; configure PJ1 pin for output
bclr
PTJ,$02
; enable LEDs to light
movb
#16,lpcnt
; initialize LED pattern count
ldx
#led_tab
; Use X as the pointer to LED pattern table
movb
1,x1,PORTB
; turn on one LED
ldy
#5
; wait for half a second
jsr
delayby100ms
; ''
dec
lpcnt
; reach the end of the table yet?
bne
led_lp
bra
forever
; start from beginning
dc.b
$80,$40,$20,$10,$08,$04,$02,$01
dc.b
$01,$02,$04,$08,$10,$20,$40,$80
#include “C:\miniide\delay.asm”
org
$FFFE
; uncomment this line for CodeWarrior
dc.w
start
; uncomment this line for CodeWarrior
end
▲
4.11.2 Interfacing with Seven-Segment Displays
Seven-segment displays are often used when the embedded product needs to display only a few
digits. Seven-segment displays are mainly used to display decimal digits and a small subset of letters.
Although an HCS12 device has enough current to drive a seven-segment display, it is not advisable to do so when an HCS12-based embedded product needs to drive many other I/O devices. In Figure 4.17, Port B drives a common-cathode seven-segment display through the buffer chip 74HC244.
The VOH (output high voltage) value of the 74HC244 is about 5 V. Adding a 470-Ω resistor will set
the display segment current to about 6.4 mA, which should be sufficient to light an LED segment.
The light patterns corresponding to 10 BCD digits are shown in Table 4.7. Depending on how segments a–g are connected to the I/O pins there are two different values for displaying the same decimal digit. In the Dragon12 demo board, segments a–g are connected to from the pin PB0 to pin PB6.
When an application needs to display multiple BCD digits, the time-multiplexing technique
is often used. An example of the circuit that displays six BCD digits is shown in Figure 4.18. In
Figure 4.18, the common cathode of a seven-segment display is connected to the Y output of the
hex buffer chip 74HC367. The output low voltage (VOL) of a 74HC367 output pin (Yi, i 5 0, . . . , 5)
is about 0.33 V when the output current (the current actually flowing into the pin) is larger than a
few mA. When a port B pin output voltage is high, an LED segment current will be about 3.33 mA
[≈ (5 2 2 2 0.33)/800]. Total current that flows into the Yi pin is about 23.3 mA (5 7 × 3.33) and is
within the rated value of the DC output current.
191
4.11 ■ Simple I/O Devices
470 Ω each
HCS12
a
a
b f
c
74HC244
PB6
PB5
PB4
PB3
PB2
PB1
PB0
b
g
d
e e
c
f
d
g
common cathode
Figure 4.17 ■ Driving a single seven-segment display
Decimal
Digit
Segments
Corresponding Hex Number
a
b
c
d
e
f
g
Figure 4.17 Circuit
Dragon12 Demo Board
0
1
1
1
1
1
1
0
$7E
$3F
1
0
1
1
0
0
0
0
$30
$06
2
1
1
0
1
1
0
1
$6D
$5B
3
1
1
1
1
0
0
1
$79
$4F
4
0
1
1
0
0
1
1
$33
$66
5
1
0
1
1
0
1
1
$5B
$6D
6
1
0
1
1
1
1
1
$5F
$7D
7
1
1
1
0
0
0
0
$70
$07
8
1
1
1
1
1
1
1
$7F
$7F
9
1
1
1
1
0
1
1
$7B
$6F
Table 4.7 ■ Decimal to seven-segment decoder
The circuit in Figure 4.18 can display up to six digits by utilizing the time-multiplexing
technique, in which each seven-segment display is lighted in turn briefly and then turned off.
When one display is lighted, all other displays are turned off. Within one second, each sevensegment display is lighted and then turned off many times. Because of the persistence of vision,
all six displays will appear to be lighted simultaneously.
Example 4.13
▼
Write an instruction sequence to display 7 on the seven-segment display #5 in Figure 4.18.
Solution: To display 7 on display #5, we need to
• Output the hex value $07 to Port B
• Set the PP5 pin to low
• Set pins PP4 through PP0 to high
192
Chapter 4 ■ Advanced Assembly Programming
#0
800 Ω
.
.
.
800 Ω g
PB6
PB0
PB1
Common
cathode
74HC367
PP0
A0
Y0
PP1
A1
Y1
PP2
A2
Y2
PP3
A3
Y3
PP4
A4
Y4
PP5
A5
Y5
#5
. . . a
. . . b
.
.
. g
74HC244
HCS12
#1
a
b
a
b
. . .
g
Common
cathode
Common
cathode
.
.
.
Figure 4.18 ■ Port B and port P together drive six seven-segment displays (MC9S12DG256)
The instruction sequence is as follows:
seven
#include
equ
…
movb
movb
movb
movb
“c:\miniide\hcs12.inc”
$07
#$FF,DDRB
#$3F,DDRP
#$1F,PTP
#seven,PTB
; enable display #5 to light
; send out the segment pattern of 7
▲
Example 4.14
▼
Write a program to display 123,456 on the six seven-segment displays shown in Figure 4.18.
Solution: The digits 1, 2, 3, 4, 5, and 6 are displayed on display #5, #4, . . . , and #0, respectively.
The values to be output to Port B and Port P to display one digit at a time are shown in Table 4.8.
This table can be created by the following assembler directives:
display
dc.b
dc.b
dc.b
dc.b
dc.b
dc.b
$06,$1F
$5B,$2F
$4F,$37
$66,$3B
$6D,$3D
$7D,$3E
; value to display 1 on seven-segment display #5
; value to display 2 on seven-segment display #4
The algorithm for displaying 123,456 on the six seven-segment displays in Figure 4.18 is as
follows:
Step 1
Set ptr to point to the first byte of the display table.
193
4.11 ■ Simple I/O Devices
Seven-Segment Display
Displayed BCD Digit
Port B
Port P
#5
#4
1
$06
$1F
2
$5B
$2F
#3
3
$4F
$37
#2
4
$66
$3B
#1
5
$6D
$3D
#0
6
$7D
$3E
Table 4.8 ■ Table of display patterns for Example 4.14
Step 2
Output the byte pointed to by ptr to Port B and then increment ptr by 1.
Step 3
Output the byte pointed to by ptr to Port P and then increment ptr by 1.
Step 4
Wait for 1 ms.
Step 5
If (ptr 55 (display 1 12)), then go to step 1;
else, go to Step 2.
The assembly program that implements this algorithm is as follows:
start
forever
loopi
DispTab
;
;
#include
org
lds
movb
movb
ldx
movb
movb
ldy
jsr
cpx
bne
bra
#include
dc.b
dc.b
dc.b
dc.b
dc.b
dc.b
org
dc.w
end
“c:\miniide\hcs12.inc”
$1500
#$1500
#$FF,DDRB
#$3F,DDRP
#DispTab
; set X to point to the display table
1,x1,PTB
; output segment pattern
1,x1,PTP
; output display select
#1
delayby1ms
; wait for 1 ms
#DispTab 1 12 ; reach the end of the table?
loopi
forever
“c:\miniide\delay.asm”
$06,$1F
$5B,$2F
$4F,$37
$66,$3B
$6D,$3D
$7D,$3E
$FFFE
; uncomment this line for CodeWarrior
start
; uncomment this line for CodeWarrior
▲
194
Chapter 4 ■ Advanced Assembly Programming
4.11.3 Generating a Digital Waveform Using I/O Pins
A periodic digital waveform can be easily generated by manipulating I/O pin voltage
level and inserting an appropriate delay between the two voltage levels. For example,
a 1-kHz periodic square wave can be generated from the PT5 pin using the following
algorithm:
Step 1
Configure the PT5 pin for output.
Step 2
Pull the PT5 pin to high.
Step 3
Wait for 0.5 ms.
Step 4
Pull the PT5 pin to low.
Step 5
Wait for 0.5 ms.
Step 6
Go to step 2.
Example 4.15
▼
Write an assembly program to generate a 1-kHz periodic square wave from the PT5 pin.
Solution: The following assembly program implements the algorithm described previously:
start
forever
;
;
#include
org
lds
bset
bset
ldy
jsr
bclr
ldy
jsr
bra
#include
org
dc.w
end
“c:\miniide\hcs12.inc”
$1500
#$1500
DDRT,BIT5
; configure PT5 pin for output
PTT,BIT5
; pull PT5 pin to high
#10
; wait for 0.5 ms
delayby50us
; ''
PTT,BIT5
; pull PT5 pin to low
#10
; wait for 0.5 ms
delayby50us
; ''
forever
“c:\miniide\delay.asm”
$FFFE
; uncomment for CodeWarrior
start
; uncomment for CodeWarrior
▲
By connecting the PT5 pin to a speaker (or a buzzer) and making the frequency in the
audible range, a sound can be made. Since the frequency of the square wave generated in
Example 4.15 is in the audible range, a sound can be heard if a speaker is connected to the
PT5 pin.
By alternating the frequency of the generated waveform from the I/O pin between two different values, a two-tone siren can be generated. The duration of the siren tone is variable. The
siren would sound more urgent if the tone duration were shorter. The following example generates a two-tone siren that alternates between 250 Hz and 500 Hz with each tone lasting half of
a second.
195
4.11 ■ Simple I/O Devices
Example 4.16
▼
Write a program to generate a two-tone siren that alternates between 250 Hz and 500 Hz
with each siren tone lasting half of a second.
Solution:
start
forever
tone1
tone2
;
;
#include
org
lds
bset
ldx
bset
ldy
jsr
bclr
ldy
jsr
dbne
ldx
bset
ldy
jsr
bclr
ldy
jsr
dbne
bra
#include
org
dc.w
end
“c:\miniide\hcs12.inc”
$1500
#$1500
DDRT,BIT5
; configure PT5 pin for output
#250
; repeat 500 Hz waveform 250 times
PTT,BIT5
; pull PT5 pin to high
#1
delayby1ms
PTT,BIT5
#1
delayby1ms
x,tone1
#125
; repeat 250 Hz waveform for 125 times
PTT,BIT5
#2
delayby1ms
PTT,BIT5
#2
delayby1ms
x,tone2
forever
“c:\miniide\delay.asm”
$FFFE
; uncomment this line for CodeWarrior
start
; uncomment this line for CodeWarrior
▲
4.11.4 Interfacing with DIP Switches
A switch is probably the simplest input device we can find. To make input more efficient, a
set of eight switches organized as a dual inline package (DIP) is often used. A DIP package can
be connected to any input port with eight pins, such as Port A, Port B, and Port H, as shown in
Figure 4.19. When a switch is closed, the associated Port A pin input is 0. Otherwise, the associated Port A pin has a value of 1. Each Port A pin is pulled up to high via a 10-kΩ resistor when
the associated switch is open.
Example 4.17
▼
Write a sequence of instructions to read the value from an eight-switch DIP connected to
Port A of the HCS12 into accumulator A.
Solution:
#include “c:\miniide\hcs12.inc”
movb
#$0,DDRA
ldaa
PTA
…
; configure Port A for input
▲
196
Chapter 4 ■ Advanced Assembly Programming
VCC
SW DIP-8
10 kΩ
HCS12
PA0
PA1
PA2
PA3
PA4
PA5
PA6
PA7
Figure 4.19 ■ Connecting a set of eight DIP switches to port A of the HCS12
4.12 Tips for Program Debugging Involving Subroutine Calls
Program debugging becomes much more difficult when a program calls subroutines. To
make program debugging easier, we test each individual subroutine thoroughly to make sure
each subroutine works correctly and returns to its caller.
4.12.1 What to Do When the Program Gets Stuck
It is a common problem that the program gets stuck in one of the subroutines and cannot
return to the caller. The procedure for debugging in this situation is as follows:
Step 1
Write an instruction sequence to call each subroutine to find out if the program gets
stuck in that subroutine. This can be done by setting a breakpoint at the instruction
immediately after the jsr or bsr instruction. One can use the D-Bug12 asm command
to find out the address of the breakpoint to be set. In CodeWarrior, this can be done
much easier.
Step 2
Find out why the program gets stuck in the subroutine. There are at least the following
four causes:
1. Forgetting to restore registers pushed onto the stack before returning to the caller
2. Forgetting to deallocate local variables before returning to the caller
3. Some infinite loops in the subroutine
4. Calling other subroutines that do not return
The first two causes can be identified by simply looking at the program. To determine
if the program gets stuck because of forgetting to restore registers, check to see if the subroutine has several push instructions at its entrance. If it does, then it should have the same
number of pull instructions in the reverse order before it returns (using the rts instruction).
For the second cause, check to see if the subroutine has the instruction leas –k,SP to allocate
4.12 ■ Tips for Program Debugging Involving Subroutine Calls
197
space to local variables at the entry point of the subroutine. If it does, then the subroutine
should have the instruction leas k,SP or its equivalent to deallocate the stack space used
by local variables before returning to the caller. The first two causes result in the incorrect
return address to be popped out from the stack. Sometimes a subroutine has several returning points. Make sure to restore registers saved in the stack and deallocate local variables
before each rts instruction.
If the first two causes are not present, it is still possible that the subroutine gets stuck in
some loop. To find out if the program gets stuck in a certain loop, insert a breakpoint after the
last instruction of the suspicious loop. If the breakpoint is never reached, then you know that
the loop has some problems. Once the infinite loop is identified, you should be able to figure
out what’s wrong.
To make sure that the subroutine does not get stuck after calling other subroutines, make
sure that the subroutines called by the current routine do not have the first three problems
described in this step.
4.12.2 Handling the Stack Variable Access with Care
The HCS12 does not have a frame pointer. When a subroutine with local variables needs
to call another subroutine, the programmer must pay attention to this issue. For example,
Example 4.9 calls the findSqr and div32 subroutines. It uses the following instruction sequence
to invoke findSqr:
testPR
ldd
ldx
pshd
pshx
jsr
pNumLo,SP
pNumHi,SP
findsqr
; find the square root of Num
; ''
; ''
; ''
; ''
The subroutine will not generate a correct result if the previous instruction sequence is
changed to
testPR
ldd
pshd
ldx
pshx
jsr
pNumLo,SP
pNumHi,SP
findsqr
In the modified instruction sequence, the pshd instruction changes the stack offset of the
original pNumHi slot and hence the intended value is not passed to the callee.
4.12.3 General Debugging Strategy
Subroutines can be classified into two categories: intermediate and leaf subroutines.
An intermediate subroutine may call other subroutines, whereas a leaf subroutine does
not call any other subroutines. Making sure that a subroutine returns to its caller does
not guarantee that it produces correct results. You need to use the methods described in
Section 3.9.5 to debug each leaf subroutine to make sure it works correctly. After making
sure that each leaf subroutine works correctly, start to debug the intermediate subroutines.
Make sure that each intermediate subroutine does not get stuck and works correctly using
the methods discussed in Sections 4.12.1 and 3.9.5. After each intermediate subroutine has
been debugged, perform the top-level program debugging. Again, the method discussed in
Section 3.9.5 can be used.
198
Chapter 4 ■ Advanced Assembly Programming
4.13 Summary
Experience shows that when designing a complicated embedded system, designers spend a
smaller percentage (10 to 20%) of their time on hardware design but a much higher percentage
(80 to 90%) of their time on software development and debugging. Since it takes much longer to
get the software designed right, it is important for us to learn the right way for software
development. The top-down design with hierarchical refinement approach is considered the most
effective system development methodology. For example, using the top-down design with hierarchical refinement approach, the two-tone siren can be generated in the following manner:
In the first iteration, the algorithm for generating the two-tone siren can be outlined as follows:
Iteration 1
Step 1
Generate a 500-Hz periodic square wave for half of a second.
Step 2
Generate a 250-Hz periodic square wave for half of a second.
Step 3
Go to step 1.
In the second iteration, we need to work out the details for generating 500-Hz and 250-Hz
periodic square waves for half of a second.
Iteration 2.1 (generation of 500-Hz square wave)
The period of a 500-Hz square wave is 2 ms, and therefore there are 250 periods of the
waveforms in half of a second.
Step 1
lpcnt1 ← 250
Step 2
Pull the PT5 pin to high.
Step 3
Wait for 1 ms.
Step 4
Pull the PT5 pin to low.
Step 5
Wait for 1 ms.
Step 6
lpcnt1 ← lpcnt1 2 1;
Step 7
If (lpcnt1 ? 0), go to step 2; else, continue to the next step.
Iteration 2.2
The period of a 250-Hz square wave is 4 ms, and therefore there are 125 periods of the
waveforms in half of a second.
Step 1
lpcnt1 ← 125
Step 2
Pull the PT5 pin to high.
4.13 ■ Summary
199
Step 3
Wait for 2 ms.
Step 4
Pull the PT5 pin to low.
Step 5
Wait for 2 ms.
Step 6
lpcnt1 ← lpcnt1 2 1;
Step 7
If (lpcnt1 ? 0), go to step 2; else, continue to the next step.
After the second iteration, the only thing left is how to generate the 2-ms and 1-ms time
delays. These time delays can be generated by calling the existing delay subroutines.
The subroutine is the mechanism that allows the same sequence of code to be reused
(called) from many places of the program. This is the start of the concept of software reuse.
In addition, the subroutines created for certain programs can also be called (reused) in other
programs. To make subroutines more reusable, the following principles must be followed:
• Avoid using global memory to hold variables that are supposed to be local to the
subroutine. Stack is the best place for holding local variables.
• Save CPU registers used in the subroutine unless they are used to pass incoming
parameters or to return computation results. By doing this, the caller does not need
to be concerned about what registers to save before calling the subroutine.
• Describe clearly how to pass incoming parameters and how to return results using
appropriate comments.
The HCS12 provides instructions bsr, jsr, and call for making subroutine calls. The
instructions bsr and jsr will save the return address in the stack before jumping to the subroutine. The call instruction will save the contents of the PPAGE register in the stack in addition
to the return address. The call instruction is provided to call subroutines located in expanded
memory. All subroutines should have rts (rtc for subroutines in expanded memory) as the last
instruction. The rts instruction will pop the return address onto the PC register from the stack,
and program control will be returned to the point that called the subroutine. The rtc instruction
should be used by subroutines that are in expanded memory. The rtc instruction will restore
the PPAGE value pushed onto the stack in addition to the return address.
Parameters can be passed in registers, program memory, the stack, or the global memory.
The result computed by the subroutine can be returned in CPU registers, the stack, or the
global memory. Local variables must be allocated in the stack so that they are not accessible to
the caller and other program units. Local variables come into being only when the subroutine
is being executed. The HCS12 provides instructions to facilitate the access to variables in the
stack. The leas instruction is most effective for local variable allocation and deallocation.
The D-Bug12 monitor provides many functions to support I/O programming on demo
boards that include the D-Bug12 monitor. The printf subroutine is a very useful subroutine because it provides complicated output data formatting.
The HCS12 has many signal pins. These signal pins are divided into I/O ports. An I/O port
consists of a set of signal pins and the registers required for the I/O operation. Since an I/O pin
can be used for input and output, the user must configure the direction of the I/O pin before
using it in data transfer. This is achieved by writing an appropriate value into a data direction
register. Most signal pins serve multiple functions. A signal pin can be used for general-purpose
I/O when it is not being used by any peripheral module.
200
Chapter 4 ■ Advanced Assembly Programming
The HCS12 I/O ports can drive many different types of I/O devices directly. It is important
to consider the electrical and timing compatibility issues when driving I/O devices. These two
issues will be discussed in more detail in Chapter 7. The function of each signal pin will also be
discussed in more detail in Chapter 7.
The LED is a simple output device. It is often used to indicate whether power is turned on,
whether the device is functioning properly, and so on. Today, LEDs also replace incandescent
light bulbs because LEDs are much more energy efficient.
Seven-segment displays are mainly used to display small amounts of information using
decimal digits and a smaller set of characters. Due to the availability of a wide range of sizes,
seven-segment displays have become one of the most versatile display devices.
DIP switches are often used to provide information to the startup program of many embedded systems. After being powered up, the MCU reads the DIP switch’s setting and configures
the embedded system accordingly. Users can change the setting of DIP switches to change the
system configuration.
4.14 Exercises
E4.1 Assuming that we have the following instruction sequence to be executed by the HCS12,
what will be the contents of the topmost 4 bytes of the stack after the execution of these
instructions?
lds
ldaa
staa
ldab
staa
ldy
sty
#$1500
#$56
1, 2SP
#22
1, 2SP
#0
2, 2SP
E4.2 Write instructions to perform the following operation:
1) Push the value $2301 (word value) onto the stack
2) Push the value $34 (byte value) onto the stack
3) Pop the top word of the stack and save it at memory location $1000,$1001.
4) Push the 16-bit value stored at $1000,$1001 into the stack
E4.3 Revise the subroutine in Example 4.2 so that it can convert a 16-bit signed integer to a
BCD string.
E4.4 Write a subroutine to find the greatest common divisor of two 16-bit unsigned integers.
These two 16-bit numbers are passed in the stack. The subroutine returns the gcd in double
accumulator D.
E4.5 Write a subroutine to convert all the lowercase letters in a string to uppercase. The pointer
to the string is passed to this subroutine in X.
E4.6 The label array_x is the starting address of an array of 100 8-bit elements. Trace the following code sequence and describe what the subroutine sub_x does:
ldx
ldaa
jsr
…
#array_x
#100
sub_x
201
4.14 ■ Exercises
sub_x
loop
next
deca
ldab
inx
cmpb
ble
ldab
inx
deca
bne
rts
0,x
0,x
next
0,x
loop
E4.7 Write a subroutine that can multiply two 32-bit unsigned integers. Both the multiplicand
and the multiplier are passed to this subroutine in the stack. The caller pushes the multiplicand
into the stack first and then pushes the multiplier. The pointer to the buffer to hold the product
is passed in index register X.
E4.8 Write a subroutine that can count the number of characters and words contained in a
given string. The pointer to the string to be examined is passed in X. The character count and
word count are returned in Y and B, respectively.
E4.9 Write a subroutine that can find whether a given word is contained in a string. The pointer
to the word to be found and the pointer to the string to be searched are passed in X and Y, respectively. This subroutine would return a 1 in B if the word is found in the string. Otherwise,
a 0 is returned in B.
E4.10 Draw the stack frame and enter the value of each stack slot (if it is known) at the end of
the following instruction sequence:
sub_abc
leas
clrb
ldaa
psha
ldaa
psha
ldx
pshx
jsr
…
pshd
leas
…
22,sp
#20
#$E0
#$7000
sub_abc
212,sp
E4.11 Draw the stack frame and enter the value of each stack slot (if it is known) at the end of
the following instruction sequence:
xyz
leas
ldd
psha
ldx
pshx
bsr
…
pshd
pshx
leas
…
28,sp
#$1020
#$800
xyz
210,sp
202
Chapter 4 ■ Advanced Assembly Programming
E4.12 Write a subroutine to convert all the uppercase letters in a string into lowercase. The
starting address of the string is passed to this subroutine in index register X.
E4.13 Write a subroutine to generate a 16-bit random number. The result is returned to the
caller in D. Pass any appropriate parameters to this subroutine in the stack.
E4.14 Write a subroutine to compute the least common multiple of two 16-bit integers.
Incoming parameters are passed in the stack and the result should be returned in a (Y, D) pair
with the upper 16-bit in Y and the lower 16-bit in D.
E4.15 Write a subroutine to convert an 8-bit signed integer into an ASCII string that represents
a decimal number. The 8-bit integer and the pointer to the buffer to hold the ASCII string are
passed to this subroutine in accumulator B and index register X, respectively.
E4.16 Give an instruction sequence to call the out4hex() function to output the 16-bit integer
stored in memory location $1000,$1001.
E4.17 Give an instruction sequence that outputs the prompt “Please enter a string” and reads the
string entered by the user from the keyboard and then echoes the string on the screen again.
E4.18 Write a subroutine that will convert a 32-bit signed integer into a BCD ASCII string so
that it can be output to the console terminal (SCI) and appear as a BCD string on the screen.
The 32-bit integer to be converted and the pointer to the buffer to hold the resultant string are
passed to this subroutine via the stack.
E4.19 Write an instruction sequence to configure Port A and Port B for input and output,
respectively; read the value of Port A and output the value to Port B.
E4.20 Give an instruction to configure the pins 7, 5, 1, and 0 of Port B for output and the
remaining pins for input.
E4.21 For the circuit shown in Figure 4.18, write a program to display 135790 from the left to
the right one digit at a time with each digit lasting for about half of a second.
E4.22 Write a program to display the following patterns on the six seven-segment displays in
Figure 4.18 continuously with each pattern lasting for half of a second:
123456
234567
345678
456789
567890
678901
789012
890123
901234
012345
4.15 Lab Exercises and Assignments
L4.1 Program entering, assembling, and downloading. Enter, assemble, and download the
following program for execution on the demo board using MiniIDE:
CR
LF
printf
getcmdline
equ
equ
equ
equ
$0D
$0A
$EE88
$EE8A
203
4.15 ■ Lab Exercises and Assignments
cmdlinelen
inbuf
err_flag
sign_flag
prompt1
prompt2
prompt3
equ
org
ds.b
ds.b
ds.b
org
lds
ldd
jsr
ldd
pshd
ldd
call
leas
ldd
jsr
ldd
pshd
ldd
jsr
leas
ldd
jsr
swi
db
db
db
end
40
$1000
20
1
1
$1500
#$1500
#prompt1
[printf,PCR]
#cmdlinelen
#inbuf
[getcmdline,PCR]
2,sp
#prompt3
[printf,PCR]
#inbuf
#prompt2
[printf,PCR]
2,sp
#prompt3
[printf,PCR]
; output a prompt to remind the user to enter
; an integer
; read in a string that represents an integer
; move cursor to the next line
;“
; output the number that you entered
; move cursor to the next line
“Please enter a number: “,CR,LF,0
“The entered number is: %s “,0
“ “,CR,LF,0
When you see the message “Please enter a number:,” enter an integer followed by a carriage
return. The screen output should be similar to what appears here.
>g 1500
Please enter a number:
The entered number is: 1234
User Bkpt Encountered
PP PC
30 1533
xx:1533
>
SP
1500
50
X
1500
Y
156C
NEGB
D = A:B
00:03
CCR = SXHI NZVC
1001 0000
L4.2 Temperature conversion. Write a subroutine that will convert the temperature in
Fahrenheit to Celsius accurate to one decimal digit. Write a main program that will
1. Prompt the user to enter a temperature in Fahrenheit by displaying the message
“Please enter a temperature in Fahrenheit:”.
2. Call the GetCmdLine() function to read in the temperature.
3. Call a subroutine to convert the input string (representing a decimal number) into a
binary number.
4. Call the temperature conversion subroutine to convert it to Celsius.
204
Chapter 4 ■ Advanced Assembly Programming
5. Output the current temperature to the screen in the following format:
Current temperature:
xxxxºF
yyyy.yºC
6. Output the next message:
Want to continue? (y/n)
7. Call the getchar() function to read in one character. If the character entered by the user
is y, then repeat the process. Otherwise, return to the D-Bug12 monitor by executing
the swi instruction.
Note: The ASCII code of the degree character 8 is 176 (or $B0).
L4.3 Seven-segment display shifting. Write a program to display the following patterns on the
four seven-segment displays on the Dragon12 demo board (or any other demo board) with each
pattern lasting for half of a second:
1234
2345
3456
4567
5678
6789
7890
8901
9012
0123
The patterns will be displayed continuously.
L4.4 Three-tone siren generation. Write a program to generate a three-tone siren using the PT5
pin with each tone lasting for half of a second. The frequencies of these three tones are 200 Hz,
500 Hz, and 1 kHz.
L4.5 I/O Routine application and time-of-day display. The complementary CD includes a file
that contains a set of I/O subroutines that you can call. By adding the file stdio0.asm to your
program (using the “#include c:\miniIDE\stdio0.asm” statement), you can output a string to the terminal window and read a string from the keyboard.
Study the subroutines contained in stdio0.asm and write a program to perform the following operations:
1. Output a message by calling printf() (or the supplied subroutine puts) to ask the user
to enter the current time in the format of hh:mm:ss using the PC keyboard (include
the colon character).
2. Read in the time of day by calling the gets subroutine and store the time of day in a buffer.
3. Display the time of day in the format of “Current time is hh:mm:ss” where hh, mm,
and ss are the time components that you just entered.
4. Wait for one second and update the current time-of-day stored in the buffer.
5. Backspace eight places (write a loop and call the putch subroutine to output the
backspace character).
6. Redisplay the new time of day.
7. Go to step 4.
5
C Language Programming
5.1 Objectives
After completing this chapter, you should be
able to
• Explain the overall structure of a C language
program
• Use the appropriate operators to perform
desired operations in C language
• Understand the basic data types and expressions
of C language
• Write program loops in C language
• Write functions and make function calls in
C language
• Use arrays and pointers for data manipulation
• Perform basic I/O operations in C language
• Use the CodeWarrior IDE to enter, compile,
and debug C programs
• Use ImageCraft ICC12 IDE to enter, compile,
and debug C programs
• Write C programs to interface with simple I/O
devices such as LEDs, seven-segment displays,
and DIP switches
206
Chapter 5 ■ C Language Programming
5.2 Introduction to C
This chapter is not intended to provide a complete coverage of C language. Instead, it provides a summary of those C language constructs that will be used in this book. You will be able
to deal with the basic HCS12 interface programming if you fully understand the contents of this
chapter. In addition to providing a tutorial to C language, this chapter will also provide tutorials
on using CodeWarrior IDE and ImageCraft C compiler to enter, compile, and debug C programs.
C language is gradually replacing assembly language in many embedded applications because it has several advantages over assembly language. The most important one is that it allows the user to work on program logic at a level higher than assembly language, and thus
programming productivity is greatly improved.
A C program, whatever its size, consists of functions and variables. A function contains
statements that specify the operations to be performed. The types of statements in a function
could be a declaration, assignment, function call, control, or null. A variable stores a value to
be used during the computation. The main( ) function is required in every C program and is
the one to which control is passed when the program is executed. A simple C program is as
follows:
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(11)
#include <stdio.h>
—include HCS12 header file
/* this is where program execution begins */
void main (void)
—defines a function named main that receives
—no argument values and returns no value
{
—statements of main are enclosed in braces
int a, b, c;
—defines three variables of type int
a 5 3;
—assigns 3 to variable a
b 5 5;
—assigns 5 to variable b
c 5 a 1 b;
—adds a and b together and assigns it to c
printf(“a 1 b 5 %d \n”, c);
—calls library function printf to print the result
return 0;
—returns 0 to the caller of main
}
—the end of main function
The first line of the program
#include <stdio.h>
causes the file stdio.h to be included in the program. This line appears at the beginning of many
C programs. The header file stdio.h contains the prototype declarations of all I/O routines that
can be called by the user program and the constant declarations that can be used by the user
program. C language requires that a function prototype be declared before that function can be
called if a function is not defined when it is called. The inclusion of the stdio.h file allows the
function printf() be invoked in the program.
The second line is a comment. A comment explains what will be performed and will be
ignored by the compiler. A comment in C language starts with /* and ends with */. Everything
in between is ignored. Comments provide documentation to the program and enhance readability. Comments affect only the size of the text file and do not increase the size of the executable
code. Many commercial C compilers also allow the use of two slashes (//) for commenting out
a single line.
The third line main() is where program execution begins. The opening brace on the fourth
line marks the start of main() function’s code. Every C program must have one and only one main()
function. Program execution is also ended with the main function. The fifth line declares three
integer variables a, b, and c. In C, all variables must be declared before they can be used.
5.3 ■ Types, Operators, and Expressions
207
The sixth line assigns 3 to the variable a. The seventh line assigns 5 to the variable b. The
eighth line computes the sum of variables a and b and assigns it to the variable c. You will see
that assignment statements are major components in C programs.
The ninth line calls the library function printf() to print the string a 1 b 5 followed by the
value of c and move the cursor to the beginning of the next line. The tenth line returns a 0 to
the caller of main(). The closing brace in the eleventh line ends the main() function.
5.3 Types, Operators, and Expressions
Variables and constants are the basic objects manipulated in a program. Variables must be
declared before they can be used. A variable declaration must include the name and type of the
variable and may optionally provide its initial value. A variable name may start with a letter
(A through Z or a through z) or an underscore character followed by zero or more letters, digits,
or underscore characters. Variable names cannot contain arithmetic signs, dots, apostrophes,
C keywords, or special symbols such as @, #, ?, and so on. Adding the underscore character (_)
may sometimes improve the readability of long variables. Don’t begin variable names with an
underscore, however, since library routines often use such names. C language is case sensitive.
Upper- and lowercase letters are distinct.
5.3.1 Data Types
There are only a few basic data types in C: void, char, int, float, and double. A variable of type
void represents nothing. The type void is used most commonly with functions and can indicate
that the function does not return any value or does not have incoming parameters. A variable
of type char can hold a single byte of data. A variable of type int is an integer, which is normally
the natural size (word length) for a particular machine. The type float refers to a 32-bit, singleprecision, floating-point number. The type double represents a 64-bit, double-precision, floatingpoint number. In addition, there are a number of qualifiers that can be applied to these basic
types. Short and long apply to integers. These two qualifiers will modify the lengths of integers.
An integer variable is 16-bit by default for many C compilers including the CodeWarrior C and
the GNU C compiler. The modifier short does not change the length of an integer. The modifier
long doubles a 16-bit integer to 32 bits. The keyword unsigned should be used if the variables
are never negative to improve the efficiency of the generated code.
5.3.2 Variable Declarations
All variables must be declared before their use. A declaration specifies a type and contains
a list of one or more variables of that type, as in
int
char
i, j, k;
cx, cy;
A variable may also be initialized when it is declared, as in
int
char
i 5 0;
echo 5 ‘y’;
/* the ASCII code of letter y is assigned to variable echo. */
5.3.3 Constants
There are four kinds of constants: characters, integers, floating-point numbers, and strings.
A character constant is an integer, written as one character within single quotes, such as ‘x’. A
208
Chapter 5 ■ C Language Programming
character constant is represented by the ASCII code of the character. A string constant is a sequence of zero or more characters surrounded by double quotes, as in
“HCS12DG256 is a microcontroller made by Freescale”
or
“”
/* an empty string */
Each individual character in the string is represented by its ASCII code.
An integer constant such as 3241 is an int. A long constant is written with a terminal l
(lowercase letter “el”) or L, as in 44332211L. The following constant characters are predefined
in C language (can be embedded in a string):
\a
alert (bell) character
\\
backslash
\b
backspace
\?
question mark
\f
form feed
\’
single quote
\n new line
\”
double quote
\r
carriage return
\ooo
octal number
\t
horizontal tab
\xhh hexadecimal number
\v
vertical tab
As in assembly language, a number in C can be specified in different bases. The method
to specify the base of a number is to add a prefix to the number. The prefixes for different
bases are
Base
Prefix
Example
decimal
none
1357
octal
0
04723
hexadecimal
0X
0X2A
; preceded by a zero
5.3.4 Arithmetic Operators
There are seven arithmetic operators.
1
add and unary plus
2
subtract and unary minus
*
multiply
/
divide
%
modulus (or remainder)
11
increment
22
decrement
The expression
a%b
produces the remainder when a is divided by b. The % operator cannot be applied to float or
double. The 11 operator adds 1 to the operand, and the 22 operator subtracts 1 from the operand. The / operator performs a division and truncates the quotient to an integer when both
operands are integers.
5.3 ■ Types, Operators, and Expressions
209
Example 5.1
▼
What value will be assigned to ck for the following statement?
ck 5 230/13;
Solution: The integral part of 230/13 is 17. Therefore, ck will receive the value of 17 after the
previous statement is executed.
▲
Example 5.2
▼
What value will be assigned to cx for the following statement?
cx 5 330 % 19;
Solution: The remainder of 330/19 is 7. Therefore, cx receives the value of 7 after the execution
of the previous statement.
▲
Example 5.3
▼
Assume that ax is a six-digit (decimal) integer. Write a few C statements to separate ax into
two parts and assign the upper three digits to the variable bx and the lower three digits to the
variable cx.
Solution: A six-digit number can be written
d5d4d3d2d1d0 5 d5d4d3 3 1000 1 d2d1d0
Therefore we can divide a six-digit integer into two halves by dividing the given number by
1000. The following two statements will achieve the desired operation:
bx 5 ax/1000;
cx 5 ax % 1000;
▲
5.3.5 Bitwise Operators
C provides six operators for bit manipulations; these may be applied only to integral operands, that is, char, short, int, and long, whether they are signed or unsigned.
&
AND
⏐
OR
^
XOR
,
NOT
..
right shift
,,
left shift
210
Chapter 5 ■ C Language Programming
The & operator is often used to clear one or more bits to 0. For example, the statement
PORTC
5 PORTC & 0xAA;
/* PORTC is 8 bits */
clears the even bits of PORTC to 0.
The | operator is often used to set one or more bits to 1. For example, the statement
PORTB
5 PORTB Z 0xAA;
/* PORTB is 8 bits */
sets the odd bits of PORTB to 1.
The XOR operator can be used to toggle one or multiple bits. For example, the statement
abc 5 abc ^ 0xF0;
/* abc is of type char */
toggles the upper 4 bits of the variable abc.
The .. operator shifts the involved operand to the right for the specified number of places.
For example, the statement
xyz 5 abc .. 3;
shifts the variable abc to the right three places and assigns it to the variable xyz.
The ,, operator shifts the involved operand to the left for the specified number of places.
For example, the statement
xyz 5 xyz ,, 4;
shifts the variable xyz to the left four places.
The assignment operator 5 is often combined with the operator. For example, the
statement
PTP 5 PTP & 0xBD;
can be rewritten as
PTP &5 0xBD;
and the statement
PORTB 5 PORTB k 0x40;
can be rewritten as
PORTB k5 0x40;
5.3.6 Relational and Logical Operators
Relational operators are used in expressions to compare the values of two operands. If the
result of the comparison is true, then the value of the expression is 1. Otherwise, the value of
the expression is 0. Here are the relational and logical operators.
55
equal to (two “5” characters)
!5
not equal to
.
greater than
.5
greater than or equal to
,
less than
,5
less than or equal to
&&
and
i
or
!
not (one’s complement)
Here are some examples of relational and logical operators.
if (!(ADCTL & 0x80))
statement1;
// if bit 7 is 0, then execute statement1
211
5.4 ■ Control Flow
if (i . 0 && i , 10)
statement2;
if (a1 55 a2)
statement3;
// if 0 , i , 10 then execute statement2
// if a1 equals a2 then execute statement3
5.3.7 Precedence of Operators
Precedence refers to the order in which operators are processed. C language maintains a
precedence for all operators, shown in Table 5.1. Operators at the same level are evaluated from
left to right. A few examples that illustrate the precedence of operators are listed in Table 5.2.
Precedence
Operator
Associativity
Highest
()[].
! , ++ – – + – * & (type) sizeof
*/%
+–
<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += – = *= /= %= &= ^= |= <<= >>=
‘
left to right
right to left
left to right
left to right
left to right
left to right
left to right
left to right
left to right
left to right
left to right
left to right
right to left
right to left
left to right
Lowest
Table 5.1 ■ Table of precedence of operators
Expression
15 – 2 * 7
(13 – 4) * 5
(0x20 | 0x01) != 0x01
0x20 | 0x01 != 0x01
1 << 3 + 1
(1 << 3) + 1
Result
Note
1
45
1
0x20
16
9
* has higher precedence than +
!= has higher precedence than |
+ has higher precedence than <<
Table 5.2 ■ Examples of operator precedence
5.4 Control Flow
Control-flow statements specify the order in which computations are performed. In C language, the semicolon is a statement terminator. Braces { } are used to group declarations and
statements together into a compound statement, or block, so that they are syntactically equivalent to a single statement.
212
Chapter 5 ■ C Language Programming
5.4.1 If Statement
The if statement is a conditional statement. The statement associated with the if statement is executed on the basis of the outcome of a condition. If the condition evaluates to nonzero, the statement is executed. Otherwise, it is skipped. The syntax of the if statement is
if (expression)
statement;
Here is an example of an if statement.
if (a . b)
sum 15 2;
The value of sum will be incremented by 2 if the variable a is greater than the variable b.
5.4.2 If-Else Statement
The if-else statement handles conditions where a program requires one statement to be
executed if a condition is nonzero and a different statement if the condition is zero. The syntax
of an if-else statement is
if (expression)
statement1
else
statement2
The expression is evaluated. If it is true (nonzero), statement1 is executed. If it is false (zero),
statement2 is executed. Here is an example of the if-else statement.
if (a !5 0)
r 5 b;
else
r 5 c;
The if-else statement can be replaced by the ?: operator. The statement
r 5 (a !5 0)? b : c;
is equivalent to the previous if-else statement.
5.4.3 Multiway Conditional Statement
A multiway decision can be expressed as a cascaded series of if-else statements. Such a
series looks like this.
if (expression1)
statement1
else if (expression2)
statement2
else if (expression3)
statement3
...
else
statementn
Here is an example of a three-way decision.
if (abc . 0) return 5;
else if (abc 55 0) return 0;
else return 25;
5.4 ■ Control Flow
213
5.4.4 Switch Statement
The switch statement is a multiway decision based on the value of a control expression.
The syntax of the switch statement is
switch (expression) {
case const_expr1:
statement1;
break;
case const_expr2:
statement2;
break;
...
default:
statementn;
}
As an example, consider the following program fragment:
switch (i) {
case 1:
pay 5 100;
break;
case 2:
pay 5 200;
break;
case 3:
pay 5 300;
break;
case 4:
pay 5 400;
break;
case 5:
pay 5 500;
break;
default:
pay 5 0;
}
The variable pay receives a value that is equal to the value of i 3 100. The keyword break forces
the program flow to drop out of the switch statement so that only the statements under the corresponding case-label are executed. If any break statement is missing, then all the statements from
that case-label until the next break statement within the same switch statement will be executed.
5.4.5 For-Loop Statement
The syntax of a for-loop statement is
for (expr1; expr2; expr3)
statement;
where expr1 and expr3 are assignments or function calls, and expr2 is a relational expression.
For example, the following for loop computes the sum of the squares of integers from 1 to 9:
sum 5 0;
for (i 5 1; i , 10; i11)
sum 5 sum 1 i * i;
214
Chapter 5 ■ C Language Programming
The following for loop prints out the numbers that are smaller than 100 and indivisible by 13:
for (i 5 1; i , 100; i11)
if (i % 13) printf(“%d “, i);
5.4.6 While Statement
During the time an expression is nonzero, the while loop repeats a statement or block of
code. The value of the expression is checked prior to each execution of the statement. The syntax of a while statement is
while (expression)
statement;
The expression is evaluated. If it is nonzero (true), statement is executed and expression is
reevaluated. This cycle continues until expression becomes zero (false), at which point execution resumes after statement. The statement may be a NULL statement. A NULL statement
does nothing and is represented by a semicolon. Consider the following program fragment:
intCnt 5 5;
while (intCnt);
The CPU will do nothing before the variable intCnt is decremented to 0. In microprocessor
applications, the decrement of intCnt is often triggered by external events such as interrupts.
5.4.7 Do-While Statement
The while and for loops test the termination condition at the beginning of a statement. By
contrast, the do-while statement tests the termination condition at the end of the statement;
the body of the statement is executed at least once. The syntax of the do-while statement is
do
statement
while (expression);
The following do-while statement displays the integers 9 down to 1:
int digit 5 9;
do
printf(“%d”, digit22);
while (digit .5 1);
5.4.8 Goto Statement
Execution of a goto statement causes control to be transferred directly to the labeled statement, which must be located in the same function as the goto statement. The use of the goto
statement interrupts the normal sequential flow of a program and thus makes it harder to follow and decipher. For this reason, the use of goto’s is not considered good programming style,
and it is recommended that you do not use them in your program.
The syntax of the goto statement is
goto
label
An example of the use of a goto statement is
if (x . 100)
goto
severe_error;
...
severe_error:
printf(“Variable x is out of bound!\n);
215
5.5 ■ Input and Output
5.5 Input and Output
Input and output facilities are not part of C language itself. However, input and output are
fairly important in applications. The ANSI standard defines a set of library functions that must
be included so that they can exist in a compatible form on any system where C exists. Some of
the functions deal with file input and output. Others deal with text input and output. In this
section we will look at the following four input and output functions:
1. int getchar ( ). This function returns a character when it is called. The following
program fragment returns a character and assigns it to the variable xch:
char xch;
xch 5 getchar ();
2. int putchar (int). This function outputs a character on the standard output device. The
following statement outputs the letter a from the standard output device:
putchar (‘a’);
3. int puts (const char *s). This function outputs the string pointed to by s on the
standard output device. The following statement outputs the string “Learning
microcontroller is fun!” from the standard output device:
puts (“Learning microcontroller is fun! \n”);
4. int printf (formatting string, arg1, arg2, . . . , argn). This function converts, formats,
and prints its arguments on the standard output under control of formatting string.
arg1, arg2, . . . , argn are arguments that represent the individual output data items.
The arguments can be written as constants, single variable or array names, or more
complex expressions. The formatting string is composed of individual groups of
characters, with one character group associated with each output data item. The
character group corresponding to a data item must start with %. In its simplest form,
an individual character group will consist of the percent sign followed by a conversion
character indicating the type of the corresponding data item.
Multiple character groups can be contiguous or separated by other characters, including
white-space characters. These other characters are simply transferred directly to the output device where they are displayed. A subset of the more frequently used conversion characters is
given in Table 5.3. Between the % character and the conversion character there may be, in order,
• A minus sign, which specifies left adjustment of the converted argument.
• A number, which specifies the minimum field width. The converted argument will
be printed in a field at least this wide. If necessary, it will be padded on the left (or
right, if left adjustment is called for) to make up the field width.
• A period, which separates the field width from the precision.
• A number that specifies the maximum number of characters to be printed from a
string or the number of digits after the decimal point of a floating-point value or
the minimum number of digits for an integer.
• An h if the integer is to be printed as a short, or an l (letter “el”) if as a long.
Several valid printf calls are
printf (“this is a challenging course! \n”);
printf (“%d %d %d”, x1, x2, x3);
/* outputs only a string */
/* outputs variables x1, x2, x3 using a minimal number of digits
with one space separating each value */
216
Chapter 5 ■ C Language Programming
printf(“Today’s temperature is %4.1d \n”, temp); /* display the string Today’s temperature is followed by the value
of temp. Display one fractional digit and use at least four digits
for the value. */
Conversion
Character
c
d
e
f
g
i
o
s
u
x
Meaning
Data item is displayed as a single character.
Data item is displayed as a signed decimal number.
Data item is displayed as a floating-point value with an exponent.
Data item is displayed as a floating-point value without an exponent.
Data item is displayed as a floating-point value using either e-type or f-type conversion,
depending on value; trailing zeros, trailing decimal point will not be displayed.
Data item is displayed as a signed decimal integer.
Data item is displayed as an octal integer, without a leading zero.
Data item is displayed as a string.
Data item is displayed as an unsigned decimal integer.
Data item is displayed as a hexadecimal integer, without the leading 0x.
Table 5.3 ■ Commonly used conversion characters for data output
5.6 Functions and Program Structure
Every C program consists of one or more functions. If a program consists of multiple functions, their definitions cannot be embedded within another. The same function can be called
from several different places within a program. Generally, a function will process information
passed to it from the calling portion of the program and return a single value. Information is
passed to the function via special identifiers called arguments (also called parameters) and
returned via the return statement. Some functions, however, accept information but do not
return anything (for example, the library function printf ).
The syntax of a function definition is
return_type function_name (declarations of arguments)
{
declarations and statements
}
The declaration of an argument in the function definition consists of two parts: the type and
the name of the variable. The return type of a function is void if it does not return any value to
the caller. An example of a function that converts a lowercase letter to an uppercase letter is
char lower2upper (char cx)
{
if (cx .5 ‘a’ && cx ,5 ‘z’) return (cx 2 (‘a’ 2 ‘A’));
else return cx;
}
A character is represented by its ASCII code. A letter is in lowercase if its ASCII code is
between 97 (0x61) and 122 (0x7A). To convert a letter from lowercase to uppercase, subtract its
ASCII code by the difference of the ASCII codes of letters a and A.
5.6 ■ Functions and Program Structure
217
To call a function, simply put down the name of the function and replace the argument
declarations by actual arguments or values and terminate it with a semicolon.
Example 5.4
▼
Write a function to find the square root of a 32-bit integer using the successive approximation method described in Section 4.7.2.
Solution: The C function that computes the square root of a 32-bit unsigned integer is as
follows:
// ********************************************************************************
// This function computes the square root of a 32-bit unsigned integer
// using the successive approximation method.
// Incoming parameter: a 32-bit number of which the square root is to be found.
// ********************************************************************************
unsigned long int FindSqr (unsigned long int xval)
{
unsigned
long
int
mask,temp,sar;
unsigned
int
ix;
mask 5 0x8000;
// Initialize mask for making bit value guessing
sar 5 0;
for (ix 5 0; ix , 16; ix11){
temp 5 sar | mask;
if((temp * temp) ,5 xval)
sar 5 temp;
mask ..5 1;
}
if ((xval 2 (sar * sar)) . ((sar11)*(sar11) 2 xval))
sar 15 1;
return sar;
}
▲
Example 5.5
▼
Write a function to test whether a 32-bit nonnegative integer is a prime number.
Solution: The integer 1 is not a prime number. A number is a prime if it cannot be divided by
any integer between 2 and its square root. The prime test function needs to call the FindSqr
function to find the test divide limit. The prime test function is
unsigned int PrimeTest(unsigned long int xval)
{
unsigned long int TLimit;
unsigned int ptest;
if ((xval 55 1) || (xval 55 2))
return 0;
218
Chapter 5 ■ C Language Programming
TLimit 5 FindSqr(xval); // call FIndSqr() to find the limit for test divide
for (ptest 5 2; ptest ,5 TLimit; ptest 11){
if ((xval % ptest) 55 0) // is remainder zero?
return 0;
}
return 1;
}
▲
Example 5.6
▼
Write a program to find out the number of prime numbers between 100 and 1000.
Solution: One can find the number of prime numbers between 100 and 1000 by calling the
function written in Example 5.5.
#include “c:\cwHCS12\include\hcs12.h”
unsigned long int FindSqr (unsigned long int xval);
unsigned int PrimeTest(unsigned long int xval);
unsigned int prime_count;
void main(void) {
unsigned long int i;
prime_count 5 0;
for (i 5 100; i ,5 1000; i11) {
if (PrimeTest(i))
prime_count11;
}
while(1);
}
unsigned int PrimeTest(unsigned long int xval)
{
unsigned long int TLimit;
unsigned int ptest;
if (xval 5 1)
return 0;
TLimit 5 FindSqr(xval);
for (ptest 5 2; ptest ,5 TLimit; ptest11){
if ((xval % ptest) 55 0)
return 0;
}
return 1;
}
// include FindSqr() function here
This program consists of three functions. It is very common for a C program to consist of
many functions. Functions of the same nature can be placed in the same file and be reused.
▲
5.6.1 Function Prototype
A function cannot be called before it has been defined. This dilemma is solved by using the
function prototype statement. The syntax for a function prototype statement is
return_type function_name (declarations of arguments);
5.7 ■ Pointers, Arrays, Structures, and Unions
219
The statement
unsigned int PrimeTest(unsigned long int xval);
the third line in the program of Example 5.6, is a function prototype statement.
To call a function, simply write the name of the function and replace the argument declarations by actual arguments or values and terminate it with a semicolon.
5.6.2 Creating Header Files
When placing multiple functions of the same nature in one file and reusing them in other
programs, the user needs also to include the prototype declaration of these functions in the
program. A common approach is to create a header file to contain the prototype declarations.
For example, the complementary CD provides a file called delay.c which contains several delay
functions. The prototype declarations for those delay functions are placed in the delay.h file.
The user will need to include the delay.h file in the program in order to invoke those delay
functions. The delay.c file contains the following functions:
• delayby10µs(). This function creates a delay that is a multiple of 10 µs.
The multiple is passed to this function.
• delayby50µs(). This function creates a delay that is a multiple of 50 µs.
The multiple is passed to this function.
• delayby1ms(). This function creates a delay that is a multiple of 1 ms.
The multiple is passed to this function.
• delayby10ms(). This function creates a delay that is a multiple of 10 ms.
The multiple is passed to this function.
• delayby100ms().This function creates a delay that is a multiple of 100 ms.
The multiple is passed to this function.
For example, the following statement creates a delay of 200 ms:
delayby100ms(2);
5.7 Pointers, Arrays, Structures, and Unions
5.7.1 Pointers and Addresses
A pointer is a variable that holds the address of a variable. Pointers are used frequently
in C, as they have a number of useful applications. For example, pointers can be used to pass
information back and forth between a function and its reference (calling) point. In particular,
pointers provide a way to return multiple data items from a function via function arguments.
Pointers also permit references to other functions to be specified as arguments to a given
function. This has the effect of passing functions as arguments to the given function.
Pointers are also closely associated with arrays and therefore provide an alternative way to
access individual array elements. The syntax for declaring a pointer type is
type_name *pointer_name;
For example,
int *ax;
declares that the variable ax is a pointer to an integer.
char *cp;
declares that the variable cp is a pointer to a character.
220
Chapter 5 ■ C Language Programming
To access the value pointed to by a pointer, use the dereferencing operator *. For example,
int a, *b;
...
a 5 *b;
// b is a pointer to int
assigns the value pointed to by b to variable a.
We can assign the address of a variable to a pointer by using the unary operator &. The following example shows how to declare a pointer and how to use & and *:
int x, y;
int *ip;
// ip is a pointer to an integer
ip 5 &x;
y 5 *ip;
// assigns the address of the variable x to ip
// y gets the value of x
5.7.2 Arrays
Many applications require the processing of multiple data items that have common characteristics (e.g., a set of numerical data, represented by x1, x2, . . . , xn). In such situations it is more
convenient to place data items into an array, where they will all share the same name. The
individual data items can be characters, integers, floating-point numbers, and so on. They must
all be of the same type and the same storage class.
Each array element is referred to by specifying the array name followed by one or more
subscripts, with each subscript enclosed in brackets. Each subscript must be expressed as a
nonnegative integer. Thus, the elements of an n-element array x are x[0], x[1], . . . , x[n – 1]. The
number of subscripts determines the dimensionality of the array. For example, x[i] refers to an
element of a one-dimensional array. Similarly, y[i][j] refers to an element of a two-dimensional
array. Higher-dimensional arrays can be formed by adding additional subscripts in the same
manner. However, higher-dimensional arrays are not used very often in 8- and 16-bit microcontroller applications. In general, a one-dimensional array can be expressed as
data-type array_name[expression];
A two-dimensional array is defined as
data-type array_name[expr1][expr2];
An array can be initialized when it is defined. This is a technique used in table lookup, which
can speed up the computation process.
Example 5.7
▼
Write the bubble sort function to sort an array of integers.
Solution: The algorithm for bubble sort is already described in Chapter 4. Here is the C language
version.
void
void
{
swap (int *px, int *py);
bubble (int a[], int n) /* n is the array count */
int i, j;
for (i 5 0; i , n 2 1; i11)
for (j 5 0; j . n 2 i 2 2; j11)
5.7 ■ Pointers, Arrays, Structures, and Unions
221
if (a[j] . a[j11])
swap (&a[j], &a[j11]);
}
void swap (int *px, int *py)
{
int temp;
temp 5 *px;
*px 5 *py;
*py 5 temp;
}
▲
5.7.3 Pointers and Arrays
In C, there is a strong relationship between pointers and arrays. Any operation that can
be achieved by array subscripting can also be done with pointers. The pointer version will in
general be faster but somewhat harder to understand. For example,
int ax[20];
defines an array ax of 20 integral numbers. The notation ax[i] refers to the ith element of the
array. If ip is a pointer to an integer, declared as
int *ip;
then the assignment
ip 5 &ax[0];
makes ip contain the address of ax[0]. Now the statement
x 5 *ip;
will copy the contents of ax[0] into x. If ip points to ax[0], then ip 1 1 points to ax[1], and ip 1 i
points to ax[i], and so on.
5.7.4 Passing Arrays to a Function
An array name can be used as an argument to a function, thus permitting the entire array to
be passed to the function. To pass an array to a function, the array name must appear by itself,
without brackets or subscripts, as an actual argument within the function call. When declaring a
one-dimensional array as a formal argument, the array name is written with a pair of empty square
brackets. The size of the array is not specified within the formal argument declaration. If the array
is two-dimensional, then there should be two pairs of empty brackets following the array name.
The following program outline illustrates the passing of an array from the main portion of
the program to a function:
int average (int n, int arr[]);
void main ( )
{
int n, avg;
int arr[50];
...
avg 5 average(n, arr);
...
}
/* variable declaration */
/* array definition */
/* function call */
222
Chapter 5 ■ C Language Programming
int average (int k, int brr[])
{
...
}
/* function definition */
Within main we see a call to the function average. This function call contains two
arguments—the integer variable n and the one-dimensional integer array arr. Note that arr
appears as an ordinary variable within the function call. In the first line of the function definition, we see two formal arguments, k and brr. The formal argument declarations establish
k as an integer variable and brr as a one-dimensional integer array. Note that the size of brr
is not defined in the function definition. As formal parameters in a function definition,
int brr[];
and
int *brr;
are equivalent.
5.7.5 Initializing Arrays
C allows the initialization of arrays. Standard data-type arrays may be initialized in a
straightforward manner. The syntax for initializing an array is
array_declarator 5 { value-list }
The following statement shows a five-element integer array initialization:
int arr[5] 5 {10, 20, 30, 40, 50};
The element arr[0] has the value of 10 and the element arr[4] has the value of 50.
A string (character array) can be initialized in two ways. One method is to make a list of
each individual character:
char strgx[5] 5 {‘w’, ‘x’, ‘y’, ‘z’, 0};
The second method is to use a string constant.
char myname [6] 5 “Edison”;
A null character is automatically appended at the end of “Edison.” When initializing an
entire array, the array size (which is one more than the actual length) must be included.
char prompt [24] 5 “Please enter an integer:”;
5.7.6 Structures
A structure is a group of related variables that can be accessed through a common name.
Each item within a structure has its own data type, which can be different from those of the
other data items. The syntax of a structure declaration is
struct struct_name {
type1
member1;
type2
member2;
...
};
/* struct_name is optional */
The struct_name is optional and, if it exists, defines a structure tag. A struct declaration
defines a type. The right brace that terminates the list of members may be followed by a list of
variables, just as for any basic type. The following example is for a card catalog in a library:
struct catalog_tag {
char author [40];
char title [40];
char pub [40];
223
5.7 ■ Pointers, Arrays, Structures, and Unions
unsigned
unsigned
} card;
int date;
char rev;
where the variable card is of type catalog_tag.
A structure definition that is not followed by a list of variables reserves no storage; it merely
describes a template or the shape of a structure. If the declaration is tagged (i.e., has a name),
however, the tag can be used later in definitions of instances of the structure. For example, suppose we have the following structure declaration:
struct point {
int x;
int y;
};
We can then define a variable pt of type point as follows:
struct point pt;
A member of a particular structure is referred to in an expression by a construction of the form
structure-name.member
or
structure-pointer → member
The structure member operator . connects the structure name and the member name. As
an example, the square of the distance of a point to the origin can be computed as follows:
long integer sq_distance;
...
sq_distance 5 pt.x * pt.x 1 pt.y * pt.y;
Structures can be nested. One representation of a circle consists of the center and radius, as
shown in Figure 5.1.
y
Radius
Center
x
Figure 5.1 ■ A circle
This circle can be defined by
struct circle {
struct
unsigned
};
point
int
center;
radius;
224
Chapter 5 ■ C Language Programming
5.7.7 Unions
A union is a variable that may hold (at different times) objects of different types and sizes,
with the compiler keeping track of size and alignment requirements. Unions provide a way to
manipulate different kinds of data in a single area of storage, without embedding any machinedependent information in the program. The syntax of the union is
union union_name {
type-name1
type-name2
...
type-namen
};
element1;
element2;
elementn;
The field union_name is optional. When it exists, it is also called union-tag. We can declare
a union variable at the same time we declare a union type. The union variable name should be
placed after the right brace }. In order to represent the current temperature using both the integer and string, we can use the following declaration:
union u_tag {
int i;
char c[4];
} temp;
Four characters must be allocated to accommodate the larger of the two types. Integer type
is good for internal computation, whereas string type is suitable for output. Of course, some
conversion may be needed before making a certain kind of interpretation. Using this method,
the variable temp can be interpreted as an integer or a string, depending on the purpose. Syntactically, members of a union are accessed as
union-name.member
or
union-pointer → member
just as for structures.
5.8 Writing C Programs to Perform Simple I/O
The parallel I/O ports have been briefly discussed in Section 4.10. Before performing an I/O
operation, the user needs to configure the I/O port for either input or output. By writing a 1 to a
bit in the data direction register (DDRx, x is the port name), the associated I/O pin is configured
for output. By writing a 0 to a bit in the data direction register, the associated I/O pin is configured for input.
For example, the following statement configures Port B for output:
DDRB 5 0xFF;
The following statement configures Port B for input:
DDRB 5 0;
The user may also configure a few pins of a port for output and other pins for input. For
example, the following statement configures the upper four pins of Port B for output and the
lower four pins of Port B for input:
DDRB 5 0xF0;
5.8 ■ Writing C Programs to Perform Simple I/O
225
To output a value to an output port, simply assign the value to its associated port data register. For example, the following statement outputs the value 0x35 to Port B:
PTB
5 0x35; // the user can also use PORTB instead of PTB
To read a value from an input port, simply assign the associated port data register to the
destination variable. The following statement reads the value from Port A and assigns the value
to the variable xyz:
xyz
5 PTA;
// the user can also use PORTA instead of PTA
Example 5.8
▼
Write a program to drive the LED circuit in Figure 4.16 and display one LED at a time from
the one driven by pin 7 toward the one driven by pin 0 and then reverse. Repeat this operation
forever. Each LED is lighted for 200 ms assuming that the HCS12 uses an 8-MHz crystal oscillator to generate a system clock.
Solution: The values to drive Port B to turn on one LED at a time should be placed in a lookup
table. The program reads one value at a time from the table and outputs it to Port B and then
waits for 200 ms.
The creation of time delays requires the user to set the E-clock frequency properly. The
clock setting issue will be discussed in Chapter 6. However, we will invoke the SetClk8 function in Chapter 6 to set the E-clock to 24 MHz. The 200-ms time delay can be created by calling
the delayby100ms() function and passing 2 as the parameter. The C program that performs the
desired operation is as follows:
#include “c:\cwHCS12\include\hcs12.h”
void SetClk8(void);
void delayby100ms(int k);
void main (void)
{
unsigned char led_tab[16]5 {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
char i;
DDRB
5 0xFF; // configure Port B for output
DDRJ
|5 0x02; // configure PJ1 pin for output
PTJ
&5 0xFD; // enable LEDs to light
SetClk8();
// enable PLL and set E-clock to 24-MHz using a 4-MHz crystal oscillator
while (1) {
for (i 5 0; i , 16; i11) {
PTB 5 led_tab[i];
// output a new LED pattern
delayby100ms(2); // wait for 200 ms
}
}
}
// Include the SetClk8() function here (to be discussed in Chapter 6)
// Include the delayby100ms() function here (to be discussed in Chapter 8)
▲
226
Chapter 5 ■ C Language Programming
Example 5.9
▼
Write a program to display the following patterns from the seven-segment display circuit
shown in Figure 4.18 from display #0 to #5 and repeat with each pattern lasting for 600 ms:
123456
234567
345678
456789
567890
678901
789012
890123
901234
012345
Solution: Since there are 10 different segment pattern sequences and two adjacent sequences are
offset by 1, we can overlap these 10 sequences as shown in Figure 5.2.
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
Figure 5.2 ■ Seven-segment display patterns overlapped
▲
Figure 5.1 gives an idea how to produce such a display pattern.
Let SegPat, i, j, and k represent the segment pattern array, the start index of the pattern array for the sequence in effect, the number of times remaining for the current display sequence
to be repeated, and the display to be lighted, respectively.
The algorithm of the program is as follows:
Step 1
i ← 0;
Step 2
j←0
Step 3
k←0
Step 4
Output SegPat[i1k] to Port B, and turn on seven-segment display #k.
Step 5
Wait for 1 ms; k ← k 1 1;
5.9 ■ Miscellaneous Items
227
Step 6
If (k , 6) go to step 4.
Step 7
j ← j 1 1;
Step 8
If (j , 100) go to step 3.
Step 9
i ← i 1 1;
Step 10
If (i , 10)
go to step 2;
else go to step 1.
The program that can display the specified seven-segment pattern sequence is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\delay.h”
void SetClk8(void);
unsigned char SegPat[16] 5 {0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x67, 0x3F,
0x06, 0x5B, 0x4F, 0x66, 0x6D};
unsigned char digit[6]
5 {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF};
void main(void) {
int i, j, k;
SetClk8(); // set E-clock frequency to 24 MHz
DDRB 5 0xFF; //configure Port B for output
DDRP 5 0xFF; //configure Port P for output
while(1) {
for (i 5 0; i , 10; i11) { // pattern array start index
for (j 5 0; j , 100; j11) { // repeat loop for each pattern sequence
for (k 5 0; k < 6; k11) { // select the display # to be lighted
PTB
5 SegPat[i1k]; // output segment pattern
PTP
5 digit[k];
// output digit select value
delayby1ms(1);
// display one digit for 1 ms
}
}
}
}
}
// include SetClk8(void) here
5.9 Miscellaneous Items
5.9.1 Automatic, External, Static, and Volatile
A variable defined inside a function is an internal variable of that function. These variables
are called automatic because they come into existence when the function is entered and disappear when it is left. Internal variables are equivalent to local variables in assembly language.
External variables are defined outside of any function and are thus potentially available to
228
Chapter 5 ■ C Language Programming
many functions. Because external variables are globally accessible, they provide an alternative
to functional arguments and return values for communicating data between functions. Any
function may access an external variable by referring to it by name, if the name has been declared somehow. External variables are also useful when two functions must share some data,
yet neither calls the other.
The use of static with a local variable declaration inside a block or a function causes the
variable to maintain its value between entrances to the block or function. Internal static variables are local to a particular function just as automatic variables are, but unlike automatic
variables, they remain in existence rather than coming and going each time the function is
activated. When a variable is declared static outside of all functions, its scope is limited to the
file that contains the definition. A function can also be declared as static. When a function is
declared as static, it becomes invisible outside of the file that defines the function.
A volatile variable has a value that can be changed by something other than the user code. A
typical example is an input port or a timer register. These variables must be declared as volatile so
the compiler makes no assumptions on their values while performing optimizations. The keyword
volatile prevents the compiler from removing apparently redundant references through the pointer.
5.9.2 Scope Rules
The functions and external variables that make up a C program need not all be compiled at
the same time; the source text of the program may be kept in several files, and previously compiled routines may be loaded from libraries.
The scope of a name is the part of the program within which the name can be used. For a
variable declared at the beginning of a function, the scope is the function in which the name is
declared. Local (internal) variables of the same name in different functions are unrelated.
The scope of an external variable or a function lasts from the point at which it is declared
to the end of the file being compiled. Consider the following program segment:
...
void f1 (. . .)
{
...
}
int a, b, c;
void f2 (. . .)
{
...
}
Variables a, b, and c are accessible to function f2 but not to f1.
When a C program is split into several files, it is convenient to put all global variables into
one file so that they can be accessed by functions in different files. Functions residing in different files that need to access global variables must declare them as external variables. In addition, the prototypes of certain functions can be placed in one file so that they can be called by
functions in other files. The following example is a skeletal outline of a two-file C program that
makes use of external variables:
In file1:
extern int xy;
extern long arr[];
main ( )
5.9 ■ Miscellaneous Items
229
{
...
}
void foo (int abc) { . . . }
long soo (void) { . . . }
In file2:
int xy;
long arr[100];
5.9.3 Type Casting
Type casting causes the program to treat a variable of one type as though it contains data of
another type. The format for type casting is
(type) variable
For example, the following expression converts the variable kk to a long integer:
int kk;
...
(long) kk
Type casting can avoid many errors caused by size mismatch among operands. For example,
in the following program segment:
long result;
int
x1, x2;
...
result 5 x1 * x2;
if the product of x1 and x2 is larger than 216 2 1, it will be truncated to 16 bits. Then the variable result will receive an incorrect value. To fix the error, use type casting to force x1 and x2 to
long integers, as follows:
result 5 ((long) x1) * ((long) x2);
This technique is used in several examples in this text.
Another example of the use of type casting is in pointer type. Sometimes one needs to treat
the contents of a structure type variable as a string. The most convenient way to do it is to
recast the pointer to a structure-type variable into a pointer to a string (character type). For the
declarations
struct personal {
char name
[10];
char addr [20];
char sub1[5];
char sub2[5];
char sub3[5];
char sub4[5];
} ptr1;
char *cp;
we can use the following statement to treat the variable ptr1 as a string:
cp 5 (char *) &ptr1;
230
Chapter 5 ■ C Language Programming
5.10 Using the C Compiler
There are many C compilers that support the HCS12 microcontroller. The freeware GNU
C compiler has a port for HCS12 and Eric Engler wrote an IDE (referred to as EGNU) that provides a simple development environment for the GNU C compiler. The combination of the
EGNU IDE and the GNU C compiler for the HCS12 is free to the user. The EGNU IDE contains
a text editor, a project manager, and a terminal program and works with D-Bug12 monitor.
Freescale provides a special edition of CodeWarrior IDE that supports the entering, compilation,
and source-level debugging of C programs for the HCS12 up to the 32-kB size limit. CodeWarrior supports the serial monitor and several BDM debuggers. The ImageCraft C compiler is
also popular in academic institutions. The tutorials for using CodeWarrior and ImageCraft C
compilers will be given in this chapter whereas the tutorial for using the GNU C compiler and
EGNU IDE is given in Appendix E.
5.10.1 Issue in Accessing Peripheral Registers
An important part of microcontroller programming is to access peripheral registers.
An address is assigned to each peripheral register. A C program may assign a value to a peripheral register or read the contents of a peripheral register. As we have done in Section 5.8, we
assign a value to the name of a register in order to write to a register. Referring to the C language
syntax, the peripheral name actually refers to the contents of a memory location. On the other
hand, a peripheral register may be 8-bit (unsigned character type) or 16-bit (unsigned integer
type). Therefore, a peripheral register should be declared using one of the following methods:
j
j
#define reg_name *(volatile unsigned char *) reg_addr
#define reg_name *(volatile unsigned int *) reg_addr
The phrase “volatile unsigned char *” typecasts reg_addr to a pointer to unsigned character whereas “volatile unsigned int *” typecasts reg_addr to a pointer to unsigned integer. After
applying the dereferencing operator *, we can assign a value to the register name to change its
value. The keyword volatile is used because the register contents may be changed by external
events instead of program assignment.
The HCS12 allows all peripheral registers as a block to be remapped to other memory locations. To support this, the reg_addr is written as the sum of a base address and an offset. By
default, the register block base address is set to 0x0000. The previous declaration can be rewritten as
#define
#define
#define
reg_base
reg_name1
reg_name2
0x0000
*(volatile unsigned char *) (reg_base 1 offset1)
*(volatile unsigned int *) (reg_base 1 offset2)
This method is used in ImageCraft C compiler MCU header file. For example, the Port A data
register PORTA is defined as
#define
#define
REG_BASE
0x0000
PORTA
(*(volatile unsigned char *)(REG_BASE 1 0x00000000))
Of course, there are other methods for defining the peripheral register name. The header file
hcs12.h provided in the complementary CD uses macro substitution as follows:
#define
#define
#define
#define
IOREGS_BASE
_IO8(off)
_IO16(off)
PORTA
0x0000
*(unsigned char volatile *)(IOREGS_BASE 1 off)
*(unsigned short volatile *)(IOREGS_BASE 1 off)
_IO8(0x00)
// port A data register
5.11 ■ Using the CodeWarrior IDE to Develop C Programs
#define
#define
PTA
ATD0DR0
_IO8(0x00)
_IO16(0x90)
231
// alternate name for PORTA
// ADC result 0 register (a 16-bit register)
The header file is usually stored under the include directory. For example, the hcs12.h file
is stored in the directory c:\iccv712\include (c:\cwHCS12\include and c:\egnu\include). The directory
cwHCS12 is the installation directory for CodeWarrior used by this author whereas the include directory is created under the cwHCS12 directory. One should add one of the following statements
to the C program if one is developing application programs that involve peripheral registers:
#include “c:\iccv712\include\hcs12.h”
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\egnu\include\hcs12.h”
5.10.2 Peripheral Register Bit Definitions
In addition to the association of register names and addresses, the header file hcs12.h also
contains the bit values for many registers. Each bit is associated with a value that is equal to its
position weight. For example, the seventh bit of the ATD0CTL2 register is defined as follows:
#define
ADPU
0x80
With this definition, you can use the following statement to set the ADPU bit of the ATD0CTL2
register:
ATD0CTL2 |5 ADPU;
// set the ADPU bit (bit 7)
To clear the ADPU bit, use the following statement:
ATD0CTL2 &5 ,ADPU;
// clear the ADPU bit
All register names and all bit names are in uppercase. The user can change the case to satisfy his or her preference. The header file provided by ImageCraft C compiler and the hcs12.h
file provided in the complementary CD use the same bit names as defined by Freescale. Header
files for other HCS12 members can be derived by editing the hcs12.h file.
5.10.3 Inline Assembly Instructions
Most C compilers allow the user to add inline assembly instructions in the C program. The
syntax for inline assembly is
asm(“<string>”);
For example, adding the following statement will cause the program control to return to the
D-Bug12 monitor when the program is running on a demo board with the D-Bug12 monitor:
asm(“swi”);
Adding the following statement will enable interrupt globally:
asm(“cli”);
5.11 Using the CodeWarrior IDE to Develop C Programs
CodeWarrior supports program debugging for demo boards programmed with the serial
monitor or through the BDM debugger. CodeWarrior can be started by clicking on its icon.
5.11.1 Entering C Programs in CodeWarrior
The startup screen of CodeWarrior is shown in Figure 5.3. The user has four options to choose
from. If the user wants to enter one or more C functions before creating a new project, she or he
232
Chapter 5 ■ C Language Programming
Figure 5.3 ■ CodeWarrior startup screen
should click on the “Start Using CodeWarrior” button. After clicking on the “Start Using CodeWarrior” button, the “Tips of the Day” will appear. Click on Close to get rid it. The user can also
choose to read a few tips of the day. The next screen will look like that in Figure 5.4.
Figure 5.4 ■ CodeWarrior screen after getting rid of “Tips of the Day”
5.11 ■ Using the CodeWarrior IDE to Develop C Programs
233
In the following, we will enter a program that finds the maximum and minimum elements
of an array of integers.
To enter a new program file, press the File menu and select New Text File from the CodeWarrior window as shown in Figure 5.5. An empty screen with the keyword untitled on the
top bar will appear and the user can start to type in the program. The entered program that
finds the maximum and minimum elements of the given array is shown in Figure 5.6.
Save the entered program by pressing the File menu and select Save As as shown in
Figure 5.7. The user should enter the filename and press Save, and the file will now be saved.
Figure 5.5 ■ Screen for selecting the command to enter a new text file
Figure 5.6 ■ Program for finding the maximum and minimum elements of an array
234
Chapter 5 ■ C Language Programming
Figure 5.7 ■ Save the entered program in a file called “minimax.c”
5.11.2 Create a New Project
Like most IDEs, CodeWarrior also uses the project to manage programming effort. A new
project can be created by pressing the File menu and selecting New . . . as shown in Figure 5.8.
After selecting New . . . , a popup dialog prompts the user to enter the project name as shown
in Figure 5.9. The user also needs to select the directory to hold the project information. This is
done by clicking on the button Set in Figure 5.9. After that another dialog box allows the user to
browse and select the directory to hold the project.
Figure 5.8 ■ Select “New . . .” under the File menu to create a new project
5.11 ■ Using the CodeWarrior IDE to Develop C Programs
235
Figure 5.9 ■ Popup dialog that allows the user to enter the project name and
project directory
Figure 5.10 ■ Popup dialog box for setting project directory
After selecting the project directory, the user should click on the Save button to save the
project information. As shown in Figure 5.10, the user enters minmax as the name of the project to save. After this, the screen will change back to that in Figure 5.9. The user should click
on OK and the screen will change to that in Figure 5.11. Click on Next and the screen for selecting the HCS12 device will appear as shown in Figure 5.12.
236
Chapter 5 ■ C Language Programming
Figure 5.11 ■ CodeWarrior screen after saving a newly created project
The user should select an appropriate HCS12 device and click on the Next button. The
screen will then change to that in Figure 5.13. The user should select the programming language using the dialog screen in Figure 5.13 and click on Next.
Figure 5.12 ■ Project wizard dialog for selecting the device
5.11 ■ Using the CodeWarrior IDE to Develop C Programs
237
Figure 5.13 ■ Project wizard dialog for selecting programming language
After selecting the programming language, there are six more dialogs before reaching the
dialog for selecting the connection method shown in Figure 5.14.
1. Use processor expert dialog: select no.
2. Want to use the OSEKturbo operating system in the project dialog: select no.
Figure 5.14 ■ Project wizard dialog for selecting connection method
238
Chapter 5 ■ C Language Programming
3. Want to create a project set up for PC-lint dialog: select no.
4. Level of startup code dialog: select ANSI startup code.
5. Select floating-point format supported dialog: select none to reduce overhead.
6. Memory model selection dialog: select banked.
In Figure 5.14, we select three connection methods so that we can switch among them.
Click on the Finish button, and the screen is changed to that in Figure 5.15.
Figure 5.15 ■ CodeWarrior screen after a new project is created
In Figure 5.15, the P&E Multilink CyclonePro connection method is selected. However,
the user can switch to two other connection methods depending on the debugging method that
is used.
5.11.3 Adding Source Code to the Project
In the left pane of Figure 5.15, there are two files under the Sources directory: main.c and
datapage.c. Both are generated by CodeWarrior. The datapage.c file contains paged data access
runtime routines that handle the data access across different pages in expanded memory. Users
do not need to be concerned about this file.
The main.c file contains the template of the required main function. The user can modify
this function to perform the desired operation or delete it from the project and add the desired
main function into the project. We will adopt the second approach.
To delete a file, press the right mouse button on the filename to bring up the popup dialog for deleting the file as shown in Figure 5.16. After removing main.c from the project, we
press the right mouse button on the Sources directory and select Add Files. A new popup dialog
as shown in Figure 5.17 will be brought up to allow the user to select a file to be added into
the Sources directory. We can now use the mouse to browse the file directory tree to select
the file to be added. In this tutorial, we add minmax.c that was created earlier to the Sources
directory. The screen will change to that in Figure 5.18 after selecting the file (minmax.c) to be
added to the project. After clicking on OK, the minmax.c file will be added under the Sources
directory.
5.11 ■ Using the CodeWarrior IDE to Develop C Programs
Figure 5.16 ■ Popup dialog for removing a file
Figure 5.17 ■ Popup dialog for adding a file into the project
Figure 5.18 ■ CodeWarrior screen after selecting a file to be added into the project
239
240
Chapter 5 ■ C Language Programming
The contents of a file can be displayed in the right pane of the CodeWarrior window by
double clicking on the filename.
5.11.4 Building the Project
A project can be built by pressing the Project menu and selecting Make (shown in
Figure 5.19) or simply pressing the F7 function key. After a project build, CodeWarrior will display error messages and warnings if there is any syntax error or potential problem, as shown in
Figure 5.20. The user must fix program errors in order to get the project built. CodeWarrior will
not display anything if there are no errors. In Figure 5.20, the warning is caused by the statement of while(1); in Figure 5.19; this is fine.
Figure 5.19 ■ Select “Make” to build (compile the programs contained in) the project
Figure 5.20 ■ Error and warnings after building a project
5.11 ■ Using the CodeWarrior IDE to Develop C Programs
241
5.11.5 Executing and Debugging the Program with CodeWarrior
Program debugging is carried out by the debugger. The CodeWarrior debugger is started
by pressing the function key F5 or pressing the Project menu and selecting Debug from the
CodeWarrior IDE window. The initial response of the CodeWarrior debugger varies depending
on the chosen connection method.
INITIAL DEBUGGER RESPONSE FOR THE SERIAL MONITOR
The debugger startup screen for the serial monitor is shown in Figure 3.34. The user should
select the appropriate communication port and then click on OK. After the debugger resets the
MCU successfully, the screen will change to that in Figure 5.21. If the screen is not the same as
that in Figure 5.21, the user should press the Reset button on the demo board.
I N I T I A L R E S P O N S E U N D E R T H E P & E M U LT I L I N K
If the user uses a P&E Multilink or a CyclonePro debugger to debug the program running
on the Dragon12-Plus demo board, the initial debugger screen after pressing the F5 function
will look like that in Figure 3.36. This screen asks the user whether it is OK to erase the flash
memory and download the program onto the memory. Click on OK and the screen will change
to that shown in Figure 5.21. The debugger screen layout is explained in Section 3.9.5.
Figure 5.21 ■ CodeWarrior debugger screen after program is downloaded into the memory
242
Chapter 5 ■ C Language Programming
CODEWARRIOR DEBUGGER SCREEN
The debugger screen is divided into eight small windows. Not all the windows are useful in the
debug session. The user can get rid of those windows that are of no interest by clicking on the 3
symbol of that window’s upper right corner. In this tutorial, we get rid of the Procedure window and
resize the Source so that more statements of the main function can be displayed at the same time.
The windows within the debugger screen can be moved around and resized. The Data:1 window displays the global variable (ave) and its value. The Data:2 window displays the local variables (max, min, i, and arr) and their values.
The Assembly window shows how the main function is translated into assembly language.
The main function has four local variables (one is an array with 30 elements) and needs 66 bytes
to hold them. The first instruction, LEAS 266,SP allocates space in the stack to these local
variables. The following instructions pushed the initial values of the array into the stack (pushing one value requires three instructions):
ldab
clra
std
...
#50
6,SP
DEBUGGER COMMANDS
CodeWarrior debugger provides commands to support debug activities. By pressing the
right mouse button on any statement of the main function or other function, one can see all the
debug commands supported by the debugger as shown in Figure 5.22.
If we single-step (by clicking on the corresponding icon shown in Figure 3.35) the program a
few times, we can see that the debugger steps over three instructions to push one array element
into the stack.
Figure 5.22 ■ CodeWarrior debugger commands
5.12 ■ Using the ImageCraft C Compiler
243
Now place the mouse on the statement min 5 arr[0];, press the right mouse button, and
select Run To Cursor. We can see that the value of max in the Data:2 window changes to 50, and the
instruction STD 2,SP is highlighted in the Assembly window (shown in Figure 5.23). The value of
max is correct.
Figure 5.23 ■ CodeWarrior debugger screen after program executes until “min 5 arr[0];”
statement
Now press the mouse on the statement while(1); and execute the Run To Cursor command
again; the debugger window will change to that in Figure 5.24. The values of variables min, max,
i, and ave are changed to 8, 910, 30, and 459, respectively, and are correct. The variables min
and max receive the minimum and maximum values of the given array as expected whereas the
value of ave equals the average of min and max.
This completes the CodeWarrior debugger tutorial. It is highly recommended that the reader
experiment with other debug commands to become more familiar with the use of the debugger.
5.12 Using the ImageCraft C Compiler
The ImageCraft ICC12 is a simple IDE that combines a text editor, a project manager, a C
compiler, and a terminal program. The terminal program allows the ICC12 to work with the
Dragon12-Plus programmed with the D-Bug12 monitor.
244
Chapter 5 ■ C Language Programming
Figure 5.24 ■ CodeWarrior debugger screen after program runs into the “while(1);”
statement
5.12.1 Starting the ICC12 IDE
The ImageCraft ICC12 IDE can be started by clicking on its icon. The startup window of
ICC12 is shown in Figure 5.25.
The user enters his or her program using the work space. When the program is successfully
compiled into a .s19 file, the user also uses the work space as the terminal window to download
Figure 5.25 ■ ImageCraft ICC12 IDE startup window
5.12 ■ Using the ImageCraft C Compiler
245
the file onto the demo board for execution. Error messages and warnings generated by the ICC12
IDE are displayed in the status pane. The project file structure is displayed in the Project pane.
When the user uses the ICC12 IDE for the first time, he or she needs to set up the compiler options. The compiler options dialog can be brought up by pressing the Project menu in
Figure 5.25 and selecting Options . . . . A dialog box as shown in Figure 5.26 will appear to allow
the user to set all the compiler options to appropriate values. The setting in Figure 5.26 will
work for the Dragon12-Plus demo board. When the user is satisfied with the compiler options,
he or she should click on OK to get rid of the dialog.
Figure 5.26 ■ Dialog for setting ICC12 compiler options
Compiler options need only be set once. The ICC12 C compiler will remember the options
until the user changes them.
5.12.2 Creating a New Project
A new project can be created by pressing the Project menu and selecting New. A popup
dialog box as shown in Figure 5.27 will appear to allow the user to enter the project name and
select the project directory (specified in the space to the right of Save in:). If we set the project
name to ICCTUTOR and save it in the directory of ch05 and then click on Save, the screen will
change to that in Figure 5.28.
5.12.3 Adding Files to the Project
A new file can be created by pressing on the File menu and selecting New to convert the
work-space pane in Figure 5.25 into an editor work space. We will use the program that finds
the greatest common devisor (gcd) as an example to illustrate the project build process for the
ICC12 IDE. The program is shown in Figure 5.29. The printf function provided by ImageCraft
requires the putchar function to work. However, putchar was not provided by the ICC12 IDE. A
version must be created and added to your project.
246
Chapter 5 ■ C Language Programming
Figure 5.27 ■ Dialog for creating a new project
Figure 5.28 ■ ICC12 IDE screen after a new project is created
We can add an existing file into the project or enter a new program and then add it into
the project. There are three directories in the Project pane. To add an existing file into the
project, press the right mouse button on Files in the Project pane to bring up the dialog for adding files into the project (shown in Figure 5.30). After adding all the files needed in the project,
the ICC12 IDE screen changes to that in Figure 5.31. We can examine the contents of a file by
double clicking on the file name.
5.12.4 Building the Project
A project can be built (compiled and linked) by pressing the Project menu and selecting
Make Project or clicking on the function key F9. Error messages and warnings will be displayed
5.12 ■ Using the ImageCraft C Compiler
Figure 5.29 ■ The GCD function and its test program
Figure 5.30 ■ Add a file into the project “ICCTUTOR”
247
248
Chapter 5 ■ C Language Programming
Figure 5.31 ■ ICC12 window after adding files to the project “ICCTUTOR”
Figure 5.32 ■ ICC12 project build message without errors
on the status pane. The gcd function and its test program have no errors, and the build message
is shown in Figure 5.32.
5.12.5 Executing and Debugging the Program with the ICC12 IDE
Unlike CodeWarrior, ICC12 does not have source-level debugging capability. The user
will need to use the D-Bug12 monitor commands to find out whether the program executes
correctly. Since variables declared within a function (including the main function) are local
variables, they are allocated in the stack and hence become invisible when the function is exited. In order to view the program execution result, the variables of interest must be declared
as global variables (i.e., outside all functions). In this example, the declaration of the variable
result should be moved out of the main function.
5.12 ■ Using the ImageCraft C Compiler
249
Figure 5.33 ■ Find out the address of program variables by examining the map file
After making result a global variable, remake the project. The project build process generates a map file. For example, the map file of the icctutor project is icctutor.mp. The user can
scroll around the file icctutor.mp and find the address of result (changed to _result). As shown
in Figure 5.33, the variable result is assigned to the memory location $1000.
A C T I VAT E T H E T E R M I N A L W I N D O W
Before the user can download the program onto the demo board, she or he needs to switch
the work space to be used as the terminal window because the work space can be used as the
editor space or terminal window. Activate the terminal window by pressing the Terminal menu
and select Show Terminal Window. The ICC12 screen with the terminal window activated is
shown in Figure 5.34.
Figure 5.34 ■ ICC12 screen with terminal window activated
250
Chapter 5 ■ C Language Programming
Figure 5.35 ■ Dialog for configuring Terminal setting
When using the terminal window for the first time, the user needs to perform the appropriate configuration. The user should press the Tools menu and select Environment Options
to bring out the dialog as shown in Figure 5.35. The baud rate should be set to the same as the
demo board (9600 for D-Bug12 monitor). The choices for the flow control and ASCII Transfer
Protocol should be set to None.
DOWNLOADING THE PROGRAM FOR EXECUTION
The terminal environment needs to only be configured once. The ICC12 IDE will save the
setting for future use. Before the PC can communicate (via the ICC12 terminal window) with
the D-Bug12 monitor on the demo board, the user should click on the Open Com Port button.
Clicking on the Open Com Port button requests the PC to configure the selected COM port
(COM 1 in Figure 5.35) to operate with the same setting as that of the demo board.
The user can then press the Enter key to bring up the D-Bug12 monitor prompt as shown in
Figure 5.36. To download a program file onto the demo board for execution, type the command
Load and then press the Enter key. After this, the user can use the mouse to browse the directory to select the appropriate .s19 (icctutor.s19 in this example) to be downloaded.
Clicking on the Open button in Figure 5.36 will cause Windows to transfer the selected file
into the main memory from the hard disk. Clicking on the Download! button in Figure 5.36
will start the download operation. After the icctutor.s19 file is downloaded, the ICC12 IDE
will make a sound and the screen will change to that in Figure 5.37. Due to a bug in ICC12, the
completion of the file download will not bring up the D-Bug12 monitor prompt.
To bring back the D-Bug12 monitor prompt, press the Reset button on the demo board. To
execute the downloaded program, type g 1500 and then press the Enter key. To display the value
of the result variable, type the md 1000 command. The screen will change to that shown in
Figure 5.38 after the previous commands are entered. The contents of the memory location at
$1000 and $1001 are $012C (5 30010) and are correct.
5.12 ■ Using the ImageCraft C Compiler
251
Figure 5.36 ■ Select a file (ICCTUTOR.s19) to be downloaded
Figure 5.37 ■ ICC12 screen after a successful program download
Because the ICC12 IDE cannot update the variable values at the breakpoint like the CodeWarrior IDE, the user is advised to use the print function call or output data on the LCD or
other output devices to verify the program execution result.
If the user wants to edit the gcd.c file, he or she can double-click on gcd.c under the Files
directory in the Project pane. This completes the tutorial of the ICC12 IDE.
252
Chapter 5 ■ C Language Programming
Figure 5.38 ■ ICC12 terminal window after program execution and the “md 1000”
command
5.13 Programming Style
Programming style refers to a set of rules or guidelines used when writing a program. It is
believed that by following a particular programming style the resultant program will be more
readable and easier to debug. The programming style used in a particular program may be derived from the coding standards or code conventions of a company or other computing organization, as well as the preferences of the author of the code. Programming styles are often designed
for a specific programming language (or language family), but some rules are commonly applied
to many languages. (Style considered good in C source code may not be appropriate for BASIC
source code, and so on.)
The essence of good programming style is communication. Any program that will be used
must be maintained by somebody and that somebody must be able to understand the code by
reading it. Any program that needs debugging will be easier to debug if the creator carefully
explains what’s going on. Within the program text, programmers have three primary tools for
communicating their intentions: comments; naming of variables, constants, and subroutines;
and program appearance (spacing, alignment, and indentation).
5.13.1 General Guidelines to Comments
The most useful things to know about program documentation are
• What to comment
• When to comment
In general, the programmer should include comments explaining what every function does,
what every variable does, and an explanation for every tricky expression or section of code.
“When” is easy—comment it before you write it. Whenever you declare a variable, include a
comment. Whenever you start to write a subroutine, first write a comment explaining it. That
will help to make clear in your mind what you are about to do. If you find the explanation for
a subroutine difficult to write, it’s a sure sign that you have done a poor job of structuring the
program. Avoid obscure programming language constructs and reliance on language-specific
precedence rules. It is often better to force precedence by use of parentheses since this leaves
no doubt as to meaning. In general, if you had to look up some rule or definition, your readers
most likely will too. Whenever you need to write a difficult expression or some other tricky
business and find it difficult to do, you should expect that it will be difficult to understand. So,
add a comment.
5.13 ■ Programming Style
253
5.13.2 Program Documentation
All programs should include, at or near the beginning of the program (the main() function in
C), a comment block. The comment block should include at least the following information:
• The programmer’s name
• The date of creation
• The operating system and IDE for which the program was written
• Hardware environment (circuit connection) to run the program
• Program structure (organization)
• Algorithm and data structures of the program
• How to run the program
Below is an example of a program comment header block.
// ********************************************************************************
// Program: RtiMultiplex7segs
// Author: Han-Way Huang
// Build Environment: CodeWarrior IDE under Windows XP
// Date: 07/09/2008
// Hardware connection: shown in Figure 4.18 of Huang’s HCS12 text
// Operation: This program shifts seven-segment patterns of 4 consecutive BCD digits
//
by using time-multiplexing technique with each pattern lasting for 0.5 s. The
//
time-multiplexing operation is controlled by the real-time interrupt. The patterns are
//
1234
//
2345
//
3456
//
4567
//
5678
//
6789
//
7890
//
8901
//
9012
//
0912
// ********************************************************************************
5.13.3 Function (Subroutine) Documentation
Each C function should have a clearly defined task. Writing the comment describing that
task before writing the function can help to keep you straight. Functions should be short enough
so that their meanings can be grasped as a unit by the reader. Normally, one page of code represents the limit of intellectual complexity that a reader can grasp.
Similar to the main() function, every function should be preceded by a comment describing the
purpose and use of that subroutine. Below is an example of a subroutine comment header block.
// ********************************************************************************
// Function: FindSquareRoot
// Purpose: Computes and returns the square root of a 32-bit unsigned integer
// Parameter: A 32-bit unsigned integer of which the square root is to be found
// Called by: PrimeTest()
// Returned value: the square root of a 32-bit unsigned integer
// Side effects: none
// ********************************************************************************
254
Chapter 5 ■ C Language Programming
One of the greatest sources of bugs is in the interface between functions. It is a common
mistake to have type mismatches between the formal parameters and the actual parameters. It
is also common to mis-order parameters, or to leave some out. The programmer should always
be scrupulous in checking for such problems when writing a program and do it early on. One
way to minimize the problem is to keep the number of parameters to any one function at a minimum. Seven or more parameters for a function is probably too many—having this many parameters is an indication that the programmer has structured his or her program in the wrong way.
5.13.4 Code Appearance
In addition to comments, program readability can be improved by
• Proper spacing
• Proper indentation
• Vertical alignment
P R O P E R S PA C I N G
Adding spaces makes your program statements easier to read. For example, it is quite obvious that the second one of the following two statements is easier to read:
for (i 5 0; i , 15; i11)
&
for (i 5 0; i , 15; i11)
P R O P E R I N D E N TAT I O N
Indentation is especially helpful in improving the readability of nested if-elseif statements
and nested program loops. With proper indentation, the logical hierarchies of nested if-elseif
statements and nested program loops become much clearer. For example, a programmer may
write the main loop of the seven-segment patterns shifting program as follows:
while(TRUE) {
for (i 5 0; i , 10; i11) {
for (j 5 0; j , 100; j11) {
for (k 5 0; k , 6; k11) {
PTB 5 SegPat[i1k];
PTP 5 digit[k];
delayby1ms(1);
}
}
}
}
// pattern array start index
// repeat loop for each pattern sequence
// select the display # to be lighted
// output segment pattern
// output digit select value
// display one digit for 1 ms
Obviously, the readability of these statements is very poor. It is quite difficult to identify
the hierarchical relationship of these for statements. The readability would be much improved
if these for statements are indented as follows:
while(TRUE) {
for (i 5 0; i , 10; i11) { // pattern array start index
for ( j 5 0; j , 100; j11) { // repeat loop for each pattern sequence
for (k 5 0; k , 6; k11) {
// select the display # to be lighted
PTB 5 SegPat[i1k];
// output segment pattern
PTP 5 digit[k];
// output digit select value
255
5.13 ■ Programming Style
delayby1ms(1);
}
// display one digit for 1 ms
}
}
}
VERTICAL ALIGNMENT
It is often helpful to align similar elements vertically, to make typo-generated bugs more
obvious. Compare the array definitions given in Figure 5.39; it is obvious that the reader will be
able to locate any missing elements more easily with version b than with version a.
(a) non-vertically aligned array
// values to generate 1 cycle of sine wave
unsigned rom char upper[60] = {0x38, 0x38, 0x39, 0x3A, 0x3B, 0x3C,
0x3C, 0x3D, 0x3D, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D,
0x3C, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x38, 0x37, 0x36, 0x35, 0x34, 0x34,
0x33, 0x32, 0x32, 0x31, 0x38, 0x37, 0x36, 0x35, 0x34, 0x34,
0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33,
0x34, 0x34, 0x35, 0x36, 0x37};
(b) vectically aligned array
// values to generate 1 cycle of sine wave
unsigned rom char upper[60] = {
0x38, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3C, 0x3D, 0x3D, 0x3E,
0x3E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3E, 0x3E, 0x3D, 0x3D, 0x3C, 0x3C, 0x3B, 0x3A, 0x39, 0x38,
0x38, 0x37,0x36,0x35,0x34,0x34,0x33,0x32,0x32,0x31,
0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x31, 0x31, 0x32, 0x32, 0x33, 0x34, 0x34, 0x35, 0x36, 0x37};
Figure 5.39 ■ Influence of vertical alignment of array on readability
5.13.5 Naming of Variables, Constants, and Functions
The names of variables, constants, and functions should spell out their meaning or
purpose.
A variable name may have one word or multiple words. Use lowercase when the name contains only one word. For example, sum, limit, and average are examples of single-word variable
names. A multiple-word variable name should be in mixed case starting with lowercase. For
example,
inBuf, outBuf, squareRoot, arrayMax
256
Chapter 5 ■ C Language Programming
Function names should follow the same principle. A few examples of function names are
sevenSegShift(), putcSPI(), putsSPI(), openLCD(), and putsLCD()
Named constants should be all uppercase using underscores to separate words. A few
examples of constant names are
SONG_TOTAL_NOTES, HI_DELAY_COUNT, LO_DELAY_COUNT
5.14 Tips for C Program Debugging
As in assembly language, C program errors can be divided into two categories:
• Syntax/semantics errors
• Logical errors
Many syntax/semantics errors can be discovered if the programmer follows the rules of C
language.
• Undeclared variables. A variable must be declared before it can be used. Most
errors of this type can be discovered easily.
• Variable and symbol names are case sensitive. Sometimes, the programmer has
declared a variable but still has an undeclared variable error with the same variable.
This error could occur because one or more characters of the variable name are in
the wrong case. For example, in the program segment
int
A1, A2, xyClk;
...
A1 5 A1 * xycLk;
the compiler would output the error message “undefined variable xycLk.”
• Missing parenthesis (or brace or bracket). A missing parenthesis can cause many
other errors. This type of error can be avoided by entering the matching parenthesis
whenever the left parenthesis is entered before entering statements between the
parentheses.
• Mismatch of function name in function prototype declaration and function
definition. The EGNU will generate the error message “unknown error—see
Make Log.” When the user clicks on Make log on the status pane of EGNU,
the message “undefined reference to . . .” will appear. ICC12 and CodeWarrior
C compilers would output the error message “reference functions without
prototype.”
Logical errors are harder to debug than syntax and semantic errors. Whenever the program
behavior is not what we expect, the first step is to read the program carefully to find out where
the most likely problem spot is. After identifying the potential problem spot, we can set a breakpoint and examine the program execution result.
If the user has a source-level debugger (CodeWarrior) to debug the program, the following
actions can be taken to solve the problem:
• Set breakpoints. Breakpoints allow the user to examine program execution results
at the suspicious point.
• Set up a watch list (in Data:1 and Data:2 windows in CodeWarrior IDE). A watch
list is used together with the breakpoints. A watch list consists of pairs of program
5.15 ■ Summary
257
variables of interest and their values at a certain time. It allows the user to find out
program execution results quickly at any breakpoint.
• Trace program execution. Users can find out the execution result of a few instructions
by tracing instruction execution. Without a source-level debugger, it is very difficult
to trace the program execution unless the D-bug12 monitor commands are used.
The freeware EGNU and ICC12 IDE provide very little support for debugging. A commercial source-level debugger for C language can cost thousands of dollars. Without a source-level
debugger, many debugging activities cannot be performed easily. CodeWarrior IDE provides
very useful and helpful features to support programming debugging.
There are several purposes for setting breakpoints.
• To determine whether a segment of code has ever been entered by the CPU. The
programmer can use the embedded assembly instruction asm(swi) to find out with
a demo board programmed with the D-Bug12 monitor. Using the swi instruction
allows the programmer to find out where the program execution gets stuck and
identify the error.
• To determine whether the execution result is correct up to the breakpoint. Without
a source-level debugger, the programmer can output the program execution result
to the LCD or terminal monitor to find out if the program executes correctly up to
the breakpoint. This also serves as a watch list for the program execution.
The following guidelines can help reduce and identify logic errors:
• Make sure the precedence of operators has been observed.
• Match the size of the source operands and that of the destination variables. Use
type casting when necessary. Type casting has been used in several examples in
this text.
• Walk through the program algorithm carefully before converting it into the
program code. An incorrect algorithm is often the cause of program bugs.
Use enough data to test the program. The program must be tested with normal inputs,
maximum and minimum inputs, and also the illegal inputs to make sure that it operates correctly under all circumstances. When the problem to be solved gets complicated, a structured
programming approach should be used to organize the program. The guidelines described in
Section 2.4 should be followed to develop the program algorithm and convert the algorithm
into program code. Each individual function should be tested thoroughly before the whole program is tested. A comprehensive discussion of structured programming and testing is beyond
the scope of this text but can be found in many textbooks on software engineering.
5.15 Summary
A C program consists of one or more functions and variables. The main ( ) function is required in every C program. It is the entry point of a C program. A function contains statements
that specify the operations to be performed. The types of statements in a function could be declaration, assignment, function call, control, and null.
A variable stores a value to be used during the computation. A variable must be declared
before it can be used. The declaration of a variable consists of the name and the type of the variable. There are four basic data types in C: char, int, float, and double. Several qualifiers can be
added to the variable declarations. They are short, long, signed, and unsigned.
258
Chapter 5 ■ C Language Programming
Constants are often needed in forming a statement. There are four types of constants: integers, characters, floating-point numbers, and strings.
There are seven arithmetic operators: 1, 2, *, /, %, 11, and 22. There are six bitwise
operators: &, |, ^, ,, .., and ,,. Bitwise operators can be applied only to integers. Relational
operators are used in control statements. They are 55, !5, ., .5, ,, ,5, &&, ||, and !.
The control-flow statements specify the order in which computations are performed. Control-flow statements include if-else statements, multiway conditional statements, switch statements,
for-loop statements, while statements, and do-while statements.
Every C program consists of one or more functions. If a program consists of multiple functions, their definitions cannot be embedded within another. The same function can be called
from several different places within a program. Generally, a function will process information passed to it from the calling portion of the program and return a single value. Information is passed to a function via special identifiers called arguments (also called parameters) and
returned via the return statement. Some functions, however, accept information but do not
return anything (for example, the library function printf).
A pointer holds the address of a variable. Pointers can be used to pass information back
and forth between a function and its reference (calling) point. In particular, pointers provide a
way to return multiple data items from a function via function arguments. Pointers also permit
references to other functions to be specified as arguments to a given function. Two operators are
related with pointers: * and &. The * operator returns the value of the variable pointed to by the
pointer. The & operator returns the address of a variable.
Data items that have common characteristics are placed in an array. An array may be onedimensional or multidimensional. The dimension of an array is specified by the number of
square bracket pairs [ ] following the array name. An array name can be used as an argument to
a function, thus permitting the entire array to be passed to the function. To pass an array to a
function, the array name must appear by itself, without brackets or subscripts. An alternative
way to pass arrays to a function is to use pointers.
A variable defined inside a function is an internal variable of that function. External variables are defined outside of any function and are thus potentially available to many functions.
The scope of a name is the part of the program within which the name can be used. The scope
of an external variable or a function lasts from the point at which it is declared to the end of the
file being compiled.
Tutorials for using the CodeWarrior IDE and ImageCraft ICC12 are provided at the end of
this chapter. The tutorial for using the EGNU IDE is in Appendix E. All three IDEs are window
driven, but only CodeWarrior provides source-level debugging capability.
Neither ImageCraft nor EGNU IDEs provides many library functions to the user. Three sets
of library functions are provided in the complementary CD and are listed in Table E.1 of Appendix E. These three sets of library functions are stored in files stdio0.c, delay.c, and convert.c.
5.16 Exercises
E5.1 Assume that ax 5 83 and bx 5 11. What is the value of ax/bx?
E5.2 Assume that ax 5 97 and bx 5 ax % 23. What is the value of bx?
E5.3 Assume that ax 5 0x39 and bx 5 ax ^ 0x79. What is the value of bx?
E5.4 Assume that ax 5 0x9D and bx 5 ax ,, 2. What is the value of bx?
E5.5 Assume that ax 5 0x6B and bx 5 ax & 0xDE. What is the value of bx?
E5.6 Write a C function to test whether an integer is a multiple of 8. If the number to be tested
is a multiple of 8, the function returns a 1 to the caller. Otherwise, it returns a 0 to the caller.
5.17 ■ Lab Exercises and Assignments
259
E5.7 Write a C program to find the median and mode of an array of integers. When the array has
an even number of elements, the median is defined as the average of the middle two elements.
Otherwise, it is defined as the middle element of the array. The mode is the element that occurs
most frequently. You need to sort the array in order to find the median.
E5.8 Write a function that tests if a given number is a multiple of 10. A 1 is returned if the
given number is a multiple of 10. Otherwise, a 0 is returned.
E5.9 Write a function that computes the least common multiple (lcm) of two integers m and n.
E5.10 What is a function prototype? What is the difference between a function prototype and
function declaration?
E5.11 Write a switch statement that will examine the value of an integer variable xx and store
one of the following messages in an array of seven characters, depending on the value assigned
to xx (terminate the message with a NULL character):
a) Cold if xx 55 1
b) Chilly if xx 55 2
c) Warm if xx 55 3
d) Hot if xx 55 4
E5.12 Write a C program to clear the screen and then move the cursor to the middle of a line
and output the message “Microcontroller is fun to use!” The screen can be cleared by outputting the character \f (form-feed character).
E5.13 Write a function that will convert an uppercase letter to lowercase.
E5.14 Write a C program that swaps the first column of a matrix with the last column, swaps
the second column of the matrix with the second-to-last column, and so on.
E5.15 Write a loop to compute the sum of the squares of the first 100 odd integers.
E5.16 An Armstrong number is a number of n digits that is equal to the sum of each digit
raised to the nth power. For example, 153 (which has three digits) equals 13 1 53 1 33. Write a
function to store all three-digit Armstrong numbers in an array.
E5.17 Write a C function to perform a binary search on a sorted array. The binary search algorithm is given in Example 4.6. The starting address, the key, and the array count are parameters
to this function. Both the key and array count are integers.
E5.18 Write a program to find the first five numbers that when divided by 2, 3, 4, 5, and 6, leave
a remainder of 1 and, when divided by 7, have no remainder.
E5.19 Take a four-digit number. Add the first two digits to the last two digits. Now, square the
sum. Surprise, you’ve got the original number again. Of course, not all four-digit numbers have
this property. Write a C program to find three numbers that have this special property.
E5.20 Write a program to find six prime numbers that are closest to 10,000, with three of them
being less than 10,000 and the other three being larger than 10,000, and print them out.
E5.21 For the seven-segment display circuit shown in Figure 4.18, write a C program to display
1, 2, 3, 4, 5, and 6 one digit at a time from left to right. Each digit is displayed for half a second.
5.17 Lab Exercises and Assignments
L5.1 Enter, compile, and download the following C program onto a demo board for execution
using the procedure described in Section 5.10 (you need to add delay.c, convert.c, and stdio.c
into the project):
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\delay.h”
260
Chapter 5 ■ C Language Programming
#include “c:\cwHCS12\include\convert.h”
#include “c:\cwHCS12\include\stdio.h”
int main (void)
{
const char *ptr 5 “ seconds passed!”;
char buf[20];
int cnt;
DDRB 5 0xFF; /* configure port B for output */
cnt 5 0;
/* initialize count to 0 */
while(1) {
delayby100ms(5);
int2alpha(cnt,buf);
putsr(&buf[0]);
puts(ptr);
cnt11;
PORTB 5 cnt;
newline();
}
return 0;
}
L5.2 Write a C program that will generate every third integer, beginning with i 5 2 and continuing for all integers that are less than 300. Calculate the sum of those integers that are divisible by
5. Store those integers and their sum in an array and as an integer variable, respectively.
L5.3 Write a C function that calculates the least common multiple (lcm) of two integers m and
n. Integers m and n are parameters to this function. Also write a main program to test this function with several pairs of integers. Use the library functions to convert and output the results.
L5.4 Use the seven-segment display circuit shown in Figure 4.18 and write a program to
display the following sequence of digits with each sequence lasting for half a second:
1
21
321
4321
54321
654321
(6)
765432
876543
987654
098765
109876
210987
321098
432109
543210
(15)
After displaying these 15 sequences once, repeat sequence 6 to 15 forever.
L5.5 Write a program to generate a four-tone siren using the PP5 pin with each tone lasting for
half a second. The frequencies of these four tones are 100 Hz, 250 Hz, 500 Hz, and 1000 Hz. The
PP5 pin is connected to a buzzer via a jumper on the Dragon12-Plus demo board.
6
Interrupts, Clock Generation,
Resets, and Operation Modes
6.1 Objectives
After completing this chapter, you should be
able to
• Explain the difference between interrupts and
resets
• Describe the handling procedures for interrupts
and resets
• Raise one of the HCS12 maskable interrupts
to the highest priority
• Enable and disable maskable interrupts
• Use one of the low-power modes to reduce the
power consumption
• Use COP watchdog timer reset to detect
software failure
• Set up the interrupt vector jump table for
demo boards that have the D-Bug12 monitor
• Use RTI to generate periodic interrupts
• Write interrupt-driven application programs
• Distinguish the HCS12 operation modes
262
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
6.2 Fundamental Concepts of Interrupts
Interrupts and resets are among the most useful mechanisms that a computer system provides.
With interrupts and resets, I/O operations are performed more efficiently, errors are handled more
smoothly, and CPU utilization is improved. This chapter begins with a general discussion of
interrupts and resets and then focuses on the specific features of the HCS12 interrupts and resets.
Examples are given to demonstrate how interrupts can be used to effectively trigger I/O operations.
6.2.1 What Is an Interrupt?
An interrupt is an event that requires the CPU to stop normal program execution and perform some service related to the event. An interrupt can be generated internally (inside the
chip) or externally (outside the chip). An external interrupt is generated when the external circuitry asserts an interrupt signal to the CPU. An internal interrupt can be generated by the
hardware circuitry inside the chip or caused by software errors. Most microcontrollers have
timers, I/O interface functions, and the CPU incorporated on the same chip. These subsystems
can generate interrupts to the CPU. Abnormal situations that occur during program execution,
such as illegal opcodes, overflow, divide by zero, and underflow, are called software interrupts.
The terms traps and exceptions are also used to refer to software interrupts.
A good analogy for an interrupt is how you act when you are sitting in front of a desk to
read this book and the phone rings. You probably act like this.
1. Remember the page number or place a bookmark on the page that you are reading,
close the book, and put it aside.
2. Pick up the phone and say, “Hello, this is so and so.”
3. Listen to the voice over the phone to find out who is calling or ask who is calling if
the voice is not familiar.
4. Talk to that person.
5. Hang up the phone when you finish talking.
6. Open the book and turn to the page where you placed the bookmark and resume
reading this book.
The phone call example spells out a few things that are similar to how the microprocessor
handles the interrupt.
1. As a student, you spend most of your time studying. Answering the phone call
happens only occasionally. Similarly, the microprocessor is executing application
programs most of the time. Interrupts will only force the microprocessor to stop
executing the application program briefly and take some necessary actions.
2. Before picking up the phone, you finish reading the sentence and then place a bookmark
to remind yourself of the page number that you are reading so that you can resume
reading after finishing the conversation over the phone. Most microprocessors will
finish the instruction they are executing and save the address of the next instruction in
memory (usually in the stack) so that they can resume the program execution later.
3. You find out who the person is by listening to the voice over the phone, or you ask
questions so that you can decide what to say. Similarly, the microprocessor needs to
identify the cause of the interrupt before it can take appropriate actions. This is built
into the microprocessor hardware.
4. After identifying the person who called you, you start the phone conversation with
that person on some appropriate subjects. Similarly, the microprocessor will take
some actions appropriate to the interrupt source.
6.2 ■ Fundamental Concepts of Interrupts
263
5. When finishing the phone conversation, you hang up the phone, open the book to the
page where you placed the bookmark, and resume reading. Similarly, after taking
some actions appropriate to the interrupt, the microprocessor will jump back to the
next instruction, after the interrupt occurred, and resume program execution. This
can be achieved easily because the address of the instruction to be resumed was
saved in memory (the stack). Most microprocessors do this by executing a returnfrom-interrupt instruction.
6.2.2 Why Are Interrupts Used?
Interrupts are useful in many applications, such as the following:
• Coordinating I/O activities and preventing the CPU from being tied up during
the data transfer process. The CPU needs to know if the I/O device is ready before
it can proceed. Without the interrupt capability, the CPU will need to check the
status of the I/O device continuously or periodically. The interrupt mechanism is
often used by the I/O device to inform the CPU that it is ready for data transfer.
CPU time can thus be utilized more efficiently because of the interrupt mechanism.
Interrupt-driven I/O operations will be explained in more detail in later chapters.
• Performing time-critical applications. Many emergent events, such as power
failure and process control, require the CPU to take action immediately. The
interrupt mechanism provides a way to force the CPU to divert from normal
program execution and take immediate actions.
• Providing a graceful way to exit from an application when a software error occurs.
The service routine for a software interrupt may also output useful information
about the error so that it can be corrected.
• Reminding the CPU to perform routine tasks. There are many microprocessor
applications that require the CPU to perform routine work, such as the following:
1. Keeping track of time of day. Without the timer interrupt, the CPU will need
to use program loops in order to update the current time. The CPU cannot do
anything else without a timer interrupt in this application. The periodic timer
interrupts prevent the CPU from being tied up.
2. Periodic data acquisition. Some applications are designed to acquire data
periodically.
3. Task switching in a multitasking operating system. In a modern computer
system, multiple application programs are resident in the main memory, and the
CPU time is divided into many short slots (one slot may be from 10 to 20 ms).
A multitasking operating system assigns a program to be executed for one time
slot. At the end of a time slot or when a program is waiting for the completion
of an I/O operation, the operating system takes over and assigns another
program for execution. This technique is called multitasking. Multitasking can
dramatically improve the CPU utilization and is implemented by using periodic
timer interrupts.
6.2.3 Interrupt Maskability
Depending on the situation and application, some interrupts may not be desired or needed
and should be prevented from affecting the CPU. Most microprocessors and microcontrollers
have the option of ignoring these interrupts. These types of interrupts are called maskable interrupts. There are other types of interrupts that the CPU cannot ignore and must take immediate
actions for; these are nonmaskable interrupts. A program can request the CPU to service or
264
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
ignore a maskable interrupt by setting or clearing an enable bit. When an interrupt is enabled,
the CPU will respond to it. When an interrupt is disabled, the CPU will ignore it. An interrupt is
said to be pending when it is active but not yet serviced by the CPU. A pending interrupt may or
may not be serviced by the CPU, depending on whether or not it is enabled.
To make the interrupt system more flexible, a computer system normally provides a global
and local interrupt masking capability. When none of the interrupts are desirable, the processor
can disable all the interrupts by clearing the global interrupt enable bit (or setting the global interrupt mask bit for some other processor). In other situations, the processor can selectively enable
certain interrupts while at the same time disabling other undesirable interrupts. This is achieved
by providing each interrupt source an enable bit in addition to the global interrupt mask. Whenever any interrupt is undesirable, it can be disabled while at the same time allowing other interrupt sources to be serviced (attended) by the processor. Today, almost all commercial processors
are designed to provide this two-level (or even three-level) interrupt-enabling capability.
6.2.4 Interrupt Priority
If a computer is supporting multiple interrupt sources, then it is possible that several interrupts would be pending at the same time. The CPU has to decide which interrupt should
receive service first in this situation. The solution is to prioritize all interrupt sources. An interrupt with higher priority always receives service before interrupts at lower priorities. Many
microcontrollers, including the HCS12, prioritize interrupts in hardware. For those microcontrollers that do not prioritize interrupts in hardware, the software can be written to handle
certain interrupts before others. By doing this, interrupts are essentially prioritized. For most
microprocessors and microcontrollers, interrupt priorities are not programmable.
6.2.5 Interrupt Service
The CPU provides service to an interrupt by executing a program called an interrupt service
routine. After providing service to an interrupt, the CPU must resume normal program execution. How can the CPU stop the execution of a program and resume it later? It achieves this
by saving the program counter and the CPU status information before executing the interrupt
service routine and then restoring the saved program counter and CPU status before exiting the
interrupt service routine. The complete interrupt service cycle involves
1. Saving the program counter value
2. Saving the CPU status (including the CPU status register and some other registers) in
the stack (This step is optional for some microcontrollers and microprocessors.)
3. Identifying the source of the interrupt
4. Resolving the starting address of the corresponding interrupt service routine
5. Executing the interrupt service routine
6. Restoring the CPU status from the stack
7. Restoring the program counter from the stack
8. Resuming the interrupted program
For all maskable hardware interrupts, the microprocessor starts to provide service when it
completes the execution of the current instruction (the instruction being executed when the
interrupt occurred). For some nonmaskable interrupts, the CPU may start the service without
completing the current instruction. Many software interrupts are caused by an error in instruction execution that prevents the instruction from being completed. The service to this type of
interrupt is simply to output an error message and abort the program.
6.2 ■ Fundamental Concepts of Interrupts
265
6.2.6 Interrupt Vector
The term interrupt vector refers to the starting address of the interrupt service routine.
In general, interrupt vectors are stored in a table called an interrupt-vector table. The starting address of each entry (holds one interrupt vector) in the interrupt vector table is called the
vector address. The interrupt-vector table is fixed for some microprocessors and may be relocated for other microprocessors.
The CPU needs to determine the interrupt vector before it can provide service. One of the
following methods can be used by a microprocessor or microcontroller to determine the interrupt vector:
1. Predefined. In this method, the starting address of the service routine is predefined
when the microcontroller is designed. The processor uses a table to store all the
interrupt service routines. The Intel 8051 microcontrollers use this approach. Each
interrupt is allocated the same number of bytes to hold its service routine. The Intel
8051 allocates eight words to each interrupt service routine. When the service routine
requires more than eight words, the solution is to place a jump instruction in the
predefined location to jump to the actual service routine.
2. Fetch the vector from a predefined memory location. In this approach, the interrupt
vector of each interrupt source is stored at a predefined location in the interruptvector table, where the microprocessor can get it directly. The Freescale HCS12 and
most other Freescale microcontrollers use this approach.
3. Execute an interrupt acknowledge cycle to fetch a vector number in order to locate
the interrupt vector. During the interrupt acknowledge cycle, the microprocessor
performs a read bus cycle, and the external I/O device that requested the interrupt
places a number on the data bus to identify itself. This number is called the interruptvector number. The CPU can figure out the starting address of the interrupt service
routine by using this number. The CPU needs to perform a read cycle in order to
obtain it. The Freescale 68000 and Intel x86 family microprocessors support this
method. The Freescale 68000 family of microprocessors also uses the second method.
This method is not used by microcontrollers because of the incurred latency.
6.2.7 Interrupt Programming
Interrupt programming deals with how to provide service to the interrupt. There are three
steps in interrupt programming.
Step 1
Initialize the interrupt-vector table. (This step is not needed for microprocessors that have
predefined interrupt vectors.) This can be done by using the assembler directive org (or its
equivalent) as follows:
org
dc.w
dc.w
.
.
.
dc.w
$xxxx
service_1
service_2
; xxxx is the vector table address
; store the starting address of interrupt source 1
;
service_n
where service_i is the starting address of the service routine for interrupt source i. The
HCS12 uses this method to set up interrupt vectors. The assembler syntax and the number of
bytes needed to store an interrupt vector on your particular microcontroller may be different.
266
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Step 2
Write the interrupt service routine. An interrupt service routine should be as short as
possible. For some interrupts, the service routine may only output a message to indicate
that something unusual has occurred. A service routine is similar to a subroutine—the only
difference is the last instruction. An interrupt service routine uses the return-from-interrupt
(or return-from-exception) instruction instead of return-from-subroutine instruction to
return to the interrupted program. The following instruction sequence is an example of an
interrupt service routine (in HCS12 instructions):
irq_isr
msg
ldx
jsr
rti
fcc
#msg
puts
; call puts to output a string pointed by X
; return from interrupt
“This is an error”
The service routine may or may not return to the interrupted program, depending on
the cause of the interrupt. It makes no sense to return to the interrupted program if the
interrupt is caused by a software error such as divide by zero or overflow, because the
program is unlikely to generate correct results under these circumstances. In such
situations, the service routine would return to the monitor program or the operating
system instead. Returning to a program other than the interrupted program can be achieved
by changing the saved program counter (in the stack) to the desired value. Execution of the
return-from-interrupt instruction will then return CPU control to the new address.
Step 3
Enable the interrupts to be serviced. An interrupt can be enabled by clearing the global
interrupt mask and setting the local interrupt enable bit in the I/O control register. It is a
common mistake to forget enabling interrupts when writing interrupt-driven application
programs.
6.2.8 Overhead of Interrupts
Although the interrupt mechanism provides many advantages, it also involves some overhead. The overhead of the HCS12 interrupt includes
1. Saving the CPU registers, including accumulators (A:B), index registers X and Y, and
the condition code register (CCR), and fetching the interrupt vector. This takes at
least 9 E-clock cycles.
2. The execution time of the RTI instruction. This instruction restores all the CPU
registers that have been stored in the stack by the CPU during the interrupt and takes
from 8 to 11 E-clock cycles to complete for the HCS12.
3. Execution time of instructions of the interrupt service routine. This depends on the
type and the number of instructions in the service routine.
The total overhead is thus at least 17 to 20 E-clock cycles, which amounts to almost 1 μs for a
24-MHz E-clock. We need to be aware of the overhead involved in interrupt processing when
deciding whether to use the interrupt mechanism.
6.3 Resets
The initial values of some CPU registers, flip-flops, and the control registers in I/O interface chips must be established before the computer can operate properly. Computers provide a
reset mechanism to establish initial conditions.
267
6.4 ■ HCS12 Exceptions
There are at least two types of resets in each microprocessor: the power-on reset and the
manual reset. A power-on reset allows the microprocessor to establish the initial values of registers and flip-flops and to initialize all I/O interface chips when power to the microprocessor
is turned on. A manual reset without power-down allows the computer to get out of most error
conditions (if hardware hasn’t failed) and reestablish the initial conditions. The computer will
reboot itself after a reset.
The starting address of the reset service routine either is a fixed value or is stored at a fixed
location (for HCS12). The reset service routine is stored in the read-only memory of all microprocessors so that it is always ready for execution. At the end of the service routine, control
should be transferred to either the monitor program or the operating system.
Like nonmaskable interrupts, resets are also nonmaskable. However, resets are different
from the nonmaskable interrupts in that no registers are saved by resets because resets establish the values of registers.
6.4 HCS12 Exceptions
The HCS12 exceptions can be classified into the following categories:
• Maskable interrupts. These include the IRQ pin interrupt and all peripheral
function interrupts. Since different HCS12 members implement a different number
of peripheral functions, they have different numbers of maskable interrupts.
• Nonmaskable interrupts. These include the XIRQ pin interrupt, the swi instruction
interrupt, and the unimplemented opcode trap.
• Resets. These include the power-on reset, the RESET pin manual reset, the
COP (computer operating properly) reset, and the clock monitor reset. For other
microcontrollers, the COP reset is also called the watchdog reset.
6.4.1 Maskable Interrupts
Since different HCS12 members implement a different number of peripheral functions,
they have a different number of maskable interrupts. The I flag in the CCR register is the global
mask of all maskable interrupts. Whenever the I flag is 1, all maskable interrupts are disabled.
All maskable interrupts have a local enable bit that allows them to be selectively enabled. They
are disabled (I flag is set to 1) when the HCS12 gets out of the reset state.
As with any other microcontroller, all HCS12 exceptions are prioritized. The priorities of
resets and nonmaskable interrupts are not programmable. However, we can raise one of the
maskable interrupts to the highest level within the group of maskable interrupts so that it can
get quicker attention from the CPU. The relative priorities of the other sources remain the same.
The bits 7 to 1 of the HPRIO register select the maskable interrupt at the highest priority within
the group of maskable interrupts. The contents of the HPRIO register are shown in Figure 6.1.
7
6
5
4
3
2
1
PSEL7
PSEL6
PSEL5
PSEL4
PSEL3
PSEL2
PSEL1
0
0
$001F
Figure 6.1 ■ Highest-priority I interrupt register
The priorities and vector addresses of all HCS12 exceptions are listed in Table 6.1. The vector number column specifies the order of a specific interrupt source in the table. It is provided
268
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Vector
Address
Vector
Number
Interrupt Source
CCR
Mask
$FFFE
$FFFC
$FFFA
$FFF8
$FFF6
$FFF4
$FFF2
$FFF0
$FFEE
$FFEC
$FFEA
$FFE8
$FFE6
$FFE4
$FFE2
$FFE0
$FFDE
$FFDC
$FFDA
$FFD8
$FFD6
$FFD4
$FFD2
$FFD0
$FFCE
$FFCC
$FFCA
$FFC8
$FFC6
$FFC4
$FFC2
$FFC0
$FFBE
$FFBC
$FFBA
$FFB8
$FFB6
$FFB4
$FFB2
$FFB0
$FFAE
$FFAC
$FFAA
$FFA8
$FFA6
$FFA4
$FFA2
$FFA0
$FF9E
$FF9C
$FF9A
$FF98
$FF96
$FF94
$FF92
$FF90
$FF8E
$FF8C
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Reset
Clock monitor failure reset
COP failure reset
Unimplemented instruction trap
swi
XIRQ
IRQ
Real-time interrupt
Enhanced capture timer channel 0
Enhanced capture timer channel 1
Enhanced capture timer channel 2
Enhanced capture timer channel 3
Enhanced capture timer channel 4
Enhanced capture timer channel 5
Enhanced capture timer channel 6
Enhanced capture timer channel 7
Enhanced capture timer overflow
Pulse accumulator A overflow
Pulse accumulator A input edge
SPI0
SCI0
SCI1
ATD0
ATD1
Port J
Port H
Modulus down counter underflow
Pulse accumulator B overflow
CRG PLL lock
CRG self-clock mode
BDLC
IIC Bus
SPI1
SPI2
EEPROM
Flash
CAN0 wake-up
CAN0 errors
CAN0 receive
CAN0 transmit
CAN1 wake-up
CAN1 errors
CAN1 receive
CAN1 transmit
CAN2 wake-up
CAN2 errors
CAN2 receive
CAN2 transmit
CAN3 wake-up
CAN3 errors
CAN3 receive
CAN3 transmit
CAN4 wake-up
CAN4 errors
CAN4 receive
CAN4 transmit
Port P interrupt
PWM emergency shutdown
none
none
none
none
none
X bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
I bit
Table 6.1 ■ Interrupt vector map
Local Enable
none
PLLCTL(CME, SCME)
COP rate select
none
none
none
IRQCR(IRQEN)
CRGINT(RTIE)
TIE(C0I)
TIE(C1I)
TIE(C2I)
TIE(C3I)
TIE(C4I)
TIE(C5I)
TIE(C6I)
TIE(C7I)
TSCR2(TOI)
PACTL(PAOVI)
PACTL(PAI)
SP0CR1(SPIE, SPTIE)
SC0CR2(TIE,TCIE,RIE,ILIE)
SC1CR2(TIE,TCIE,RIE,ILIE)
ATD0CTL2(ASCIE)
ATD1CTL2(ASCIE)
PTJIF(PTJIE)
PTHIF(PTHIE)
MCCTL(MCZI)
PBCTL(PBOVI)
CRGINT(LOCKIE)
CRGINT(SCMIE)
DLCBCR1(IE)
IBCR(IBIE)
SP1CR1(SPIE, SPTIE)
SP2CR2(SPIE, SPTIE)
EECTL(CCIE, CBEIE)
FCTL(CCIE, CBEIE)
CAN0RIER(WUPIE)
CAN0RIER(CSCIE, OVRIE)
CAN0RIER(RXFIE)
CAN0TIER(TXEIE2-TXEIE0)
CAN1RIER(WUPIE)
CAN1RIER(CSCIE, OVRIE)
CAN1RIER(RXFIE)
CAN1TIER(TXEIE2-TXEIE0)
CAN2RIER(WUPIE)
CAN2RIER(CSCIE, OVRIE)
CAN2RIER(RXFIE)
CAN2TIER(TXEIE2-TXEIE0)
CAN3RIER(WUPIE)
CAN3RIER(CSCIE, OVRIE)
CAN3RIER(RXFIE)
CAN3TIER(TXEIE2-TXEIE0)
CAN4RIER(WUPIE)
CAN4RIER(CSCIE, OVRIE)
CAN4RIER(RXFIE)
CAN4TIER(TXEIE2-TXEIE0)
PTPIF(PTPIE)
PWMSDN(PWMIE)
HPRIO Value to
Elevate
to Highest I Bit
–
–
–
–
–
–
$F2
$F0
$EE
$EC
$EA
$E8
$E6
$E4
$E2
$E0
$DE
$DC
$DA
$D8
$D6
$D4
$D2
$D0
$CE
$CC
$CA
$C8
$C6
$C4
$C2
$C0
$BE
$BC
$BA
$B8
$B6
$B4
$B2
$B0
$AE
$AC
$AA
$A8
$A6
$A4
$A2
$A0
$9E
$9C
$9A
$98
$96
$94
$92
$90
$8E
$8C
269
6.4 ■ HCS12 Exceptions
for the convenience of setting up the whole interrupt-vector table. To raise a maskable interrupt source to the highest priority, simply write the low byte of the vector address of this interrupt to the HPRIO register. For example, to raise the capture timer channel 7 interrupt to the
highest priority, write the value of $E0 to the HPRIO register.
All HCS12 reset and interrupt vectors are stored in a table (shown in Table 6.1) located at
$FF8C to $FFFF. In Table 6.1, exceptions that have higher vector addresses are at higher priorities. Not all the exceptions are available in all HCS12 members.
IRQ PIN INTERRUPT
The IRQ pin (multiplexed with the PE1 pin) is the only external maskable interrupt signal.
The IRQ pin interrupt can be edge triggered or level triggered. The triggering method is selected
by programming the IRQE bit of the Interrupt Control Register (IRQCR). The IRQ pin interrupt
has a local enable bit IRQEN bit, which is bit 6 of the IRQCR. The contents of the IRQCR register are shown in Figure 6.2.
Reset:
7
6
5
4
3
2
1
IRQE
IRQEN
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
IRQE: IRQ edge-sensitive only bit
IRQE can be written once in normal mode. In special modes, it can be
written any time, but the first write is ignored.
1 = IRQ pin responds only to falling edge
0 = IRQ pin responds to low level
IRQEN: IRQ enable bit
IRQEN bit can be written any time in all modes. The IRQ pin has an
internal pull-up.
1 = IRQ pin interrupt enabled
0 = IRQ pin interrupt disabled
Figure 6.2 ■ Interrupt control register (IRQCR)
The advantage of making the IRQ interrupt level sensitive (active low) is that it allows multiple external interrupt sources to be tied to this pin. Whenever one of the interrupt sources (that
are tied to the IRQ pin) is low, an interrupt request will be detected by the HCS12. The user of
this method must make sure that the IRQ signal is de-asserted (goes high) before the HCS12 exits
the interrupt service routine if there are no other pending interrupts connected to the IRQ pin.
The major advantage of making the IRQ interrupt edge sensitive (falling edge) is that the
user does not need to be concerned about the duration of the assertion time of the IRQ signal.
However, this approach is not appropriate for a noisy environment. In a noisy environment, any
noise spike could generate an undesirable interrupt request on the IRQ pin.
INTERRUPT RECOGNITION
Once enabled, an interrupt request can be recognized at any time after the I mask bit is
cleared. When an interrupt service request is recognized, the CPU responds at the completion
of the instruction being executed. Interrupt latency varies according to the number of cycles
required to complete the current instruction. The HCS12 has implemented a few instructions to
support fuzzy-logic rule evaluation. These instructions, which take a much longer time to complete, include fuzzy-logic rule evaluation (REV), fuzzy-logic rule evaluation weighted (REVW),
and weighted-average (WAV) instructions. The HCS12 does not wait until the completion of
270
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
these instructions to service the interrupt request. These instructions will resume at the point
when they were interrupted.
Before the HCS12 starts to service an interrupt, it will set the I mask to disable other maskable interrupts. When the CPU begins to service an interrupt, the instruction queue is refilled,
a return address is calculated, and then the return address and the contents of all CPU registers
(except SP) are saved in the stack in the order shown in Figure 6.3.
[CCR]
SP
[A]
SP + 1
[B]
SP + 2
[X]
SP + 3
[Y]
SP + 5
Return address
SP + 7
Figure 6.3 ■ Stack order on entry to interrupts
THE RTI INSTRUCTION
RTI is used to terminate interrupt service routines. RTI is an 8-cycle instruction when no
other interrupt is pending and an 11-cycle instruction when another interrupt is pending. In
either case, the first 5 cycles are used to restore the CCR, B:A, X, Y, and return address from the
stack. The HCS12 then clears the I mask to enable further maskable interrupts.
If no other interrupt is pending at this point, three program words are fetched to refill
the instruction queue from the area of the return address, and processing proceeds from there.
If another interrupt is pending after registers are restored, a new vector is fetched, and the
stack pointer is adjusted to point at the CCR value that was just recovered (SP 5 SP – 9).
This makes it appear that the registers have been stacked again. After the SP is adjusted,
three program words are fetched to refill the instruction queue, starting at the address the
vector points to. Processing then continues with execution of the instruction that is now at
the head of the queue.
6.4.2 Nonmaskable Interrupts
There are three interrupts in this category: XIRQ pin (multiplexed with the PE0 pin), swi
instruction, and unimplemented opcode trap.
XIRQ PIN INTERRUPT
The XIRQ pin interrupt is disabled during a system reset and on entering the interrupt service routine for an XIRQ interrupt.
During reset, both the I and X bits in the CCR register are set. This disables maskable interrupts and interrupt requests made by asserting the XIRQ pin (pulled to low). After minimum
system initialization, software can clear the X bit using an instruction such as andcc #$BF.
Software cannot reset the X bit from 0 to 1 once it has been cleared, and hence the interrupt
requests made via the XIRQ pin become nonmaskable.
6.4 ■ HCS12 Exceptions
271
When a nonmaskable interrupt is recognized, both the X and I bits are set after CPU
registers are saved. The X bit is not affected by maskable interrupts. The execution of an RTI
instruction at the end of the XIRQ service routine will restore the X and I bits to the preinterrupt request state.
UNIMPLEMENTED OPCODE TRAP
The HCS12 uses up to 16 bits (2 pages) to encode the opcode. All 256 combinations in the
page 1 opcode map have been used. However, only 54 of the 256 positions on page 2 of the opcode map are used. If HCS12 attempts to execute one of the 202 unused opcodes on page 2, an
unimplemented opcode trap occurs. The 202 unimplemented opcodes are essentially interrupts
that share a common interrupt-vector address, $FFF8:$FFF9. The HCS12 uses the next address
after an unimplemented page 2 opcode as a return address.
S O F T WA R E I N T E R R U P T I N S T R U C T I O N ( S W I )
Execution of the swi instruction causes an interrupt without an interrupt request signal.
swi is not inhibited by the global mask bits in the CCR. So far, we have been using this instruction to jump back to the D-Bug12 monitor.
The swi instruction is commonly used in the debug monitor to implement breakpoints and
to transfer control from a user program to the debug monitor. A breakpoint in a user program is a
memory location where we want program execution to be stopped and information about instruction execution (in the form of register contents) to be displayed. To implement breakpoints, the
debug monitor sets up a breakpoint table. Each entry of the table holds the address of the breakpoint
and the opcode byte at the breakpoint. The monitor also replaces the opcode byte at the breakpoint with the opcode of the swi instruction. When the instruction at the breakpoint is executed, it
causes an swi interrupt. The service routine of the swi interrupt will look up the breakpoint table
and take different actions depending on whether the saved PC value is in the breakpoint table:
Case 1
The saved PC value is not in the breakpoint table. In this case, the service routine will
simply replace the saved PC value (in the stack) with the address of the monitor program
and return from the interrupt.
Case 2
The saved PC is in the breakpoint table. In this case, the service routine will
1. Replace the swi opcode with the opcode in the breakpoint table
2. Display the contents of the CPU registers
3. Replace the saved PC value (in the stack) with the address of the monitor program
4. Return from the interrupt (return to the debug monitor because of step 3)
6.4.3 Interrupts in D-Bug12 EVB Mode
Most users would use the EVB mode of the D-Bug12 monitor to develop applications on a
demo board having the D-Bug12 monitor. However, the D-bug12 monitor’s EVB mode does not
allow the use of the on-chip flash memory, which prevents using the default interrupt-vector
table to hold interrupt vectors. To allow the user to develop interrupt-driven applications, the
D-Bug12 monitor provides an SRAM-based interrupt-vector table. This table starts at $3E00 and
has 64 entries of 2 bytes each. The contents of this table are shown in Table 6.2. Initially, all
entries in the table contain an address of $0000. Storing a value other than $0000 in any of the
RAM interrupt-vector table entries causes execution of the interrupt service routine pointed to
by the address when an associated interrupt occurs.
272
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Interrupt Source
Reserved $FF80
Reserved $FF82
Reserved $FF84
Reserved $FF86
Reserved $FF88
Reserved $FF8A
PWM emergency shutdown
Port P interrupt
MSCAN 4 transmit
MSCAN 4 receive
MSCAN 4 errors
MSCAN 4 wake-up
MSCAN 3 transmit
MSCAN 3 receive
MSCAN 3 errors
MSCAN 3 wake-up
MSCAN 2 transmit
MSCAN 2 receive
MSCAN 2 errors
MSCAN 2 wake-up
MSCAN 1 transmit
MSCAN 1 receive
MSCAN 1 errors
MSCAN 1 wake-up
MSCAN 0 transmit
MSCAN 0 receive
MSCAN 0 errors
MSCAN 0 wake-up
Flash
EEPROM
SPI2
SPI1
RAM Vector
Address
$3E00
$3E02
$3E04
$3E06
$3E08
$3E0A
$3E0C
$3E0E
$3E10
$3E12
$3E14
$3E16
$3E18
$3E1A
$3E1C
$3E1E
$3E20
$3E22
$3E24
$3E26
$3E28
$3E2A
$3E2C
$3E2E
$3E30
$3E32
$3E34
$3E36
$3E38
$3E3A
$3E3C
$3E3E
Interrupt Source
IIC bus
BDLC
SCME
CRG clock
Pulse accumulator B overflow
Modulus down counter underflow
Port H interrupt
Port J interrupt
ATD1
ATD0
SCI1
SCI0
SPI0
Pulse accumulator A input edge
Pulse accumulator A overflow
Timer overflow
Timer channel 7
Timer channel 6
Timer channel 5
Timer channel 4
Timer channel 3
Timer channel 2
Timer channel 1
Timer channel 0
Real-time interrupt
IRQ
XIRQ
swi
Unimplemented instruction trap
N/A
N/A
N/A
RAM Vector
Address
$3E40
$3E42
$3E44
$3E46
$3E48
$3E4A
$3E4C
$3E4E
$3E50
$3E52
$3E54
$3E56
$3E58
$3E5A
$3E5C
$3E5E
$3E60
$3E62
$3E64
$3E66
$3E68
$3E6A
$3E6C
$3E6E
$3E70
$3E72
$3E74
$3E76
$3E78
$3E7A
$3E7C
$3E7E
Table 6.2 ■ D-Bug12 RAM interrupt vector address
If an unmasked interrupt occurs and a table entry contains the default address of $0000, program execution is returned to D-Bug12. The D-Bug12 would display a message indicating the
source of the interrupt and also display the CPU registers at the point where the program was
interrupted. The only exception to this is the SCI0 interrupt. Even though there is an entry for
SCI0 in Table 6.2, one cannot use the SCI0 interrupt because it has been used by D-Bug12 for all
of its communications. The hcs12.inc (and hcs12.h) file also provides the mnemonic name for each
interrupt source so that the user can use the mnemonic name to specify the SRAM vector address.
The complete list of these mnemonic names is shown in Table 6.3.
273
6.4 ■ HCS12 Exceptions
Interrupt Source
RAM Vector
Address
Vector
Number
UserRsrv0x80
UserRsrv0x82
UserRsrv0x84
UserRsrv0x86
UserRsrv0x88
UserRsrv0x8a
UserPWMShDn
UserPortP
UserMSCAN4Tx
UserMSCAN4Rx
UserMSCAN4Errs
UserMSCAN4Wake
UserMSCAN3Tx
UserMSCAN3Rx
UserMSCAN3Errs
UserMSCAN3Wake
UserMSCAN2Tx
UserMSCAN2Rx
UserMSCAN2Errs
UserMSCAN2Wake
UserMSCAN1Tx
UserMSCAN1Rx
UserMSCAN1Errs
UserMSCAN1Wake
UserMSCAN0Tx
UserMSCAN0Rx
UserMSCAN0Errs
UserMSCAN0Wake
UserFlash
UserEEPROM
UserSPI2
UserSPI1
$3E00
$3E02
$3E04
$3E06
$3E08
$3E0A
$3E0C
$3E0E
$3E10
$3E12
$3E14
$3E16
$3E18
$3E1A
$3E1C
$3E1E
$3E20
$3E22
$3E24
$3E26
$3E28
$3E2A
$3E2C
$3E2E
$3E30
$3E32
$3E34
$3E36
$3E38
$3E3A
$3E3C
$3E3E
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Interrupt Source
UserIIC
UserDLC
UserSCME
UserCRG
UserPAccBOv
UserModDwnCtr
UserPortH
UserPortJ
UserAtoD1
UserAtoD0
UserSCI1
UserSCI0
UserSPI0
UserPAccEdge
UserPAccOvf
UserTimerOvf
UserTimerCh7
UserTimerCh6
UserTimerCh5
UserTimerCh4
UserTimerCh3
UserTimerCh2
UserTimerCh1
UserTimerCh0
UserRTI
UserIRQ
UserXIRQ
UserSWI
UserTrap
N/A
N/A
N/A
RAM Vector
Address
Vector
Number
$3E40
$3E42
$3E44
$3E46
$3E48
$3E4A
$3E4C
$3E4E
$3E50
$3E52
$3E54
$3E56
$3E58
$3E5A
$3E5C
$3E5E
$3E60
$3E62
$3E64
$3E66
$3E68
$3E6A
$3E6C
$3E6E
$3E70
$3E72
$3E74
$3E76
$3E78
$3E7A
$3E7C
$3E7E
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
21
21
21
Note: Vector number is used by the SetUserVector function to set up the interrupt vector.
Table 6.3 ■ Mnemonic names for D-Bug12 RAM interrupt-vector addresses
It is very simple to set up the SRAM interrupt vector. Assuming that the service routine
for the IRQ interrupt starts with the label irqISR, then one of the following two methods can be
used to establish the SRAM table entry for the IRQ interrupt:
Method 1
org
dc.w
$3E72
irqISR
; one can also use “org UserIRQ”
Method 2
ldd
std
#irqISR
$3E72
; one can also use “std UserIRQ”
274
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Example 6.1
▼
Assume that the IRQ pin of the HCS12DP256 is connected to a 1-Hz digital waveform and
Port B is connected to eight LEDs. Write a program to configure Port B for output and enable the
IRQ interrupt, and also write the service routine for the IRQ interrupt. The service routine for
the IRQ interrupt simply increments a counter and outputs it to Port B. This program is to be
executed on an HCS12 demo board programmed with the D-Bug12 monitor.
Solution: The program is as follows:
#include
count
forever
“c:\miniide\hcs12.inc”
org
$1000
ds.b
1
org
$1500
lds
#$1500
movw
#IRQISR,UserIRQ
clr
count
movb
#$FF,DDRB
bset
DDRJ,$02
bclr
PTJ,$02
movb
count,PTB
movb
#$C0,IRQCR
cli
nop
bra
forever
; reserve 1 byte for count
; set up the stack pointer
; set up interrupt vector in SRAM
; configure Port B for output
; configure PJ1 pin for output (required in Dragon12)
; enable LEDs to light (required in Dragon12)
; display the count value on LEDs
; enable IRQ pin interrupt and select edge triggering
;
“
; wait for IRQ pin interrupt
; ********************************************************************************
; This is the IRQ service routine.
; ********************************************************************************
IRQISR
inc
count
; increment count
movb
count,PTB
; and display count on LEDs
rti
end
▲
6.5 Interrupt Programming in C Language
Interrupt programming in C language varies with the C compiler. In this section, we examine how interrupt programming is done in CodeWarrior, ICC12 IDE, and EGNU IDE.
6.5.1 Interrupt Programming in CodeWarrior
CodeWarrior uses the keyword interrupt to inform its C compiler that a function is an interrupt service routine. Whenever the keyword interrupt appears before a function name, the CodeWarrior C compiler generates RTI as the last instruction of the function. The template for an
interrupt service routine in CodeWarrior is as follows:
Interrupt void ISR_name (void)
{
...
// statements to service the interrupt
}
where ISR_name is the name of the interrupt service routine.
275
6.5 ■ Interrupt Programming in C Language
After writing the interrupt service routine, the user also needs to set up the interrupt vector. The template for setting up the interrupt vector table is given in Appendix G. There are two
parts in setting up the interrupt vector.
Part 1. Declare the interrupt service routine to be external.
Part 2. Insert the name of the interrupt service routine into the appropriate place of the
interrupt-vector table.
For example, the RTI interrupt vector can be set up as follows assuming that the name of
its service routine is rtiISR:
extern void near rtiISR (void); // rtiISR is defined outside this file
#pragma CODE_SEG __NEAR_SEG NON_BANKED // interrupt section for this module
_interrupt void
UnimplementedISR(void)
{
for( ; ; );
// do nothing, but return from interrupt
}
#pragma CODE_SEG DEFAULT
typedef void (*near tIsrFunc)(void);
const
tIsrFunc _vect[ ] @0xFF80 5 { // interrupt-vector table starts from this line
// interrupt-vector table
UnimplementedISR,
...
...
rtiISR,
// RTI interrupt vector (at the address 0xFFF0)
...
...
};
The entry UnimplementedISR is provided as a catchall handler for all unintended interrupts. If the user is not concerned about unintended interrupts, then the interrupt-vector table
can be reduced to
const tIsrFunc _vect [ ] @0xFFF0 5 { // 0xFFF0 is the address to store RTI vector
rtiISR
};
6.5.2 Interrupt Handling in ImageCraft ICC12
The ICC12 C compiler uses the #pragma statement to indicate that a function is an interrupt service routine. For example, the following statements indicate that the function rtiISR( )
is an interrupt service routine:
#pragma interrupt_handler rtiISR
void riISR (void)
{
...
}
A pragma statement can declare several interrupt service routines separated by spaces.
When working with a demo board programmed with the D-Bug12 monitor, the user can
use the SetUserVector function provided by the D-Bug12 monitor to set up the interrupt vector.
The prototype declaration of the SetUserVector function is as follows:
int SetUserVector (int VectNum, Address UserAddress);
— at $EEA4
The function argument VectNum refers to the vector number associated with the interrupt source
which is given in Table 6.3. The argument UserAddress is the interrupt vector of the interrupt
276
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
source and is represented by the name of the interrupt service routine. One way to set up the
interrupt vector in ICC12 when working with a demo board programmed with the D-Bug12
monitor is to use the inline assembly instructions.
asm(“ldd #_rtiISR”);
asm(“pshd”);
asm(“ldd #56”);
asm(“ldx $EEA4”);
asm(“jsr 0,X”);
// push address of RTI interrupt service routine
// “
// place vector number of RTI in D
// place the SetUserVector function address in X
// jump to SetUserVector function to set up RTI
// interrupt vector (in SRAM)
6.5.3 Interrupt Programming in EGNU IDE
In the EGNU IDE, we must take the following actions in order to use interrupts:
1. Use the interrupt attribute of the GCC compiler. We must include the following
statement as one of the first few statements in the program (there are two ‘_’
characters before and after “attribute”):
#define INTERRUPT __attribute__((interrupt))
2. Apply the interrupt attribute to the interrupt service routine. Assuming that rtiISR()
and oc5ISR() are two interrupt service routines, then we add the following two
prototype declaration statements (for rtiISR() and oc5ISR()) in the program:
void INTERRUPT rtiISR(void);
void INTERRUPT oc5ISR(void);
3. Include the vectors12.h header file so that one can use mnemonic names to store the
starting addresses of interrupt service routines in the vector table (in SRAM). Include
the following statements as one of the first few statements in the program:
#include “c:\egnu\include\vectors12.h”
4. Store interrupt vectors in the SRAM vector table. This can be done by assignment
statements.
UserRTI 5 (unsigned short)&rtiISR;
UserTimerCh5 5 (unsigned short)&oc5ISR;
5. Write the actual interrupt service routine.
The C language version of the IRQ interrupt program given in Example 6.1 is as follows:
#include
“c:\egnu\include\hcs12.h”
#include
“c:\egnu\include\vectors12.h”
#define
INTERRUPT __attribute__((interrupt))
void
INTERRUPT IRQISR(void);
unsigned char cnt;
void main(void)
{
UserIRQ
DDRB
cnt
DDRJ
PTJ
IRQCR
5 (unsigned short)&IRQISR;
5 0xFF;
// configure Port B for output
5 0;
|5 BIT1;
// configure PJ1 pin for output
&5 ,BIT1;
// enable LEDs to light (required for Dragon12 demo board)
5 0xC0;
// enable IRQ interrupt on falling edge
277
6.6 ■ Clock and Reset Generation Block (CRG)
asm(“cli”);
while(1);
// enable interrupt globally
// wait for interrupt forever
}
void INTERRUPT IRQISR(void)
{
cnt11;
PTB
5 cnt;
}.
// increment the count value
// display the count value on LEDs
6.6 Clock and Reset Generation Block (CRG)
This block is responsible for generating the clock signals required by the HCS12 instruction execution and all peripheral operations and providing default values for all on-chip registers. The block diagram of a CRG is shown in Figure 6.4.
A microcontroller needs a clock signal to operate. The clock signal has the waveform of
a square wave. Most people use a crystal oscillator to generate the clock signal. However, the
output of a crystal oscillator is a sinusoidal waveform that cannot be used to drive digital circuitry directly. Most microcontrollers and microprocessors have an on-chip oscillator to square
up the incoming sinusoidal waveform so that it can be used as the clock signal. Many crystal
oscillators also include the circuitry to square up the sinusoidal waveform. The on-chip oscillator circuitry can be bypassed for this type of crystal oscillator (also called external oscillators in
Freescale literature).
Power-on
reset
VREG
CRG
Clock and reset
control
RESET
Reset
generator
System
reset
Clock quality
checker
E-Clock
COP
RTI
Core clock
Registers
Oscillator
clock
CM fail
XCLKS
EXTAL
XTAL
Clock
monitor
OSC
OSCCLK
XFC
VDDPLL
VSSPLL
PLLCLK
PLL
Figure 6.4 ■ Block diagram of CRG
278
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
The phase-locked-loop (PLL) is a circuit technique that can accept a low-frequency clock
signal and produce a high-frequency clock output. In addition, the PLL has a feedback circuitry to stabilize the frequency of its output signal. The PLL has two operation modes:
acquisition mode and tracking mode. When the PLL is first powered up, the frequency of its
output is far from its target frequency. During this period, the PLL is in acquisition mode in
which the PLL can make large adjustments to quickly reach the target frequency. After the
PLL has attained its target output frequency, it enters tracking mode, in which it makes only
small adjustments so as not to deviate from the target frequency. This mode is useful when
the incoming clock signals (square waveform) deviate slightly because the frequency of the
crystal oscillator output may change slightly due to a change in temperature, humidity, or
barometric pressure.
6.6.1 Modes of CRG Operation
The CRG block can operate in one of the following four modes:
• Run mode. All functional parts of the CRG block are running in this mode.
• Wait mode. This mode allows the user to disable the system and core clocks by
programming the individual bits in the CLKSEL register.
• Stop mode. Depending on the setting of the PSTP bit of the CLKSEL register, stop
mode can be differentiated between full-stop (PSTP 5 0) and pseudo-stop mode
(PSTP 5 1). In full-stop mode, the oscillator is disabled, and thus all system and
core clocks are stopped. In pseudo-stop mode, the oscillator continues to run and
most of the system and core clocks are stopped. If the respective enable bits are set,
the COP and Real-Time Interrupt (RTI) modules will continue to run.
• Self-clock mode. This mode is entered if both the clock monitor enable bit (the
CME bit of the PLLCTL register) and the self-clock mode enable bit (the SCME bit
of PLLCTL) are set and the clock monitor detects a loss of clock (external oscillator
or crystal). As soon as the self-clock mode is entered, the CRG starts to perform
a clock quality check. The self-clock mode remains active until the clock check
indicates that the required quality (frequency and amplitude) of the incoming
clock signal is met. The self-clock mode should be used for safety purposes only. It
provides reduced functionality to the microcontroller (MCU) in case a loss of clock
is causing severe system conditions.
6.6.2 CRG Signals
• VDDPLL and VSSPLL. The PLL is a critical component for deriving a clock signal
with stable frequency. These two pins provide the operating voltage (VDDPLL) and
ground (VSSPLL) for the PLL circuitry and allow the supply voltage to the PLL to be
independent of the power supply to the rest of the circuit.
• XFC. A passive external loop filter must be placed on the XFC pin. The filter is a
second-order, low-pass filter to eliminate the VCO input ripple. The value of the
external filter network and the reference frequency determine the speed of the
corrections and the stability of the PLL. If the PLL usage is not required, the XFC
pin must be tied to VDDPLL. The PLL loop filter connection recommended by
Freescale is shown in Figure 6.5.
• EXTAL and XTAL. These two pins allow the user to connect an external crystal
oscillator or a CMOS compatible clock to control the internal clock generator
circuitry. The circuit connection for an external crystal oscillator is shown in
279
6.6 ■ Clock and Reset Generation Block (CRG)
VDDPLL
CS
HCS12
CP
RS
XFC
Figure 6.5 ■ PLL loop filter connections
CDC*
VDD
EXTAL
Crystal or
ceramic resonator
C1
HCS12
XCLKS
XTAL
C2
VSSPLL
Figure 6.6 ■ Common crystal connections
EXTAL
CMOS compatible
external oscillator
(VDDPLL level)
HCS12
XCLKS
XTAL
Not connected
Figure 6.7 ■ External oscillator connections
Figure 6.6; the circuit connection that uses a CMOS compatible external clock is
shown in Figure 6.7.
• RESET . This pin is an active low, bidirectional reset pin. As an input, this signal
initializes the microcontroller to a known state. As an output, it indicates that a
system reset (internal to MCU) has been triggered.
• XCLKS . This signal is an input that controls whether a crystal in combination with
the internal oscillator or an external clock source on the EXTAL pin (oscillator
circuitry is bypassed) is used to provide the clock signal required for the MCU
operation. The XCLKS signal is sampled on the rising edge of RESET . When this
signal is high, the crystal and the internal oscillator provide the OSC_CLK signal.
When this signal is low, the external clock provides the OSC_CLK signal.
280
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
6.6.3 The Oscillator (OSC) Block
The OSC block in Figure 6.4 has two external pins, EXTAL and XTAL. The oscillator input
pin, EXTAL, is intended to be connected to either a crystal or an external clock source. The
selection of the crystal or external clock source depends on the XCLKS signal, which is sampled
during the reset. The XTAL pin is an output signal that provides crystal circuit feedback and
can be buffered to drive other devices with the same voltage amplitude.
A buffered EXTAL signal, OSCCLK, becomes the internal reference clock. The oscillator is
enabled on the basis of the PSTP bit and the stop condition. The oscillator is disabled when the
MCU is in stop mode except when the pseudo-stop mode is enabled.
To improve noise immunity, the oscillator is powered by the VDDPLL and VSSPLL power
supply pins.
6.6.4 Phase-Locked-Loop (PLL)
The PLL is used to run the microcontroller with a clock frequency different from the
incoming OSCCLK signal and provides high stability to the system clock. In Figure 6.4, the
frequency of the PLLCLK signal is determined by the synthesizer (SYNR) and the reference
divide (REFDV) registers using the following equation:
PLLCLK 5 2 3 OSCCLK 3
(SYNR 1 1)
(6.1)
(REFDV 1 1)
The contents of the SYNR register and the REFDV register are shown in Figures 6.8 and 6.9,
respectively.
Reset:
7
6
5
4
3
2
1
0
0
0
SYN5
SYN4
SYN3
SYN2
SYN1
SYN0
0
0
0
0
0
0
0
0
2
1
0
Figure 6.8 ■ The CRG synthesizer register (SYNR)
Reset:
7
6
5
4
0
0
0
0
0
0
0
0
3
REFDV3 REFDV2 REFDV1 REFDV0
0
0
0
0
Figure 6.9 ■ The CRG reference divider register (REFDV)
The PLL operates in either the acquisition mode or tracking mode, depending on the difference between the output frequency and the target frequency. The PLL can change between
acquisition and tracking modes either automatically or manually. The functional diagram of
the PLL is shown in Figure 6.10.
281
6.6 ■ Clock and Reset Generation Block (CRG)
Reference
XTAL
REFDV<3:0>
Feedback
Reduced
consumption
oscillator
OSCCLK
Reference
programmable
divider
Pdet
phase
detector
EXTAL
Crystal
monitor
Loop
programmable
divider
SYNR<5:0>
Lock
Lock
detector
Up
Down CPUMP
VCO
VDDPLL
Loop
filter
XFC
pin
PLLCLK
Figure 6.10 ■ PLL functional diagram
P L L O P E R AT I O N
In Figure 6.10, the OSCCLK clock is fed through the reference programmable divider and is
divided into a range from 1 to 16 (REFDV 1 1) to output the reference clock. The VCO output
clock (PLLCLK) is fed back through the programmable loop divider and is in a range of 2 to 128
in increments of [2 3 (SYNR 1 1)] to output the feedback clock.
The phase detector then compares the feedback clock with the reference clock. Correction
pulses are generated on the basis of the phase difference between these two signals. The loop filter then slightly alters the DC voltage on the external filter capacitor connected to the XFC pin
on the basis of the width and direction of the correction pulse. The filter can make fast or slow
corrections depending on its mode. The values of the external filter network and the reference
frequency determine the speed of the corrections and the stability of the PLL.
ACQUISITION AND TRACKING MODES
The lock detector compares the frequencies of the feedback clock and the reference clock.
Therefore, the speed of the lock detector is directly proportional to the final reference frequency. The
circuit determines the mode of the PLL and the lock condition on the basis of this comparison.
The PLL filter can be manually or automatically configured into one of two possible operating modes: acquisition mode and tracking mode. In acquisition mode, the filter can make large
frequency corrections to the Voltage Controlled Output (VCO) circuit. This mode is used at
PLL startup or when the PLL has suffered a severe noise hit and the VCO frequency is far off the
desired frequency. In tracking mode, the filter makes only small corrections to the frequency of
the VCO. PLL jitter is much lower in tracking mode, but the response to noise is also slower.
The PLL enters tracking mode when the VCO frequency is nearly correct.
The operation of the PLL is controlled by four registers: CRGINT, CRGFLG, CLKSEL, and
PLLCTL. The contents of these four registers are shown in Figures 6.11, 6.12, 6.13, and 6.14.
The CRG interrupt register (CRGINT) enables or disables the interrupts associated with
the CRG module. The CRG Flag Register (CRGFLG) holds the status flags of the CRG module.
The CLKSEL register selects the clock source for the PLL. The PLLCTL register provides the
overall control to the PLL module.
282
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
7
6
5
4
3
2
1
0
RTIE
0
0
LOCKIE
0
0
SCMIE
0
0
0
0
0
0
0
0
0
Reset:
RTIE: Real-time interrupt enable bit
0 = Interrupt requests from RTI are disabled.
1 = Interrupt requests from RTI are enabled.
LOCKIE: Lock interrupt enable bit
0 = LOCK interrupt requests are disabled.
1 = LOCK interrupt requests are enabled.
SCMIE: Self-clock mode interrupt enable bit
0 = SCM interrupt requests are disabled.
1 = Interrupt will be requested whenever the SCMIF bit is set.
Figure 6.11 ■ The CRG interrupt enable register (CRGINT)
Reset:
7
6
5
4
3
2
1
0
RTIF
0
PORF
0
LOCKIF
LOCK
TRACK
SCMIF
SCM
0
0
0
0
0
0
0
RTIF: real-time interrupt flag
The RTIF flag is set to 1 at the end of the RTI period. This flag can only be cleared by
writing a 1 to it. When the RTIE bit is 1, the setting of this bit will cause an interrupt.
0 = RTI time-out has not occurred.
1 = RTI time-out has occurred.
PORF: power-on reset flag
This flag is set to 1 when a power-on reset occurs. It can only be cleared by writing a
1 to it.
0 = Power-on reset has not occurred.
1 = Power-on reset has occurred.
LOCKIF: PLL lock interrupt flag
This flag is set to 1 when the LOCK status bit changes. This flag can only be cleared
by writing a 1 to it.
0 = No change in the LOCK bit.
1 = The LOCK bit has changed.
LOCK: lock status bit
This bit reflects the current state of PLL lock condition. This bit is cleared in self-clock
mode.
0 = PLL VCO is not within the desired tolerance of the target frequency.
1 = PLL VCO is within the desired tolerance of the target frequency.
TRACK: track status bit
This bit reflects the current state of PLL lock condition. This bit is cleared in self-clock
mode.
0 = Acquisition mode status.
1 = Tracking mode status.
SCMIF: self-clock mode interrupt flag
This bit is set to 1 when the SCM status bit changes. This flag can only be cleared by
writing a 1 to it.
0 = No change in SCM bit.
1 = SCM bit has changed.
SCM: self-clock mode status bit
SCM reflects the current clocking mode.
0 = MCU is operating normally with OSCCLK available.
1 = MCU is operating in self-clock mode with OSCCLK in an unknown state. All
clocks are derived from PLLCLK running at its minimum frequency, fSCM*.
Figure 6.12 ■ The CRG flag register (CRGFLG)
283
6.6 ■ Clock and Reset Generation Block (CRG)
Reset:
7
6
PLLSEL
PSTP
0
0
5
4
3
SYSWAI ROAWAI PLLWAI
0
0
2
CWAI
0
0
1
0
RTIWAI COPWAI
0
0
PLLSEL: PLL select bit
0 = System clocks are derived from OSCCLK.
1 = System clocks are derived from PLLCLK.
PSTP: pseudo-stop bit
This bit controls the functionality of the oscillator during the stop mode.
0 = Oscillator is disabled in stop mode.
1 = Oscillator continues to run in stop mode (pseudo mode). The oscillator amplitude
is reduced.
SYSWAI: system clocks stop in wait-mode bit
0 = The system clocks continue to run in wait-mode.
1 = The system clocks stop.
ROAWAI: reduced oscillator amplitude in wait-mode bit
0 = normal oscillator amplitude in wait mode.
1 = reduced oscillator amplitude in wait mode.
PLLWAI: PLL stops in wait mode bit
0 = PLL keeps running in wait mode.
1 = PLL stops in wait-mode. The CRG will clear the PLLSEL bit before entering wait
mode. The PLLON bit remains set during wait mode but the PLL is powered
down.
CWAI: core stops in wait-mode bit
0 = Core clock keeps running in wait mode.
1 = Core clock stops in wait mode.
RTIWAI: RTI stops in wait-mode bit
0 = RTI keeps running in wait mode.
1 = RTI stops and initializes the RTI dividers whenever the part goes into wait mode.
COPWAI: COP stops in wait-mode bit
0 = COP keeps running in wait mode.
1 = COP stops and initializes the COP dividers whenever the part goes into wait
mode.
Figure 6.13 ■ The CRG clock select register (CLKSEL)
The PLL can change the bandwidth or operational mode of the loop filter manually or
automatically. In automatic bandwidth control mode (the AUTO bit of the PLLCTL register is set to 1), the lock detector automatically switches between acquisition and tracking
modes. The automatic bandwidth control mode is also used to determine when the PLL clock
(PLLCLK) is safe to use as the source for the system and core clocks. When the LOCK bit of the
CRGFLG register is set to 1, the PLLCLK signal can be used as the system clock safely. The
setting of the LOCK bit can be detected by using the interrupt or polling method. Any change
of the LOCK bit will cause the LOCKIF flag bit to be set to 1 and may optionally request an
interrupt to the CPU.
The PLL circuit can also operate in manual mode (AUTO bit 5 0). The manual mode is
used by systems that do not require an indicator of the lock condition for proper operation.
Such systems typically operate well below the maximum system frequency (fsys) and require
fast startup. To operate in manual mode, we must observe the following procedure:
1. Assert the ACQ bit (in PLLCTL register) before turning on the PLL manual mode.
This configures the filter in acquisition mode.
284
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Reset:
7
6
5
4
3
2
1
0
CME
PLLON
AUTO
ACQ
0
PRE
PCE
SCME
0
0
0
0
0
0
0
0
CME: clock monitor enable bit
0 = Clock monitor is disabled.
1 = Clock monitor is enabled. Slow or stopped clocks will cause a clock monitor reset
sequence or self-clock mode.
PLLON: phase-lock-loop on bit
0 = PLL is turned off.
1 = PLL is turned on. If AUTO bit is set, the PLL will lock automatically.
AUTO: automatic bandwidth control bit
0 = Automatic mode control is disabled and the PLL is under software control, using
ACQ bit.
1 = High-bandwidth filter is selected.
ACQ: acquisition bit (if AUTO bit = 1, this bit has no effect)
0 = Low-bandwidth filter is selected.
1 = High-bandwidth filter is selected.
PRE: RTI enable during pseudo-stop bit
0 = RTI stops running during pseudo-stop mode.
1 = RTI continues running during pseudo-stop mode.
PCE: COP enable during pseudo-stop bit
0 = COP stops running during pseudo-stop mode.
1 = COP continues running during pseudo-stop mode.
SCME: self-clock mode enable bit
0 = Detection of crystal clock failure causes clock monitor reset.
1 = Detection of crystal clock failure forces the MCU in select-clock mode.
Figure 6.14 ■ The CRG PLL control register (PLLCTL)
2. After turning on the PLL, wait for a given time (tacq) before entering tracking mode
(clear ACQ to 0). The parameter tacq can be found in the HCS12DP256 block user guide.
3. After entering the tracking mode, wait for tal ns before selecting the PLLCLK signal
as the source for system and core clocks (by setting the PLLSEL bit of the PLLCTL
register to 1).
6.6.5 System Clock Generation
The clock generator is illustrated in Figure 6.15. This circuit generates the system and core
clocks used in the microcontroller. When dealing with external memory or peripheral modules,
the bus clock, referred to as E-clock in this book, is used. The E-clock is derived by dividing the
SYSCLK clock by 2. If the PLLCLK is chosen to be the SYSCLK, then the frequency of the Eclock is half that of PLLCLK.
When the MCU enters the self-clock mode, the oscillator clock source is switched to the
PLLCLK running at its minimum frequency, fSCM.
Either the oscillator output OSCCLK (when PLLSEL 5 0) or the PLL output PLLCLK
(PLLSEL 5 1) can be selected as SYSCLK. The oscillator can be completely bypassed and turned
off by selecting an external clock source instead. The clock monitor, PLL, RTI, COP, and all
clock signals based on OSCCLK are driven by this external clock instead of the output of the
oscillator. As Figure 6.15 shows, SYSCLK can be a buffered version of the external clock input
and the E-clock can be derived by dividing the external clock by 2.
285
6.6 ■ Clock and Reset Generation Block (CRG)
PLLSEL or SCM
Phaselock
loop
PLLCLK
1
wait (CWAI,SYSWAI)
stop
SYSCLK
0
Core
clock
wait,
stop
÷2
SCM
1
EXTAL
Oscillator
wait(RTIWAI),
stop(PSTP, PRE)
RTI enable
Clock
phase
generator
E-clock
RTI
OSCCLK
0
XTAL
wait(COPWAI),
stop(PSTP, PCE)
COP enable
Clock
monitor
COP
wait(SYSWAI),
stop
Oscillator
clock
Gating
condition
stop(PSTP)
Oscillator
clock (pseudostop mode)
= Clock gate
Figure 6.15 ■ HCS12 clock generation circuit
Example 6.2
▼
There is a system that derives its E-clock from the PLL circuit, and an external clock of
8 MHz is selected. The desired E-clock is 24 MHz. Write an instruction sequence to perform the
desired configuration.
Solution: Since the E-clock frequency is higher than the external clock’s, we need to use the PLL
circuit. According to Figure 6.15, the frequency of SYSCLK would be 48 MHz. The frequencies
of OSCCLK and PLLCLK are 8 MHz and 48 MHz, respectively. According to Equation 6.1,
48 MHz 5 2 3 8 MHz 3 [SYNR 1 1]/[REFDV 1 1]
One of the alternatives could be to set SYNR and REFDV to 2 and 0, respectively. The following
subroutine achieves the desired configuration:
SetClk8
movb
movb
movb
#$02,SYNR
#$0,REFDV
#$80,CLKSEL
; set SYNR to 2
; set REFDV to 0
; enable PLL, keep SYSCLK running in wait mode,
; keep RTI, COP, PLL, and core running in wait mode
286
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
movb
#$60,PLLCTL
brclr
rts
CRGFLG,LOCK,*
; disable clock monitor, enable PLL, set automatic
; bandwidth control, disable RTI and COP in pseudo-stop
; wait until PLL locks into the target frequency
The C language version of the function is as follows:
void SetClk8 (void)
{
SYNR
5 0x02;
REFDV
5 0;
CLKSEL 5 0x80;
PLLCTL 5 0x60;
while (!(CRGFLG & 0x08));
}
// use PLL and 4-MHz crystal to generate 24-MHz system clock
// “
// enable PLL, keep SYSCLK running in wait mode
// enable PLL, set automatic bandwidth control
// wait until PLL locks into the target frequency
In addition to this instruction sequence, we also need to tie the XCLKS pin to ground to bypass
the crystal oscillator.
▲
Example 6.3
▼
There is a system that uses a 4-MHz crystal oscillator to derive a 24-MHz E-clock. Write a
subroutine to perform the required configuration.
Solution: The frequency of SYSCLK would be 48 MHz. The frequencies of OSCCLK and
PLLCLK are 4 MHz and 48 MHz, respectively. According to Equation 6.1,
48 MHz 5 2 3 4 MHz 3 [SYNR 1 1]/[REFDV 1 1]
One of the alternatives could be to set SYNR and REFDV to 5 and 0, respectively. The following
subroutine achieves the desired configuration:
SetClk4
movb
movb
movb
#$05,SYNR
#$0,REFDV
#$80,CLKGSEL
movb
#$60,PLLCTL
brclr
rts
CRGFLG,LOCK,*
; set SYNR to 5
; set REFDV to 0
; enable PLL, keep SYSCLK running in wait mode,
; keep RTI, COP, PLL, and core running in wait mode
; disable clock monitor, enable PLL, set automatic
; bandwidth control, disable RTI and COP in pseudo-stop
; wait until PLL locks into the target frequency
We also need to pull the XCLKS pin to high to select the crystal oscillator to generate the
OSCCLK signal.
▲
6.6.6 Clock Monitor
The clock monitor circuit is based on an internal resistor-capacitor (RC) time delay so that it can
operate without any MCU clocks. If no OSCCLK edges are detected within this RC time delay, the
clock monitor indicates failure; this asserts self-clock mode or generates a system reset depending on
the state of the SCME bit. If the clock monitor is disabled or the presence of clocks is detected, no
failure is indicated. The clock monitor function is enabled or disabled by the CME control bit.
287
6.7 ■ Real-Time Interrupt
6.7 Real-Time Interrupt
The main function of the RTI circuit is to generate hardware interrupts periodically. If
enabled, this interrupt will occur at the rate selected by the RTICTL register. The contents of
this register are shown in Figure 6.16. The possible interrupt periods (in number of OSCCLK
cycles) are listed in Table 6.4.
The time-multiplexing technique has been used to display multiple seven-segment displays
at the same time in Chapter 4. However, the method used in Chapter 4 requires the CPU to call
a delay function to generate the desired time delay to switch the digit to be displayed; this would
prevent the CPU from performing other operations. One solution to this problem is to remind the
CPU to switch digits to be displayed periodically. The RTI function serves this purpose perfectly
well. The next three examples use the RTI program to perform seven-segment pattern shifting.
Reset:
7
6
5
4
3
2
1
0
0
RTR6
RTR5
RTR4
RTR3
RTR2
RTR1
RTR0
0
1
0
0
0
0
0
0
Figure 6.16 ■ CRG RTI control register (RTICTL)
RTR[6:4]
RTR[3:0]
000
(off)
001
(210)
010
(211)
011
(212)
100
(213)
101
(214)
110
(215)
111
(216)
0000 (41)
off*
210
211
212
213
214
215
216
0001(42)
off*
2 3 210
2 3 211
2 3 212
2 3 213
2 3 214
2 3 215
2 3 216
0010 (43)
off*
3 3 210
3 3 211
3 3 212
3 3 213
3 3 214
3 3 215
3 3 216
0011 (44)
off*
4 3 210
4 3 211
4 3 212
4 3 213
4 3 214
4 3 215
4 3 216
0100 (45)
off*
5 3 210
5 3 211
5 3 212
5 3 213
5 3 214
5 3 215
5 3 216
off*
6 3 210
6 3 211
6 3 212
6 3 213
6 3 214
6 3 215
6 3 216
off*
7 3 210
7 3 211
7 3 212
7 3 213
7 3 214
7 3 215
7 3 216
off*
8 3 210
8 3 211
8 3 212
8 3 213
8 3 214
8 3 215
8 3 216
off*
9 3 210
9 3 211
9 3 212
9 3 213
9 3 214
9 3 215
9 3 216
1001 (410)
off*
10 3 210
10 3 211
10 3 212
10 3 213
10 3 214
10 3 215
10 3 216
1010 (411)
off*
11 3 210
11 3 211
11 3 212
11 3 213
11 3 214
11 3 215
11 3 216
1011 (412)
off*
12 3 210
12 3 211
12 3 212
12 3 213
12 3 214
12 3 215
12 3 216
1100 (413)
off*
13 3 210
13 3 211
13 3 212
13 3 213
13 3 214
13 3 215
13 3 216
off*
14 3 210
14 3 211
14 3 212
14 3 213
14 3 214
14 3 215
14 3 216
off*
15 3 210
15 3 211
15 3 212
15 3 213
15 3 214
15 3 215
15 3 216
off*
16 3 210
16 3 211
16 3 212
16 3 213
16 3 214
16 3 215
16 3 216
0101 (46)
0110 (47)
0111 (48)
1000 (49)
1101 (414)
1110 (415)
1111 (416)
* Denotes the default value out of reset. This value disables RTI.
Table 6.4 ■ RTI period (in units of OSCCLK cycle)
288
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Example 6.4
▼
Write an assembly program to use the RTI to time-multiplex four seven-segment displays
using the circuit shown in Figure 4.18. Turn on one display at a time and light each display for
about 1 ms, then switch to the next display. Use display #0 to display #3. Use CodeWarrior and
a demo board programmed with a serial monitor to implement the circuit.
Solution: The algorithm in time-multiplexing four seven-segment displays is similar to that
described in Example 5.8 but uses a RTI to trigger the switch of display. There are 10 display
sequences.
1234
2345
...
0123
For each sequence, digits are indexed from 0 to 3. By repeating each sequence 100 times and turning on one digit for 1 ms, a sequence will last for 0.4 s. Within 0.4 s, there will be 400 RTIs. Let
base, dindex, and timeCnt represent the starting address of a sequence, index to digits within a
sequence, and the repetition count of a sequence, respectively. The following assembly program
performs the seven-segment display shifting:
base
timeCnt
dindex
start
include “c:\miniide\hcs12.inc”
org
$1000
ds.w
1
ds.w
1
ds.b
1
org
$1500
lds
#$1500
jsr
SetClk8
movw
#Seg7Pat,base
clr
dindex
movb
#$FF,DDRB
movb
#$0F,DDRP
movw
#400,timeCnt
movb
#$40,RTICTL
bset
CRGINT,RTIE
cli
bra
$
; base address of the current four-digit sequence
; repetition count of the current sequence of four digits
; digit index in a sequence (0, 1, 2, or 3)
; set up stack pointer
; set E-clock to 24 MHz
; initialize the base address of a new sequence of digits
; the offset digit to be displayed from the base
; configure Port B for output
; configure Port P lower 4 pins for output
; allow four digits to multiplex for 0.4 s
; set RTI to about 1 ms
; enable RTI locally
; enable interrupt globally
; wait for interrupt to occur
; ********************************************************************************
SetClk8
movb
#$02,SYNR
; set SYNR to 2
movb
#$0,REFDV
; set REFDV to 0
movb
#$80,CLKSEL
; enable PLL, keep SYSCLK running in wait mode,
; keep RTI, COP, PLL, and core running in wait mode
movb
#$60,PLLCTL
; disable clock monitor, enable PLL, set automatic
; bandwidth control, disable RTI and COP in pseudo; stop
289
6.7 ■ Real-Time Interrupt
brclr
rts
CRGFLG,LOCK,*
; wait until PLL locks into the target frequency
; ********************************************************************************
rtiISR
movb
#RTIF,CRGFLG
; clear the RTIF flag
ldx
base
; load the base address stored in base
ldab
dindex
; place the digit offset
movb
b,X,PTB
; output the segment pattern
cmpb
#0
beq
digit0
; turn on display #0
cmpb
#1
beq
digit1
; turn on display #1
cmpb
#2
beq
digit2
; turn on display #2
movb
#$07,PTP
; turn on display #3
bra
update
digit2
movb
#$0B,PTP
; turn on digit 2
bra
update
digit1
movb
#$0D,PTP
; turn on digit 1
bra
update
digit0
movb
#$0E,PTP
; turn on digit 0
update
inc
dindex
; switch to the next digit
ldaa
dindex
; check if we need to reset digit index to 0
cmpa
#4
; “
bne
nextc
movb
#0,dindex
nextc
ldy
timeCnt
; decrement repletion count
dey
; “
sty
timeCnt
; “
cpy
#0
; do we need to change to a new sequence?
bne
next
; the current four digits need not be changed yet
movw
#400,timeCnt
; restore the time count to 400
ldx
base
; shift to the next four digits (shift by 1)
inx
; “
stx
base
; “
cpx
#Seg7Pat110
; is this the last sequence? (0,1,2,3)
bne
next
; “
movw
#Seg7Pat,base
; go back to the first sequence again
next
rti
; ********************************************************************************
Seg7Pat
dc.b
$06, $5B, $4F, $66, $6D, $7D, $07, $7F, $6F, $3F, $06, $5B, $4F
; ********************************************************************************
org $FFF0
dc.w rtiISR
; RTI vector stored here
org $FFFE
dc.w start
; reset vector
end
▲
290
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Example 6.5
▼
Write a C program to use the RTI to time-multiplex four seven-segment displays using
the circuit shown in Figure 4.18 and shift the seven-segment display pattern as described in
Example 6.4. Turn on one display at a time and light each display for about 1 ms, then switch
to the next display. Use display 0 to display 3. Use CodeWarrior and a demo board programmed
with serial monitor to implement the circuit.
Solution: To implement the digit sequence shifting program in C language, we make the following arrangement:
• Place the segment patterns in one array (segPat[]). This array is the overlapping
arrangement of 10 display sequences as shown in Figure 5.2.
• Place digit select values in one array (digit[]).
• Use a variable (seq) as an index to the segment pattern array that identifies the first
digit of the current sequence.
• Use a variable (ix) as an index to the digits within one sequence. The range of this
variable is from 0 to 3.
• Use a variable (count) to specify the repetition count of a sequence.
The main function that performs the initialization and the RTI service routine that performs
time multiplexing is
#include
“c:\cwHCS12\include\hcs12.h”
#include
“c:\cwHCS12\include\SetClk.h”
int
seq;
// start index to segPat[] of a sequence of digits (0 to 9)
int
ix;
// index of digits of a sequence (0 to 3)
int
count;
// repetition count of a sequence
char segPat[13] 5 {0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x67, 0x3F, 0x06, 0x5B, 0x4F};
char digit[4] 5 {0xFE, 0xFD, 0xFB, 0xF7};
void main (void) {
seq 5 0;
ix
5 0;
count 5 400;
SetClk8();
RTICTL 5 0x40;
DDRB 5 0xFF;
DDRP 5 0xFF;
CRGINT| 5 RTIE;
asm(“CLI”);
while(1);
}
// RTI service routine
interrupt void rtiISR(void) {
CRGFLG 5 0x80;
PTB 5 segPat[seq1ix];
PTP 5 digit[ix];
ix11;
if (ix 55 4)
ix 5 0;
// initialize the start index to segPat[] for the display sequence
// initialize the index of a new sequence
// initialize the RTI count of a sequence
// set E-clock to 24 MHz from an 8-MHz crystal oscillator
// RTI interval set to 2**10 OSCCLK cycles
// configure Port B for output
// configure Port P for output
// enable RTI
// enable interrupt globally
// clear RTIF bit
// send out digit segment pattern
// turn on the display
// increment the index to digits of a sequence
// make sure the index to digits of a sequence is from 0 to 3
// “
291
6.7 ■ Real-Time Interrupt
count22;
if(count 55 0){
seq11;
count 5 400;
}
if(seq 55 10)
seq 5 0;
// is time for the current sequence expired?
// change to a new sequence of digits
// reset repetition count
// is this the last sequence?
// reset start index of a sequence
}
This programming project also contains the vectors.c file.
extern void near rtiISR(void);
#pragma CODE_SEG __NEAR_SEG NON_BANKED
#pragma CODE_SEG DEFAULT
// Change code section to DEFAULT.
typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFFF0 5 {
rtiISR
};
▲
Example 6.6
▼
Modify the C program in Example 6.5 so that it can be compiled using the ICC12 C compiler and run in a demo board programmed with the D-Bug12 monitor.
Solution: Since the ICC12 IDE does not support flash memory programming, it can only support
program execution in SRAM. We will use inline assembly instructions to set up an interrupt
vector for RTI. The C program for ICC2 to implement seven-segment display shifting described
in Example 6.5 is
#include
“c:\cwHCS12\include\hcs12.h”
#include
“c:\cwHCS12\include\SetClk.h”
void rtiISR(void);
int seq,ix,count;
char segPat[13] 5 {0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x67, 0x3F, 0x06, 0x5B, 0x4F};
char digit[4] 5 {0xFE, 0xFD, 0xFB, 0xF7};
void main (void) {
asm(“ldd #_rtiISR”);
// set up RTI vector by calling SetUserVector
asm(“pshd”);
// function using inline assembly instructions
asm(“ldd #56”);
// “
asm(“ldx $EEA4”);
// “
asm(“jsr 0,x”);
// “
seq
5 0;
ix
5 0;
count
5 400;
SetClk8();
RTICTL
5 0x40;
// RTI interval set to 2**10 OSCCLK cycles
DDRB
5 0xFF;
// configure Port B for output
292
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
DDRP
5 0xFF;
CRGINT |5 RTIE;
asm(“CLI”);
while(1);
// configure Port P for output
// enable RTI interrupt
// enable interrupt globally
}
#pragma interrupt_handler rtiISR
void rtiISR(void) {
CRGFLG 5 0x80;
PTB 5 segPat[seq1ix];
PTP 5 digit[ix];
ix11;
if (ix 55 4)
ix 5 0;
count22;
if(count 55 0){
seq11;
count 5 400;
}
if(seq 55 10)
seq 5 0;
}
// clear RTIF bit
// output the segment pattern
// output digit select value
// increment digit index within a sequence
// decrement repetition count
// if repetition count is 0, then change to the next sequence
// Reach the last sequence?
The required modification to this program for it to compile with the EGNU IDE is given in
Appendix E.
▲
6.8 Computer Operating Properly
The COP (a free-running watchdog timer) enables the user to find out if a user program is
running and sequencing properly. When the COP times out, it resets the CPU. The CPU reset
by the COP is an indication that the software is no longer being executed in the intended sequence. The software that utilizes the COP function must include an instruction sequence to
prevent the COP from timing out.
To prevent the COP module from resetting the MCU, one must write the value $55 followed by $AA into the ARMCOP register. Other instructions can be inserted between the values $55 and $AA, but the sequence ($55, $AA) must be completed prior to the COP timeout
period to avoid a COP reset. Writing any value other than $55 or $AA into this register will
reset the MCU.
The functioning of the COP module and its timeout period are controlled by the COPCTL
register. The contents of this register are shown in Figure 6.17. The COP module is disabled
if the CR2:CR0 bits of this register are set to 000. The COP has a windowed option for its
operation. The windowed COP operation is enabled by setting the WCOP bit of the COPCTL
register. In this mode, writes to the ARMCOP register to clear the COP timer must occur in
the last 25 percent of the selected timeout period. A premature write will immediately reset
the MCU.
293
6.9 ■ Low-Power Modes
7
6
WCOP RSBCK
Reset:
0
1
5
4
3
2
1
0
0
0
0
CR2
CR1
CR0
0
0
0
0
0
0
WCOP: windowed COP mode bit
When set, a write to the ARMCOP register must occur in the last 25%
of the selected period. A write during the first 75% of the selected
period will reset the MCU.
0 = Normal COP operation.
1 = Windowed COP operation.
RSBCK: COP and RTI stop in active BDM mode bit
0 = Allows the COP and RTI to keep running in active BDM mode.
1 = Stops the COP and RTI whenever the HCS12 is in active BDM mode.
CR2:CR0: COP watchdog time rate select (number of OSCCLK cycles)
000: COP disabled
001: 214
010: 216
011: 218
100: 220
101: 222
110: 223
111: 224
Figure 6.17 ■ CRG COP control register (COPCTL)
6.9 Low-Power Modes
When a microcontroller is performing normal operations, power consumption is unavoidable. However, the microcontroller in an embedded system may not always be performing useful operations. Under this situation, it would be ideal for the power consumption to be reduced
to the minimum. This issue is especially important for those embedded products powered by
batteries. The HCS12 has two low-power modes that can reduce power consumption dramatically: wait and stop modes.
6.9.1 The Wai Instruction
The wait mode is entered by executing the wai instruction. The wai instruction pushes all
CPU registers (except the stack pointer) and the return address onto the stack and enters a wait
state. During the wait state, CPU clocks are stopped (clock signals that drive the ALU and register file), but other clocks in the microcontroller (clock signals that drive peripheral functions)
continue to run.
The CPU leaves the wait state when it senses one or more of the following events:
• Maskable interrupts that are not masked
• Nonmaskable interrupts
• Resets
On leaving the wait state, the HCS12 CPU sets the appropriate interrupt mask bit(s), fetches
the vector corresponding to the exception sensed, and continues instruction execution at the
location the vector points to.
294
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
6.9.2 The Stop Instruction
When the S bit in the CCR register is cleared and a stop instruction is executed, the HCS12
saves all CPU registers (except the stack pointer) in the stack, stops all system clocks, and puts
the microcontroller in standby mode. The standby operation minimizes the system power consumption. The contents of registers and the states of I/O pins remain unchanged.
Asserting the RESET, XIRQ, or IRQ signal ends standby mode. If it is the XIRQ signal that
ends the stop mode and the X mask bit is 0, instruction execution resumes with a vector fetch
for the XIRQ interrupt. If the X mask bit is 1 (XIRQ disabled), a two-cycle recovery sequence is
used to adjust the instruction queue, and execution continues with the next instruction after
the stop instruction.
6.10 Resets
There are four possible sources of resets.
• Power-on (POR) and low-voltage detector (LVD) reset
• RESET pin
• COP reset
• Clock monitor reset
The COP and clock monitor resets have been discussed earlier. Power-on, low-voltage detector,
and RESET pin resets share the same reset vector. The COP reset and the clock monitor reset
each have a separate vector.
6.10.1 Power-On Reset
The HCS12 has a circuitry to detect when the V DD supply to the MCU has reached a
certain level and asserts reset to the internal circuits. The detector circuit is triggered by the
slew rate. As soon as a power-on reset is triggered, the CRG module performs a quality check
on the incoming clock signal. Start of the reset sequence is delayed until the clock check indicates a valid clock signal or the clock check was unsuccessful, and the CRG module enters
self-clock mode.
6.10.2 External Reset
The HCS12 distinguishes between internal and external resets by sensing how quickly the
signal on the RESET pin rises to logic high after it has been asserted. When the HCS12 senses
any of the four reset conditions, internal circuitry drives the RESET pin low for 128 SYSCLK
cycles (this number might be increased by 3 to 6 SYSCLK cycles), then releases. Sixty-four SYSCLK cycles later, the CPU samples the state of the signal applied to the RESET pin. If the signal
is still low, an external reset has occurred. If the signal is high, the reset has been initiated internally by either the COP system or the clock monitor.
The power supply to an embedded system may drop below the required level. If the microcontroller keeps working under this situation, the contents of the EEPROM might be corrupted.
The common solution is to pull the RESET signal to low so that the microcontroller cannot
execute instructions. A low-voltage-inhibit (LVI) circuit, such as the Freescale MC34064, can
be used to protect against the EEPROM corruption. Figure 6.18 shows an example of the reset
circuit with a manual reset and LVI circuit.
295
6.11 ■ HCS12 Operation Modes
VDD
VDD
4.7 kΩ
2μF
IN
MC34064
RESET
Manual reset
1
4.7 kΩ
GND
3
4.7 kΩ
To RESET
of HCS12
Figure 6.18 ■ A typical external reset circuit
6.11 HCS12 Operation Modes
As shown in Table 6.5, the HCS12 can operate in eight different modes. Each mode has
a different default memory map and external bus configuration. After reset, most system resources can be mapped to other addresses by writing to the appropriate control registers.
The states of the BKGD, MODB, and MODA pins when the RESET signal is low determine
the operation mode after the CPU leaves the reset state. The SMODN, MODB, and MODA bits
in the MODE register show the current operation mode and provide limited mode switching
during the operation. The states of the BKGD, MODB, and MODA pins are latched into these
bits on the rising edge of the RESET signal. During reset, an active pull-up (on-chip transistor) is
connected to the BKGD pin (as input) and active pull-downs (on-chip transistors) are connected
to the MODB and MODA pins. If an open circuit occurs on any of these pins, the device will
operate in normal single-chip mode.
The two basic types of operation modes are
1. Normal modes. Some registers and bits are protected against accidental changes.
2. Special modes. Greater access for special purposes such as testing and emulation to
protected control registers and bits is allowed.
BKGD
MODB
MODA
Mode
Port A
Port B
0
0
0
0
1
1
1
1
0
0
1
1
0
0
1
1
0
1
0
1
0
1
0
1
Special single chip
Special expanded narrow
Special peripheral
Special expanded wide
Normal single chip
Normal expanded narrow
Reserved (forced to peripheral)
Normal expanded wide
General-purpose I/O
ADDR[15:8]DATA[7:0]
ADDR/DATA
ADDR/DATA
General-purpose I/O
ADDR[15:8]DATA[7:0]
2
ADDR/DATA
General-purpose I/O
ADDR[7:0]
ADDR/DATA
ADDR/DATA
General-purpose I/O
ADDR[7:0]
2
ADDR/DATA
Table 6.5 ■ HCS12 mode selection
296
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
The background debug mode (BDM) is a system development and debug feature and is available
in all modes. In special single-chip mode, BDM is active immediately after reset.
6.11.1 Normal Operation Modes
These modes provide three operation configurations. Background debugging is available in
all three modes, but must first be enabled for some operations by means of a BDM command.
BDM can then be made active by another command.
N O R M A L E X PA N D E D W I D E M O D E
In this mode, Ports A and B are used as the multiplexed 16-bit address and data buses.
ADDR[15..8] and DATA[15..8] are multiplexed on Port A. ADDR[7..0] and DATA[7..0] are multiplexed on Port B.
N O R M A L E X PA N D E D N A R R O W M O D E
The 16-bit external address bus uses Port A for the high byte and Port B for the low byte. The
8-bit external data bus uses Port A. ADDR[15..8] and DATA[7..0] are multiplexed on Port A.
NORMAL SINGLE-CHIP MODE
Normal single-chip mode has no external buses. Ports A, B, and E are configured for general-purpose I/O. Port E bits 1 and 0 are input only with internal pull-ups and the other 22 pins
are bidirectional I/O pins that are initially configured as high-impedance inputs. Port E pull-ups
are enabled on reset. Port A and B pull-ups are disabled on reset.
6.11.2 Special Operation Modes
Special operation modes are commonly used in factory testing and system development.
S P E C I A L E X PA N D E D W I D E M O D E
This mode is for emulation of normal expanded wide mode and emulation of normal
single-chip mode with a 16-bit bus. The bus-control pins of Port E are all configured for their
bus-control output functions rather than general-purpose I/O.
S P E C I A L E X PA N D E D N A R R O W M O D E
This mode is for emulation of normal expanded narrow mode. External 16-bit data is handled as two back-to-back bus cycles, one for the high byte followed by one for the low byte.
Internal operations continue to use full 16-bit data paths.
SPECIAL SINGLE-CHIP MODE
This mode can be used to force the microcontroller to active BDM to allow a system debug
through the BKGD pin. The HCS12 CPU does not fetch the reset vector or execute application
code as it would in other modes. Instead, the active background mode is in control of CPU
execution, and BDM firmware waits for additional serial commands through the BKGD pin.
There are no external address and data buses in this mode. The microcontroller operates as a
stand-alone device, and all program and data space are on-chip. External port pins can be used
for general-purpose I/O.
SPECIAL PERIPHERAL MODE
The HCS12 CPU is not active in this mode. An external master can control on-chip peripherals for testing purposes. It is not possible to change to or from this mode without going
through reset. Background debugging should not be used while the microcontroller is in special
peripheral mode, as internal bus conflicts between the BDM and the external master can cause
improper operation of both modes.
6.12 ■ Summary
297
6.12 Summary
Interrupt is a special event that requires the CPU to stop normal program execution and
provide a certain service to the event. The interrupt mechanism has many applications, including coordinating I/O activities, exiting from software errors, and reminding the CPU to perform
routine work, and so on.
Some interrupts are maskable and can be ignored by the CPU. Other interrupts are nonmaskable and cannot be ignored by the CPU. Nonmaskable interrupts are often used to handle
critical and emergent events such as process control and power failure.
Multiple interrupts may be pending at the same time. The CPU needs to decide which one
to service first. The solution to this issue is to prioritize all of the interrupt sources. The pending interrupt with the highest priority will receive service before other pending interrupts.
The CPU provides service to an interrupt request by executing an interrupt service routine.
The current program counter value is saved in the stack before the CPU executes the service
routine so that CPU control can be returned to the interrupted program when the interrupt service routine is completed.
In order to provide service to the interrupt, the CPU must have some way to find out the
starting address of the interrupt service routine. There are three methods to determine the starting address (called interrupt vector) of the interrupt service routine.
1. Each interrupt vector is predefined when the microcontroller is designed. In this
method, the CPU simply jumps to the predefined location to execute the service
routine.
2. Each interrupt vector is stored in a predefined memory location. When an
interrupt occurs, the CPU fetches the interrupt vector from that predefined
memory location.
The HCS12 uses this approach.
3. The interrupt source provides an interrupt vector number to the CPU so that the CPU
can figure out the memory location where the interrupt vector is stored. The CPU
needs to perform a read bus cycle to obtain the interrupt vector number.
There are three steps in the interrupt programming.
Step 1
Initialize the interrupt vector table that holds all the interrupt vectors. This step is not
needed for those microcontrollers that use the first method to resolve the interrupt vector.
Step 2
Write the interrupt service routine.
Step 3
Enable the interrupt to be serviced.
Users who own a demo board with a resident D-Bug12 monitor would use the EVB mode
for application development. The D-Bug12 monitor has occupied the default memory space
where the interrupt vectors are to be stored. To enable the user to use interrupts in their applications, D-Bug12 allows vectors to be stored in a table in the SRAM.
Clock signals are critical to the proper operation of the MCU. The HCS12 has a phaselocked-loop (PLL) circuit that can be used to generate the system clock and E-clock with a frequency higher than that of the external crystal oscillator. The PLL can generate a high-frequency
clock signal using a low-frequency crystal. In addition, the PLL provides stability to the clock
signals.
298
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
Reset is a mechanism for
1. Setting up operation mode for the microcontroller
2. Setting up initial values for control registers
3. Exiting from software errors and some hardware errors
All HCS12 microcontrollers have the same number of resets and nonmaskable interrupt
sources despite the fact that they may not have the same number of maskable interrupts. The
HCS12 has two low-power modes that are triggered by the execution of wai and stop instructions.
Power consumption will be reduced dramatically in either low-power mode. The HCS12 has a
COP timer reset mechanism to detect the software error. A software program that behaves properly will reset the COP timer before it times out and prevent it from resetting the CPU.
The HCS12 has a clock monitor reset mechanism that can detect the slowing down or loss
of clock signals. Whenever the clock frequency gets too low, the clock monitor will detect it
and reset the CPU.
The real-time interrupt (RTI) mechanism, when enabled, generates periodic interrupts to
remind the CPU to perform routine work such as time-multiplexing seven-segment displays,
environment monitoring, task switching in a multitasking operating system, and so on.
The HCS12 has seven different operation modes divided into two basic categories: normal
modes and special modes. Normal modes are used for embedded applications, whereas special
modes are used in fabrication testing and development debugging activities.
6.13 Exercises
E6.1 What is the name given to a routine that is executed in response to an interrupt?
E6.2 What are the advantages of using interrupts to handle data inputs and outputs?
E6.3 What are the requirements for interrupt processing?
E6.4 How do you enable other interrupts when the HCS12 is executing an interrupt service
routine?
E6.5 Why would there be a need to promote one of the maskable interrupts to the highest priority among all maskable interrupts?
E6.6 Write the assembler directives to initialize the IRQ interrupt vector located at $3000 for
the EVB mode of the D-Bug12 monitor.
E6.7 What is the last instruction in most interrupt service routines? What does this instruction do?
E6.8 Suppose that the HCS12 is executing the following instruction segment and the IRQ
interrupt occurs when the TSY instruction is being executed. What will be the contents of the
top 10 bytes in the stack?
org
lds
clra
ldx
bset
ldab
inca
tap
pshb
tsy
adda
$2000
#$2000
#$0
10,X $48
#$40
#10
6.14 ■ Lab Exercises and Assignments
299
E6.9 Suppose that the OSCCLK clock frequency is 5 MHz. Compute the COP watchdog timer
timeout period for all the possible combinations of the CR2, CR1, and CR0 bits in the COPCTL
register.
E6.10 Assume that the interrupt vector for the timer overflow is $3000. Write the assembler
directives to initialize its vector table entry on a demo board with the D-Bug12 monitor.
E6.11 Write an instruction sequence to clear the X and I bits in the CCR. Write an instruction
sequence to set the S, X, and I bits in the CCR.
E6.12 Why does the HCS12 need to be reset when the power supply is too low?
E6.13 Write an instruction sequence to prevent the COP timer from timing out and resetting
the microcomputer.
E6.14 Suppose you want to generate a 25-MHz E-clock; propose a set of values for the SYNR
and REFDV registers to achieve this goal using a 5-MHz crystal oscillator and the PLL
circuit.
E6.15 Suppose you want to generate a 24-MHz E-clock; propose a set of values for the SYNR
and REFDV registers to achieve this goal using a 3-MHz crystal oscillator and the PLL
circuit.
E6.16 Suppose you want to generate a 20-MHz E-clock; propose a set of values for the SYNR
and REFDV registers to achieve this goal using a 4-MHz crystal oscillator and the PLL
circuit.
E6.17 Suppose you want to generate a 24-MHz E-clock; propose a set of values for the SYNR
and REFDV registers to achieve this goal using a 6-MHz crystal oscillator and the PLL
circuit.
E6.18 Set up the interrupt vectors for the enhanced capture timer Ch1, enhanced capture timer
Ch0, RTI, and IRQ to work with the CodeWarrior IDE by modifying the vectors.c file given in
Example 6.5.
E6.19 Write a program to drive the LED circuit in Figure 4.16 and display one LED at a time
from the one driven by pin 7 toward the one driven by pin 0 and then reverse. Repeat this
operation forever. Each LED is lighted for about 400 ms assuming that the HCS12 uses an
8-MHz crystal oscillator to generate a system clock. Use the RTI to trigger the change of the
LED light patterns. It may take several RTIs to trigger one change of the LED pattern for this
problem.
E6.20 Write a program to generate a periodic square waveform that is about 500 Hz (roughly)
using the PT0 pin and the RTI.
6.14 Lab Exercises and Assignments
L6.1 IRQ input interrupt experiment. Use the 555 timer (or take the signal from a function
generator) to generate a digital waveform with frequency equal to approximately 1 Hz. The
circuit connection of the 555 timer is illustrated in Figure L6.1. Connect the 555 timer output
(pin 3) to the IRQ pin. The IRQ interrupt service routine would output the message “Interrupt k.”
Enter the program (both assembly and C) to a file, assemble or compile, and download the Srecord file onto the demo board for execution.
L6.2 Simple interrupts. Connect the IRQ pin of the demo board to a debounced switch that can
generate a negative-going pulse. Write a main program and an IRQ interrupt service routine.
The main program initializes the variable irq_cnt to 10, stays in a loop, and keeps checking the
value of irq_cnt. When irq_cnt is decremented to 0, the main program jumps back to monitor.
The IRQ service routine simply decrements irq_cnt by 1 and returns.
300
Chapter 6 ■ Interrupts, Clock Generation, Resets, and Operation Modes
VCC (5 V)
HCS12
VOUT
IRQ
f=
4
3
1.44
(R1 + 2R2)C
R1
8
7
R2
555
Timer
6
2
1
5
C
R1 = 6.2 kΩ
R2 = 150 kΩ
C = 4.7μF
0.01μF
Figure L6.1 ■ HCS12 IRQ pin interrupt circuit
The lab procedure is as follows:
Step 1
Connect the IRQ pin of the demo board to a debounced switch that can generate a clean
negative-going pulse. A debounced switch is available on the SSE256 demo board.
Step 2
Enter the main program and IRQ service routine, assemble them, and then download them
to the single-board computer. Remember to enable the IRQ interrupt in your program.
Step 3
Pulse the switch 10 times. If everything works properly, you should see the D-Bug12
monitor prompt after 10 pulses applied to the IRQ pin.
L6.3 RTI Interrupts and LED flashing. Write a C (or assembly) program to flash the LEDs on
your demo board with the following patterns:
1. Turn on all LEDs for about 0.4 s and turn them off also for about 0.4 s.
2. Repeat step 1 three more times.
3. Turn on one LED at a time from left to right with each LED turned on for about 0.4 s.
4. Repeat step 3 three more times.
5. Turn on one LED at a time from right to left with each LED turned on for about 0.4 s.
6. Repeat step 5 three more times.
7. Turn LEDs driven by pins RB7 and RB0 on and off four times. The on-time and off-time
are each about 0.4 s.
8. Turn LEDs driven by pins RB6 and RB1 on and off four times. The on-time and off-time
are each about 0.4 s.
9 . Turn LEDs driven by pins RB5 and RB2 on and off four times. The on-time and off-time
are each about 0.4 s.
10. Turn LEDs driven by pins RB4 and RB3 on and off four times. The on-time and off-time
are each about 0.4 s.
11. Turn LEDs driven by pins RB3 and RB4 on and off four times. The on-time and off-time
are each about 0.4 s.
6.14 ■ Lab Exercises and Assignments
301
12. Turn LEDs driven by pins RB2 and RB5 on and off four times. The on-time and off-time
are each about 0.4 s.
13. Turn LEDs driven by pins RB1 and RB6 on and off four times. The on-time and off-time
are each about 0.4 s.
14. Turn LEDs driven by pins RB0 and RB7 on and off four times. The on-time and off-time
are each about 0.4 s.
15. Go to step 1 and repeat.
You are required to use the RTI to generate the required delay and trigger the change of LED
patterns.
This page intentionally left blank
7
Advanced Parallel I/O
7.1 Objectives
• Explain I/O addressing issue
• Explain I/O synchronization methods
• Verify voltage compatibility when interfacing
a microcontroller with peripheral devices
• Verify current compatibility when driving
peripheral devices
• Configure the LCD
• Write program to display messages on the
LCD
• Write program to enter data using keypads or
keyboards
• Explain keypad- or keyboard-debouncing
method
• Explain the principles of operation of stepper
motors
• Write programs to drive stepper motors
• Write programs to generate waveforms using
the D/A converter
• Explain the operation, application, and
programming of key-wake-up ports
304
Chapter 7 ■ Advanced Parallel I/O
7.2 I/O Related Issues
A brief introduction to the I/O operation and I/O devices was given in Chapter 4. In this
chapter, the issues related to I/O addressing, I/O synchronization, electrical characteristics compatibility, and HCS12 I/O ports configuration are explored. More complicated I/O devices such
as liquid crystal displays (LCDs), keypads and keyboards, stepper motors, and D/A converters
will be explained.
7.3 I/O Addressing Issue
I/O devices are also called peripheral devices in the sense that they are outside the core of a
computer system. To perform an I/O operation, the CPU needs to specify the I/O device that it
intends to deal with. This involves two issues.
• Address space. This issue is related to the question of whether I/O devices should
be treated the same as memory devices, that is, whether the I/O devices should
occupy the same address space (some people like to call it memory space) as the
memory devices (SRAM, DRAM, EEPROM, or flash memory). Both approaches
have been used by microprocessor and microcontroller vendors. The current trend
is for I/O devices and memory components to share the same memory space.
• Addressing modes and instructions. I/O devices may have their own addressing
modes and instruction set or share the same addressing modes and instruction set
with the memory devices. In the first approach, the microprocessor may use the
following instructions for input and output:
out 3: sends data in accumulator to I/O device at address 3.
in 5: inputs a byte from input device 5 to the accumulator.
In the second approach, the microprocessor uses the same instructions and addressing
modes to perform input and output operations. Again, the current trend is the second
approach.
Traditionally, Motorola microprocessors and microcontrollers have used the same addressing modes and instruction sets to access I/O and memory devices. Memory and I/O devices
share a single memory space.
7.4 I/O Synchronization
As described in Section 1.3.2, microprocessors either cannot provide the current required
by the peripheral devices or operate at a voltage level different from those of peripheral devices. Therefore they usually communicate with peripheral devices via interface chips. For
the microcontroller, the functions of most of these interface chips are built into the same
chip as the microcontroller. When transferring data in the parallel format (multiple bits at a
time), the microcontroller reads and writes data through the parallel port. When transferring
data in the serial format (1 bit in one clock cycle), the microcontroller reads and writes data
through the serial interface such as a serial communication interface (SCI), serial peripheral
interface (SPI), inter-integrated circuit (I2C), or controller area network (CAN).
Because data transfers go through the interface chip (or logic for the MCU), the synchronization issue occurs in two places. One is between the processor and the interface chip
7.5 ■ The HCS12 Parallel Ports
305
(or logic for the MCU). The other is between the interface chip (or logic for the MCU) and
the peripheral devices.
7.4.1 Synchronization Issue for Parallel Ports
The design of parallel ports of today’s microcontrollers (including the HCS12) is to allow
the data written into the data register to appear on the output pins directly and allow the
read operation from the data register to obtain the instantaneous voltage levels on the input
port pins. There is no concern about the synchronization issue. This is quite different from
the era of microprocessors. To perform an I/O operation, the microprocessor needs to make
sure that interface chip for the input device has new data before it reads it or that the interface chip for the output device can handle new data before it sends new data to it. This is
achieved by either polling or interrupt. The interface chip is designed to have status flags to
indicate whether it has new data or can accept new data for output. The interface chip may
use a strobe signal or handshake signals to achieve synchronization between the interface
chip and the peripheral device. This type of synchronization is no longer needed in today’s
microcontrollers.
7.4.2 Synchronization Issue for Serial Interface
For the serial interface, the data transfer rate between the processor and the interface logic
is much faster than that between the interface logic and the peripheral device. The processor
needs to make sure that there is new data in the interface logic (usually held in a data register)
before reading it. It also needs to make sure that the interface logic can handle more data before
sending new data to the interface logic. This is achieved by using either the polling or the interrupt method to make sure that the new I/O operation can be started.
The synchronization between the interface logic and the peripheral device is achieved by
following certain data transfer protocols. For example, the interface logic and the peripheral
device will use the same clock signal to synchronize data transfer in the synchronous protocol.
The SPI and I2C module use this approach. For data transfers that use asynchronous protocols,
both the transmitter and receiver will agree on a common data transfer rate and the receiver
will use a sampling clock signal with a frequency that is a multiple (16, 32, or 64) of the data
rate to detect the incoming data. This is used in the SCI module.
7.5 The HCS12 Parallel Ports
As mentioned in Section 4.10, the user configures an I/O port for input or output by programming the associated data direction register. To output, the user writes data to the port data
register. To input, the user reads data from the port data register. Most I/O ports have additional
registers to control their operations, and most I/O pins service multiple purposes. This section
discusses the pins of each parallel I/O port and their registers in more detail.
7.5.1 Port A and Port B
In expanded mode, both Port A and Port B are used as time-multiplexed address or data
pins. When configured in single-chip mode, these two ports are used as general-purpose I/O
ports. Each Port A or Port B pin can be configured as an input or output pin. When the HCS12 is
configured in expanded mode, Port A carries the time-multiplexed upper address and data signals (A15/D15,A8/D8), whereas Port B carries the time-multiplexed lower address and data
signals (A7/D7,A0/D0).
306
Chapter 7 ■ Advanced Parallel I/O
7.5.2 Port E
As shown in Figure 7.1, Port E pins are used for bus control and interrupt service request
signals. When a pin is not used for one of these specific functions, it can be used as a generalpurpose I/O. However, two of the Port E pins (PE[1:0]) can only be used for input, and the states
of these pins can be read in the port data register even when they are used for IRQ and XIRQ.
PE0/XIRQ
PE1/IRQ
PE2/R/W
PE3/LSTRB/TAGLO
PE4/ECLK
PE5/MODA/IPIPE0
PE6/MODB/IPIPE1
PE7/NOACC/XCLKS
Figure 7.1 ■ Port E pins and their alternate functions
The PE7 pin serves three functions.
1. A general-purpose I/O pin
2. A signal that indicates that the MCU is not accessing external memory
3. A signal that selects between the external clock or crystal oscillator signal as the
clock input to the HCS12
The PE6 and PE5 pins serve three functions.
1. General-purpose I/O pins (in single-chip mode)
2. Signals that set the operation mode of the HCS12 after reset
3. Instruction queue tracking signals (in expanded mode)
The PE4 pin has two functions.
1. A general-purpose I/O pin (in single-chip mode)
2. The E-clock output (in expanded mode)
The PE3 pin has three functions.
1. A general-purpose I/O pin (in single-chip mode)
2. The LCD front-plane segment driver output pin (in H subfamily)
3. The low-byte strobe signal to indicate the type of access (in expanded mode)
The PE2 pin has two functions.
1. A general-purpose I/O pin (in single-chip mode)
2. A signal to indicate whether the current bus cycle is a read cycle or a write cycle (in
expanded mode)
The PE1 pin has two functions.
1. A general-purpose I/O pin
2. The IRQ input
307
7.5 ■ The HCS12 Parallel Ports
The PE0 pin has two functions.
1. A general-purpose I/O pin
2. The XIRQ input
In addition to the DDRE and PORTE registers, Port E also has the following registers:
• Port E assignment register (PEAR)
• Mode register (MODE)
• Pull-up control register (PUCR)
• Reduced drive register (RDRIV)
• External bus interface control register (EBICTL)
PORT E ASSIGNMENT REGISTER (PEAR)
In expanded mode, this register assigns the function of each Port E pin. The contents of the
register PEAR are shown in Figure 7.2. The PEAR register is not accessible for reads or writes in
peripheral mode.
7
6
5
4
3
2
1
0
NOACCE
0
PIPOE
NECLK
LSTRE
0
RDWE
0
Reset value
= $000A
NOACCE: No Access output enable. Can be read/written any time.
0 = PE7 is used as general-purpose I/O pin.
1 = PE7 is output and indicates whether the cycle is a CPU free cycle.
PIPOE: Pipe signal output enable.
In normal modes, write once. Special modes, write anytime except the first
time. This bit has no effect in single-chip modes.
0 = PE[6:5] are general-purpose I/O.
1 = PE[6:5] are outputs and indicate the state of the instruction queue.
NECLK: No external E-clock. Can be read anytime.
In expanded modes, writes to this bit have no effect. E-clock is required for
demultiplexing the external address. NECLK can be written once in
normal single-chip mode and can be written anytime in special single-chip mode.
0 = PE4 is the external E-clock.
1 = PE4 is a general-purpose I/O pin.
LSTRE: Low strobe (LSTRB) enable. Can be read anytime.
In normal modes, write once. Special modes, write anytime. This bit has no
effect in single-chip modes or normal expanded narrow mode.
0 = PE3 is a general-purpose I/O pin.
1 = PE3 is configured as the LSTRB bus-control output, provided the
HCS12 is not in single-chip or normal expanded narrow modes.
RDWE: Read/write enable. Can be read anytime.
In normal modes, write once. Special modes, write anytime except the
first time. This bit has no effect in single-chip modes.
0 = PE2 is a general-purpose I/O pin.
1 = PE2 is configured as the R/W pin. In single-chip modes, RDWE has
no effect and PE2 is a general-purpose I/O pin.
R/W is used for external writes. After reset in normal expanded mode, it
is disabled. If needed, it should be enabled before any external writes.
Figure 7.2 ■ PEAR register
308
Chapter 7 ■ Advanced Parallel I/O
MODE REGISTER
This register establishes the operation mode and other miscellaneous functions (i.e., internal
visibility and emulation of Ports E and K). The contents of this register are shown in Figure 7.3.
7
6
5
4
3
2
1
0
MODC
MODB
MODA
0
IVIS
0
EMK
EME
MODC, MODB, MODA: mode select bits
000 = special single-chip mode
001 = emulation narrow mode
010 = special test mode
011 = emulation wide mode
100 = normal single-chip mode
101 = normal expanded narrow mode (external memory data bus is 8-bit)
110 = special peripheral mode
111 = normal expanded wide mode (external memory data bus is 16-bit)
IVIS: internal visibility
0 = no visibility of internal bus operations on external bus.
1 = internal bus operations are visible on external bus.
EMK: emulate Port K
0 = PTK and DDRK are in memory map and port K can be used in general I/O.
1 = If in any expanded mode, PTK and DDRK are removed from memory map.
EME: emulate Port E
0 = PTE and DDRE are in the memory map so Port E can be used for general I/O.
1 = If in any expanded mode or special peripheral mode, PTE and DDRE are
removed from memory map, which allows the user to emulate the function
of these registers externalIy.
Figure 7.3 ■ The MODE register
PULL-UP CONTROL REGISTER (PUCR)
This register is used to select the pull-up resistors for the pins associated with the core part.
The MC9S12DG256 has Ports A, B, E, and K in its core part. This register can be written any
time; its contents are shown in Figure 7.4.
7
6
5
4
3
2
1
0
PUPKE
0
0
PUPEE
0
0
PUPBE
PUPAE
PUPKE: Pull-up Port K enable
0 = pull-up resistors of Port K are disabled.
1 = pull-up resistors of Port K are enabled.
PUPEE: pull-up Port E enable
0 = pull-up resistors of Port E input pins 7 and 4-0 are disabled.
1 = pull-up resistors of Port E input pins 7 and 4-0 are enabled.
PUPBE: Pull-up Port B enable
0 = pull-up resistors of Port B are disabled.
1 = pull-up resistors of Port B are enabled.
PUPAE: Pull-up Port A enable
0 = pull-up resistors of Port A are disabled.
1 = pull-up resistors of Port A are enabled.
Figure 7.4 ■ Pull-Up Control register (PUCR)
Reset value
= 0x90
309
7.5 ■ The HCS12 Parallel Ports
REDUCED DRIVE REGISTER (RDRIV)
This register is used to select reduced drive for the pins associated with the core ports,
which gives reduced power consumption and reduced RFI with a slight increase in transition
time, a feature used on ports that have a light loading. This register is not in the memory map
in expanded mode. The contents of the register are shown in Figure 7.5.
7
6
5
4
3
2
1
0
RDPK
0
0
RDPE
0
0
RDPB
RDPA
Reset value
= 0x00
RDPK: reduced drive of Port K
0 = all Port K pins have full drive enabled.
1 = all Port K pins have reduced drive enabled.
RDPE: reduced drive of Port E
0 = all Port E pins have full drive enabled.
1 = all Port E pins have reduced drive enabled.
RDPB: reduced drive of Port B
0 = all Port B pins have full drive enabled.
1 = all Port B pins have reduced drive enabled.
RDPA: reduced drive of Port A
0 = all Port A pins have full drive enabled.
1 = all Port A pins have reduced drive enabled.
Figure 7.5 ■ Reduced Drive register (RDRIV)
E X T E R N A L B U S I N T E R FA C E C O N T R O L R E G I S T E R ( E B I C T L )
Only the bit 0 (ESTR) of this register is implemented. It controls the stretching of the external E-clock. When the ESTR bit is set to 0, the E-clock is free running and does not stretch
(lengthen). When the HCS12 is interfacing with a slower memory device, then the E-clock can
be lengthened (have its high interval stretched) by setting this bit.
7.5.3 Port K
Port K has a Port K data register (PORTK or PTK) and a data direction register (DDRK).
In the expanded mode, Port K carries the expanded address XADDR14,XADDR19, emulated
chip-select (ECS), and external chip-select (XCS) signals. The PK6 pin is available only in the
H subfamily. The functions of Port K pins are shown in Figure 7.6. At the rising edge of the
PK0/X14
PK1/X15
PK2/X16
PK3/X17
PK4/X18
PK5/X19
PK6/XCS (only available in H subfamily)
PK7/ECS/ROMONE
Figure 7.6 ■ Port K pins and their alternate functions
310
Chapter 7 ■ Advanced Parallel I/O
RESET signal, the state of the PK7 pin is latched to the ROMON bit of the MISC register. If this
bit is 1, the on-chip flash memory is enabled in the expanded mode. This bit is forced to 1 in
single-chip modes.
7.5.4 Port T
Port T has a port I/O register (PTT), port data direction register (DDRT), port input register (PTIT), reduced drive register (RDRT), pull device enable register (PERT), and port polarity
select register (PPST).
The PTIT register allows us to read back the status of Port T pins. This register can also be
used to detect overload or short circuit conditions on output pins. Reading the port data register
achieves the same purpose.
The RDRT register configures the drive strength of each port T output pin as either full or
reduced current. If a port T pin is used as input, this bit is ignored. The reduced drive feature for
each pin can be enabled individually. The contents of RDRT are shown in Figure 7.7.
7
6
5
4
3
2
1
0
RDRT7
RDRT6
RDRT5
RDRT4
RDRT3
RDRT2
RDRT1
RDRT0
Reset value
= 0x00
RDRT[7:0]: reduced drive Port T
0 = full drive strength at output.
1 = associated pin drives at about 1/3 of the full drive strength.
Figure 7.7 ■ Port T Reduced Drive register (RDRT)
The PERT register configures whether a pull-up or pull-down device is enabled if the port is
used as input. Each pin’s pull-up or pull-down device can be enabled individually. No pull-up or
pull-down device is enabled out of reset. The contents of PERT are shown in Figure 7.8.
7
6
5
4
3
2
1
0
PERT7
PERT6
PERT5
PERT4
PERT3
PERT2
PERT1
PERT0
Reset value
= 0x00
PERT[7:0]: pull device enable Port T
0 = pull-up or pull-down is disabled.
1 = either pull-up or pull-down is enabled.
Figure 7.8 ■ Port T Pull Device Enable register (PERT)
The PPST register selects whether a pull-down or a pull-up device is connected to the pin.
This register has an effect on input pins only. The contents of PPST are shown in Figure 7.9.
7
6
5
4
3
2
1
0
PPST7
PPST6
PPST5
PPST4
PPST3
PPST2
PPST1
PPST0
PPST[7:0]: pull device enable Port T
0 = a pull-up device is connected to the associated Port T pin, if enabled
by the associated bit in register PERT and if the port is used as input
or as wired-OR output.
1 = a pull-down device is connected to the associated Port T pin, if enabled
by the associated bit in register PERT and if the port is used as input.
Figure 7.9 ■ Port T Polarity Select register (PPST)
Reset value
= 0x00
311
7.5 ■ The HCS12 Parallel Ports
PT0/IOC0
PT1/IOC1
PT2/IOC2
PT3/IOC3
PT4/IOC4
PT5/IOC5
PT6/IOC6
PT7/IOC7
Figure 7.10 ■ Port T pins and their alternate functions
The pin functions of Port T are shown in Figure 7.10. In addition to being used as general
I/O pins, Port T pins can also be used as input capture or output compare action pins.
7.5.5 Port S
Port S has a Port S Wired-OR Mode register (WOMS) in addition to all the registers associated
with Port T (PTS, DDRS, PTIS, RDRS, PERS, and PPSS). Each bit of this register configures the
associated output pin as wired-OR. The contents of this register are shown in Figure 7.11. The contents of PTIS, RDRS, PERS, and PPSS are identical to those of PTIT, RDRT, PERT, and PPST, respectively. As shown in Figure 7.12, Port S pins can be used as general I/O pins or serial interface signals.
7
6
5
4
3
2
1
0
WOMS7 WOMS6 WOMS5 WOMS4 WOMS3 WOMS2 WOMS1 WOMS0
WOMS[7:0]: wired-OR mode Port S
0 = output buffers operate as push-pull outputs.
1 = output buffers operate as open-drain outputs.
Figure 7.11 ■ Port S wired-OR mode register (WOMS)
PS0/RXD0
PS1/TXD0
PS2/RXD1
PS3/TXD1
PS4/MISO0
PS5/MOSI0
PS6/SCK0
PS7/SS0
Figure 7.12 ■ Port S pins and their alternate functions
Reset value
= 0x00
312
Chapter 7 ■ Advanced Parallel I/O
7.5.6 Port M
In addition to having all the equivalent registers (PTM, DDRM, PTIM, RDRM, PERM,
PPSM, and WOMM) contained in Port S, Port M also has a Module Routing register (MODRR).
This register configures the rerouting of CAN0, CAN4, SPI0, SPI1, and SPI2 on defined port
pins. The contents of the MODRR register are shown in Figure 7.13. The functions of Port M
pins are illustrated in Figure 7.14.
7
0
6
5
4
3
2
1
0
MODRR6 MODRR5 MODRR4 MODRR3 MODRR2 MODRR1 MODRR0
CAN0 routing
Reset value
= 0x00
SPI0 routing
MODRR1
MODRR0
0
0
1
1
0
1
0
1
RXCAN0
TXCAN0
MODRR4 MISO0 MOSI0 SCK0
PS6
PM56
PS7
PM35
MODRR5 MISO1 MOSI1 SCK1
SS1
0
1
PM0
PM1
PM21
PM31
PM52
PM42
reserved
CAN4 routing
MODRR3
MODRR2
0
0
1
1
0
1
0
1
RXCAN4
PS4
PM25
PS5
PM46
SPI1 routing
PP2
PH2
PP3
PH3
MODRR6 MISO2 MOSI2 SCK2
SS2
0
1
TXCAN4
PJ6
PJ7
PM43
PM53
PM74
PM64
reserved
SS0
PP0
PH0
PP1
PH1
SPI2 routing
0
1
PP4
PH4
PP5
PH5
PP7
PH6
Notes: 1. Routing to this pin takes effect only if CAN1 is disabled.
2. Routing to this pin takes effect only if CAN2 is disabled.
3. Routing to this pin takes effect only if CAN2 is disabled and CAN0 is not
routed here.
4. Routing to this pin takes effect only if CAN3 is disabled.
5. Routing to this pin takes effect only if CAN1 is disabled, and CAN0 is disabled
if routed here.
6. Routing to this pin takes effect only if CAN2 is disabled; CAN0 is disabled if
routed here and CAN4 is disabled if routed here.
Figure 7.13 ■ Module Routing register (MODRR)
PM0/RXCAN0/RXB
PM1/TXCAN0/TXB
PM2/RXCAN1/RXCAN0/MISO0
PM3/TXCAN1/TXCAN0/SS0
PM4/RXCAN2/RXCAN0/RXCAN4/MOSI0
PM5/TXCAN2/TXCAN0/TXCAN4/SCK0
PM6/RXCAN3/RXCAN4
PM7/TXCAN3/TXCAN4
Figure 7.14 ■ Port M pins and their alternate functions
PP6
PH7
7.5 ■ The HCS12 Parallel Ports
313
Example 7.1
▼
Give an instruction to configure the MODRR register to achieve the following port
routing:
1. CAN0: use pins PM1 and PM0
2. CAN1: use pins PM3 and PM2
3. CAN2: use pins PM5 and PM4
4. CAN3: use pins PM7 and PM6
5. I2C: use PJ7 and PJ6
6. SPI0: use pins PS7,PS4
7. SPI1: use pins PH3,PH0
8. SPI2: use pins PH7,PH4
Solution: For this routing requirement, all we need to do is to prevent CAN4 from using any
port pins and keep the default routing after reset. The following instruction will satisfy the
requirement:
movb
#$60,MODRR
; CAN4 must be disabled
▲
Example 7.2
▼
Give an instruction to configure the MODRR register to achieve the following port
routing:
1. CAN0: use pins PM1 and PM0
2. CAN1: use pins PM3 and PM2
3. CAN2: disabled
4. CAN3: disabled
5. I2C: use PJ7 and PJ6
6. SPI0: use pins PS7,PS4
7. SPI1: use pins PP3,PP0
8. SPI2: use pins PH7,PH4
Solution: This routing requirement can be satisfied by the following instruction:
movb
#$40,MODRR
; CAN2,CAN4 must be disabled
▲
7.5.7 Ports H, J, and P
These three I/O ports have the same set of registers associated with them. All of the pins
associated with these three ports have edge-triggered interrupt capability in the wired-OR fashion. The SPI function pins can be routed to Ports H and P. The rerouting of SPI functions is done
by programming the MODRR register. Each of these three ports has eight associated registers.
1. Port I/O register (PTH, PTJ, PTP)
2. Port Input register (PTIH, PTIJ, PTIP)
314
Chapter 7 ■ Advanced Parallel I/O
3. Port Data Direction register (DDRH, DDRJ, DDRP)
4. Port Reduced Drive register (RDRH, RDRJ, RDRP)
5. Port Pull Device Enable register (PERH, PERJ, PERP)
6. Port Polarity Select register (PPSH, PPSJ, PPSP)
7. Port Interrupt Enable register (PIEH, PIEJ, PIEP)
8. Port Interrupt Flag register (PIFH, PIFJ, PIFP)
All except the last two registers have their equivalents in Port T. The contents of the Port
H Interrupt Enable register and Port H Interrupt Flag register are shown in Figures 7.15 and
7.16, respectively.
7
6
5
4
3
2
1
0
PIEH7
PIEH6
PIEH5
PIEH4
PIEH3
PIEH2
PIEH1
PIEH0
Reset value
= 0x00
PIEH[7:0]: interrupt enable Port H
0 = interrupt is disabled.
1 = interrupt is enabled.
Figure 7.15 ■ Port H Interrupt Enable register (PIEH)
7
6
5
4
3
2
1
0
PIFH7
PIFH6
PIFH5
PIFH4
PIFH3
PIFH2
PIFH1
PIFH0
Reset value
= 0x00
PIFH[7:0]: interrupt flag Port H
0 = no active edge pending.
1 = active edge has occurred (writing a 1 clears the associated flag).
Figure 7.16 ■ Port H Interrupt Flag register (PIFH)
Port H is associated with two SPI modules. Port J is associated with the fifth CAN and the
I2C module. Port P is associated with the PWM and two SPI modules. In all modes, Port P pins
PP[7:0] can be used either for general-purpose I/O or with the PWM and SPI subsystems. The
pins are shared between the PWM channels and the SPI1 and SPI2 modules. If the PWM function is enabled, these pins become PWM output channels, with the exception of pin 7, which
can be PWM input or output. If SPI1 or SPI2 are enabled and PWM is disabled, the respective
pin configuration is determined by several status bits in the SPI module. Both Port H and Port P
have eight pins, whereas Port J has only four pins.
The interrupt enable as well as the sensitivity to rising or falling edges can be individually configured on a per-pin basis. If a pin’s pull-down device is enabled, then the interrupt
is rising edge triggered. Otherwise, it is falling edge triggered. All 8 bits or pins of the port
share the same interrupt vector. Interrupts can be used with the pins configured as inputs or
outputs.
An interrupt is generated when a bit in the Port Interrupt Flag register and its
corresponding port interrupt enable bit are both set. This feature can be used to wake up
the CPU when it is in the stop or wait mode. Each Port P pin can also be used as an edgesensitive interrupt source. A digital filter on each pin prevents pulses shorter than a specified time from generating an interrupt. The minimum time varies over process conditions,
temperature, and voltage.
315
7.5 ■ The HCS12 Parallel Ports
PH0/MISO1/KWH0
PP0/MISO1/PWM0/KWP0
PH1/MOSI1/KWH1
PP1/MOSI1/PWM1/KWP1
PH2/SCK1/KWH2
PP2/SCK1/PWM2/KWP2
PH3/SS1/KWH3
PP3/SS1/PWM3/KWP3
PH4/MISO2/KWH4
PJ0/KWJ0
PP4/MISO2/PWM4/KWP4
PH5/MOSI2/KWH5
PJ1/KWJ1
PP5/MOSI2/PWM5/KWP5
PH6/SCK2/KWH6
PJ6/KWJ6/RXCAN4/SDA/RXCAN0
PP6/SS2/PWM6/KWP6
PH7/SS2/KWH7
PJ7/KWJ7/TXCAN4/SCL/TXCAN0
PP7/SCK2/PWM7/KWP7
Figure 7.17 ■ Port H pins and
their alternate functions
Figure 7.18 ■ Port J pins and
their alternate functions
Figure 7.19 ■ Port P pins and
their alternate functions
The functions of Port H, J, and P pins are shown in Figures 7.17, 7.18, and 7.19,
respectively.
7.5.8 Ports AD0 and AD1
Many HCS12 devices have implemented two 8-channel A/D converters (AD1 and AD0). For
those devices with a single 8-channel A/D converter, the module is referred to as AD instead
of AD0. These two ports are analog input interfaces to the analog-to-digital subsystem. When
analog-to-digital functions are not enabled, these two ports are available for general-purpose
I/O. Since these two ports cannot be used as output, there are no data direction registers associated with them.
The ports have no resistive input loads and no reduced drive controls. PTAD0 and PTAD1
are data registers of Port AD0 and AD1, respectively. Each A/D module has an ATD Digital
Input Enable register. In order to use an A/D pin as a digital input, its associated bit in this
register needs to be set to 1. The contents of this register are shown in Figure 7.20.
7
6
5
4
3
2
1
0
IEN7
IEN6
IEN5
IEN4
IEN3
IEN2
IEN1
IEN0
Reset value
= 0x00
IENx: ATD digital input enable on channel x
0 = disable digital input buffer to PTADx pin.
1 = enable digital input buffer to PTADx pin.
Figure 7.20 ■ ATD Input Enable register (ATD0DIEN and ATD1DIEN)
7.5.9 Port L
This port is available in the HCS12’s H subfamily. In addition to being a general I/O port,
Port L is connected to the LCD driver. The HCS12 H subfamily is designed to support LCD
interfacing and motor control. Port L has the following registers:
• Port I/O register (PTL)
• Port Data Direction register (DDRL)
316
Chapter 7 ■ Advanced Parallel I/O
• Port Input register (PTIL)
• Reduced Drive register (RDRL)
• Pull Device Enable register (PERL)
• Port Polarity Select register (PPSL)
The contents of these registers are identical to their equivalents in Port T. The functions of
Port L pins are shown in Figure 7.21.
PL0/FP16 (LCD frontplane pin 16)
PL1/FP17 (LCD frontplane pin 17)
PL2/FP18 (LCD frontplane pin 18)
PL3/FP19 (LCD frontplane pin 19)
PL4/FP28 (LCD frontplane pin 28)
PL5/FP29 (LCD frontplane pin 29)
PL6/FP30 (LCD frontplane pin 30)
PL7/FP31 (LCD frontplane pin 31)
Figure 7.21 ■ Port L pins and their alternate functions
7.5.10 Ports U, V, and W
These three ports are available in HCS12’s H subfamily. In addition to being general
I/O ports, these three ports are provided for motor control. The following registers support the
operations of these three ports:
• Port I/O register (PTU, PTV, PTW)
• Port Data Direction register (DDRU, DDRV, DDRW)
• Port Input register (PTIU, PTIV, PTIW)
• Pull Device Enable register (PERU, PERV, PERW)
• Port Polarity Select register (PPSU, PPSV, PPSW)
• Port Slew Rate register (SRRU, SRRV, SRRW)
The functions of the first five of these registers are identical to their equivalents in Port L.
The contents of the Port Slew Rate register are illustrated in Figure 7.22.
7
6
5
4
3
2
1
0
SRRx7
SRRx6
SRRx5
SRRx4
SRRx3
SRRx2
SRRx1
SRRx0
SRRx[7:0]: slew rate port x (x = U, V, or W)
0 = disable slew rate control.
1 = enable slew rate control.
Figure 7.22 ■ Port Slew Rate register (SRRU, SRRV, and SRRW)
Reset value
= 0x00
317
7.6 ■ Electrical Characteristic Considerations for I/O Interfacing
PU0/M0C0P
PV0/M2C0P
PW0/M4C0P
PU1/M0C0M
PV1/M2C0M
PW1/M4C0M
PU2/M0C1P
PV2/M2C1P
PW2/M4C1P
PU3/M0C1M
PV3/M2C1M
PW3/M4C1M
PU4/M1C0P
PV4/M3C0P
PW4/M5C0P
PU5/M1C0M
PV5/M3C0M
PW5/M5C0M
PU6/M1C1P
PV6/M3C1P
PW6/M5C1P
PU7/M1C1M
PV7/M3C1M
PW7/M6C1M
(a) Port U
(b) Port V
(c) Port W
Figure 7.23 ■ Port U, V, and W pins and their alternate functions
The functions of these port pins are shown in Figure 7.23a–c, respectively.
7.6 Electrical Characteristic Considerations for I/O Interfacing
Most embedded systems require the use of logic chips, peripheral devices in addition to
the microcontroller, to perform their function. Because these chips may use different types of
integrated circuit (IC) technologies, there is a concern that the resultant embedded system may
not function properly.
The major concern in interfacing IC chips that are made with different technologies is whether they are electrically compatible. There are two issues involved in electrical
compatibility.
• Voltage-level compatibility. Is the high output level of an IC chip high enough to
be considered as a high for the input of another IC chip? Is the low output level of
an IC chip low enough to be considered as a low for the input of another IC chip?
• Current drive capability. Does the output of an IC chip have enough current to
drive its load? Can the output circuit of an IC chip sink the currents of its load?
Signal timing is also an important factor for making sure that the digital circuit functions correctly. The main concern about timing is whether the signal from one chip becomes valid early
enough to be used by other chips. This is a timing compatibility issue. When the operating frequency becomes very high, the transmission line effect and ground bounce effect will need to
be considered too. However, we are not dealing with high-frequency systems in this text. These
two issues will not be discussed.
7.6.1 Voltage-Level Compatibility
There are many IC technologies in use today. Some of them are bipolar; others are unipolar. A bipolar IC technology has both the electron and hole currents in any moment. However, in a unipolar IC technology, the current in any part of the circuit is either the electron
current or the hole current. The unipolar CMOS technology is the dominant IC technology
in use today.
318
Chapter 7 ■ Advanced Parallel I/O
The voltage-level compatibility issue arises because IC technologies differ in the following
four voltages:
• Input high voltage (VIH). This is the voltage that will be treated as a logic 1 when
applied to the input of a digital circuit.
• Input low voltage (VIL). This is the voltage that will be treated as a logic 0 when
applied to the input of a digital circuit.
• Output high voltage (VOH). This is the voltage level when a digital circuit outputs
a logic 1.
• Output low voltage (VOL). This is the voltage level when a digital circuit outputs
a logic 0.
In order for the digital circuit X to be able to drive circuit Y correctly, the following conditions
must be satisfied:
• VOHX $ VIHY (the output high voltage of circuit X must be higher than the input
high voltage of circuit Y). The difference between VOH and VIH of the same
technology is referred to as the noise margin high (NMH).
• VOLX # VILY (the output low voltage of circuit X must be lower than the input low
voltage of circuit Y). The difference between VIL and VOL of the same technology is
referred to as the noise margin low (NML).
The input and output voltage levels of a few popular logic families are listed in Table 7.1,
from which one can draw the following conclusions:
• There is no problem using CMOS logic chips to drive bipolar logic chips at the
same power supply level.
• The HCS12 has no problem driving the CMOS logic chips and being driven by the
CMOS logic chips at the same power supply level.
Logic Family
VDD
VIH
VOH
VIL
VOL
HCS123
5V
5V
5V
5V
5V
5V
5V
5V
5V
5V
5V
3.25 V
2V
2V
2V
2V
3.5 V
3.5 V
2V
2V
2V
2V
4.2 V
3.0,3.4 V1
3.0,3.4 V1
3.0,3.4 V1
3.4 V
4.9 V
4.9 V
4.9 V
3V
3.3 V
2.4 V
1.75 V
0.8 V
0.8 V
0.8 V
0.8 V
1.5 V
1.5 V
0.8 V
0.8 V
0.8 V
0.8 V
0.8 V
0.4,0.5 V2
0.4,0.5 V2
0.35 V
0.3 V
0.1 V
0.1 V
0.1 V
0.55 V
0.42 V
0.55 V
S4
LS4
AS4
F4
HC3
HCT3
ACT3
ABT5
BCT5
FCT5
1V
OH value will get lower when output current is larger.
2V
OL value will get higher when output current is larger. The VOLvalues of
different logic gates are slightly different.
3HCS12, HC, HCT, and ACT are based on the CMOS technology.
4S, LS, AS, and F logic families are based on the bipolar technology.
5ABT, BCT, and FCT use the BiCMOS technology.
Table 7.1 ■ Input and output voltage levels of common logic families
319
7.6 ■ Electrical Characteristic Considerations for I/O Interfacing
• The BiCMOS logic is not suitable for driving the HCS12 and other CMOS logic
chips at the same power supply level.
• The bipolar logic ICs are not suitable for driving the CMOS logic ICs or the HCS12
microcontroller at the same power supply level.
7.6.2 Current Drive Capability
A microcontroller needs to drive other peripheral I/O devices in an embedded system. The
second electrical compatibility issue is whether the microcontroller can supply (when the output voltage is high, also called source) or sink (when the output voltage is low) the current
needed by the I/O devices that it interfaces with. Depending on the voltage level of an output
pin, the current may flow out from (supply the current) or into (sink the current) the pin. The
designer must make sure that the following two requirements are satisfied:
• Each I/O pin can supply (flowing out from the pin) and sink (flowing into the pin)
the current needed by the I/O devices that it interfaces with.
• The total current required to drive I/O devices does not exceed the maximum
current rating of the microcontroller.
Each logic chip has the following four currents that are involved in the current drive calculation:
• Input high current (IIH). This is the input current (flowing into the input pin) when
the input voltage is high.
• Input low current (IIL). This is the input current (flowing out of the input pin) when
the input voltage is low.
• Output high current (IOH). This is the output current (flowing out of the output pin)
when the output voltage is high.
• Output low current (IOL). This is the output current (flowing into the output pin)
when the output voltage is low.
The current capabilities of several common logic families and the HCS12 are listed in
Table 7.2. In the CMOS technology, the gate terminal (one of the three terminals in an N or
P transistor) draws a significant current only when they are charged up toward VCC or pulled
Logic Family
VCC
IIH
IIL
IOH
IOL
HCS122,3
5V
5V
5V
5V
5V
5V
5V
5V
5V
5V
5V
2.5 μA
50 μA
20 μA
20 μA
20 μA
1 μA
1 μA
1 μA
1 μA
20 μA
1 μA
2.5 μA
1.0 mA
0.2 mA
0.5 mA
0.5 mA
1 μA
1 μA
1 μA
1 μA
1 mA
1 μA
25 mA
1 mA
15 mA
15 mA
1 mA
25 mA
25 mA
24 mA
32 mA
15 mA
15 mA
25 mA
20 mA
24 mA
64 mA
20 mA
25 mA
25 mA
24 mA
64 mA
64 mA
64 mA
S
LS
AS
F
HC3
HCT3
ACT3
ABT3
BCT
FCT3
1Values are based on the 74xx244 of Texas Instrument (xx is the technology name).
2The total HCS12 supply current is 65 mA.
3The values for I
IH and IIL are input leakage currents.
Table 7.2 ■ Current capabilities of common logic families1
320
Chapter 7 ■ Advanced Parallel I/O
down toward GND level. After that, the gate terminal draws only leakage currents. Bipolar
technology is different from the CMOS technology in that a DC current always flows into or
out of the base terminal of the transistor of a bipolar logic chip.
To determine whether a pin can supply and sink currents to all the peripheral pins that it
drives directly, the designer needs to check the following two requirements:
1. The IOH of an I/O pin of the microcontroller is equal to or greater than the sum
of currents flowing into all peripheral pins that are connected directly to the
microcontroller I/O pins.
2. The IOL of an I/O pin of the microcontroller is equal to or greater than the sum
of currents flowing out of all peripheral pins that are connected directly to the
microcontroller I/O pins.
In addition, the designer must also make sure that the total current needed to drive the peripheral signal pins does not exceed the total current that the microcontroller can supply.
One question that arises here is what should be done if an I/O pin cannot supply (or sink) the
current needed to drive the peripheral pins? A simple solution is to add buffer chips (for example,
74ABT244) that can supply enough current between the microcontroller and the peripheral chips.
This technique is widely used in microcontroller applications and is illustrated in Example 4.13.
Example 7.3
You are given the three seven-segment display circuits shown in Figure 7.24a to 7.24c.
Perform an appropriate analysis to find out if any circuit has a current incompatibility problem.
HCS12
f
g
e
#0
a
b
f
c
e
d
g
b
c
d
Common
cathode
#6
a
Common
cathode
PP7
PP6
PP5
PP4
PP3
PP2
PP1
PP0
#7
a
a
b
c f
b
g
d
e
c
f e
g
d
Common
cathode
560
PB6
PB5
PB4
PB3
PB2
PB1
PB0
▼
Figure 7.24a ■ HCS12 seven-segment display circuit 1
Solution: In Figure 7.24a, the VOH of the 74HC244 is 5 V with 5-V power supply. The VOL of HCS12
is 0.8 V. Assume the voltage drop of the LED is 1.8 V, then the current flowing into each segment is
(4.2 2 1.8 2 0.8) 4 560 5 2.86 mA. This value is within the current source capability of each HCS12
I/O pin. The total maximum current that the HCS12 must supply is 7 3 (4.2 2 1.8 2 0.8) 4 560 5
20 mA. This value is within the current-source capability of the HCS12 (25 mA). An HCS12 I/O pin
can also sink this amount of current. Therefore, there is no current drive incompatibility problem.
In Figure 7.24b, the VOH of the 74HC244 is 5 V with 5-V power supply. The VOL of 74HC244
is 0.1 V. Assume the voltage drop of the LED is 1.8 V, then the current flowing through each
321
7.6 ■ Electrical Characteristic Considerations for I/O Interfacing
#7
a
a
b
c f
b
g
d
e
c
f e
g
#6
#0
a
a
f
g
e
PB6
PB5
PB4
PB3
PB2
PB1
PB0
e
b
c
d
74HC244
PP7
PP6
PP5
PP4
PP3
PP2
PP1
PP0
HCS12
c
g
Common
cathode
Common
cathode
74HC244
I6 I5 I4 I3 I2 I1 I0
f
d
d
Y6 Y5 Y4 Y3 Y2 Y1 Y0
b
Common
cathode
560
I7
I6
I5
I4
I3
I2
I1
I0
Y7
Y6
Y5
Y4
Y3
Y2
Y1
Y0
Figure 7.24b ■ HCS12 seven-segment display circuit 2
74HC244
PB6
PB5
PB4
PB3
PB2
PB1
PB0
I6 I5 I4 I3 I2 I1 I0
HCS12
PP7
PP6
PP5
PP4
PP3
PP2
PP1
PP0
#0
a
a
g
e
b
f
c
e
d
d
g
b
c
d
Common
cathode
Y6 Y5 Y4 Y3 Y2 Y1 Y0
f
#6
Common
cathode
#7
a
a
b
c f
b
g
d
e
c
f e
g
Common
cathode
560
74ABT244
I7
I6
I5
I4
I3
I2
I1
I0
Y7
Y6
Y5
Y4
Y3
Y2
Y1
Y0
Figure 7.24c ■ HCS12 seven-segment display circuit 3
LED segment is (4.9 2 1.8 2 0.1) 4 560 5 5.36 mA. This value is well within the current
drive capability of each 74HC244 pin. The total current that the 74HC244 needs to drive for
the segment pattern is about 37.5 mA, which is within the total drive capability (70 mA) of the
74HC244 from TI. However, each digit select pin (connected to a single 74HC244 pin) needs to
sink about 37.5 mA current, which is above the pin current sink capability of the SN74HC244
(35 mA) from TI. Therefore, the current drive compatibility problem exists. However, this problem can be solved by using a slightly larger current-limiting resistor (for example, 680 Ω).
In Figure 7.24c, the VOH of the 74HC244 is 5 V with 5-V power supply. The VOL of 74ABT244
is 0.55 V. Assume the voltage drop of the LED is 1.8 V, then the current flowing through each
322
Chapter 7 ■ Advanced Parallel I/O
LED segment is (4.9 2 1.8 2 0.55) 4 560 5 4.55 mA. This value is well within the current drive
capability of each 74HC244 pin. The total current that the 74HC244 needs to drive for the
segment pattern is about 32 mA which, is within the total drive capability of the 74HC244. A
single 74ABT244 pin needs to sink this amount of current, which is also within the capability
of the 74ABT244. There is no current drive incompatibility problem.
▲
7.6.3 Timing Compatibility
If an I/O pin is driving a peripheral pin that does not contain latches or flip-flops, then timing
is not an issue. A latch or flip-flop usually has a control signal or clock signal to control the
latching of an input signal. As illustrated in Figure 7.25, the D input to the D flip-flop must be
valid for tsu ns before the rising edge of the CLK signal and remain valid for at least thd ns after
the rising edge of the CLK signal in order for its value to be correctly copied to the output signal
Q. The timing parameters tsu and thd are referred to as the setup and hold time requirements of
the D flip-flop. The main timing consideration is that the setup and hold time requirements for
all latches and flip-flops in a digital system must be satisfied in order for the system to work correctly. A signal may pass through several intermediate chips before it is used by the final latch or
flip-flop. The time delays of all intermediate devices must be added when considering the timing analysis. Timing requirement analysis can be very complicated and is best illustrated using
examples. Two examples of timing compatibility analysis are given in Section 14.8.
D
D
Q
CLK
Q
thd
tsu
CLK
(a)
(b)
Figure 7.25 ■ D flip-flop and its latching timing requirement
7.7 Liquid Crystal Displays (LCDs)
Although seven-segment displays are easy to use, they are bulky and quite limited in the
set of characters that they can display. When more than a few letters and digits are to be displayed, seven-segment displays become inadequate. Liquid crystal displays (LCDs) come in
handy when the application requires the display of many characters.
A liquid crystal display has the following advantages:
• High contrast
• Low power consumption
• Small footprint
• Ability to display both characters and graphics
The basic construction of an LCD is shown in Figure 7.26. The most common type of LCD allows light to pass through when activated. A segment is activated when a low-frequency bipolar
323
7.8 ■ The HD44780U LCD Controller
Segment
Frontplane
Glass
Liquid crystal
Backplane
Black cardboard backing
Figure 7.26 ■ A liquid crystal display (LCD)
signal in the range of 30 to 1000 Hz is applied to it. The polarity of the voltage must alternate,
or the LCD will not be able to change very quickly.
When a voltage is applied across the segment, an electrostatic field is set up that aligns the
crystals in the liquid. This alignment allows light to pass through the segment. If no voltage is
applied across a segment, the crystals appear to be opaque because they are randomly aligned.
Random alignment is assured by the AC excitation voltage applied to each segment. In a digital
watch, the segments appear darker when they are activated because light passes through the
segment to a black cardboard backing that absorbs all light. The area surrounding the activated
segment appears to grow brighter in color because the randomly aligned crystals reflect much of
the light. In a backlit computer display, the segment appears to grow brighter because of a light
placed behind the display; the light is allowed to pass through the segment when it is activated.
In recent years, the price of LCD displays has dropped to such an acceptable level that
all PC vendors bundle LCD displays instead of CRT displays with their PC systems. Notebook computers used LCD as displays right from the beginning. Because of the price reduction of LCDs, the prices of notebook computers also have dropped sharply, and more and more
computer users have switched from desktop to notebook computers.
Although LCDs can display graphics and characters, only character-based LCDs are discussed in this text. LCDs are often sold in a module that consists of the LCD and its controller.
The Hitachi HD44780 (with two slightly different versions: HD44780U and HD44780S) is one
of the most popular LCD display controllers in use today. The following section examines the
operation and programming of this controller.
7.8 The HD44780U LCD Controller
The block diagram of an LCD kit that incorporates the HD44780U controller is shown in
Figure 7.27. The pin assignment shown in Table 7.3 is the industry standard for character-based
LCD modules with a maximum of 80 characters. The pin assignment shown in Table 7.4 is the
industry standard for character-based LCD modules with more than 80 characters.
The DB7,DB0 pins are used to exchange data with the microcontroller. The E pin is an
enable signal to the kit. The R/W signal determines the direction of data transfer. The RS signal selects the register to be accessed. When the RS signal is high, the data register is selected.
324
Chapter 7 ■ Advanced Parallel I/O
COM 16
DB7
LCDP (FRD7069)
DB0
Controller
LSI
HD44780U
E
R/W
RS
VEE
VCC
VSS
SEG 40
SEG 160
4
Segment driver × 4
Figure 7.27 ■ Block diagram of an HD44780U-based LCD kit
Pin No.
Symbol
I/0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
VSS
VCC
VEE
RS
R/W
E
DB0
DB1
DB2
DB3
DB4
DB5
DB6
DB7
—
—
—
I
I
I
1/0
1/0
1/0
1/0
1/0
1/0
1/0
1/0
Function
Power supply (GND)
Power supply (15 V)
Contrast adjust
0 5 instruction input, 1 5 data input
0 5 write to LCD, 1 5 read from LCD
Enable signal
Data bus line 0
Data bus line 1
Data bus line 2
Data bus line 3
Data bus line 4
Data bus line 5
Data bus line 6
Data bus line 7
Table 7.3 ■ Pin assignment for displays with less than 80 characters
Otherwise, the instruction register is selected. The VEE pin is used to control the brightness of
the display and is often connected to a potentiometer. The VEE input should not be set to the
maximum value (5 VCC) for an extended period of time to avoid burning the LCD.
An LCD module can be used as a memory-mapped device and be enabled by an address
decoder. The E signal is normally connected to the address decoder output qualified by other
control signals to meet the timing requirement. The R/W pin can be connected to the same pin
of the microcontroller. The RS pin can be connected to the least significant bit of the address
pin (A0) from the microcontroller. This approach is applicable only for those microcontrollers
that support external memory. The LCD programming in this approach is generally easier and
more straightforward.
An LCD module can also be interfaced directly with an I/O port. In this configuration, the
designer will need to use I/O pins to control the signals E, R/W, and RS. Programming will be
325
7.8 ■ The HD44780U LCD Controller
Pin No.
Symbol
I/0
Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DB7
DB6
DB5
DB4
DB3
DB2
DB1
DB0
E1
R/W
RS
VEE
VSS
VCC
E2
N.C
I/0
I/0
I/0
I/0
I/0
I/0
I/0
I/0
I
I
I
—
—
—
I
—
Data bus line 7
Data bus line 6
Data bus line 5
Data bus line 4
Data bus line 3
Data bus line 2
Data bus line 1
Data bus line 0
Enable signal row 0 and 1
0 5 write to LCD, 1 5 read from LCD
0 5 instruction input, 1 5 data input
Contrast adjust
Power supply (GND)
Power supply (15 V)
Enable signal row 2 and 3
Table 7.4 ■ Pin assignment for displays with more than 80 characters
slightly more cumbersome than the memory-mapped approach due to the need to manipulate
these three signals. The HD44780U provides a set of instructions for the user to set up the LCD
parameters. The operations performed by these instructions are summarized in Table 7.5. The
meanings of certain bits in these instructions are explained in Table 7.6.
The HD44780U can be configured to control one-line, two-line, and four-line LCDs. The
mappings of the character positions on the LCD screen and the DDRAM addresses are not
sequential and are shown in Table 7.7.
7.8.1 Display Data RAM
Display data RAM (DDRAM) stores display data represented in 8-bit character codes. Its
extended capacity is 80 3 8 bits, or 80 characters. The area in DDRAM that is not used for display can be used as general data RAM. The relationships between DDRAM addresses and positions on the LCD are shown in Table 7.7.
7.8.2 Character Generator ROM (CGROM)
The character generator ROM generates 5 3 8 or 5 3 10 dot character patterns from 8-bit
character codes. It can generate 208 5 3 8 dot character patterns and 32 5 3 10 dot character
patterns.
7.8.3 Character Generator RAM (CGRAM)
The user can rewrite character patterns into the CGRAM by programming. For 5 3 8 fonts,
eight character patterns can be written, and for 5 3 10 fonts, four character patterns can be
written.
0
0
0
0
0
0
0
0
1
1
Cursor home
Entry mode
set
Display on/off
control
Cursor/
display shift
Function set
Set CGRAM
address
Set DDRAM
address
Read busy
flag and
address
counter
Write
CGRAM or
DDRAM
Read from
CGRAM or
DDRAM
1
0
1
0
0
0
0
0
0
0
0
R/W
BF
1
0
0
0
0
0
0
0
B7
1
0
0
0
0
0
0
B6
Table 7.5 ■ HD44780U instruction set
0
RS
Clear display
Instruction
1
0
0
0
0
0
B5
F
R/L
D
1
0
0
B2
CGRAM address
N
S/C
1
0
0
0
B3
read data
writes data
CGRAM/DDRAM address
DDRAM address
DL
1
0
0
0
0
B4
Code
*
*
*
B
S
*
Reads data from CGRAM or DDRAM.
Writes data to CGRAM or DDRAM.
Reads busy flag (BF) indicating internal
operation being performed and reads
CGRAM or DDRAM address counter
contents (depending on previous
operation).
Sets the DDRAM address. DDRAM
data are sent and received after this
setting.
Sets the CGRAM address. CGRAM
data are sent and received after this
setting.
Sets interface data length (DL),
number of display line (N), and
character font (F).
Sets cursor-move or display-shift
(S/C), shift direction (R/L). DDRAM
contents remain unchanged.
Sets on/off of all display (D), cursor
on/off (c), and blink of cursor position
character (B).
40 µs
40 µs
0 µs
40 µs
40 µs
40 µs
40 µs
40 µs
40 µs
1.64 ms
Returns cursor to home position
without changing DDRAM contents.
Also returns display being shifted to
the original position.
Sets cursor move direction (I/D);
specifies to shift the display (S). These
operations are performed during data
read/write.
1.64 ms
Clears display and returns cursor to
the home position (address 0).
Execution
Time
1
B0
*
C
I/D
1
0
B1
Description
326
Chapter 7 ■ Advanced Parallel I/O
327
7.8 ■ The HD44780U LCD Controller
Bit Name
Settings
l/D
0 5 decrement cursor position
1 5 increment cursor position
S
0 5 no display shift
1 5 display shift
D
0 5 display off
1 5 display on
C
0 5 cursor off
1 5 cursor on
B
0 5 cursor blink off
1 5 cursor blink on
S/C
0 5 move cursor
1 5 shift display
R/L
0 5 shift left
1 5 shift right
DL
0 5 4-bit interface
1 5 8-bit interface
N
0 5 1/8 or 1/11 duty (1 line)
1 5 1/16 duty (2 lines)
F
0 5 5 3 8 dots
1 5 5 3 10 dots
BF
0 5 can accept instruction
1 5 internal operation in progress
Table 7.6 ■ LCD instruction bit names
Visible
Display
Size
Character
Positions
DDRAM
Addresses
138
00,...,07
0x00,...,0x07
1 3 16
00,...,15
0x00,...,0x0F
1 3 20
00,...,19
0x00,...,0x13
1 3 24
00,...,23
0x00,...,0x17
1 3 32
00,...,31
0x00,...,0x1F
1 3 40
00,...,39
0x00,...,0x27
Table 7.7a ■ DDRAM address usage for a one-line LCD
Visible
Display Size
Character
Positions
DDRAM Addresses
2 3 16
00,...,15
0x00,...,0x0F + 0x40..0x4F
2 3 20
00,...,19
0x00,...,0x13 + 0x40..0x53
2 3 24
00,...,23
0x00,...,0x17 + 0x40..0x57
2 3 32
00,...,31
0x00,...,0x1F + 0x40..0x5F
2 3 40
00,...,39
0x00,...,0x27 + 0x40..0x67
Table 7.7b ■ DDRAM address usage for a two-line LCD
328
Chapter 7 ■ Advanced Parallel I/O
Visible
Display
Size
Character Positions
DDRAM Addresses
4 3 16
00..15
0x00..0x0F 1 0x40..0x4F 1 0x14..0x23 1 0x54..0x63
4 3 20
00..19
0x00..0x13 1 0x40..0x53 1 0x14..0x27 1 0x54..0x67
4 3 40
00..39 on 1st
controller and 00..39
on 2nd controller
0x00..0x27 1 0x40..0x67 on 1st controller and
0x00..0x27 1 0x40..0x67 on 2nd controller
Note: Two LCD controllers are needed to control LCD displays with 4 3 40 characters.
Table 7.7c ■ DDRAM address usage for a four-line LCD
7.8.4 Registers
The HD44780U has two 8-bit registers, an instruction register (IR) and a data register (DR).
The IR stores instruction codes, such as display clear and cursor move, and address information
for display data RAM (DDRAM) and character generator RAM (CGRAM). The microcontroller
writes commands into this register to set up the LCD operation parameters. To write data into
the DDRAM or CGRAM, the microcontroller writes data into the DR. Data written into the
DR will be automatically written into DDRAM or CGRAM by an internal operation. The DR
is also used for data storage when reading data from DDRAM or CGRAM. When address information is written into the IR, data is read and then stored in the DR from DDRAM or CGRAM
by an internal operation. The microcontroller can then read the data from the DR. After a read
operation, data in DDRAM or CGRAM at the next address is sent to the DR and the microcontroller does not need to send another address. The IR and DR are distinguished by the RS signal.
The IR is selected when the RS input is low. The DR is selected when the RS input is high.
Register selection is illustrated in Table 7.8.
RS
R/W
Operation
0
0
IR write as an internal operation (display clear, etc.).
0
1
Read busy flag (DB7) and address counter (DB0 to DB6).
1
0
DR write as an internal operation (DR to DDRAM or CGRAM).
1
1
DR read as an internal operation (DDRAM or CGRAM to DR).
Table 7.8 ■ Register selection
BUSY FLAG (BF)
The HD44780U has a busy flag (BF) to indicate whether the current internal operation is
complete. When BF is 1, the HD44780U is still busy with an internal operation. When RS 5 0
and R/W is 1, the busy flag is output to the DB7 pin. The microprocessor can read this pin to
find out if the HD44780U is still busy.
ADDRESS COUNTER (AC)
The HD44780U uses a 7-bit address counter to keep track of the address of the next
DDRAM or CGRAM location to be accessed. When an instruction is written into the IR, the
address information contained in the instruction is transferred to the AC register. The selection
329
7.8 ■ The HD44780U LCD Controller
of DDRAM or CGRAM is determined by the instruction. After writing into (reading from)
DDRAM or CGRAM, the content of the AC register is automatically incremented (decremented) by 1. The contents of the AC register is output to the DB6..DB0 pins when the RS signal
is low and the R/W signal is high.
7.8.5 Instruction Description
The functions of LCD instructions are discussed in this section.
C L E A R D I S P L AY
This instruction writes the space code 0x20 into all DDRAM locations. It then sets 0 into
the address counter and returns the display to its original status if it was shifted. In other words,
the display disappears and the cursor or blinking goes to the upper left corner of the display. It
also sets the I/D bit to 1 (increment mode) in entry mode.
RETURN HOME
This instruction sets DDRAM address 0 into the address counter and returns to its original
status if it was shifted. The DDRAM contents are not changed. The cursor or blinking goes to
the upper left corner of the display.
ENTRY MODE SET
The I/D bit of this instruction controls the incrementing (I/D 5 1) or decrementing
(I/D 5 0) of the DDRAM address. The cursor or blinking will be moved to the right or left
depending on whether this bit is set to 1 or 0. The same applies to writing and reading of
CGRAM.
The S bit of this instruction controls the shifting of the LCD display. The display shifts if
S 5 1. Otherwise, the display does not shift. If S is 1, it will seem as if the cursor does not move
but the display does. The display does not shift when reading from DDRAM. Also, writing into
or reading from CGRAM does not shift the display.
D I S P L AY O N /O F F C O N T R O L
This instruction has three bit parameters: D, C, and B. When the D bit is set to 1, the display is turned on; otherwise it is turned off. The cursor is turned on when the C bit is set to 1.
The character indicated by the cursor will blink when the B bit is set to 1.
C U R S O R O R D I S P L AY S H I F T
This instruction shifts the cursor position to the right or left without writing or reading
display data. The shifting is controlled by 2 bits, as shown in Table 7.9. This function is used to
correct or search the display. When the cursor gets to the end of a line, it will be moved to the
beginning of the next line.
S/C
R/L
0
0
Shifts the cursor position to the left. (AC is decremented by 1)
Operation
0
1
Shifts the cursor position to the right. (AC is incremented by 1)
1
0
Shifts the entire display to the left. The cursor follows the display shift.
1
1
Shifts the entire display to the right. The cursor follows the display shift.
Table 7.9 ■ LCD shift function
330
Chapter 7 ■ Advanced Parallel I/O
When the displayed data is shifted repeatedly, each line moves only horizontally. The second line of the display does not shift into the first row. The contents of the address counter will
not change if the only action performed is a display shift.
FUNCTION SET
This instruction allows the user to set the interface data length, select the number of display lines, and select the character fonts. There are three bit variables in this
instruction:
DL: Data is sent or received in 8-bit length (DB7 to DB0) when DL is set to 1 and in
4-bit length (DB7 to DB4) when DL is 0. When the pin count is at a premium for the
application, the 4-bit data length should be chosen even though it is cumbersome to
perform the programming.
N: This bit sets the number of display lines. When set to 0, one-line display is
selected. When set to 1, two-line display is selected.
F: When set to 0, the 5 3 8 font is selected. When set to 1, the 5 3 10 font is selected.
SET CGRAM ADDRESS
This instruction contains the CGRAM address to be set into the address counter.
SET DDRAM ADDRESS
This instruction allows the user to set the address of the DDRAM in the address counter.
This instruction is used whenever the user wants to set the cursor to a certain position on the
LCD screen.
READ BUSY FLAG AND ADDRESS
This instruction reads the busy flag (BF) and the address counter. The BF flag indicates
whether the LCD controller is still executing the previously received instruction.
7.8.6 Interfacing the HD44780U to the HCS12 Microcontroller
The data transfer between the HD44780U and the MCU can be done in 4 or 8 bits at
a time. When in 4-bit mode, data are carried on the upper four data pins (DB7,DB4). The
upper 4 bits are sent over DB7,DB4 first and followed immediately by the lower 4 bits.
For those HCS12 members that do not support external memory, the designer must
use I/O ports to interface with the LCD module. For those HCS12 members that support
external memory, the designer has the choice of using I/O ports to interface with the LCD
module or treating the LCD as a memory device. This chapter will treat the LCD only as an
I/O device.
The LCD circuit connection for the Dragon12-Plus (4-bit data bus) is illustrated in
Figure 7.28. The R/W signal to the LCD kit in the Dragon12 demo board is grounded; this prevents the user from polling the BF flag to determine whether the LCD internal operation has
been completed.
Certain timing parameters must be satisfied in order to access the LCD successfully. The
read and write timing diagrams are shown in Figures 7.29 and 7.30, respectively. The values
of timing parameters depend on the frequency of the operation. HD44780U-based LCDs can
operate at either 1 MHz (cycle time of E signal) or 2 MHz. The values of timing parameters at
these two frequencies are shown in Tables 7.10 and 7.11, respectively.
331
7.8 ■ The HD44780U LCD Controller
HD44780U-based
LCD Module
HCS12 MCU
PK0
RS
PK1
E
5V
VCC
5V
R/W
VEE
PK5...PK2
DB7..DB4
GND
Figure 7.28 ■ LCD interface example (4-bit bus, used in
Dragon12)
RS
tAH
tAS
R/W
PWEH
E
tEr
tEf
tDDR
tDHR
Valid data
DB0-DB7
tCYCLE
Figure 7.29 ■ HD44780U LCD controller read timing
diagram
RS
tAH
tAS
R/W
PWEH
E
DB0-DB7
tEr
tEf
tDSW
tH
Valid data
tCYCLE
Figure 7.30 ■ HD44780U LCD controller write timing diagram
332
Chapter 7 ■ Advanced Parallel I/O
Symbol
Meaning
Min.
Typ.
Max.
Unit
tCYCLE
Enable cycle time
1000
–
–
ns
PWEH
Enable pulse width (high level)
450
–
–
ns
tEr, tEf
Enable rise and decay time
–
–
25
ns
tAS
Address setup time, RS, R/W, E
60
–
–
ns
tDDR
Data delay time
–
–
360
ns
tDSW
Data setup time
195
–
–
ns
tH
Data hold time (write)
10
–
–
ns
tDHR
Data hold time (read)
5
–
–
ns
tAH
Address hold time
20
–
–
ns
Table 7.10 ■ HD44780U bus timing parameters (1-MHz operation)
Symbol
Meaning
Min.
Typ.
Max.
Unit
–
ns
tCYCLE
Enable cycle time
500
–
PWEH
Enable pulse width (high level)
230
–
–
ns
tEr, tEf
Enable rise and decay time
–
–
20
ns
tAS
Address setup time, RS, R/W, E
40
–
–
ns
tDDR
Data delay time
–
–
160
ns
tDSW
Data setup time
80
–
–
ns
tH
Data hold time (write)
10
–
–
ns
tDHR
Data hold time (read)
5
–
–
ns
tAH
Address hold time
10
–
–
ns
Table 7.11 ■ HD44780U bus timing parameters (2-MHz operation)
Example 7.4
▼
Write a set of LCD routines that perform the following functions for the Dragon12 demo board:
1. cmd2LCD (char cmd). This function sends the command cmd to the LCD kit.
2. openLCD (void). This function initializes the LCD.
3. putcLCD (char cx). This function outputs the character cx to the LCD kit.
4. putsLCD (char *ptr). This function outputs a NULL-terminated string pointed to by ptr to
the LCD kit.
Solution: The procedure for sending a command to the IR of the LCD is as follows:
Step 1
Pull the RS and the E signals to low.
Step 2
Pull the R/W signal to low.
333
7.8 ■ The HD44780U LCD Controller
Step 3
Pull the E signal to high.
Step 4
Output data to the output port attached to the LCD data bus. We need to configure the I/O
port for output before writing data to the LCD kit.
Step 5
Pull the E signal to low and make sure that the internal operation is complete.
The procedure for writing a byte to the LCD data register is as follows:
Step 1
Pull the RS signal to high.
Step 2
Pull the R/W signal to low.
Step 3
Pull the E signal to high.
Step 4
Output data to the I/O port attached to the LCD data bus.
Step 5
Pull the E signal to low and make sure that the internal operation is complete.
These procedures need to be repeated once for an LCD kit that has a 4-bit interface.
The following constant definitions will be used in the specified functions:
lcd_dat
lcd_dir
lcd_E
lcd_RS
equ
equ
equ
equ
PTK
DDRK
$02
$01
; LCD data pins (PK5,PK2)
; LCD data direction port
; E signal pin
; RS signal pin
All of the LCD commands take a much longer time to complete than an instruction
does. There is always a possibility that the LCD is still busy with its internal operation
when we want to send a new command to the LCD. There are two methods for solving this
problem.
1. The cmd2LCD function calls a subroutine to make sure that the LCD is idle before
proceeding with the new command. After performing the desired operation, the
cmd2lcd function simply returns without waiting for the internal LCD operation to
complete. This approach cannot work with the Dragon12 demo board.
2. The cmd2LCD function performs the desired operation, waits for 40 μs (or slightly
longer), and then returns to the caller. By waiting for 40 μs, all except two instructions
(clear display and cursor home) will be completed. For these two instructions, the
caller needs to call a delay subroutine to wait until the internal LCD operation is
completed before proceeding with other operations.
The function that sends a command to the LCD kit using the second approach is as
follows:
cmd2LCD
psha
bclr
bset
anda
lsra
lcd_dat,lcd_RS
lcd_dat,lcd_E
#$F0
; save the command in stack
; select the instruction register
; pull the E signal high
; clear the lower 4 bits
; match the upper 4 bits with the LCD
334
Chapter 7 ■ Advanced Parallel I/O
lsra
oraa
staa
nop
nop
nop
bclr
pula
anda
lsla
lsla
bset
oraa
staa
nop
nop
nop
bclr
ldy
jsr
rts
#lcd_E
lcd_dat
lcd_dat,lcd_E
#$0F
lcd_dat,lcd_E
#lcd_E
lcd_dat
lcd_dat,lcd_E
#1
delayby50us
; data pins
; maintain the E signal value
; send the command, along with the RS and E signals
; extend the duration of the E pulse
; “
; “
; pull the E signal low
; retrieve the LCD command
; clear the upper 4 bits
; match the lower 4 bits with the LCD
; data pins
; pull the E signal high
; maintain the E signal value
; send the lower 4 bits of command with E and RS
; extend the duration of the E pulse
; “
; “
; clear the E signal to complete the write operation
; adding this delay will complete the internal
; operation for most instructions
Before using the LCD, the user must configure it properly. The configuration of the LCD
involves at least the following four LCD instructions:
1. Entry mode set. The common setting for this instruction is to move the cursor to the
right after reading or writing a character from or to the LCD.
2. Display on/off. The common setting for this instruction is to turn on the display,
cursor, and cursor blinking.
3. Function set. This instruction sets the number of rows for display, the font size, and
the width of the interface data (4 or 8 bits).
4. Clear display. Before outputting any data, it is always a good idea to clear the LCD
screen and move the cursor to the home position (upper left corner).
The following function performs the LCD configuration:
openLCD
movb
ldy
jsr
ldaa
jsr
ldaa
jsr
ldaa
jsr
ldaa
jsr
ldy
jsr
rts
#$FF,lcd_dir
#10
delayby10ms
#$28
cmd2lcd
#$0F
cmd2lcd
#$06
cmd2lcd
#$01
cmd2lcd
#2
delayby1ms
; configure Port K for output
; wait for LCD to be ready
; “
; set 4-bit data, two-line display, 5 3 8 font
; “
; turn on display, cursor, and blinking
; “
; move cursor right (entry mode set instruction)
; “
; clear display screen and return to home position
; “
; wait until clear display command is complete
; “
335
7.8 ■ The HD44780U LCD Controller
The function that outputs a character to the LCD and makes sure that the write operation
is complete is as follows:
putcLCD
psha
bset
bset
anda
lsra
lsra
oraa
staa
nop
nop
nop
bclr
pula
anda
lsla
lsla
bset
oraa
staa
nop
nop
nop
bclr
ldy
jsr
rts
lcd_dat,lcd_RS
lcd_dat,lcd_E
#$F0
#$03
lcd_dat
lcd_dat,lcd_E
#$0F
lcd_dat,lcd_E
#$03
lcd_dat
lcd_dat,lcd_E
#1
delayby50us
; save a copy of the data
; select lcd Data register
; pull E to high
; mask out the lower 4 bits
; match the upper 4 bits with the LCD
; data pins
; keep signal E and RS unchanged
; send the upper 4 bits and E, RS signals
; provide enough duration to the E signal
; “
; “
; pull the E signal low
; retrieve the character from the stack
; clear the upper 4 bits
; match the lower 4 bits with the LCD
; data pins
; pull the E signal high
; keep E and RS unchanged
; pull E low to complete the write cycle
; wait until the write operation is
; complete
The function that outputs a NULL-terminated string pointed to by the index register X is
as follows:
putsLCD
donePS
ldaa
beq
jsr
bra
rts
1, x1
donePS
putcLCD
putsLCD
; get one character from the string
; reach NULL character?
▲
Example 7.5
▼
Write an assembly program to test the previous four subroutines by displaying the following
messages on two lines:
hello world!
LCD is working!
Solution: The program is as follows:
#include
lcd_dat
lcd_dir
“c:\miniide\hcs12.inc”
equ
PTK
equ
DDRK
; LCD data pins (PK5,PK2)
; LCD data direction port
336
Chapter 7 ■ Advanced Parallel I/O
lcd_E
lcd_RS
msg1
msg2
equ
$02
; E signal pin
equ
$01
; RS signal pin
org
$1500
lds
#$1500
; set up stack pointer
jsr
openLCD
; initialize the LCD
ldx
#msg1
jsr
putsLCD
ldaa
#$C0
; move to the second row
jsr
cmd2LCD
; “
ldx
#msg2
jsr
putsLCD
swi
dc.b
“hello world!”,0
dc.b
“LCD is working!”,0
#include “c:\miniIDE\delay.asm”
; include delay routines here
;
; include the previous four LCD functions
;
;
org
$FFFE
dc.w
start
end
; uncomment this line for CodeWarrior
; uncomment this line for CodeWarrior
▲
Example 7.6
▼
Write the C language versions of the previous four LCD functions and a program to test them.
Solution: The C functions for the LCD kit on the Dragon12 demo board and their test program
are as follows:
#include
“c:\cwHCS12\include\hcs12.h”
#include
“c:\cwHCS12\include\delay.h”
#include
“c:\cwHCS12\include\lcd_util.h”
#define
LCD_DAT PORTK // Port K drives LCD data pins, E, and RS
#define
LCD_DIR DDRK
// Direction of LCD port
#define
LCD_E
0x02
// E signal
#define
LCD_RS 0x01
// RS signal
#define
LCD_E_RS 0x03
// assert both E and RS signals
void main (void)
{
char *msg1 5 “hello world!”;
char *msg2 5 “LCD is working!”;
openLCD();
putsLCD(msg1);
cmd2LCD(0xC0);
// move cursor to 2nd row, 1st column
putsLCD(msg2);
asm(“swi”);
while(1);
}
void cmd2LCD (char cmd)
{
char
temp;
7.8 ■ The HD44780U LCD Controller
temp 5 cmd;
cmd &50xF0;
LCD_DAT &5 (,LCD_RS);
LCD_DAT |5 LCD_E;
cmd ..5 2;
LCD_DAT 5 cmd | LCD_E;
asm (“nop”);
asm (“nop”);
asm (“nop”);
LCD_DAT &5 (,LCD_E);
cmd 5 temp & 0x0F;
LCD_DAT |5 LCD_E;
cmd ,,5 2;
LCD_DAT 5 cmd | LCD_E;
asm(“nop”);
asm(“nop”);
asm(“nop”);
LCD_DAT &5 (,LCD_E);
delayby50us(1);
}
void openLCD(void)
{
LCD_DIR 5 0xFF;
delayby10ms(10);
cmd2LCD(0x28);
cmd2LCD(0x0F);
cmd2LCD(0x06);
cmd2LCD(0x01);
delayby1ms(2);
}
void putcLCD(char cx)
{
char temp;
temp 5 cx;
LCD_DAT |5 LCD_RS;
LCD_DAT |5 LCD_E;
cx &5 0xF0;
cx ..5 2;
LCD_DAT 5 cx|LCD_E_RS;
asm(“nop”);
asm(“nop”);
asm(“nop”);
LCD_DAT &5 (,LCD_E);
cx 5 temp & 0x0F;
LCD_DAT |5 LCD_E;
cx ,,5 2;
LCD_DAT 5 cx|LCD_E_RS;
asm(“nop”);
asm(“nop”);
asm(“nop”);
LCD_DAT &5 (,LCD_E);
// save a copy of the command
// clear out the lower 4 bits
// select LCD instruction register
// pull E signal to high
// shift to match LCD data pins
// output upper 4 bits, E, and RS
// dummy statements to lengthen E
//
“
// pull E signal to low
// extract the lower 4 bits
// pull E to high
// shift to match LCD data pins
// output upper 4 bits, E, and RS
// dummy statements to lengthen E
//
“
// pull E-clock to low
// wait until the command is complete
// configure LCD_DAT port for output
// set 4-bit data, 2-line display, 537 font
// turn on display, cursor, blinking
// move cursor right
// clear screen, move cursor to home
// wait until clear display command is complete
// select LCD data register
// pull E signal to high
// clear the lower 4 bits
// shift to match the LCD data pins
// output upper 4 bits, E, and RS
// dummy statements to lengthen E
//
“
// pull E to low
// get the lower 4 bits
// pull E to high
// shift to match the LCD data pins
// output lower 4 bits, E, and RS
// dummy statements to lengthen E
//
“
// pull E to low
337
338
Chapter 7 ■ Advanced Parallel I/O
delayby50us(1);
}
void putsLCD (char *ptr)
{
while (*ptr) {
putcLCD(*ptr);
ptr11;
}
}
▲
The assembly and C language versions of the LCD functions have been grouped into files
to be included in the user’s program and are provided on the complementary CD.
7.9 Interfacing Parallel Ports to a Keypad
A keypad is another commonly used input device. Like a keyboard, a keypad is arranged as an
array of switches, which can be mechanical, membrane, capacitive, or Hall effect in construction.
In mechanical switches, two metal contacts are brought together to complete an electric circuit.
In membrane switches, a plastic or rubber membrane presses one conductor onto another; this
type of switch can be made very thin. Capacitive switches comprise two plates of a parallel plate
capacitor; pressing the key cap effectively increases the capacitance between the two plates. Special circuitry is needed to detect this change in capacitance. In Hall-effect key switches, the motion
of the magnetic flux lines of a permanent magnet perpendicular to a crystal is detected as voltage
appearing between the two faces of the crystal; it is this voltage that registers a switch closure.
Mechanical keypads and keyboards are most popular due to their low cost and strength of
construction. However, mechanical switches have a common problem called contact bounce.
Instead of producing a single, clean pulse output, pressing a mechanical switch generates a series
of pulses because the switch contacts do not come to rest immediately. This phenomenon is
illustrated in Figure 7.31.
When the key is not pressed, the voltage output to the computer is 5 V. In order to detect
which key has been pressed, the microcontroller needs to scan every key switch of the keypad.
A human being cannot press and release a key switch in less than 20 ms. During this interval,
the microprocessor can scan the same key switch closure tens or even hundreds of thousands of
times, interpreting each low signal as a new input when in fact only one input should be sent.
Because of the contact bounce and the disparity in speed between the microprocessor and
human key pressing, a debouncing process is needed. A keypad input program can be divided
into three stages.
VCC
5 to 20 ms
H
to microprocessor
L
Active low output
Figure 7.31 ■ Key switch contact bounce
339
7.9 ■ Interfacing Parallel Ports to a Keypad
1. Keypad scanning to find out which key was pressed
2. Key switch debouncing to make sure a key is indeed pressed
3. Table lookup to find the ASCII code of the key that was pressed
7.9.1 Keypad Scanning
Keypad scanning is usually performed row by row and column by column. A 16-key keypad
can easily be interfaced with any available I/O port. Figure 7.32 shows a 16-key keypad organized into four rows with each row consisting of four switches.
For the keypad input application, the upper four pins (PA7,PA4) of Port A should be configured for output, whereas the lower four pins (PA3,PA0) of Port A should be configured for input.
HCS12 MCU
PA7
PA6
PA5
PA4
PA3
3
7
B
F
PA2
2
6
A
E
PA1
1
5
9
D
PA0
0
4
8
C
10 kΩ
VCC
Figure 7.32 ■ Sixteen-key keypad connected to the HCS12
The rows and columns of a keypad are simply conductors. In Figure 7.32, Port A pins
PA3,PA0 are pulled up to high by pull-up resistors. Whenever a key switch is pressed, the corresponding row and column are shorted together. In order to distinguish the row being scanned
from those not being scanned, the row being scanned is driven low, whereas the other rows are
driven high. The row selection of the 16-key keypad is shown in Table 7.12.
PA7
PA6
PA5
PA4
1
1
1
0
1
1
0
1
1
0
1
1
0
1
1
1
Selected Keys
0,
4,
8,
C,
1,
5,
9,
D,
2, and 3
6, and 7
A, and B
E, and F
Table 7.12 ■ Sixteen-key keypad row selections
340
Chapter 7 ■ Advanced Parallel I/O
7.9.2 Keypad Debouncing
Contact bounce is due to the dynamics of a closing contact. The signal falls and rises a few
times within a period of about 5 ms as a contact bounces. Since a human being cannot press and
release a switch in less than 20 ms, a debouncer will recognize that the switch is closed after
the voltage is low for about 10 ms and will recognize that the switch is open after the voltage is
high for about 10 ms.
Both hardware and software solutions to the key bounce problem are available. Hardware
solutions to contact bounce include an analog circuit that uses a resistor and a capacitor to
smooth the voltage and two digital solutions that use set-reset latches or CMOS buffers and
double-throw switches. Dedicated scanner chips that perform keypad scanning and debouncing
are also available. National Semiconductor 74C922 and 74C923 are two examples.
H A R D WA R E D E B O U N C I N G T E C H N I Q U E S
The following are hardware debouncing techniques:
• Set-reset latches. A key switch can be debounced by using the set-reset latch
shown in Figure 7.33a. Before being pressed, the key is touching the set input and
VDD
Set
Q
R
Q
Reset
Set
Reset
R
(a) Set-reset latch
VDD
R
4050
Vout
(b) CMOS gate debouncer
VDD
R
H
Vout
Threshold level
C
L
Switch closed
(c) Integrating RC circuit debouncer
Figure 7.33 ■ Hardware debouncing techniques
7.9 ■ Interfacing Parallel Ports to a Keypad
341
the Q voltage is high. When pressed, the key moves toward the reset position.
When the key touches the reset position, the Q voltage will go low. When the key
is bouncing and touching neither the set nor the reset input, both set and reset
inputs are pulled low by the pull-down resistors. Since both set and reset are low,
the Q voltage will remain low and the key will be recognized as pressed.
• Noninverting CMOS buffer with high input impedance. The CMOS buffer
output is identical to its input. When the switch is pressed, the input of the buffer
chip 4050 is grounded and hence VOUT is forced to low. When the key switch is
bouncing (not touching the input), the resistor R keeps the output voltage low. This
is due to the high input impedance of 4050, which causes a negligible voltage drop
on the feedback resistor. Thus the output is debounced. This solution is shown in
Figure 7.33b.
• Integrated debouncers. The RC constant of the integrator determines the rate
at which the capacitor charges up toward the supply voltage once the ground
connection via the switch has been removed. As long as the capacitor voltage
does not exceed the logic 0 threshold value, the VOUT signal will continue to be
recognized as a logic 0. This solution is shown in Figure 7.33c.
S O F T WA R E D E B O U N C I N G T E C H N I Q U E S
A simple software debouncing method is wait and see; that is, wait for 10 ms and reexamine the same key to see if it is still pressed. A key is considered pressed if its output voltage is
still low 10 ms after it is detected low. If the output voltage is high, the program will consider
the signal as noise or the key as still bouncing. In either case, the program will continue to perform the scanning. For most applications, this simple wait-and-see method is adequate.
7.9.3 ASCII Code Lookup
For an application that needs a keyboard, the easiest way to find out the ASCII code of the pressed
key is to perform a table lookup. However, table lookup is not necessary for the keypad because
ASCII code lookup can be embedded in the program that performs the scanning and debouncing.
Example 7.7
▼
Write a program to perform keypad scanning and debouncing and return the ASCII code of
the pressed key to the caller.
Solution: To implement keypad scanning, debouncing, and ASCII code lookup requires the use
of four local variables.
• maskr. the mask to select a row to be scanned
• maskc. the mask to select a column to be scanned
• column. the column (0 to 3) being scanned
• row. the row (0 to 3) being scanned
The following assembly routine will perform scanning and debouncing and return the
ASCII code of the pressed key in accumulator A to the caller:
keypad
maskr
maskc
equ
equ
equ
PTA
0
1
; the mask to scan a row
; the mask to scan a column
342
Chapter 7 ■ Advanced Parallel I/O
column equ
2
; the column being scanned
row
equ
3
; the row being scanned
; *********************************************************************************
; The following subroutine reads a character entered from the keypad. It performs
; scanning and then debouncing when it detects a pressed key. It returns the ASCII
; code in A after making sure the key is indeed pressed. It uses stack to allocate four
; 1-byte local variables.
; *********************************************************************************
getkey
leas
24,SP
; allocate 4 bytes for local variables
movb #$F0,DDRA
; configure keypad Port A pin directions
gkloope clr
row,SP
; start from row 0
movb #$EF,maskr,SP ; mask for selecting row 0
nextR
clr
column,SP
; start from column 0 in a row
movb #$01,maskc,SP ; mask for scanning column 0
movb #$FF,keypad
; prepare to scan
ldaa
maskr,SP
; select a row to scan
anda
keypad
;
“
staa
keypad
gkloopi ldaa
maskc,SP
; select a column to check
anda
keypad
;
“
beq
debnce
; if low, then a key press has been detected
next
ldaa
#3
cmpa column,SP
blo
inc_col
; not reach the last column in a row yet, branch
ldaa
#3
cmpa row,SP
; not reach row 3 yet, branch and try next row
blo
inc_row
;
“
bra
gkloope
; restart from row 0, column 0
inc_col inc
column,SP
; move to next column
lsl
maskc,SP
; update the column mask to check the
bra
gkloopi
; goto scan next column
inc_row inc
row,SP
; move to next row
lsl
maskr,SP
; update the mask for scanning
bset
maskr,SP,$01
; always force bit 0 of maskr to 1
bra
nextR
debnce ldy
#1
jsr
delayby10ms
ldaa
maskc,SP
; reexamine the same key
anda
keypad
;
“
beq
getcode
; if still low, get the ASCII code
bra
next
; not low, continue to scan
getcode ldaa
row,SP
; find out the key number that
lsla
; has been pressed
lsla
;
“
adda
column,SP
;
“
cmpa #10
; is [A] .5 10?
blo
isdeci
; jump if A , 10
adda
#$37
; the pressed key is greater than 9
leas
4,SP
343
7.10 ■ Using the D/A Converter
rts
leas
4,SP
adda
#$30
rts
#include “c:\miniIDE\delay.asm”
isdeci
; compute the ASCII code
The C language version of the function is as follows:
#define
keypad
PTA
// keypad port
#define
keypad_dir
DDRA
// keypad port direction register
// ********************************************************************************
// rmask is row mask, cmask is column mask, row is the row being scanned, col is the
// column being scanned
// ********************************************************************************
char getkey (void)
{
char rmask, cmask, row, col;
char temp, keycode;
keypad_dir 5 0xF0; // configure lower four pins for input
while (1) {
rmask
5 0xEF;
for (row 5 0; row , 4; row11){
cmask
5 0x01;
keypad &5 rmask; // select the current row
for (col 5 0; col , 4; col11){
if (!(keypad & cmask)){
// key switch detected pressed
delayby10ms(1);
if(!(keypad & cmask)){ // check the same key again
keycode 5 row * 4 1 col;
if (keycode , 10)
return (0x30 1 keycode);
else
return (0x37 1 keycode);
}
}
cmask 5 cmask ,, 1;
}
rmask 5 (rmask ,, 1) | 0x0F;
}
}
}
▲
7.10 Using the D/A Converter
A digital-to-analog converter (DAC) converts fixed-point binary numbers into an electric
voltage (more often) or current. Normally the output is a linear function of the input number. These numbers are often obtained from a sampling process (for example, digital audio)
at uniform intervals. To restore to the original physical quantity, these numbers are fed to
Voltage
Chapter 7 ■ Advanced Parallel I/O
Time
(a) Original analog signal
Voltage
344
(b) Signal after sampling and DAC process
Time
Figure 7.34 ■ Analog signal and the resulting signal after sampling, ADC, and
DAC process
the DAC also at the uniform intervals. As a result, all practical DACs output a sequence of
piecewise constant values or rectangular pulses. An example of this process is illustrated in
Figure 7.34.
A DAC has many applications. Examples are digital gain and offset adjustment, programmable voltage and current sources, programmable attenuators, digital audio, closed-loop positioning,
robotics, and so on. In the last few years, digital video is also getting more and more popular. As
more and more flat panel displays come with DVI and HDMI interfaces, digital video will become
the norm in a few years. Although there are a few microcontrollers incorporating the D/A converter on the chip, most microcontrollers still need to use an off-chip D/A converter to perform
the D/A conversion function. The HCS12 is no exception. A D/A converter may use a serial or
parallel interface to obtain digital code from the microprocessor or microcontroller.
There are several factors to consider when choosing a DAC.
• Resolution. This is the number of possible output levels the DAC is designed to
reproduce. The resolution is usually stated as the number of bits it uses and is the
base 2 logarithm of the number of levels. For example, an 8-bit DAC can represent
256 levels.
• Dynamic range. This is a measurement of the difference between the largest and
smallest signals the DAC can reproduce represented in decibels. This characteristic
is related to the DAC resolution and noise floor (the sum of all the noise sources).
• Number of channels. A DAC may have more than one output channel to satisfy
the needs of the applications that require more than one channel.
345
7.10 ■ Using the D/A Converter
• Type of output. The output of a DAC may be in the form of voltage or current to
meet the requirement of the target application.
• Monotonicity. This refers to the ability of the DAC’s analog output to increase
with digital code.
In this section, we use the 8-bit DAC AD7302 from Analog Devices to illustrate the use of the DAC.
7.10.1 The 8-bit AD7302 DAC
The AD7302 is a dual-channel (two DACs on the same chip), 8-bit, DAC chip from Analog
Devices that has a parallel interface with the microcontroller. The AD7302 converts an 8-bit
digital value into an analog voltage. The block diagram of the AD7302 is shown in Figure 7.35.
The AD7302 is designed to be a memory-mapped device. In order to send data to the AD7302,
the CS signal must be pulled to low. On the rising edge of the WR signal, the values on pins
D7–D0 will be latched into the Input register. When the signal LDAC is low, the data in the Input
register is transferred to the DAC register and a new D/A conversion is started. The AD7302
needs a reference voltage to perform the D/A conversion. The reference voltage can come from
either the external REFIN input or the internal VDD. The A/B signal selects the channel (A or B)
to perform the D/A conversion. The PD pin puts the AD7302 in power-down mode and reduces
the power consumption to 1 μW.
The AD7302 operates from a single 12.7- to 5.5-V supply and typically consumes 15 mW at
5 V, making it suitable for battery-powered applications. Each digital sample takes about 2 μs to
convert. The output voltage (VOUT A or VOUTB) from either DAC is given by
VOUT A/B 5 2 3 VREF 3 (N/256)
where VREF is derived internally from the voltage applied at the REFIN pin or VDD. If the voltage
applied to the REFIN pin is within 1 V of the VDD, VDD/2 is used as the reference voltage automatically. Otherwise, the voltage applied at the REFIN pin is used as the reference voltage. The
range of VREF is from 1 V to VDD/2. N is the decimal equivalent of the code loaded to the DAC
register, ranging from 0 to 255.
AD7302
Input
register
DAC
register
I DAC A
I/V
VOUT A
D7
D0
Input
register
DAC
register
I DAC B
I/V
VOUT B
A/B
WR
CS
Control
logic
Power-on
reset
MUX
÷2
PD
CLR
LDAC
REFIN
Figure 7.35 ■ Functional block diagram of the AD7302
VDD
DGND
AGND
346
Chapter 7 ■ Advanced Parallel I/O
7.10.2 Interfacing the AD7302 with the HCS12
Interfacing the AD7302 with the HCS12 can be very simple. Both the CS and LDAC signals
can be tied to ground permanently. The value to be converted must be sent to the AD7302 via
a parallel port (connect to pins D7,D0). An output pin can be used (as the WR signal) to control
the transferring of data to the Input register. A typical connection between the HCS12 and the
AD7302 is shown in Figure 7.36.
HCS12
AD7302
PB7,PB0
D7..D0
5V
VDD
REFIN
PJ0
VDD
WR
PD
CLR
PJ1
A/B
CS
VOUT A
VOUT B
AGND
DGND
LDAC
Figure 7.36 ■ Circuit connection between the AD7302
and the HCS12
Example 7.8
▼
Write a program to generate a sawtooth waveform from the VOUTA pin.
Solution: The procedure for generating a sawtooth waveform is as follows:
Step 1
Configure PB7,PB0, PJ0, and PJ1 for output.
Step 2
Output the digital value from 0 to 255 and repeat. For each value, pull the PJ0 to low and
then to high so that the value on pins PB7,PB0 can be transferred to the AD7302. Pull the
signal PJ1 to low during the process.
The assembly program is as follows:
start
loop
#include
org
movb
bset
bclr
inc
bclr
bset
bset
bset
bset
bset
“c:\miniide\hcs12.inc”
$1500
#$FF,DDRB ; configure Port B for output
DDRJ,$03 ; configure PJ1,PJ0 for output
PTJ,$02 ; select VOUT A output
PORTB
; increase the output by one step
PTJ,$01 ; generate a rising edge on PJ0 pin
PTJ,$01 ;
“
PTJ,$01 ; add nine more bset instructions to provide 2 µs
PTJ,$01 ; for D/A conversion to complete
PTJ,$01 ;
“
PTJ,$01 ;
“
7.11 ■ Stepper Motor Control
;
;
bset
bset
bset
bset
bset
bra
org
dc.w
end
PTJ,$01
PTJ,$01
PTJ,$01
PTJ,$01
PTJ,$01
loop
$FFFE
start
347
; “
; “
; “
; “
; “
; to complete the D/A conversion
; uncomment this line for CodeWarrior
; uncomment this line for CodeWarrior
The C language version of the program is as follows:
#include “c:\cwHCS12\include\hcs12.h”
void main(void)
{
DDRB
5 0xFF;
// configure PORTB for output
DDRJ
|5 0x03;
// configure pins PJ1,PJ0 for output
PTJ
&5 0xFD;
// pull the signal A/B to low to select channel A
while (1) {
PORTB 15 1;
PTJ &5 0xFE;
// generate a rising edge
PTJ |5 0x01;
//
“
PTJ |5 0x01;
// use dummy statements to provide 2 µs
PTJ |5 0x01;
// time for D/A conversion to complete
PTJ |5 0x01;
PTJ |5 0x01;
PTJ |5 0x01;
PTJ |5 0x01;
PTJ |5 0x01;
PTJ |5 0x01;
PTJ |5 0x01;
}
}
▲
The AD7302 can be used to generate many interesting waveforms. Several exercise problems
are given on the applications of this DAC at the end of the chapter.
7.11 Stepper Motor Control
Stepper motors are digital motors. They are convenient for applications where a high degree
of positional control is required. Printers, tape drives, disk drives, and robot joints, for example,
are typical applications of stepper motors.
7.11.1 Principles of Rotation for the Stepper Motor
In its simplest form, a stepper motor has a permanent magnet rotor and a stator consisting
of two coils. The rotor aligns with the stator coil that is energized. By changing which coil is
energized, as illustrated in the following figures, the rotor is turned.
In Figure 7.37a–d, the permanent magnet rotor lines up with the coil pair that is energized.
The direction of the current determines the polarity of the magnetic field, and thus the angular
348
Chapter 7 ■ Advanced Parallel I/O
position of the rotor. Energizing coil pair C3–C4 causes the rotor to rotate 90 degrees. Again,
the direction of the current determines the magnetic polarity and thus the angular position of
the rotor. In this example, the direction of the current causes the rotor to rotate in a clockwise
direction, as shown in Figure 7.37b.
Next, coils C1–C2 are energized again, but with a current opposite to that in step 1. The
rotor moves 90 degrees in a clockwise direction, as shown in Figure 7.37c. The last full step
moves the rotor another 90 degrees in a clockwise direction. Note that again the coil pair
C3–C4 is energized, but with a current opposite to that in step 2.
C4
Current flow
1st
S
N
N
S
C2
N
S
C1
C3
2nd
Figure 7.37a ■ Stepper motor full step 1
N
C4
S
1st
N
C2
S
C1
N
Current flow
C3
2nd
S
Figure 7.37b ■ Stepper motor full step 2
349
7.11 ■ Stepper Motor Control
C4
Current flow
1st
S
N
N
S
C2
S
N
C1
C3
2nd
Figure 7.37c ■ Stepper motor full step 3
S
C4
N
1st
S
C2
N
C1
S
Current flow
C3
2nd
N
Figure 7.37d ■ Stepper motor full step 4
We can also rotate the stepper motor in the counterclockwise direction. This can be done
by reversing the polarities of coils C3 and C4 in Figure 7.37b and d. Figure 7.38 shows the counterclockwise sequence.
The stepper motor may also be operated with half steps. A half step occurs when the rotor
(in a four-pole step) is moved to eight discrete positions (45 degrees). To operate the stepper motor
in half steps, sometimes both coils may have to be on at the same time. When two coils in close
350
Chapter 7 ■ Advanced Parallel I/O
N
S
C4 Rotor
C4 Rotor
N
N
C2
S
C1
S
N
S
N
C2
S
C1
N
C3
Step 1
C3
S
Step 2
S
N
C4 Rotor
C4 Rotor
S
N
S
C2
N
C1
S
C3
Step 3
N
S
N
S
N
C1
C2
C3
Step 4
Figure 7.38 ■ Full-step counterclockwise operation of step motor
proximity are energized, there is a resultant magnetic field whose center will depend on the relative strengths of the two magnetic fields. Figure 7.39 illustrates the half-stepping sequence.
The step sizes of stepper motors vary from approximately 0.72 to 90 degrees. However, the
most common step sizes are 1.8, 7.5, and 15 degrees. The steps of 90 or 45 degrees are too crude
for many applications.
The actual stator (the stationary electromagnets) of a real motor has more segments on it
than previously indicated. One example is shown in Figure 7.40. The rotor is also a little bit different and is also shown in Figure 7.40.
In Figure 7.40, the stator has eight individual sections (coils) on it and hence the angle
between two adjacent sections is 45 degrees. The rotor has six sections on it and hence there
are 60 degrees between two adjacent sections. Using the principle of a Vernier mechanism, the
actual movement of the rotor for each step would be 60–45 degrees, or 15 degrees. Interested
readers should try to figure out how these sections are energized to rotate the motor in the
clockwise and counterclockwise directions.
7.11.2 Stepper Motor Drivers
Driving a stepper motor involves applying a series of voltages to the coils of the motor. A subset of coils is energized at the same time to cause the motor to rotate one step. The pattern of
coils energized must be followed exactly for the motor to work correctly. The pattern will vary
depending on the mode used on the motor. A microcontroller can easily time the duration that
the coil is energized, and hence control the speed of the stepper motor in a precise manner.
The circuit in Figure 7.41 shows how the transistors are used to switch the current to
each of the four coils of the stepper motor. The diodes in Figure 7.41 are called fly back
351
7.11 ■ Stepper Motor Control
S
S
C4 Rotor
S
S
S
S
S
C3
Step 2
N
S
C1
C2
C1
S
Step 1
N
N
N
N
C2
Rotor
C4
NN
N
C3
N
N
S
N
S
N
C2
Rotor
S
S
N
S
N
S
N
C4
Rotor
N
C4
C2
C1
S
C1
N
C3
Step 3
Step 4
C3
S
N
N
S
S
N
N
C1
N
C2
N
C1
N
N
C3
Step 5
S
S
S
S
C2
Rotor
C4
C4 Rotor
C3
Step 6
S
S
S
C4 Rotor
N
Rotor
C4
S
N
S
S
N
C2
N
S
C1
N
S
C1
S
C3
Step 7
Step 8
N
S
N
C2
C3
N
Figure 7.39 ■ Half-step operation of the stepper motor
352
Chapter 7 ■ Advanced Parallel I/O
C1
C2
C8
S
Rotor
N
S
C7
S
N
C3
S
N
N
S
N
C4
C6
C5
Figure 7.40 ■ Actual internal construction
of stepper motor
VCC
R
R
Q4
VCC
PP3
R
PP2
Q3
VCC
PP1
PP0
R
R
R
VCC
Q2
VCC
R
R
Step motor
VCC
Q1
Figure 7.41 ■ Driving a stepper motor
353
7.11 ■ Stepper Motor Control
diodes and are used to protect the transistors from reverse bias. The transistor loads are the
windings in the stepper motor. The windings are inductors, storing energy as a magnetic
field. When the current is cut off, the inductor dispenses its stored energy in the form of
an electric current. This current attempts to flow through the transistor, reverse biasing its
collector-emitter pair. The diodes are placed to prevent this current from going through the
transistors.
For higher-torque applications, the normal full-step sequence is used as shown in
Table 7.13. For lower-torque applications, the half-step mode is used and its sequence is
shown in Table 7.14. To control the motor, the microcontroller must output the values
in the table in the sequence shown. Tables 7.13 and 7.14 are circular in that after the last
step, the next output must be the first step. The values may be output in the order shown
to rotate the motor in one direction, or in reverse order to rotate in the reverse direction.
It is essential that the order be preserved even if the motor is stopped for a while. The next
step to restart the motor must be the next sequential step following the last step used. The
mechanical inertia of the motor will require a short delay (usually 5 to 20 ms) between two
steps to prevent the motor from missing steps.
Step
Q4
PP3
Q3
PP2
Q2
PP1
Q1
PP0
Value
1
on
on
off
off
1100
2
off
on
on
off
0110
3
off
off
on
on
0011
4
on
off
off
on
1001
Table 7.13 ■ Full-step sequence for
clockwise rotation
Step
Q4
PP3
Q3
PP2
Q2
PP1
Q1
PP0
Value
1
on
off
off
off
1000
2
on
on
off
off
1100
3
off
on
off
off
0100
4
off
on
on
off
0110
5
off
off
on
off
0010
6
off
off
on
on
0011
7
off
off
off
on
0001
8
on
off
off
on
1001
Table 7.14 ■ Half-step sequence for
clockwise rotation
354
Chapter 7 ■ Advanced Parallel I/O
Example 7.9
▼
Suppose that the pins PP3..PP0 are used to drive the four transistors as shown in
Figure 7.41. Write a subroutine to rotate the stepper motor clockwise one cycle using the
half-step sequence.
Solution: The assembly language subroutine is as follows:
#include
step1
step2
step3
step4
step5
step6
step7
step8
HStep
“c:\miniIDE\hcs12.inc”
equ
$08
equ
$0C
equ
$04
equ
$06
equ
$02
equ
$03
equ
$01
equ
$09
movb #$FF,DDRP
; configure PTP for output
movb #step1,PTP
bsr
delay10ms
movb #step2,PTP
bsr
delay10ms
movb #step3,PTP
bsr
delay10ms
movb #step4,PTP
bsr
delay10ms
movb #step5,PTP
bsr
delay10ms
movb #step6,PTP
bsr
delay10ms
movb #step7,PTP
bsr
delay10ms
movb #step8,PTP
bsr
delay10ms
rts
; the following subroutine waits for 10 ms
delay10ms movb #$90,TSCR1
; enable TCNT and fast flags clear
movb #$06,TSCR2
; configure the prescale factor to 64
bset
TIOS,IOS0
; enable OC0
ldd
TCNT
addd #3750
; start an output compare operation
std
TC0
; with 10-ms time delay
brclr
TFLG1,$01,*
rts
end
▲
7.12 ■ Key Wake-Ups
355
7.12 Key Wake-Ups
Most embedded products are powered by batteries. In order for batteries to last longer,
most microcontrollers have incorporated power-saving modes such as the wait mode or the
stop mode in the HCS12. Whenever there is no activity from the end user over a period of
time, the application software would put the embedded product in one of the power-saving
modes and reduce the power consumption. Whenever the end user wants to use the product, a keystroke would wake up the microcontroller and put the embedded product back to
normal operation mode. As you learned in Chapter 6, both the reset and unmasked interrupts can put the microcontroller back to normal operation mode. However, reset is not
recommended because it will restart the microcontroller, which would delay the response
to the user request. An unmasked interrupt does not have this drawback. All of the HCS12
members have incorporated the key wake-up feature that will issue an interrupt to wake up
the MCU when it is in the stop or wait mode. After the appropriate configuration, an active
edge on these pins will generate an interrupt to the MCU and force the MCU to exit from
the low-power mode.
7.12.1 Key-Wake-Up Registers
As described in Section 7.5.7, Ports H, J, and P have the key-wake-up capability. Each port
has eight associated registers. The Port Pull Device Enable register and the Port Polarity Select
register together allow the user to choose to use either the rising edge or falling edge to wake up
the MCU. The Port Interrupt Enable register and the Port Interrupt Flag register together allow
the user to use key pressing to wake up the MCU.
7.12.2 Key-Wake-Up Initialization
In order to use the HCS12 key-wake-up feature, the user needs to initialize the wake-up
port properly. The procedure for using the key-wake-up feature is as follows:
Step 1
Set the direction of the key-wake-up bits to input by writing zeros to the related Data
Direction register.
Step 2
Select the rising edge or the falling edge of the wake-up pin to interrupt the MCU by
programming the related registers.
Step 3
Write the service routine for the key-wake-up interrupt and initialize the key-wake-up
interrupt vector.
Step 4
Clear any flags that have been set in the Key-Wake-Up Flag register.
Step 5
Enable the key-wake-up bit by setting the appropriate bits in the Wake-Up Interrupt
Enable register.
Step 6
Clear the global interrupt mask (the I bit of the CCR register).
356
Chapter 7 ■ Advanced Parallel I/O
7.12.3 Considerations for the Key-Wake-Up Application
The main application of the key-wake-up feature is to support the power-saving modes of
the HCS12. Application software puts the microcontroller in low-power mode by executing a
stop or a wai instruction when the inactivity of the end user has exceeded the preset time.
Many applications are designed to be a wait loop that waits for the user to enter a request
for service. When a request is entered, the application calls an appropriate routine to provide the
service. After the service is done, the routine returns to the wait loop. After providing service
to a user request, the application software starts a timer. If the user enters another command
before the timer times out, the application software resets the timer and responds to the user
request. If the timer times out before the user makes another service request, the application
software puts the microcontroller in low-power mode to save power. As long as there is no user
request for service, the microcontroller will stay in the low-power mode. The timer timeout
interval could be a few minutes or longer depending on how much power the user wants to
save. Either the timer output compare or modulus down counter function can be used to implement the timeout interval. When the user presses the key, the microcontroller will exit the
low-power mode and continue to execute the instruction following the stop (or wai) instruction
and another cycle of the normal application loop is started.
In order to be used in a key-wake-up application, port pins must be configured for input.
Ports H, J, and P can be configured to use the rising or falling edge to wake up the microcontroller. The choice of signal edge will dictate the choice of the pull-up or pull-down resistive device. As shown in Figure 7.42, a rising edge results when a high voltage is applied to a
pull-down resistor; a falling edge results when a low voltage is connected to a pull-up resistor.
Therefore, the designer should enable the pull-down resistor when a rising edge is selected and
enable a pull-up resistor when a falling edge is chosen to wake up the microcontroller.
Since the purpose of the key-wake-up feature is to enable the microcontroller to resume
normal operation mode, the interrupt service routine need only perform minimal operation.
The minimal operation to be performed would be to clear the interrupt flag set by the wake-up
interrupt.
The logic flow of an embedded application that incorporates key-wake-up interrupt is
illustrated in Figure 7.43.
VDD
Inside the chip
Pull-up
device
VIN
VX(= VDD)
Pull-down
device
(from key
switch)
Current
flow
direction
VIN
Inside the
chip
Current
flow
direction
VX(= 0 V)
(from key
switch)
Figure 7.42 ■ (a) Pull-down resistor creates rising edge; (b) Pull-up resistor creates
falling edge
357
7.12 ■ Key Wake-Ups
Start
Perform reset initialization
Perform key-wake-up initialization
Enable key-wake-up interrupt
Start a timer
Yes
User service request?
Service the request
No
No
Timer times out?
Yes
Enter low-power mode
(execute stop or wai instruction)
Key-wake-up interrupt?
Yes after servicing the interrupt
No
Stay in low-power mode
Implemented
in hardware
Figure 7.43 ■ Logic flow of embedded applications that incorporate key-wake-up feature
Example 7.10
▼
Write an instruction sequence to configure the Port P upper four pins for the key-wake-up
feature. Program Port P so that pins PP7,PP4 generate an interrupt whenever there is a falling
edge applied to any one of these four pins.
Solution:
#include
“c:\miniide\hcs12.inc”
…
bclr
DDRP,$F0
bset
PERP,$F0
; configure PP<7:4> pins for input
; enable PP7,PP4 pins’ pull device
358
Chapter 7 ■ Advanced Parallel I/O
bclr
movb
bset
cli
PPSP,$F0
#$FF,PIFP
PIEP,$F0
; choose pull-up device
; clear the Port P key-wake-up flags
; enable Port P interrupt
; enable key-wake-up interrupt globally
▲
7.13 Summary
The on-chip peripheral modules such as parallel ports, SCI, SPI, I2C, and CAN are implemented mainly to perform synchronization functions during I/O operations. Since these peripheral modules use the same power source as does the processor, an external interface chip may
still be needed when dealing with peripheral devices. The user needs to pay attention to electrical compatibility when interfacing the microcontroller with other peripheral chips.
Liquid crystal displays can display a lot of information while consuming very little power
and hence are the device of choice for displaying a large amount of information. For simple embedded systems, a character-based LCD is adequate. The Hitachi HD44780 is the most popular
character-based LCD controller. The LCD is often sold as a kit that consists of the LCD panel,
LCD controller, and other required electronic circuits.
Keypads and keyboards are the most important input devices for computer systems. The
input process for keypads and keyboards consists of three steps: key scanning, key debouncing,
and ASCII code lookup. The purpose of key debouncing is to make sure that one keystroke is
only recognized as one character. In addition, the debouncing technique can also be used to generate a single pulse with one key press.
A digital-to-analog converter converts a digital value into an electric quantity in the form of
current or voltage. Voltage output is more popular for the DAC. The DAC has many applications.
Examples include digital gain and offset adjustment, programmable voltage and current sources,
programmable attenuators, digital audio, and digital video. When choosing a DAC, the user needs
to consider the resolution, dynamic range, monotonicity, number of channels, and type of output.
A stepper motor is a digital motor in the sense that each step of the rotation rotates a fixed
number of degrees. It is most suitable for applications that require a high degree of positional
control, such as plotters, disk drives, magnetic tape drives, robot joints, and so on. The resolution of one step of a stepper motor can be as small as 0.72 and as large as 90 degrees. The simplest stepper motor has two pairs of coils.
Driving a stepper motor involves applying a series of voltages to the coils of the motor.
A subset of coils is energized at the same time to cause the motor to rotate one step. The pattern
of coils energized must be followed exactly for the motor to work correctly. The pattern will vary
depending on the mode used on the motor. A microcontroller can easily time the duration during
which the coil is energized and hence control the speed of the stepper motor in a precise manner.
Saving power is a major concern in most battery-powered embedded products. When the
user is not using an embedded system, the microcontroller should be switched to a low-power
mode. The HCS12 has two low-power modes: wait mode and stop mode. The HCS12 consumes
the least power in the stop mode. The stop (wait) mode can be entered by executing the stop
(wai) instruction. To facilitate the exit of the stop or wait mode, the HCS12 provides the keywake-up feature. Whenever the HCS12 is in one of the low-power modes and a selected signal
edge arrives at one of the key-wake-up port pins, an interrupt request will be generated and the
HCS12 will be waked up. The service for the key-wake-up interrupt is simply to clear the interrupt flag and resume the execution of the instruction following the wai or stop instruction.
359
7.14 ■ Exercises
The signal edge to wake up the microcontroller could be rising or falling. When the rising
edge is selected, the pull-down device should be enabled. When the falling edge is chosen, the
pull-up device should be enabled. Ports H, J, and P provide the key-wake-up capability.
7.14 Exercises
E7.1 Can the AS logic family drive the HCS12 input pins? Can the HCS12 output drive the AS
logic input?
E7.2 Can the ACT logic family drive the HCS12 input? Can the HCS12 output drive the ACT
logic input?
E7.3 Can the ABT logic family drive the HCS12 input? Can the HCS12 output drive the ABT
logic input?
E7.4 Can the BCT logic family drive the ABT logic input? Can the ABT logic drive the BCT
logic input?
E7.5 For the circuit shown in Figure 4.15a, what resistor value should be used to allow 5 mA
current flow through the LED assuming the voltage drop across the LED is 2.1 V when it is forward biased and the port pin output is 4.9 V?
E7.6 For the circuit shown in Figure 4.15a, what resistor value should be used to allow 5 mA
current flow through the LED assuming the voltage drop across the LED is 1.7 V when it is forward biased and the port pin output is 4.8 V?
E7.7 Write an assembly and a C program to display the following information in two rows in
the LCD connected to the demo board:
Date: 10 10 1952
Time: 10:20:10
E7.8 Calculate the period of the sawtooth waveform generated in Example 7.7. What are the
voltages that correspond to the digital values 20, 30, 50, 127, and 192?
E7.9 Write a program to generate a sine waveform from the VOUTA pin in Figure 7.36.
E7.10 Write a program to generate a 1-kHz periodic square wave from the VOUTA pin and a
2-kHz periodic square wave from the VOUTB pin in Figure 7.36.
E7.11 Write a program to generate a periodic square wave from the V OUTA pin and let the
frequency switch between 1 and 4 kHz every 5 s in Figure 7.36.
E7.12 Write a program to generate the waveform shown in Figure 7E.13 from the pin VOUTA in
Figure 7.36.
4V
2V
1 ms
1 ms
1 ms
1 ms
1 ms
Figure 7E.13 ■ A waveform to be generated
1 ms
360
Chapter 7 ■ Advanced Parallel I/O
E7.13 Write an assembly and a C program to generate a triangular waveform from the VOUTA
pin in Figure 7.36. What is the period of the waveform generated by your program?
E7.14 Write an instruction sequence to rotate the stepper motor shown in Figure 7.41 clockwise
one cycle using the full-step sequence.
E7.15 Write a C program to rotate the stepper motor one cycle in the counterclockwise direction using the half-step sequence by using 5 ms as the delay between two half steps. The circuit
connection of the stepper motor is shown in Figure 7.41.
E7.16 Write a C program to rotate the stepper motor shown in Figure 7.41 clockwise one cycle
using the full-step sequence with 5 ms as the delay between two steps.
E7.17 Write a C function to initialize the Port H key-wake-up function for the HCS12 that interrupts the microcontroller on the falling edge (automatic) of any Port H pin. Also write an interrupt
service routine for this interrupt that simply clears the key-wake-up interrupt flags and returns.
7.15 Lab Exercises and Assignments
L7.1 Write an assembly or C program to generate a periodic square wave with a frequency that
alternates between 1 and 2 kHz every 4 s using the DAC circuit shown in Figure 7.36.
L7.2 Connect a stepper motor circuit as shown in Figure 7.41. The stepper motor mini board
from Futurlec (www.futurlec.com) as shown in Figure L7.2 may work well with the circuit
given in Figure 7.40. Write a program to perform the following operations:
Step 1
Rotate the stepper motor using full-step sequence clockwise for 5 s.
Step 2
Rotate the stepper motor using full-step sequence counterclockwise for 5 s.
Step 3
Rotate the stepper motor using half-step sequence clockwise for 5 s.
Step 4
Rotate the stepper motor using half-step sequence counterclockwise for 5 s.
Experiment with intrastep delays of 2, 5, and 10 ms. The IND, INC, INB, and INA pins of the
stepper motor should be connected to PP3, PP2, PP1, and PP0 pins, respectively.
Figure L7.2 ■ A hobby stepper motor kit from Futurlec
7.15 ■ Lab Exercises and Assignments
361
L7.3 Keypad input. Use the circuit shown in Figure 7.7 to perform keypad input practice. The
procedure is as follows:
Step 1
Configure the upper four pins of Port P3 for output and the lower four pins of P3 for input.
Step 2
Initialize the LCD properly.
Step 3
Output the message “Enter an integer:” on the first row of the LCD. After seeing this
message, you enter a number on the keypad. Use the F key (or other key) to terminate
the number. Your program reads in the number, converts it into a binary, and saves it in a
buffer.
Step 4
Output the message “Enter another integer:” on the first row of the LCD. After seeing this
message, you enter a number on the keypad. Use the F key to terminate the number. Your
program reads in the number, converts it into a binary, and saves it in a buffer.
Step 5
Compute the gcd of these two numbers and display it on the LCD screen as follows:
The gcd of xxxx and
yyyy is zzzz.
where xxxx and yyyy are the numbers that you entered from the keypad and zzzz
is the gcd of these two numbers.
This page intentionally left blank
8
Timer Functions
8.1 Objectives
After completing this chapter, you should be
able to
• Explain the overall structure of the HCS12
timer system
• Use the input-capture function to measure the
duration of a pulse or the period of a square
wave
• Use the input-capture function to measure
the duty cycle of a waveform or the phase
difference of two waveforms having the same
frequency
• Use the input-capture function to measure the
frequency of a signal
• Use the output-compare function to create
a time delay or generate a pulse or periodic
waveform
• Use the forced output-compare function
• Use the pulse accumulator function to
measure the frequency of an unknown signal
and count the number of events occurring in
an interval
• Configure PWM channels to generate
waveforms of certain frequencies and duty
cycles
364
Chapter 8 ■ Timer Functions
8.2 Why Are Timer Functions Important?
There are many applications that require a dedicated timer system, including
• Time delay creation and measurement
• Period and pulse-width measurement
• Frequency measurement
• Event counting
• Arrival time comparison
• Time-of-day tracking
• Waveform generation
• Periodic interrupt generation
These applications will be very difficult to implement without a dedicated timer system. The
HCS12 implements a very complicated timer system to support the implementation of these
applications.
At the heart of the HCS12 timer system is the 16-bit timer counter (TCNT). This counter
can be started or stopped, as you like. One of the timer functions is called input-capture. The
input-capture function copies the contents of the 16-bit timer to a latch when the specified
event arrives. An event is represented by a signal edge, which could be a rising or a falling edge.
By capturing the timer value, many measurements can be made. Some of them include
• Pulse-width measurement
• Period measurement
• Duty cycle measurement
• Event arrival time recording
• Time reference
Another timer function is called output-compare. The output-compare circuit compares
the 16-bit timer value with that of the output-compare register in each clock cycle and performs the following operations when they are equal:
• (optionally) Triggers an action on a pin (set to high, set to low, or toggle its signal
level)
• Sets a flag in a register
• (optionally) Generates an interrupt request
The output-compare function is often used to generate a time delay, trigger an action at some
future time, and generate a digital waveform. The key to using the output-compare function is
to make a copy of the 16-bit timer, add a delay to it, and store the sum in an output-compare
register. The HCS12 has eight output-compare channels, which share the signal pins and registers with input-capture channels.
The third timer function is the pulse accumulator. This circuit is often used to count the
events arriving in a certain interval or measure the frequency of an unknown signal.
Certain HCS12 members (for example, A family, C family, and H family) implement a Standard Timer Module (TIM) that consists of eight channels of input-capture or output-compare
functions and a 16-bit pulse accumulator, PACA. Other HCS members add additional features
to the TIM module, and the resultant module is referred to as Enhanced Captured Timer Module (ECT). The design goal of the ECT is to support the requirements of automotive, process
control, and other applications that need additional timer features.
365
8.3 ■ Standard Timer Module
8.3 Standard Timer Module
The HCS12 standard timer module is illustrated in Figure 8.1. The block diagram in Figure 8.1
shows that the timer module has eight channels of input-capture and output-compare modules.
Each channel can be configured to perform input-capture or output-compare, but not both at the
same time. The 16-bit counter serves as the base timer for these eight channels. The 16-bit counter
needs a clock signal to operate. The clock signal is derived by dividing the E-clock by a prescaler.
Channel 0
E-clock
Prescaler
Input-capture
IOC0
Output-compare
Channel 1
Timer overflow
interrupt
16-bit counter
Input-capture
Output-compare
IOC1
Channel 2
Input-capture
IOC2
Output-compare
TC0 interrupt
Channel 3
TC1 interrupt
Input-capture
IOC3
Output-compare
TC2 interrupt
Registers
Channel 4
TC3 interrupt
Input-capture
IOC4
Output-compare
TC4 interrupt
Channel 5
TC5 interrupt
Input-capture
Output-compare
TC6 interrupt
IOC5
Channel 6
TC7 interrupt
PA overflow
interrupt
PA input
interrupt
Input-capture
IOC6
Output-compare
16-bit pulse
accumulator A
Channel 7
Input-capture
Output-compare
Figure 8.1 ■ HCS12 standard timer (TIM) block diagram
IOC7
366
Chapter 8 ■ Timer Functions
The timer module has a 16-bit pulse accumulator, which can be used to count the number
of events that have occurred or to measure the frequency of an unknown signal. The timer
module shares the use of Port T pins PT7,PT0. The signal pins IOC7,IOC0 correspond to
PT7,PT0. The 16-bit pulse accumulator shares the use of the PT7 pin. The PT7 pin is referred
to as the PAI pin when it is used as the pulse-accumulator input.
8.4 Timer Counter Register
The timer counter register (TCNT), a 16-bit register, is required for the functioning of all
input-capture and output-compare functions. The user must access this register in one access
rather than two separate accesses, to its high byte and low byte. Because the TCNT does not
stop during the access operation, the value accessed in a 16-bit read won’t be the same as two
separate accesses to its high byte and low byte.
There are three registers related to the operation of the TCNT. They are
1. Timer System Control register 1 (TSCR1)
2. Timer System Control register 2 (TSCR2)
3. Timer Interrupt Flag 2 register (TFLG2)
8.4.1 Timer System Control Register 1
The contents of the TSCR1 register are shown in Figure 8.2. The timer counter must be
enabled before it can count. Setting bit 7 of the TSCR1 register enables the TCNT to count (up).
Value
after reset
7
6
5
4
3
2
1
0
TEN
TSWAI
TSFRZ
TFFCA
0
0
0
0
0
0
0
0
0
0
0
0
TEN: timer enable bit
0 = disables timer; this can be used to save power consumption.
1 = allows timer to function normally.
TSWAI: timer stop while in wait mode bit
0 = allows timer to continue running during wait mode.
1 = disables timer when MCU is in wait mode.
TSFRZ: timer and modulus counter stop while in freeze mode
0 = allows timer and modulus counter to continue running while in
freeze mode.
1 = disables timer and modulus counter when MCU is in freeze mode.
TFFCA: timer fast flag clear all bits
0 = allows timer flag clearing to function normally.
1 = for TFLG1, a read from an input-capture or a write to
the output-compare channel causes the corresponding channel
flag, CnF, to be cleared. For TFLG2, any access to the TCNT
register clears the TOF flag. Any access to the PACN3 and
PACN2 registers clears the PAOVF and PAIF flags in the PAFLG
register. Any access to the PACN1 and PACN0 registers clears the
PBOVF flag in the PBFLG register.
Figure 8.2 ■ Timer System Control register 1 (TSCR1)
367
8.4 ■ Timer Counter Register
The timer counter can be stopped (by setting bit 6 of TSCR1) during the wait mode to save more
power. If the timer function is not needed, it can also be stopped during the freeze mode.
All timer interrupt flags can be cleared by writing a 1 to them. However, there is a faster
way to clear timer flags. When bit 4 of the TSCR1 register is set to 1, a read from or a write to
the appropriate TC register will clear the corresponding flag in the TFLG1 register; any access
to the TCNT register will clear the TOF flag, and any access to the PACNT register will clear
the PAOVF and PAIF flags in the PAFLG register. This feature reduces the software overhead in
a separate clear sequence.
8.4.2 Timer System Control Register 2 (TSCR2)
Timer system control register 2 is another register that controls the operation of the timer
counter. Its contents are shown in Figure 8.3. An interrupt will be requested when the TCNT
overflows (when TCNT rolls over from $FFFF to $0000) and bit 7 of TSCR2 is set to 1.
The timer counter needs a clock signal to operate. The clock input to the timer counter
could be the E-clock prescaled by a factor or the PAI pin input prescaled by a factor. The user
has the option to choose the prescale factor. When bit 3 and bit 2 of the PACTL register are 00,
the clock to the TCNT is the E-clock prescaled by a factor. The lowest 3 bits of the TSCR2
register specify the prescale factor for the E-clock, as shown in Table 8.1.
Value
after reset
7
6
5
4
3
2
1
0
TOI
0
0
0
TCRE
PR2
PR1
PR0
0
0
0
0
0
0
0
0
TOI: timer overflow interrupt enable bit
0 = interrupt inhibited.
1 = interrupt requested when TOF flag is set.
TCRE: timer counter reset enable bit
0 = counter reset inhibited and counter free runs.
1 = counter reset by a successful output-compare 7.
If TC7 = $0000 and TCRE = 1, TCNT stays at $0000
continuously. If TC7 = $FFFF and TCRE = 1, TOF will never
be set when TCNT rolls over from $FFFF to $0000.
Figure 8.3 ■ Timer System Control register 2
PR2
PR1
PR0
Prescale Factor
0
0
0
1
0
0
1
2
0
1
0
4
0
1
1
8
1
0
0
16
1
0
1
32
1
1
0
64
1
1
1
128
Table 8.1 ■ Timer counter prescale factor
368
Chapter 8 ■ Timer Functions
8.4.3 Timer Interrupt Flag 2 Register (TFLG2)
Only bit 7 (TOF) of this register is implemented. When TCNT rolls over from $FFFF to
$0000, bit 7 of this register is set to 1. This flag can be cleared by writing a 1 to it.
8.5 Input-Capture Function
Some applications need to know the arrival time of events. In a computer, physical time is
represented by the count value in a counter, and the occurrence of an event is represented by a
signal edge (either the rising or falling edge). The time when an event occurs can be recorded by
latching the count value when a signal edge arrives, as illustrated in Figure 8.4.
Rising edge
Falling edge
or
Figure 8.4 ■ Events presented by signal edges
The HCS12 timer system has eight input-capture channels that implement this operation.
Each input-capture channel includes a 16-bit input-capture register, an input pin, input edgedetection logic, and an interrupt generation circuit. In the HCS12, physical time is represented
by the count in the TCNT.
8.5.1 Input-Capture/Output-Compare Selection
Since input-capture and output-compare functions share signal pins and registers, they cannot be enabled simultaneously. When one is enabled, the other is disabled. The selection is done
by the Timer Input-Capture/Output-Compare Select register (TIOS), as shown in Figure 8.5.
Value
after reset
7
6
5
4
3
2
1
0
IOS7
IOS6
IOS5
IOS4
IOS3
IOS2
IOS1
IOS0
0
0
0
0
0
0
0
0
IOS[7:0]: input-capture or output-compare channel configuration bits
0 = the corresponding channel acts as an input-capture.
1 = the corresponding channel acts as an output-compare.
Figure 8.5 ■ Timer Input-Capture/Output-Compare Select register (TIOS)
Example 8.1
▼
Write an instruction sequence to enable the output-compare channels 7,4 and input-capture
channels 3,0.
Solution: The following instruction sequence will achieve the desired configuration:
#include
“c:\miniide\hcs12.inc”
...
movb
#$F0,TIOS
▲
369
8.5 ■ Input-Capture Function
8.5.2 Pins for Input-Capture
Port T has eight signal pins (PT7,PT0) that can be used as input-capture/output-compare or
general I/O pins. PT3,PT0 can also be used as the pulse accumulator input (PA3,PA0). The user
must make sure that the PT3,PT0 pins are enabled for one and only one of these three functions
(OCn, ICn, and PAn). When these pins are not used for timer functions, they can also be used as
general-purpose I/O pins. When being used as general I/O pins, the user must use the DDRT register to configure their direction (input or output). When a Port T pin is used as a general-purpose
I/O pin, its value is reflected in the corresponding bit in the PTT register.
8.5.3 Registers Associated with Input-Capture
The user needs to specify what signal edge to capture. The edge selection is done via the
Timer Control registers 3 and 4, as shown in Figure 8.6.
Reset:
7
6
5
4
3
2
1
0
EDG7B
EDG7A
EDG6B
EDG6A
EDG5B
EDG5A
EDG4B
EDG4A
0
0
0
0
0
0
0
0
(a) Timer control register 3 (TCTL3)
Reset:
7
6
5
4
3
2
1
0
EDG3B
EDG3A
EDG2B
EDG2A
EDG1B
EDG1A
EDG0B
EDG0A
0
0
0
0
0
0
0
0
(b) Timer control register 4 (TCTL4)
EDGnB EDGnA: Edge configuration (n = 0, . . . , 7)
0
0 : capture disabled
0
1 : capture on rising edges only
1
0 : capture on falling edges only
1
1 : capture on both edges
Figure 8.6 ■ Timer Control registers 3 and 4
When an input-capture channel is selected but capture is disabled, the associated pin can
be used as a general-purpose I/O pin.
An input-capture channel can optionally generate an interrupt request on the arrival of a
selected edge if it is enabled. The enabling of an interrupt is controlled by the Timer Interrupt
Enable register (TIE). The enabling of input-capture 7 through input-capture 0 interrupt is controlled by bit 7 through bit 0 of TIE. When a selected edge arrives at the input-capture pin, the
corresponding flag in the Timer Flag register 1 (TFLG1) is set. The contents of TIE and TFLG1
are shown in Figures 8.7 and 8.8, respectively.
Reset:
7
6
5
4
3
2
1
0
C7I
C6I
C5I
C4I
C3I
C2I
C1I
C0I
0
0
0
0
0
0
0
0
C7I:C0I: input-capture/output-compare interrupt enable bits
0 = interrupt disabled
1 = interrupt enabled
Figure 8.7 ■ Timer Interrupt Enable register (TIE)
370
Chapter 8 ■ Timer Functions
Reset:
7
6
5
4
3
2
1
0
C7F
C6F
C5F
C4F
C3F
C2F
C1F
C0F
0
0
0
0
0
0
CnF: input-capture/output-compare interrupt flag bits
0 = interrupt condition has not occurred.
1 = interrupt condition has occurred.
0
0
Figure 8.8 ■ Timer Interrupt Flag register 1 (TFLG1)
To clear a flag in the TFLG1 register, write a 1 to it. However, there is a better way to clear the
flag that incurs less overhead. Setting bit 4 (TFFCA) of the TSCR1 register allows clearing a flag by
reading the corresponding input-capture register or writing a new value into the output-compare
register. This operation is needed for the normal operation of the input-capture or output-compare
function.
Each input-capture channel has a 16-bit register (TCx, x 5 0 to 7) to hold the count value
when the selected signal edge arrives at the pin. This register is also used as the output-compare
register when the output-compare function is selected instead.
8.5.4 Input-Capture Applications
There are many applications for the input-capture function. Examples include the following:
• Event arrival-time recording. Some applications, for example, swimming
competitions, need to compare the arrival times of several different swimmers.
The input-capture function is very suitable for this application. The number of
events that can be compared is limited by the number of input-capture channels.
• Period measurement. To measure the period of an unknown signal, the inputcapture function should be configured to capture the timer values corresponding
to two consecutive rising or falling edges, as illustrated in Figure 8.9.
• Pulse-width measurement. To measure the width of a pulse, two adjacent rising
and falling edges are captured, as shown in Figure 8.10.
One period
(a) Capture two rising edges
One period
(b) Capture two falling edges
Figure 8.9 ■ Period measurement by capturing two consecutive edges
371
8.5 ■ Input-Capture Function
Pulse width
Rising edge
Falling edge
Figure 8.10 ■ Pulse-width measurement using input-capture
• Interrupt generation. All input-capture pins can serve as edge-sensitive interrupt
sources. Once enabled, interrupts will be generated on the selected edge(s).
• Event counting. An event can be represented by a signal edge. An input-capture
channel can be used in conjunction with an output-compare function to count the
number of events that occur during an interval. An event counter can be set up
and incremented by the input-capture interrupt service routine. This application is
illustrated in Figure 8.11.
e1
e2
e3
e4
...
ei
...
ej
Start of
interval
End of
interval
Figure 8.11 ■ Using an input-capture function for event counting
• Time reference. In this application, an input-capture function is used in conjunction with an output-compare function. For example, if the designer wants to
activate an output signal a certain number of clock cycles after detecting an input
event, the input-capture function would be used to record the time at which the
edge is detected. A number corresponding to the desired delay would be added to
this captured value and stored to an output-compare register. This application is
illustrated in Figure 8.12.
Time t0
Time t0 + delay
Time of reference
(set up by signal edge)
Time to activate
output signal
(set up by output-compare)
Figure 8.12 ■ A time-reference application
• Duty cycle measurement. The duty cycle is the percent of time that the signal is
high within a period in a periodic digital signal. The measurement of the duty cycle
is illustrated in Figure 8.13.
372
Chapter 8 ■ Timer Functions
T
ΔT
Duty cycle =
ΔT
× 100%
T
Figure 8.13 ■ Definition of duty cycle
• Phase difference measurement. The phase difference is defined as the difference of
arrival times (in percentage of a period) of two signals that have the same frequency
but do not coincide in their rising and falling edges. The definition of the phase
difference is illustrated in Figure 8.14.
T
Signal S1
ΔT
Signal S2
Phase difference =
ΔT
× 360°
T
Figure 8.14 ■ Phase difference definition for two signals
The unit used in most of the measurements is the number of clock cycles. When it is desirable, the unit should be converted into an appropriate unit, such as seconds.
Example 8.2
▼
Period measurement. Use the input-capture channel 0 to measure the period of an unknown
signal. The period is known to be shorter than 128 ms. Assume that the E-clock frequency is
24 MHz. Use the number of clock cycles as the unit of the period.
Solution: Since the input-capture register is 16-bit, the longest period of the signal that can be
measured with the prescaler to TCNT set to 1 is
216 4 24 MHz 5 2.73 ms
To measure a period that is equal to 128 ms, we have two options.
1. Set the prescale factor to 1 and keep track of the number of times that the timer
counter overflows.
2. Set the prescale factor to 64 and do not keep track of the number of times that the
timer counter overflows.
In this example, we adopt the second approach to make the programming easier. The result
of this measurement is in number of clock cycles, and the period of each clock cycle is 2.67 μs.
373
8.5 ■ Input-Capture Function
The circuit connection for the period measurement is shown in Figure 8.15, and the logic flow
for the period measurement is shown in Figure 8.16.
68HC12
PT0
1 period
Figure 8.15 ■ Period measurement signal connection
Start
Choose to capture the rising edge
Set the timer counter prescale factor to 64
Enable the timer counter
Clear the C0F flag
No
C0F = 1?
Yes
Save the captured first edge
Clear the C0F flag
No
C0F = 1?
Yes
Take the difference of the second and
the first captured edges
Stop
Figure 8.16 ■ Logic flow of period measurement
The assembly program that measures the period using this method is as follows:
#include
edge_1st
period
“c:\miniide\hcs12.inc”
org
$1000
rmb
2
rmb
2
; memory to hold the first edge
; memory to store the period
374
Chapter 8 ■ Timer Functions
org
movb
bclr
movb
movb
movb
brclr
ldd
std
brclr
ldd
subd
std
swi
end
$1500
#$90,TSCR1
TIOS,IOS0
#$06,TSCR2
#$01,TCTL4
#C0F,TFLG1
TFLG1,C0F,*
TC0
edge_1st
TFLG1,C0F,*
TC0
edge_1st
period
; enable timer counter and enable fast timer flag clear
; enable input-capture 0
; disable TCNT overflow interrupt, set prescaler to 64
; capture the rising edge of PT0 signal
; clear the C0F flag
; wait for the arrival of the first rising edge
; save the first edge and clear the C0F flag
; wait for the arrival of the second edge
; compute the period
The C language version of the program is as follows:
#include “c:\cwHCS12\include\hcs12.h”
void main(void)
{
unsigned int edge1, period;
TSCR1 5 0x90;
TIOS
&5 ,IOS0;
TSCR2 5 0x06;
TCTL4
5 0x01;
TFLG1 5 0x01;
while (!(TFLG1 & C0F));
edge1 5 TC0;
while (!(TFLG1 & C0F));
period 5 TC0 2 edge1;
while(1);
// enable timer counter, enable fast flag clear
// enable input-capture 0
// disable TCNT overflow interrupt, set prescaler to 64
// capture the rising edge of the PT0 pin
// clear the C0F flag */
// wait for the arrival of the first rising edge
// save the first captured edge and clear the C0F flag
// wait for the arrival of the second rising edge
}
▲
Example 8.3
▼
Write a program to measure the pulse width of a signal connected to the PT0 pin. Assume
that the E-clock frequency is 24 MHz.
Solution: We set the prescale factor to 32 and use the clock cycle as the unit of measurement.
The period of one clock cycle is 1.33 μs. Since the pulse width could be much longer than 216
clock cycles, we need to keep track of the number of times that the TCNT overflows. Each
TCNT overflow adds 216 clock cycles to the pulse width.
Let
ovcnt
5 TCNT counter overflow count
diff
5 the difference of two consecutive edges
edge1 5 the captured time of the first edge
edge2 5 the captured time of the second edge
375
8.5 ■ Input-Capture Function
The pulse width can be calculated by the following equations:
Case 1
edge2 $ edge1
pulse width 5 ovcnt 3 216 1 diff
Case 2
edge2 , edge 1
pulse width 5 (ovcnt 2 1) 3 216 1 diff
In case 2, the timer overflows at least once even if the pulse width is shorter than 216 – 1 clock
cycles. Therefore, we need to subtract 1 from the timer overflow count in order to get the
correct result. The pulse width is obtained by appending the difference of the two captured
edges to the TCNT overflow count. The logic flow of the program is shown in Figure 8.17.
Start
overflow ← 0
Set up to capture the rising edge
Disable all interrupts
No
C0F = 1?
Yes
Timer overflow interrupt
service routine
Clear timer overflow flag
Enable main timer overflow interrupt
No
C0F = 1?
TO
Clear C0F flag
Save the first captured edge
V
in
te
rru
pt
Clear TOF flag
overflow ← overflow + 1
Execute the RTI instruction
Retu
rn fro
m int
errup
t
Yes
Compute the difference of two edges
No
Is second edge smaller?
Yes
overflow ← overflow – 1
Combine the results
Stop
Figure 8.17 ■ Logic flow for measuring pulse width of slow signals
376
Chapter 8 ■ Timer Functions
The assembly program that implements this algorithm is as follows:
#include “c:\miniide\hcs12.inc”
org
$1000
edge1 ds.w
1
ovcnt
ds.w
1
PW
ds.w
1
org
$1500
movw
#tov_isr,UserTimerOvf; set up TCNT overflow interrupt vector
lds
#$1500
; set up stack pointer
movw
#0,ovcnt
movb
#$90,TSCR1
; enable TCNT and fast timer flag clear
movb
#$05,TSCR2
; disable TCNT interrupt, set prescaler to 32
bclr
TIOS,IOS0
; select IC0
movb
#$01,TCTL4
; capture the rising edge
movb
#$01,TFLG1
; clear the C0F flag
brclr
TFLG1,C0F,*
; wait for the first rising edge
movw
TC0,edge1
; save the first edge, clear the C0F flag
movb
#$80,TFLG2
; clear the TOF flag
bset
TSCR2,$80
; enable TCNT overflow interrupt
cli
;
“
movb
#$02,TCTL4
; capture the falling edge on PT0 pin
brclr
TFLG1,C0F,*
; wait for the arrival of the falling edge
ldd
TC0
subd
edge1
std
PW
bcc
next
; is the second edge smaller?
ldx
ovcnt
; second edge is smaller, so decrement
dex
; overflow count by 1
stx
ovcnt
;
“
next
swi
tov_isr movb
#$80,TFLG2
; clear the TOF flag
ldx
ovcnt
inx
stx
ovcnt
rti
end
The C language version of this program to be compiled by the CodeWarrior C compiler is as
follows:
#include
unsigned
unsigned
void main(void)
{
overflow
TSCR1
TSCR2
TIOS
“c:\cwHCS12\include\hcs12.h”
diff, edge1, overflow;
long pulse_width;
5 0;
5 0x90;
5 0x05;
&5 ,IOS0;
// enable timer and fast flag clear
// set prescaler to 32, no timer overflow interrupt
// select input-capture 0
377
8.6 ■ Output-Compare Function
TCTL4
5 0x01;
// prepare to capture the rising edge
TFLG1
5 C0F;
// clear C0F flag
while(!(TFLG1 & C0F));
// wait for the arrival of the rising edge
TFLG2
5 0x80;
// clear TOF flag
TSCR2
|5 0x80;
// enable TCNT overflow interrupt
asm(“cli”);
edge1
5 TC0;
// save the first edge
TCTL4
5 0x02;
// prepare to capture the falling edge
while (!(TFLG1 & C0F));
// wait for the arrival of the falling edge
diff
5 TC0 2 edge1;
if (TC0 < edge1)
overflow
25 1;
pulse_width 5 (long)overflow * 65536u 1 (long)diff;
while (1);
}
interrupt void tovisr(void)
{
TFLG2
5 0x80;
overflow11;
}
/* clear the TOF flag */
The timer overflow vector is set up by using the following function:
extern void near tovisr(void);
#pragma CODE_SEG __NEAR_SEG NON_BANKED
#pragma CODE_SEG DEFAULT
// Change code section to DEFAULT.
typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFFDE 5 {
tovisr
};
The required modification for setting up an interrupt vector for the ICC12 C compiler is
similar to Example 6.6.
▲
8.6 Output-Compare Function
The HCS12 has eight output-compare channels. Each channel consists of
• A 16-bit comparator
• A 16-bit compare register TCx, x 5 0..7 (also used as input-capture register)
• An output action pin (PTx—can be pulled up to high, pulled down to low, or
toggled)
• An interrupt request circuit
• A forced-compared function (CFORCx)
• Control logic
378
Chapter 8 ■ Timer Functions
8.6.1 Operation of the Output-Compare Function
One of the major applications of an output-compare function is performing an action at a
specific time in the future (when the 16-bit timer counter reaches a specific value). The action
might be to toggle a signal, turn on a switch, turn off a valve, and so on. To use an outputcompare function, the user
1. Makes a copy of the current contents of the TCNT register
2. Adds to this copy a value that can generate the desired delay
3. Stores the sum into an output-compare register (TCx)
The delay value to be added is dependent on the prescaler to TCNT. The prescaler needs to be set
before the delay value is chosen. The user has the option of specifying the action to be activated on
the selected output-compare pin by programming the TCTL1 and TCTL2 registers. The comparator compares the value of the TCNT and that of the specified output-compare register (TCx) in
every clock cycle (the clock input to TCNT). If they are equal, the specified action on the outputcompare pin is activated and the associated status bit in TFLG1 is set to 1. An interrupt request is
generated if it is enabled. The 16-bit output-compare register can be read and written any time.
8.6.2 Registers Related to the Output-Compare Function
The actions that can be activated on an output-compare pin are
• Pull up to high
• Pull down to low
• Toggle
The action of an OC pin can be selected by programming the TCTL1 and TCTL2 registers as
shown in Figure 8.18. When either OMn or OLn is 1, the pin associated with OCn becomes an
output tied to OCn regardless of the state of the associated DDRT bit.
Reset:
7
6
5
4
3
2
1
0
OM7
OL7
OM6
OL6
OM5
OL5
OM4
OL4
0
0
0
0
0
0
0
0
(a) TCTL1 register
Reset:
7
6
5
4
3
2
1
0
OM3
OL3
OM2
OL2
OM1
OL1
OM0
OL0
0
0
0
0
0
0
0
0
(b) TCTL2 register
Read: anytime
Write: anytime
OMn OLn : output level (n = 0, . . . , 7)
0
0
No action (timer disconnected from output pin)
0
1
Toggle PTn pin
1
0
Clear PTn pin to 0
1
1
Set PTn pin to high
Figure 8.18 ■ Timer Control registers 1 and 2 (TCTL1 and TCTL2)
379
8.6 ■ Output-Compare Function
A successful compare sets the corresponding flag bit in the 8-bit TFLG1 register. An
interrupt is generated if it is enabled. An output-compare interrupt is enabled by setting the
corresponding bit in the TIE register. The same bit enables either the input-capture or outputcompare interrupt, depending on which one is selected.
The OC7 channel has the option to reset TCNT when the TC7 register matches the TCNT.
This option is enabled by setting the TCRE bit of the TSCR2 register. This feature makes the
TCNT a programmable modulus counter.
8.6.3 Applications of the Output-Compare Function
An output-compare function can be programmed to perform a variety of functions.
Generation of a single pulse, a square wave, and a specific delay are among the most popular
applications.
To generate a periodic square waveform, the user may use the output-compare function
to continuously toggle the selected Port T pin with appropriate delay added in each outputcompare operation. The first output-compare operation must be started by the main function
whereas the remaining output-compare operations may be performed by the main function
or the output-compare interrupt service routine depending on the approach. The following
example uses the interrupt-driven approach.
Example 8.4
▼
Generate an active high 1-kHz digital waveform with a 30 percent duty cycle from the PT5
pin. Use the interrupt-driven method to check the success of the output-compare operation.
The frequency of the E-clock is 24 MHz.
Solution: An active high 1-kHz waveform with a 30 percent duty cycle is shown in Figure 8.19.
300 µs
700 µs
Figure 8.19 ■ A 1-kHz 30 percent duty cycle waveform
The logic flow of this problem is illustrated in Figure 8.20. Suppose we set the prescale
factor to 8 so that the period of the clock input to TCNT is set to 1/3 μs. Then the intervals of the PT0 signal to be high and low in one period would be 900 and 2100 clock cycles,
respectively.
The algorithm first pulls the PT5 pin to high in a very short period of time using the outputcompare operation and then changes the pin action to toggle and enable the OC5 interrupt. The
OC5 interrupt simply starts the next output-compare operation.
Chapter 8 ■ Timer Functions
Start
1. Set OC5 pin action to pull
high
2. Clear C5F flag
3. Start OC5 operation with
delay value equal to 10
C5F = 0?
Pull the PT5 pin to high
quickly
No
Yes
No
pt
er
ru
1. Set OC5 pin action to toggle
2. Set HiorLo flag to 0
3. Start a new OC5 operation
with delay equal to HiCnt
HiorLo = 0?
int
380
Yes
1. Start a new OC5 operation
With LoCnt as delay
2. HiorLo = 1
t
rup
Continue to perform other
operations
r
etu
R
rom
er
int
nf
1. Start a new OC5 operation
With HiCnt as delay
2. HiorLo = 0
Return from interrupt
Figure 8.20 ■ The program logic flow for digital waveform generation
The following assembly program implements this algorithm using the OC5 function:
#include
HiCnt equ
LoCnt equ
org
HIorLO ds.b
org
lds
movw
movb
movb
bset
movb
ldd
addd
std
brclr
ldd
addd
std
“c:\miniide\hcs12. inc”
1200
1800
$1000
1
$1500
#$1500
#OC5ISR,UserTimerCh5
#$90,TSCR1
#$03,TSCR2
TIOS,BIT5
#$0C,TCTL1
TCNT
#10
TC5
TFLG1,C5F,*
TC5
#HiCnt
TC5
; delay count for high interval of the waveform
; delay count for low interval of the waveform
; flag to select HiCnt (1) or LoCnt (0)
; establish stack pointer
; set up OC0 interrupt vector
; enable TCNT and fast timer flag clear
; set TCNT clock prescaler to 8
; enable OC5
; configure the OC5 pin action to pull high
; start OC5 with delay count equal to 10 and
; pin action to pull high
; wait until C5F is set to 1 and PT5 pin pulled high
; start another OC5 operation with
; delay count set to HiCnt
; “
381
8.6 ■ Output-Compare Function
movb
#$04,TCTL1
; change pin action to toggle
clr
HIorLO
; LoCnt will be the delay count next time
bra
$
; prepare to perform other operations
; *********************************************************************************
; OC5 interrupt service routine starts a new OC5 operation with delay count equal to
; HiCnt or LoCnt depending on whether HIorLO flag is 1 or 0. After starting a new OC5
; operation, toggle HIorLO flag.
; *********************************************************************************
OC5ISR
addLow
tst
beq
ldd
addd
std
clr
rti
ldd
addd
std
movb
rti
end
HIorLO
addLow
TC5
#HiCnt
TC5
HIorLO
; which delay count should be added?
; if 0 then select LoCnt
; select HiCnt as the delay count for
; the new OC5 operation
;
“
; toggle HIorLo flag
TC5
#LoCnt
TC5
#1,HIorLO
; select LoCnt as the delay count for
; the new OC5 operation
;
“
; toggle HIorLO flag
The C language version of the program is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#define HiCnt 1200
#define LoCnt 1800
char HiorLo;
void main (void)
{
TSCR1
5 0x90;
TSCR2
5 0x03;
TIOS
|5 OC5;
TCTL1
5 0x0C;
TFLG1
5 0xFF;
TC5
5 TCNT 1 10;
while(TFLG1 & C5F);
TCTL1
5 0x04;
TC5
15 HiCnt;
HiorLo
5 0;
TIE
5 0x20;
asm(“cli”);
while(1);
}
interrupt void oc5ISR (void)
{
if(HiorLo){
TC5
15 HiCnt;
HiorLo
5 0;
}
// enable TCNT and fast timer flag clear
// disable TCNT interrupt, set prescaler to 8
// enable OC5 function
// set OC5 action to pull high
// clear all CxF flags
// wait until C5F is set
// set OC5 pin action to toggle
// start an new OC5 operation
// add LoCnt for the next OC5 operation
// enable OC5 interrupt locally
// enable interrupt globally
382
Chapter 8 ■ Timer Functions
else{
TC5 15 LoCnt;
HiorLo 5 1;
}
}
The OC5 interrupt vector is set up using the following function (in vectors.c):
extern void near oc5ISR(void);
#pragma CODE_SEG __NEAR_SEG NON_BANKED
#pragma CODE_SEG DEFAULT
// Change code section to DEFAULT.
typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFFE4 5 {
oc5ISR
};
Using the OC5 interrupt to restart the subsequent OC5 operations allows the HCS12 to
continue to perform other operations.
▲
Example 8.5
▼
Write a function to generate a time delay that is a multiple of 1 ms. Assume that the
E-clock frequency is 24 MHz. The number of milliseconds is passed in Y. Also write an instruction sequence to test this function.
Solution: There are many ways to create a 1-ms time delay using the output-compare function.
One method is
• Set the prescaler to TCNT to 8.
• Perform the number of output-compare operations given in Y with each operation
creating a 1-ms time delay. The number to be added to the TC0 register is 3000
(3000 3 8 4 24,000,000 5 1 ms).
The corresponding assembly function is as follows:
delayby1ms
again0
wait_lp0
pshd
movb
movb
bset
ldd
addd
std
brclr
ldd
dbne
puld
rts
#$90,TSCR1
#$03,TSCR2
TIOS,OC0
TCNT
#3000
TC0
TFLG1,OC0,wait_lp0
TC0
y,again0
; enable TCNT and fast flag clear
; configure prescaler to 8
; enable OC0
; start an output-compare operation
; with 1-ms time delay
The C language version of the function and its test program are as follows:
#include “c:\cwHSC12\include\hcs12.h”
void delayby1ms(int k);
void main (void)
8.6 ■ Output-Compare Function
383
{
unsigned char count;
DDRB 5 0xFF;
/* configure Port b for output */
count 5 0;
PTB 5 count;
while(1) {
delayby1ms(200);
count11;
PTB 5 count;
}
}
void delayby1ms(int k)
{
int ix;
TSCR1 5 0x90;
/* enable TCNT and fast timer flag clear */
TSCR2 5 0x03;
/* disable timer interrupt, set prescaler to 8*/
TIOS |5 OC0;
/* enable OC0 */
TC0 5 TCNT 1 3000;
for(ix 5 0; ix < k; ix11) {
while(!(TFLG1 & C0F));
TC0 15 3000;
}
TIOS &5 ,OC0; /* disable OC0 */
}
▲
Example 8.6
▼
Generate a sequence of pulses using an OC function. Write a program to generate a number
of pulses with the specified high interval duration (12 ms) and low interval duration (8 ms). Use
the interrupt-driven approach so that the CPU can perform other operations.
Solution: Let the number of pulses to be generated, the high interval duration, low interval
duration, the flag to select DelayHi or DelayLo, and number of OC0 operations to be performed
be NN, DelayHi, DelayLo, HiorLo, and pcnt, respectively.
The algorithm for generating a sequence of pulses with the specified duration of high interval and low interval is as follows:
Step 1:
Pull the PT0 pin high quickly using the OC0 operation.
Step 2
Change the OC0 pin action to toggle. Start the next OC0 operation with delay equal to
DelayHi.
Step 3
pcnt R 2 * NN 2 1. HiorLo R 0.
Step 4
Enable OC0 interrupt.
384
Chapter 8 ■ Timer Functions
Step 5
The main program continues to perform other operations.
The interrupt service routine of OC0 performs the following operations:
If (HiorLo 55 1)
Start an OC0 operation using DelayHi as the delay count
HiorLo ← 0
Return from interrupt
Else
Start an OC0 operation using DelayLo as the delay count
HiorLo ← 1
Return from interrupt.
The assembly program that implements this algorithm is as follows:
#include
DelayHi equ
DelayLo equ
NN
equ
org
pcount ds.b
HiorLo ds.b
org
lds
movw
movb
movb
bset
movb
movb
ldd
addd
std
brclr
movb
ldd
addd
std
movb
clr
bra
oc0ISR ldaa
beq
ldd
addd
std
movb
bra
pulseLo ldd
addd
std
movb
“c:\miniide\hcs12.inc”
18000
12000
10
$1000
1
1
$1500
#$1500
#oc0ISR,UserTimerCh0
#$90,TSCR1
#$04,TSCR2
TIOS,OC0
#C0F,TFLG1
#$03,TCTL2
TCNT
#12
TC0
TFLG1,C0F,*
#$01,TCTL2
TC0
#DelayHi
TC0
2*NN-1,pcount
HiorLo
$
HiorLo
pulseLo
TC0
#DelayHi
TC0
#0,HiorLo
decCnt
TC0
#DelayLo
TC0
#1,HiorLo
; pulse high interval duration
; pulse low interval duration
; number of pulses to be created
; number of OC0 operations remaining to be performed
; flag to choose DelayHi(1) or DelayLo(0)
; set up OC0 interrupt vector
; enable OC0
; clear C0F flag
; set OC0 pin action to pull high
; use OC0 operation to pull TC0 pin high
; quickly
; “
; wait until C0F flag is set
; set OC0 pin action to toggle
; start next OC0 operation with
; delay set to DelayHi
; “
; prepare to perform pcount OC0 operations
; next OC0 operation use DelayLo as delay
; check the flag to choose delay count
; if flag is 0, then go and use DelayLo
; start an OC0 operation and use
; DelayHi as delay count
; “
; toggle the flag
; start an OC0 operation and use
; DelayLo as delay count
; “
; toggle the flag
385
8.6 ■ Output-Compare Function
decCnt
quit
dec
bne
movb
bclr
rti
end
pcount
quit
#0,TIE
TIOS,$01
; disable OC0 and
; its interrupt
The C language version of the program to be compiled by CodeWarrior is as follows:
#include
“c:\cwHCS12\include\hcs12.h”
#include
“c:\cwHCS12\include\SetClk.h”
#define
DelayHi 18000
// high time of the pulses to be created
#define
DelayLo 12000
// low time of the pulses to be created
#define
NN
10
// number of pulses to be created
int
pcnt;
// pulse count
char HiorLo;
// flag to choose
void main(void)
{
SetClk8();
TSCR1
5 0x90;
// enable TCNT and faster timer flag clear
TSCR2
5 0x04;
// set TCNT clock input prescaler to 16
TFLG1
5 C0F;
// clear C0F flag
TIOS
|5 OC0;
// enable OC0
TCTL2
5 0x03;
// set OC0 pin action to be pull high
TC0
5 TCNT 1 16;
// pull PT0 pin high quickly
while(!(TFLG1 & C0F));
// “
pcnt
5 2 * NN 2 1;
// prepare to create NN pulses
(need to toggle 2*NN 2 1 times)
TCTL2
5 0x01;
// set OC0 pin action to be toggle
TC0
15 DelayHi;
// start the second OC0 operation
HiorLo
5 0;
// next time use DelayLo as delay count of OC0 operation
TIE
|5 C0I;
// enable TC0 interrupt
asm(“cli”);
// “
while (1);
// do nothing or do something else
}
interrupt void tc0ISR(void) {
if(HiorLo){
TC0
15 DelayHi;
HiorLo
5 0;
} else {
TC0
15 DelayLo;
HiorLo
5 1;
}
pcnt22;
if(pcnt
55 0){
TIE
5 0;
// disable OC0 interrupt
TIOS
&5 0xFE;
// disable OC0
}
}
▲
386
Chapter 8 ■ Timer Functions
8.6.4 Making Sound Using the Output-Compare Function
Using the output-compare function to make sound is easy. A sound can be made by creating
a digital waveform of appropriate frequency and using it to drive a speaker or a buzzer. A small
speaker of 8-Ω resistance that consumes between 10 and 20 mW can produce clear sound. The
next example illustrates how to use an output-compare channel to generate a siren.
Example 8.7
▼
Describe the circuit for making a sound and write a program that uses an output-compare
channel to generate a siren that oscillates between 300 and 1200 Hz.
Solution: A simple 8-mW speaker (or a buzzer on a demo board) has two terminals: one terminal
is for signal input, whereas the other terminal is for ground connection. The circuit connection
for siren generation is shown in Figure 8.21.
HCS12DP256
3.3 µF
PT5
Buzzer
Figure 8.21 ■ Circuit connection for a buzzer
The algorithm for generating the siren is as follows:
Step 1
Enable an appropriate OC channel (OC5 in this example) to drive the speaker circuit.
Step 2
Start an output-compare operation and enable its interrupt with a delay count equal to
half the period of the siren.
Step 3
Wait a certain amount of time (say half a second). During the waiting period, interrupts
will be requested by the output-compare match many times. The interrupt service routine
simply starts the next output-compare operation and then returns.
Step 4
At the end of the delay, choose a different delay count for the output-compare operation so
the siren sound with a different frequency can be generated.
Step 5
Wait for the same amount of time as in step 3. Again, the interrupt caused by the outputcompare match will be requested many times. At the end of the delay, switch back to the
delay count used in step 2.
Step 6
Go to step 2.
387
8.6 ■ Output-Compare Function
The assembly program that implements this algorithm for an HCS12DP256 running with
a 24-MHz E-clock is as follows:
#include “c:\miniide\hcs12.inc”
hi_freq equ
1250
; delay count for 1200 Hz (with 1:8 prescaler)
lo_freq equ
5000
; delay count for 300 Hz (with 1:8 prescaler)
toggle equ
$04
; value to toggle the TC5 pin
org
$1000
delay
ds.w
1
; store the delay for output-compare operation
org
$1500
lds
#$1500
movw
#oc5_isr,UserTimerCh5 ; initialize the interrupt vector entry
movb
#$90,TSCR1
; enable TCNT, fast timer flag clear
movb
#$03,TSCR2
; set main timer prescaler to 8
bset
TIOS,OC5
; enable OC5
movb
#toggle,TCTL1
; select toggle for OC5 pin action
ldd
#hi_freq
std
delay
; use high-frequency delay count first
ldd
TCNT
; start the high-frequency sound
addd
delay
;
“
std
TC5
;
“
bset
TIE,OC5
; enable OC5 interrupt
cli
;
“
forever ldy
#5
; wait for half a second
jsr
delayby100ms
;
“
movw
#lo_freq,delay
; switch to low-frequency delay count
ldy
#5
jsr
delayby100ms
movw
#hi_freq,delay
; switch to high-frequency delay count
bra
forever
oc5_isr ldd
TC5
addd
delay
std
TC5
rti
#include c:\miniide\delay.asm”
end
In this program, both the OC0 and OC5 channels rely on the TCNT for timing control. One
should use the same prescaler throughout all the timing operations. The C language version of
the program to generate the siren to be compiled by CodeWarrior is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\delay.h”
#define HiFreq 1250
#define LoFreq 5000
int
dlycnt;
void
oc5ISR(void);
void
main(void)
{
TSCR1 5 0x90;
TSCR2 5 0x03;
// delay count for OC5 operation
// enable TCNT and fast timer flag clear
// set prescaler to TCNT to 1:8
388
Chapter 8 ■ Timer Functions
TIOS
|5 BIT5;
TCTL1
5 0x04;
dlycnt
5 HiFreq;
TC5
5 TCNT 1 dlycnt;
TIE
|5 BIT5;
asm(“cli”);
while(1) {
delayby100ms(5);
dlycnt 5 LoFreq;
delayby100ms(5);
dlycnt 5 HiFreq;
}
// enable OC5
// select toggle for OC5 pin action
// use high-frequency delay count first
// start an OC5 operation
// enable TC5 interrupt
// wait for half a second
// switch to low-frequency tone
// wait for half a second
// switch to high-frequency tone
}
interrupt void oc5ISR(void)
{
TC5 15 dlycnt;
}
The required interrupt-vector setup function (vectors_0807.c) is as follows:
extern void near oc5ISR(void);
#pragma CODE_SEG __NEAR_SEG NON_BANKED
#pragma CODE_SEG DEFAULT
// Change code section to DEFAULT.
typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFFE4 5 {
oc5ISR
};
▲
A siren can be considered as a song with only two notes. A song can be created by modifying the siren program. To make the switching of frequency easier, the whole score should be
stored in a table. A note has two components: one is the frequency and the other is the duration. The duration of a quarter note is about 0.4 s. The durations of other notes can be derived
proportionally. The complete list of the music notes and their frequencies is in Appendix F.
To play a song from the speaker, one places the frequencies and durations of all the notes
in a music score in a table. For every note, the user program uses the output-compare function
to generate the digital waveform with the specified frequency and duration. The following example illustrates this idea.
It is common for a song to have several contiguous identical notes in different places. When
playing multiple contiguous identical notes, the buzzer will generate one long note instead of
multiple notes. This problem can be avoided by inserting a short note with an inaudible frequency. The following example uses this method:
Example 8.8
▼
Use the circuit shown in Figure 8.21 and write a program to play “The Star-Spangled
Banner.” Assume that the demo board is running with a 24-MHz E-clock.
Solution: The following assembly program plays the national anthem:
#include “c:\miniide\hcs12.inc”
G3
equ
7653
; delay count to generate G3 note (with 1:8 prescaler)
B3
equ
6074
; delay count to generate B3 note (with 1:8 prescaler)
8.6 ■ Output-Compare Function
C4
C4S
D4
E4
F4
F4S
G4
A4
B4F
B4
C5
D5
E5
F5
ZZ
notes
toggle
equ
5733
; delay count to generate C4 note (with 1:8 prescaler)
equ
5412
; delay count to generate C4S note
equ
5108
; delay count to generate D4 note (with 1:8 prescaler)
equ
4551
; delay count to generate E4 note (with 1:8 prescaler)
equ
4295
; delay count to generate F4 note (with 1:8 prescaler)
equ
4054
; delay count to generate F4S note (with 1:8 prescaler)
equ
3827
; delay count to generate G4 note (with 1:8 prescaler)
equ
3409
; delay count to generate A4 note (with 1:8 prescaler)
equ
3218
; delay count to generate B4F note (with 1:8 prescaler)
equ
3037
; delay count to generate B4 note (with 1:8 prescaler)
equ
2867
; delay count to generate C5 note (with 1:8 prescaler)
equ
2554
; delay count to generate D5 note (with 1:8 prescaler)
equ
2275
; delay count to generate E5 note (with 1:8 prescaler)
equ
2148
; delay count to generate F5 note (with 1:8 prescaler)
equ
20
; delay count to generate an inaudible note
equ
118
equ
$04
; value to toggle the TC5 pin
org
$1000
delay
ds.w
1
; store the delay for output-compare operation
rep_cnt ds.b
1
; repeat the song this many times
ip
ds.b
1
; remaining notes to be played
org
$1500
; establish the SRAM vector address for OC5
movw
#oc5_isr,UserTimerCh5
lds
#$1500
movb
#$FF,DDRB
movb
#$90,TSCR1
; enable TCNT, fast timer flag clear
movb
#$03,TSCR2
; set main timer prescaler to 8
bset
TIOS,OC5
; enable OC5
movb
#toggle,TCTL1
; select toggle for OC5 pin action
ldx
#score
; use as a pointer to score table
ldy
#duration
; points to duration table
movb
#1,rep_cnt
; play the song twice
movb
#notes,ip
movw
2,x1,delay
; start with 0th note
ldd
TCNT
; play the first note
addd
delay
; “
std
TC5
; “
bset
TIE,C5I
; enable OC5 interrupt
cli
; “
forever pshy
; save duration table pointer in stack
ldy
0,y
; get the duration of the current note
jsr
delayby10ms
; “
puly
; get the duration pointer from stack
iny
; move the duration pointer
iny
; “
ldd
2,x1
; get the next note, move pointer
std
delay
; “
dec
ip
bne
forever
389
390
Chapter 8 ■ Timer Functions
dec
rep_cnt
beq
done
; if not finished playing, reestablish
ldx
#score
; pointers and loop count
ldy
#duration
;
“
movb #notes,ip
;
“
movw 0,x,delay
; get the first note delay count
ldd
TCNT
; play the first note
addd
delay
;
“
std
TC5
bra
forever
done
swi
; ********************************************************************************
; The OC5 interrupt service routine simply starts a new OC5 operation.
; ********************************************************************************
oc5_isr
ldd
TC5
addd
delay
std
TC5
rti
; ********************************************************************************
; The following subroutine creates a time delay which is equal to [Y] times
; 10 ms. The timer prescaler is 1:8.
; ********************************************************************************
delayby10ms
bset
TIOS,OC0
; enable OC0
ldd
TCNT
again1
addd
#30000
; start an output-compare operation
std
TC0
; with 10-ms time delay
brclr
TFLG1,C0F,*
ldd
TC0
dbne
y,again1
bclr
TIOS,OC0
; disable OC0
rts
; ********************************************************************************
; This table determines the frequency of each note.
; ********************************************************************************
score
dc.w
D4,B3,G3,B3,D4,G4,B4,A4,G4,B3,C4S
dc.w
D4,ZZ,D4,ZZ,D4,B4,A4,G4,F4S,E4,F4S,G4,ZZ,G4,D4,B3,G3
dc.w
D4,B3,G3,B3,D4,G4,B4,A4,G4,B3,C4S,D4,ZZ,D4,ZZ,D4
dc.w
B4,A4,G4,F4S,E4,F4S,G4,ZZ,G4,D4,B3,G3,B4,ZZ,B4
dc.w
B4,C5,D5,ZZ,D5,C5,B4,A4,B4,C5,ZZ,C5,ZZ,C5,B4,A4,G4
dc.w
F4S,E4,F4S,G4,B3,C4S,D4,ZZ,D4,G4,ZZ,G4,ZZ,G4,F4S
dc.w
E4,ZZ,E4,ZZ,E4,A4,C5,B4,A4,G4,ZZ,G4,F4S,D4,ZZ,D4
dc.w
G4,A4,B4,C5,D5,G4,A4,B4,C5,A4,G4
; ********************************************************************************
; Each of the following entries multiplied by 10 ms gives the duration of a note.
; ********************************************************************************
duration
dc.w
30,10,40,40,40,80,30,10,40,40,40
dc.w
80, 3,20,3,20,60,20,40,80,20,20,40,3,40,40,40,40
dc.w
30,10,40,40,40,80,30,10,40,40,40,80,3,20,3,20
dc.w
60,20,40,80,20,20,40,3,40,40,40,40,20,3,20
8.6 ■ Output-Compare Function
dc.w
dc.w
dc.w
dc.w
end
391
40,40,40,3,80,20,20,40,40,40,3,80,3,40,60,20,40
80,20,20,40,40,40,80,3,40,40,3,40,3,20,20
40, 3,40,3,40,40,20,20,20,20,3,40,40,20,3,20
60,20,20,20,80,20,20,60,20,40,80
The C language version of the program to be compiled by CodeWarrior and also support a
serial monitor is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\SetClk.h”
#define
G3
7653
#define
B3
6074
#define
C4
5733
#define
C4S
5412
#define
D4
5108
#define
E4
4551
#define
F4
4295
#define
F4S
4054
#define
G4
3827
#define
A4
3409
#define
B4F
3218
#define
B4
3037
#define
C5
2867
#define
D5
2554
#define
E5
2275
#define
F5
2148
#define
ZZ
20
// delay count to create an inaudible sound
#define
toggle
0x04
// value to toggle OC5 pin
int delay;
unsigned int score[] 5 {
D4,B3,G3,B3,D4,G4,B4,A4,G4,B3,C4S,
D4,ZZ,D4,ZZ,D4,B4,A4,G4,F4S,E4,F4S,G4,ZZ,G4,D4,B3,G3,
D4,B3,G3,B3,D4,G4,B4,A4,G4,B3,C4S,D4,ZZ,D4,ZZ,D4,
B4,A4,G4,F4S,E4,F4S,G4,ZZ,G4,D4,B3,G3,B4,ZZ,B4,
B4,C5,D5,ZZ,D5,C5,B4,A4,B4,C5,ZZ,C5,ZZ,C5,B4,A4,G4,
F4S,E4,F4S,G4,B3,C4S,D4,ZZ,D4,G4,ZZ,G4,ZZ,G4,F4S,
E4,ZZ,E4,ZZ,E4,A4,C5,B4,A4,G4,ZZ,G4,F4S,D4,ZZ,D4,
G4,A4,B4,C5,D5,G4,A4,B4,C5,A4,G4,0};
unsigned int dur[] 5 {
30,10,40,40,40,80,30,10,40,40,40,
80,3,20,3,20,60,20,40,80,20,20,40,3,40,40,40,40,
30,10,40,40,40,80,30,10,40,40,40,80,3,20,3,20,
60,20,40,80,20,20,40,3,40,40,40,60,20,3,20,
40,40,40,3,80,20,20,40,40,40,3,80,3,40,60,20,40,
80,20,20,40,40,40,80,3,40,40,3,40,3,20,20,
40,3,40,3,40,40,20,20,20,20,3,40,40,20,3,20,
60,20,20,20,80,20,20,60,20,40,80};
void delayby10ms(int kk);
void main(void) {
int
j;
SetClk8();
// set E-clock to 24 MHz
392
Chapter 8 ■ Timer Functions
TSCR1
5 0x90;
TSCR2
5 0x03;
TIOS
|5 OC5;
TCTL1
5 toggle;
delay
5 score[0];
j
5 0;
TC0
5 TCNT 1 delay;
TIE
|5 C5I;
asm(“cli”);
while(score[j]){
delay 5 score[j];
delayby10ms(dur[j]);
j11;
}
TIOS &5 0xDF; //disable OC5
while(1);
// enable TCNT to fast timer flag clear
// set TCNT clock prescaler to 8
// enable OC5
// set OC5 pin action to toggle
//start an OC0 operation
//enable TC5 interrupt
}
void delayby10ms(int kk) {
char ix;
TIOS |5 0x40;
//enable OC6
TC6 5 TCNT 1 30000;
//start OC6 operation with 20-ms delay
for(ix 5 0; ix < kk; ix11){
while(!(TFLG1 & 0x40));
TC6 15 30000;
}
TIOS &5 0xBF; //disable OC6
}
interrupt void tc5ISR(void) {
TC5 15 delay;
}
The function for setting up the interrupt vector for OC5 is as follows:
extern void near tc5ISR(void);
#pragma
CODE_SEG __NEAR_SEG NON_BANKED
#pragma
CODE_SEG DEFAULT
// Change code section to DEFAULT.
typedef
void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFFE4 5 {
tc5ISR
};
▲
There are at least two drawbacks in using this method to play a song.
1. A periodic square waveform is only an approximation to an actual note. Those who
have learned communication theory know that a periodic square waveform also
contains harmonics with higher frequencies. The solution to this problem is beyond
the scope of this text.
2. There is no loudness control. Using the PWM function rather than the outputcompare function and adjusting the duty cycle of each note is one of the possible
solutions to this problem.
393
8.6 ■ Output-Compare Function
8.6.5 Using OC7 to Control Multiple Output-Compare Functions
The output-compare function OC7 is special because it can control up to eight output-compare functions at the same time. The register OC7M specifies the output-compare channels to
be controlled by OC7. The value that any PTx (x 5 0, . . . , 7) pin can assume when the value of
TC7 equals that of TCNT is specified by the OC7D register. To control an output-compare pin
using OC7, the user sets the corresponding bit in the OC7M register. When a successful OC7
compare is made, each affected pin assumes the value of the corresponding bit of the OC7D register. The contents of the OC7M and OC7D registers are shown in Figures 8.22 and 8.23.
7
6
5
4
3
2
1
0
OC7M7 OC7M6 OC7M5 OC7M4 OC7M3 OC7M2 OC7M1 OC7M0
Reset
0
0
0
0
0
0
0
0
OC7Mn n = 0, . . . , 7
0 = PTn pin is not affected by OC7 function.
1 = a successful OC7 action will override a successful OC6:OC0
compare action during the same cycle and the OCn action taken
will depend on the corresponding OC7D bit.
Figure 8.22 ■ Output-Compare 7 Mask register (OC7M)
7
6
5
4
3
2
1
0
OC7D7 OC7D6 OC7D5 OC7D4 OC7D3 OC7D2 OC7D1 OC7D0
Reset
0
0
0
0
0
0
0
0
Figure 8.23 ■ Output-Compare 7 Data register (OC7D)
For the OC7 pin, the HCS12 document does not specify what will happen when both the
TCTL1 register and the OC7M:OC7D register pair specify the OC7 pin action on a successful
OC7 compare. Avoid using this combination.
For OC0:OC6, when the OC7Mn (n 5 0, . . . , 6) bit is set, a successful OC7 action will
override a successful OC6:OC0 compare action during the same cycle; therefore, the OCn
action taken will depend on the corresponding OC7D bit. This feature allows a PTn pin to be
controlled by two output-compare functions simultaneously [OC7 and OCn (n 5 0, . . . , 6)].
Example 8.9
▼
What value should be written into OC7M and OC7D if one wants pins PT2, PT3, and PT4
to assume the values of 1, 0, and 1, respectively, when OC7 compare succeeds?
Solution: Bits 4, 3, and 2 of OC7M must be set to 1, and bits 4, 3, 2 of OC7D should be set to 1,
0, and 1, respectively. The following instruction sequence set up these values:
movb
movb
#$1C,OC7M
#$14,OC7D
394
Chapter 8 ■ Timer Functions
The following C statements achieve the same goal:
OC7M 5 0x1C;
OC7D 5 0x14;
▲
The ability to control multiple output-compare pins is very useful in applications that
require multiple actions to be triggered simultaneously in the near future.
Example 8.10
▼
Suppose that there is an application that requires the following operations to be triggered
50 ms later:
• Turn off the light controlled by the TC5 pin
• Turn on the temperature sensor controlled by the TC4 pin
• Turn off the heater controlled by the TC3 pin
• Turn on the music controlled by the TC2 pin
Write an instruction sequence to perform the desired operation.
Solution: The values to be written into OC7M and OC7D are $3C and $14, respectively. Set the prescaler to TCNT to 64. Then the value to be added to create a 50-ms delay can be computed as follows:
Delay 5 (24,000,000 4 64) × 0.05 5 18,750
The following instruction sequence can perform the desired operations 50 ms later:
movb
movb
movb
movb
ldd
addd
std
#$90,TSCR1
#$06,TSCR2
#$3C,OC7M
#$14,OC7D
TCNT
#18750
TC7
; enable TCNT and fast timer flag clear
; set prescaler to 64
; allow OC7 to control TC5, TC4, TC3, and TC2 pins
; pin actions for TC5, TC4, TC3, and TC2
; start an OC7 operation with 50 ms as delay
; “
; start the OC7 operation
▲
Two output-compare functions can control the same pin simultaneously. Thus, OC7 can
be used in conjunction with one or more other output-compare functions to achieve even more
time flexibility. We can generate a digital waveform with a given duty cycle by using the OC7
and any other output-compare function.
Example 8.11
▼
Use OC7 and OC0 together to generate a 2-kHz digital waveform with a 40 percent duty
cycle on the PT0 pin. Assume that the E-clock frequency is 24 MHz.
Solution: We set the prescale factor to 8. The period of a 2-kHz waveform is 0.0005 s. The period
of this signal corresponds to 0.0005 × (24,000,000 4 8) 5 1500 cycles. The high interval in one
period of the waveform is 600 clock cycles, whereas the low interval is 900 clock cycles.
The idea in using two OC functions in generating the waveform is as follows:
• Use OC0 (or OC7) to pull the PT0 pin to high every 1500 clock cycles.
• Use OC7 (or OC0) to pull the PT0 to low after it is high for 600 clock cycles.
395
8.6 ■ Output-Compare Function
We use the interrupt-driven approach to implement this algorithm. After starting the first
action on OC7 and OC0, the program will stay in a wait loop to wait for interrupts to be requested from OC7 and OC0. The interrupt service routine will simply start the next outputcompare operation with 15,000 as the delay count. The assembly program that implements this
idea is as follows:
#include “c:\miniide\hcs12.inc”
high_cnt
equ
600
delay
equ
1500
org
$1500
lds
#$1500
movw
#oc7_isr,UserTimerCh7
movw
#oc0_isr,UserTimerCh0
movb
#$90,TSCR1
movb
#$03,TSCR2
movb
#$81,TIOS
movb
#$01,OC7M
movb
#$01,OC7D
movb
#$02,TCTL2
movb
#$81,TIE
ldd
TCNT
addd
#delay
std
TC7
addd
#high_cnt
std
TC0
cli
loop
bra
loop
swi
oc7_isr
ldd
TC7
addd
#delay
std
TC7
rti
oc0_isr
ldd
TC0
addd
#delay
std
TC0
rti
end
; interval during which pulse is high
; a delay corresponds to the period of 2 kHz
; set up OC7 interrupt vector
; set up OC0 interrupt vector
; enable TCNT and fast timer flag clear
; set prescaler to 8
; enable OC7 and OC0
; allow OC7 to control OC0 pin
; OC7 action on PT0 pin to pull high
; select pull low as the OC0 action
; enable OC7 and OC0 to interrupt
; start an OC7 operation
; start an OC0 operation
; enable interrupt
; infinite loop to wait for interrupt
; start the next OC7 action with 15,000
; clock cycles delay, also clear the C7F
; flag
; start the next OC0 action with 15,000
; clock cycles delay, also clear the C0F
; flag
▲
The C language version of this program is very straightforward and hence is left as an exercise.
8.6.6 Forced Output-Compare
There may be applications in which the user requires an output-compare pin action to
occur immediately instead of waiting for a match between TCNT and the proper outputcompare register. This situation arises in the spark-timing control in some automotive engine
control applications. To use the forced output-compare mechanism, the user would write to the
CFORC register with 1s in the bit positions corresponding to the output-compare channels to
be forced. At the next timer count after the write to CFORC, the forced channels will trigger
their programmed pin actions to occur.
396
Chapter 8 ■ Timer Functions
Reset
7
6
5
4
3
2
1
0
FOC7
FOC6
FOC5
FOC4
FOC3
FOC2
FOC1
FOC0
0
0
0
0
0
0
0
0
Figure 8.24 ■ Contents of the CFORC register
The forced actions are synchronized to the timer counter clock input. The forced outputcompare signal causes pin action but does not affect the timer flag or generate an interrupt.
Normally, the force mechanism would not be used in conjunction with the automatic pin action that toggles the corresponding output-compare pin. The contents of CFORC are shown in
Figure 8.24. CFORC always reads as all zeroes.
Example 8.12
▼
Suppose that the contents of the TCTL1 and TCTL2 registers are $D6 and $6E, respectively. The content of the TFLG1 register is $00. What would occur on pins PT7 to PT0 on the
next clock cycle if the value $7F is written into the CFORC register?
Solution: The TCTL1 and TCTL2 registers configure the output-compare actions as shown in
Table 8.2. Since the content of the TFLG1 register is 0, none of the started output-compare operations have succeeded yet.
Because the CFORC register specifies that the output-compare channels 6 to 0 are
to be forced immediately, the actions specified in the fourth column in Table 8.2 occur
immediately.
Register
TCTL1
TCTL2
Bit Positions
Value
Action to Be Triggered
7
6
1
1
Set the PT7 pin to high
5
4
0
1
Toggle the PT6 pin
3
2
0
1
Toggle the PT5 pin
1
0
1
0
Pull the PT4 pin to low
7
6
0
1
Toggle the PT3 pin
5
4
1
0
Pull the PT2 pin to low
3
2
1
1
Set the PT1 pin to high
1
0
1
0
Pull the PT0 pin to low
Table 8.2 ■ Pin actions on PT7–PT0 pins
▲
8.7 Pulse Accumulator
The HCS12 standard timer system has a 16-bit pulse accumulator, PACA, whereas the Enhanced
Captured Timer system has four 8-bit pulse accumulators (PAC3, . . . , PAC0). Two adjacent
397
8.7 ■ Pulse Accumulator
8-bit pulse accumulators can be concatenated into a single 16-bit pulse accumulator. PAC3 and PAC2
can be concatenated into the 16-bit pulse accumulator A (PACA), whereas PAC1 and PAC0 can be
concatenated into the 16-bit pulse accumulator B (PACB). The block diagrams of four 8-bit pulse
accumulators and two 16-bit pulse accumulators are shown in Figures 8.25 and 8.26, respectively.
Load holding register and reset pulse accumulator
Edge detector
Delay counter
0
8-bit PAC0
(PACN0)
PA0H holding
register
PT1
Edge detector
Delay counter
EDG1
0
Interrupt
8-bit PAC1
(PACN1)
Host CPU data bus
PT0
EDG0
PA1H holding
register
PT2
Edge detector
Delay counter
EDG2
0
8-bit PAC2
(PACN2)
PA2H holding
register
PT3
Edge detector
Delay counter
EDG3
0
Interrupt
8-bit PAC3
(PACN3)
PA3H holding
register
Figure 8.25 ■ Block diagram of four 8-bit pulse accumulators
There are four possible configurations for these pulse accumulators.
• Two 16-bit pulse accumulators PACA and PACB
• One 16-bit pulse accumulator PACA and two 8-bit pulse accumulators PAC1 and
PAC0
• One 16-bit pulse accumulator PACB and two 8-bit pulse accumulators PAC3 and
PAC2
• Four 8-bit accumulators PAC3,PAC0
8.7.1 Signal Pins
Four 8-bit pulse accumulators PAC3,PAC0 are sharing the signal pins PT3,PT0 with the
lower four input-capture modules IC3,IC0, one pin per pulse accumulator. However, when concatenated into a 16-bit pulse accumulator, PACA and PACB use signal pins PT7 and PT0, respectively.
Chapter 8 ■ Timer Functions
Timer clock (TIMCLK)
Interrupt
8-bit PAC3
(PACN3)
PACLK
PACLK ÷ 256
Prescaled clock
from timer
4:1 Mux
PACLK ÷ 65536
CLK1
CLK0
Intermodule Bus
398
Clock select
(PAMOD)
8-bit PAC2
(PACN2)
Edge
detector
PT7
MUX
PACA
÷ 64
Bus clock
Interrupt
8-bit PAC1
(PACN1)
8-bit PAC0
(PACN0)
Delay counter
PACB
Edge detector
PT0
Figure 8.26 ■ 16-bit accumulator block diagram
8.7.2 Operation Modes
The 16-bit pulse accumulator PACA has two operation modes.
1. Event-counting mode. In this mode, the PACA counter increments on the active edge of
the PT7 pin. Many microcontroller applications involve counting things. These things are
called events, but in real applications they might be anything: pieces on an assembly line,
cycles of an incoming signal, or units of time. To be counted by the accumulator, these
things must be translated into rising or falling edges on the PT7 pin. A trivial example of
an event might be to count pieces on the assembly line: a light emitter and detector pair
could be placed across a conveyor so that as each piece passes the sensor, the light beam is
interrupted and a logic-level signal that can be connected to the PT7 pin is produced.
2. Gated-time-accumulation mode. The 16-bit PACA counter is clocked by a freerunning E 4 64 signal, subject to the PT7 signal being active. One common use of this
mode is to measure the duration of a single pulse. The counter is set to 0 before the
pulse starts, and the resultant pulse time is read directly when the pulse is finished.
The 16-bit pulse accumulator B and all four 8-bit pulse accumulators have only one mode:
event counting. That is, their counters are incremented on the active edge of their associated
pins. The active edges of PACB and PAC3, . . . , PACO0 are identical to those of timer capture
functions IC0 (PT0 pin) and IC3, . . . , ICO (PT3, . . . , PTO pins), respectively. The active edges
of the PT3,PT0 pins are defined by the TCTL4 register.
8.7.3 Interrupt Sources
The 16-bit PACA has two interrupt sources.
1. PT7-edge interrupt
2. PACA counter overflow
399
8.7 ■ Pulse Accumulator
Only two (PAC3 and PAC1) of the four 8-bit pulse accumulators may request interrupt to
the MCU. Interrupts are requested whenever PAC3 or PAC1 rolls over from $FF to $00. The
16-bit PACB may request an interrupt to the MCU whenever its upper 8-bit counter (PAC1)
rolls over from $FF to $00.
8.7.4 Registers Related to Pulse Accumulator
The operation of the 16-bit PACA is controlled by the PACTL register. The status of the
PACA is recorded in the PAFLG register. The PACA has a 16-bit counter. Since PACA is formed
by concatenating PAC3 and PAC2, we can use the instruction ldd PAC3 to copy the contents of
the 16-bit counter. To be backward compatible with the 68HC12, the register name PACNT is
added to the header file so that one can also use the instruction ldd PACNT to copy the contents of
the 16-bit counter. The contents of the PACTL and PAFLG registers are shown in Figures 8.27
and 8.28, respectively.
7
0
6
5
4
3
PAEN PAMOD PEDGE CLK1
2
1
0
CLK0
PAOVI
PAI
Reset value
= 0x00
PAEN: pulse accumulator system enable bit
0 = PACA is disabled (PACN3 and PACN2 can be enabled).
1 = PACA is enabled (PACN3 and PACN2 cannot be enabled).
PAMOD: pulse accumulator mode bit
0 = event counter mode.
1 = gated time accumulation mode.
PEDGE: pulse accumulator edge control bit
For PAMOD = 0 (event counter mode)
0 = falling edges on the PAI pin cause the count to increment.
1 = rising edges on the PAI pin cause the count to increment.
For PAMOD = 1 (gated time acumulation mode)
0 = PAI pin high enables E ÷ 64 clock to pulse accumulator and
the trailing falling edge on the PAI pin sets the PAIF flag.
1 = PAI pin low enables E ÷ 64 clock to pulse accumulator and
the trailing rising edge on the PAI pin sets the PAIF flag.
CLK1 and CLK0: clock select bits
00 = use timer prescaler clock as timer counter clock.
01 = use PACLK as input to timer counter (TCNT) clock.
10 = use PACLK/256 as timer counter clock.
11 = use PACLK/65536 as timer counter clock.
PAOVI: pulse accumulator overflow interrupt enable bit
0 = disable
1 = enable
PAI: PAI pin interrupt enable bit
0 = disabled
1 = enabled
Figure 8.27 ■ Pulse Accumulator Control register (PACTL)
The PACTL register also controls the clock source for the timer counter (TCNT). When the
CLK1 and CLK0 bits are not 00, the PACLK signal (from the PT7 pin) is prescaled by 1, 256, or
65,536 and used as the clock input to the timer counter.
Bits 1 and 0 of the PAFLG register keep track of the status of the operation of the PACA, as
shown in Figure 8.28. Any access to the PAC3 or PAC2 register will clear all the flag bits in the
PAFLG register if the TFFCA bit in the TSCR1 register is set to 1.
400
Chapter 8 ■ Timer Functions
7
6
5
4
3
2
1
0
0
0
0
0
0
0
PAOVF
PAIF
Reset value
= 0x00
PAOVF: pulse accumulator overflow flag
This flag is set when PACNT overflows from $FFFF to $0000 and can
be cleared by writing a 1 to it.
PAIF: PT7 pin edge flag
When in event-counting mode, this bit is set when the selected edge on the
PT7 pin is detected.
When in gated-accumulation mode, the selected trailing edge sets this
flag.
Figure 8.28 ■ Pulse Accumulator Flag register (PAFLG)
7
6
5
4
3
2
1
0
0
PBEN
0
0
0
0
PBOVI
0
Reset value
= 0x00
PBEN: pulse accumulator B system enable bit
0 = 16-bit pulse accumulator disabled. Eight-bit PAC1 and PAC0 can
be enabled when their related enable bits in ICPACR are set.
1 = pulse accumulator B system enabled.
PBOVI: pulse accumulator B overflow interrupt enable bit
0 = interrupt inhibited.
1 = interrupt requested if PBOVF is set.
(a) Pulse accumulator B control register (PBCTL)
7
6
5
4
3
2
1
0
0
0
0
0
0
0
PBOVF
0
Reset value
= 0x00
PBOVF: pulse accumulator B overflow flag
This bit is set when the 16-bit pulse accumulator B overflows from
$FFFF to $0000 or when 8-bit accumulator 1 (PAC1) overflows from
$FF to $00. It is cleared by writing 1 to it or by accessing PACN1 and
PACN0 when the TFFCA bit in the TSCR1 register is set.
(b) Pulse accumulator B flag register (PBFLG)
Figure 8.29 ■ Pulse Accumulator B Control and Flag registers
The PACB pulse accumulator is controlled by the PBCTL register, and the PBFLG register
records its status. The contents of these two registers are shown in Figure 8.29.
Each of the 8-bit pulse accumulators can be enabled if its associated 16-bit pulse accumulator is disabled. The enabling of an 8-bit pulse accumulator is done by programming the ICPAR
register. The contents of ICPAR are shown in Figure 8.30.
Each of the 8-bit pulse accumulator also has an 8-bit holding register (PA3H,PA0H).
7
6
5
4
0
0
0
0
3
2
1
0
PA3EN PA2EN PA1EN PA0EN
Reset value
= 0x00
PAxEN: 8-bit pulse accumulator x enable bit
0 = pulse accumulator x disabled
1 = pulse accumulator x enabled
Figure 8.30 ■ Input Control Pulse Accumulator Control register (ICPACR)
401
8.7 ■ Pulse Accumulator
8.7.5 Operations of the Enhanced Pulse Accumulators
The 16-bit PACA has two operation modes: event counting and gated time accumulation.
When in the event-counting mode, the PACA counter increases on the selected edge of the
PT7 signal. The PAIF flag of the PAFLG register is set to 1 whenever the selected signal edge
is detected on the PT7 pin. The setting of this flag may request an interrupt to the MCU if the
PAI bit of the PACTL register is set to 1. When in gated-time-accumulation mode, the 16-bit
counter is enabled to increment by the E/64 clock signal if the selected signal level is applied
on the PT7 pin.
All other pulse accumulators (PACB, PAC3,PAC0) count the number of active edges at
their associated pins. Whenever the PAC3 or PAC1 rolls over from $FF to $00, its associated
flag (PAOVF or PBOVF) will be set to 1 and may optionally request an interrupt to the MCU.
Pulse accumulators PAC2 and PAC0 do not have the interrupt capability. The user can prevent
8-bit pulse accumulators counting further than $FF by setting the PACMX bit in the ICSYS register. In this case, a value of $FF means that 255 counts or more have occurred. The contents of
the ICSYS register are illustrated in Figure 8.31.
7
6
5
SH37
SH26
SH15
4
3
2
1
0
SH04 TFMOD PACMX BUFEN LATQ
Reset value
= 0x00
SHxy: share input action of input-capture channel x and y bits
0 = normal operation.
1 = the channel input x causes the same action on the channel y.
TFMOD: timer-flag-setting mode bit
0 = the timer flags C3F:C0F in TFLG1 are set when a valid inputcapture transition on the corresponding port pin occurs.
1 = if in the queue mode (BUFEN = 1 and LATQ = 0), the timer flags
C3F:C0F in TFLG1 are set only when a latch on the corresponding
holding register occurs. If the queue mode is not engaged, the timer
flags C3F:C0F are set the same way as for TFMOD = 0.
PACMX: 8-bit pulse accumulator maximum count bit
0 = normal operation. When the 8-bit pulse accumulator has reached
$FF, with the next active edge, it will be incremented to $00.
1 = when the 8-bit pulse accumulator has reached the value $FF, it
will not be incremented further. The value $FF indicates a count
of 255 or more.
BUFEN: IC buffer enable bit
0 = input-capture and pulse accumulator holding registers are
disabled.
1 = input-capture and pulse accumulator holding registers are
enabled.
LATQ: input-capture latch or queue mode select bit
The BUFEN bit should be set to enable IC and the pulse accumulator's
holding registers. Otherwise, the LATQ latching mode is disabled.
0 = the queue mode of input-capture is enabled.
1 = the latch mode is enabled. Latching function occurs when modulus
down counter reaches 0 or a 0 is written into the count register
MCCNT. With a latching event, the contents of IC registers and
8-bit pulse accumulators are transferred to their holding registers.
The 8-bit pulse accumulators are cleared.
Figure 8.31 ■ Input Control System Control register (ICSYS)
402
Chapter 8 ■ Timer Functions
8.7.6 Pulse Accumulator Applications
The pulse accumulator has a few interesting applications, such as interrupting after N
events, frequency measurement, and pulse duration measurement.
Example 8.13
▼
Suppose certain events are converted into pulses and connected to the PT7 pin. Write a program so that the pulse accumulator generates an interrupt to the HCS12 when N events have
occurred. Assume that N is less than 65,536.
Solution: By writing the two’s complement of N into PACNT, it will overflow after N events
and generate an interrupt. The assembly program is as follows:
#include
N
;
paov_isr
“c:\miniide\hcs12.inc”
equ
1350
org
$1500
lds
#$1500
movw
#paov_isr,UserPAccOvf
ldd
#N
coma
comb
addd
#1
std
PACNT
movb
#$52,PACTL
cli
...
swi
movb
end
#PAOVF,PAFLG
; set up stack pointer
; set up PAOV interrupt vector
; place the two’s complement in PACNT
; “
; “
; “
; “
; enable PACA, event counting mode, active
; edge is rising
; enable PAOV interrupt
; clear the PAOVF flag
The C language version of the program for CodeWarrior C compiler is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#define
NN 1350
void main(void) {
PACNT 5 ,N 1 1;
PACTL5 0x52;
asm(“cli”);
}
interrupt void paovISR(void) {
PAFLG 5 PAOVF;
}
//enable PACTL, event count mode,
increment on rising edge
//enable PACNT overflow interrupt
// . . .
//clear the flag and return
▲
403
8.7 ■ Pulse Accumulator
The pulse accumulator system can be used to measure the signal frequency. The procedure
is as follows:
Step 1
Connect the unknown signal to the PT7 pin.
Step 2
Set up the PACA to operate in event-counting mode.
Step 3
Use one of the output-compare functions to create a 1-s time interval.
Step 4
Use a memory location to keep track of the number of pulse accumulator counter
overflow interrupts.
Step 5
Enable the PAOV interrupt.
Step 6
Disable the PAOV interrupt at the end of 1 s.
Example 8.14
▼
Write a program to measure the frequency of a signal connected to the PT7 pin using the
algorithm described previously.
Solution: We use the OC0 function to create a 1-s delay. Fifty OC0 operations will be
performed, with each OC0 operation creating a 20-ms delay. The service routine for the
PACNT overflow interrupt will increase the overflow count by 1. Let paov_cnt represent
the PACNT overflow count. At the end of 1 s, the frequency is equal to the following
expression:
Frequency 5 paov_cnt 3 216 1 PACNT
The assembly program that implements this procedure is as follows:
#include
“c:\miniide\hcs12.inc”
org
$1000
oc_cnt
ds.b
1
paov_cnt ds.b
2
frequency ds.b
4
org
$1500
lds
#$1500
movw #paov_isr,UserPAccOvf
movb #50,oc_cnt
ldd
#0
std
PACNT
std
paov_cnt
std
frequency
std
frequency12
; PACNT overflow count
; signal frequency
; set up PAOV interrupt vector
; prepare to perform 50 OC0 actions
; let PACNT count up from 0
; initialize PACNT overflow count to 0
; initialize frequency to 0
; “
404
Chapter 8 ■ Timer Functions
movb #$90,TSCR1
; enable TCNT and fast timer flag clear
bset
TIOS,OC0
; select OC0 function
movb #$03,TSCR2
; set prescaler to TCNT to 8
bclr
DDRT,$80
; configure PT7 for input
; configure PA function: enable PA, select event-counting mode, rising edge
; of PAI signal increments the PACNT counter, enable PAOV interrupt
movb #$52,PACTL
cli
; enable PAOV interrupt
ldd
TCNT
sec_loop addd
#60000
std
TC0
brclr
TFLG1,C0F,*
; wait for 20 ms here
ldd
TC0
dec
oc_cnt
bne
sec_loop
movb
#0,PACTL
; disable PA function
sei
; disable interrupt
ldd
PACNT
std
frequency12
ldd
paov_cnt
std
frequency
swi
paov_isr movb
#PAOVF,PAFLG
; clear the PAOVF flag
ldx
paov_cnt
; increment PACNT overflow
inx
; count by 1
stx
paov_cnt
;
“
end
The C language version of the program is as follows:
#include
“c:\cwHCS12\include\hcs12.h”
unsigned
long int frequency;
unsigned
int paov_cnt;
void main (void)
{
int oc_cnt;
PACNT
5 0;
frequency 5 0;
paov_cnt 5 0;
TSCR1
5 0x90;
// enable TCNT and fast flag clear
TIOS
5 OC0;
// select OC0 function
TSCR2
5 0x03;
// set prescale factor to 8
PACTL
5 0x52;
// enable PA function, enable PAOV interrupt
DDRT
&5 0x7F;
// configure the PT7 pin for input
asm(“cli”);
// enable interrupt globally
oc_cnt
5 50;
TC0
5 TCNT 1 60000u;
while (oc_cnt) {
while(!(TFLG1 & C0F));
TC0 5 TC0 1 60000u;
405
8.7 ■ Pulse Accumulator
oc_cnt22;
}
PACTL
5 0x00;
// disable PA function
asm(“sei”);
frequency 5 (long)paov_cnt * 65536l 1 (long)PACNT;
asm(“swi”);
}
interrupt void paovISR (void)
{
PAFLG
5 PAOVF;
paov_cnt 5 paov_cnt 1 1;
}
// clear PAOVF flag
The function that sets up the interrupt vector for pulse accumulator A is as follows:
extern void near paovISR(void);
#pragma CODE_SEG __NEAR_SEG NON_BANKED
#pragma CODE_SEG DEFAULT
// Change code section to DEFAULT.
typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFFDC 5 {
paovISR
};
▲
The pulse accumulator module can be set up to measure the duration of a pulse using the
gated time accumulation mode. When the active level is applied to the PT7 pin, the PACNT
can count and will stop counting on the trailing edge of the PT7 signal. The clock input to
the pulse accumulator is E 4 64. The procedure for measuring the duration of a pulse is as
follows:
Step 1
Set up the pulse accumulator system to operate in the gated time accumulation mode, and
initialize PACNT to 0.
Step 2
Select the falling edge as the active edge (for measuring positive pulse). In this setting, the
pulse accumulator counter will increment when the signal connected to the PAI pin is
high and generate an interrupt to the HCS12 on the falling edge.
Step 3
Enable the PAI active edge interrupt and wait for the arrival of the active edge of PAI.
Step 4
Stop the pulse accumulator counter when the interrupt arrives.
Without keeping track of the PACNT overflows, the longest pulse width (E 5 24 MHz) that can
be measured is
pulse_width 5 216 3 64TE 5 216 3 64 3 1/24 μs 5 174.763 ms
To measure a longer pulse width, we need to keep track of the number of times that the
PACNT counter overflows in the duration of the pulse. Let paov_cnt be the overflow count of
the PACNT counter, then
pulse_width 5 [(216 3 paov_cnt) 1 PACNT] 3 64TE
406
Chapter 8 ■ Timer Functions
Example 8.15
▼
Write a program to measure the duration of an unknown signal connected to the PAI pin.
Solution: The assembly program that implements the previous algorithm is as follows:
#include “c:\miniide\hcs12.inc”
org
$1000
paovCnt ds.b
1
; use to keep track of the PACNT overflow count
pw
ds.b
3
; hold the signal pulse width
org
$1500
movw
#paovISR,UserPAccOvf
; set up PAOV interrupt vector
ldd
#0
std
PACNT
; let PACNT count up from 0
clr
paovCnt
; initialize PACNT overflow count to 0
movb
#$0,TSCR2
; set TCNT timer prescaler to 1
; configure PA function: enable PA, select gated-time—accumulator mode, high level
; of the PAI signal enables PACNT counter, enable PAOV interrupt
movb
#$62,PACTL
bclr
DDRT,$80
; configure PAI pin for input
cli
; enable PAOV interrupt
brclr
PAFLG,PAIF,*
; wait for the arrival of the falling edge of PAI
movb
#0,PACTL
; disable PA function
sei
; disable interrupt
ldd
PACNT
std
pw
ldaa
paovCnt
staa
pw
swi
paovISR movb
#PAOVF,PAFLG
; clear PAOVF flag
inc
paovCnt
; increment PACNT overflow count by 1
end
The C language version of the program for the CodeWarrior C compiler is as follows:
#include “c:\cwHCS12\include\hcs12.h”
unsigned int paovCnt;
long unsigned int pw;
void main(void)
{
PACNT 5 0;
// let PACNT count up from 0
TSCR1 5 0x90;
// enable TCNT and fast timer flag clear
paovCnt 5 0;
pw 5 0;
TSCR2 5 0x00;
// set TCNT prescaler to 1
DDRT 5 0x00;
// configure all timer port pins for input
// configure PA function: enable PA, select gated-time-accumulator mode, high level
// of PAI enables PACNT to count, enable PAOV interrupt
PACTL 5 0x62;
asm(“cli”);
407
8.8 ■ Modulus Down Counter
while(!(PAFLG & PAIF)); // wait for the arrival of the PAI falling edge
PACTL 5 0x00;
// disable PA system
asm(“sei”);
pw 5 (long)paovCnt * 65536l 1 (long)PACNT;
asm (“swi”);
}
interrupt void paovISR (void)
{
PAFLG
5 PAOVF; // clear PAOVF flag
paovCnt 5 paovCnt 1 1;
}
The function for setting up an interrupt vector for the pulse accumulator A is identical to
that of Example 8.14.
▲
Example 8.16
▼
Write an instruction sequence to enable the 8-bit pulse accumulators PAC1 and PAC3
and let them increase on the rising edge of their associated pins. Disable their overflow
interrupts.
Solution: To enable PAC1 and PAC3, the PAEN and PBEN bits must be cleared and the
PA3EN and PA1EN bits set. To select the rising edge as their active edge, the value $44 needs
to be written into the TCTL4 register and also the PT3 and PT1 pins must be configured for
input-capture. To disable overflow interrupt, clear the PAOVI and PBOVI bits.
The following instruction sequence will perform the required configuration:
bclr
bclr
movb
bset
bclr
PACTL,$42
PBCTL,$42
#$44,TCTL4
ICPAR,$0A
DDRT,$0A
; disable 16-bit PACA, disable overflow interrupt
; disable 16-bit PACB, disable overflow interrupt
; select the rising edges as active edge
; enable PAC3 and PAC1
; configure PT3 and PT1 for input
▲
8.8 Modulus Down Counter
The HCS12 timer system contains a modulus down counter that can be used as a time
base to generate periodic interrupts. It can also be used to latch the value of the IC registers
and the pulse accumulators to their holding registers. The action of latching can be periodic or
only once.
The modulus down counter has a prescaler, which divides the E-clock and uses its output
as the clock input to the down counter. The prescaler can be 1, 4, 8, and 16. The operation of
the modulus down counter is controlled by the MCCTL register. The MCFLG register records
the status of the modulus down counter. The contents of the MCCTL and MCFLG registers are
shown in Figures 8.32 and 8.33, respectively.
408
Chapter 8 ■ Timer Functions
Reset:
7
6
5
4
3
2
1
0
MCZI
MODMC
RDMCL
ICLAT
FLMC
MCEN
MCPR1
MCPR0
0
0
0
0
0
0
0
0
MCZI: modulus counter underflow interrupt enable bit
0 = modulus counter underflow interrupt is disabled.
1 = modulus counter underflow interrupt is enabled.
MODMC: modulus mode enable bit
0 = the counter counts once from the value written to it and will stop at $0000.
1 = modulus mode is enabled. When the counter reaches $0000, the counter is
loaded with the latest value written into to the modulus count register.
RDMCL: read modulus down counter load bit
0 = reads of the modulus count register will return the present value of the count
register.
1 = reads of the modulus count register will return the contents of the load
register (i.e., the reload value is returned).
ICLAT: input-capture force latch action bit
This bit has effect only when both the LATQ and BUFEN bits in ICSYS are set.
0 = no effect.
1 = forces the contents of the input-capture registers TC0 to TC3 and their corresponding 8-bit pulse accumulators to be latched into the associated holding registers.
The pulse accumulators will be cleared when the latch action occurs.
FLMC: force load register into the modulus counter count register bit
This bit has effect only when MCEN = 1.
0 = no effect.
1 = loads the load register into the modulus counter count register. This also resets
the modulus counter prescaler.
MCEN: modulus down counter enable bit
0 = modulus counter is disabled and preset to $FFFF.
1 = modulus counter is enabled.
MCPR1 and MCPR0: modulus counter prescaler select bits
0 0 = prescale rate is 1.
0 1 = prescale rate is 4.
1 0 = prescale rate is 8.
1 1 = prescale rate is 16.
Figure 8.32 ■ Modulus Down Counter register (MCCTL)
Reset:
7
6
5
4
MCZF
0
0
0
0
0
0
0
3
2
1
0
POLF3 POLF2 POLF1 POLF0
0
0
0
0
MCZF: modulus counter underflow interrupt flag
This flag is set when the modulus down counter reaches 0. Writing
1 to this bit clears the flag.
POLF3:POLF0: first input-capture polarity status bits
These are read-only bits. Writing to these bits has no effect. Each
status bit gives the polarity of the first edge that has caused an inputcapture to occur after capture latch has been read.
0 = the first input-capture has been caused by a falling edge.
1 = the first input-capture has been caused by a rising edge.
Figure 8.33 ■ Modulus Down Counter Flag register (MCFLG)
409
8.8 ■ Modulus Down Counter
8.8.1 Using the Modulus Down Counter to Generate Periodic Interrupts
Periodic interrupts can be generated by selecting the modulus mode and enabling its
interrupt. The interrupt period is equal to the value written into the load register multiplied
by the prescaler. When writing a value into MCCNT, the same value is also written into the
load register. In the modulus mode, when the MCCNT is decremented to zero, the value
in the load register will be reloaded into the MCCNT register and another cycle of down
counting is started.
Example 8.17
▼
Write an instruction sequence to generate periodic interrupt every 10 ms.
Solution: One possible value to be written into the MCCTL register is $C0, which will
• Enable MCCNT
• Enable MCCNT interrupt
• Enable modulus mode
• Set prescaler to 16
The instruction sequence to achieve the desired setting is as follows:
movb
movw
#$C7,MCCTL
#15000,MCCNT
cli
; place the value that will be decremented
; to 0 in 10 ms
; enable interrupt
▲
8.8.2 Using the Modulus Down Counter to Generate Time Delays
The method for generating a time delay by using the modulus down counter is similar to
that for generating periodic interrupts. However, one can choose to use either the modulus
mode or nonmodulus mode. The following example use the nonmodulus mode to generate a
time delay that is equal to 10 ms multiplied by the value in register Y.
Example 8.18
▼
Write a subroutine to generate a time delay that is equal to 10 ms multiplied by the value
in register Y.
Solution: The assembly program that can generate a delay that is equal to 10 ms multiplied by
the value in register Y is as follows:
delay10ms
bset
movb
movw
brclr
bclr
dbne
rts
TSCR1,TFFCA
#$07,MCCTL
#15000,MCCNT
MCFLG,MCZF,*
MCCTL,$04
y,delay10ms
; enable timer fast flag clear
; enable modulus down counter with 1:16 as prescaler
; load the value to be down-counted
; disable modulus down counter
▲
410
Chapter 8 ■ Timer Functions
The C language version of the function to be compiled by the CodeWarrior is as follows:
void delayby10ms(int k)
{
int i;
TSCR1 |5 TFFCA;
for (i 5 0; i , k; i11) {
MCCTL 5 0x07;
MCCNT 5 15000;
while(!(MCFLG & MCZF));
MCCTL &5 ,0x04;
}
}
// enable fast timer flag clear
// enable modulus down counter with 1:16 as prescaler
// let modulus down counter count down from 15,000
// disable modulus down counter
By storing the previous delay function in the appropriate directory, one can include it in
the program. The following program calls the previous subroutine to create a delay of 200 ms,
increments a count value, and then outputs this count value to Port B:
#include “c:\miniIDE\hcs12.inc”
org
$1000
count
ds.b
1
org
$1500
movb #$FF,DDRB
clr
count
movb count,PORTB
loops
ldy
#20
jsr
delayby10ms
inc
count
movb count,PORTB
bra
loops
#include “c:\miniIDE\delay.asm”
end
; create 200-ms delay
; “
; increment count by 1
; update LED displays
Delay functions that create time delays by a multiple of other delay times can be created by
modifying the previous delay function. The file delay.asm (and delay.c) contains the following
delay functions and is contained in the complementary CD:
1. delayby10μs
2. delayby50μs
3. delayby1ms
4. delayby10ms
5. delayby100ms
8.9 Enhanced Capture Timer (ECT) Module
Some of the HCS12 members (e.g., MC9S12DG256) implement an enhanced capture timer
(ECT) module that has the features of the standard timer (TIM) module enhanced by additional
features in order to enlarge the field of applications. These additional features are
• One 16-bit buffer register for each of the four input-capture (IC) channels.
• Four 8-bit pulse accumulators. Each of these 8-bit pulse accumulators has an
associated 8-bit buffer. Two of these 8-bit pulse accumulators can be concatenated
into a single 16-bit pulse accumulator.
411
8.9 ■ Enhanced Capture Timer (ECT) Module
• A 16-bit modulus down counter with 4-bit prescaler.
• Four user-selectable delay counters for increasing input noise immunity.
8.9.1 Enhanced Capture Timer Modes of Operation
The enhanced capture timer has eight input-capture/output-compare (IC/OC) channels, the
same as on the HCS12 standard timer module. Four IC channels (IC7, . . . , IC4) are the same as the
standard timer with one capture register that memorizes the timer value captured by an action on
the associated input pin. Four other IC channels (IC3, . . . , IC0), in addition to the capture register,
also have one buffer, called the holding register. This permits the register to memorize two different timer values without generating any interrupt. This feature can reduce software overhead
in applications that require capturing two edges in order to perform further computation. In addition, the ECT module provides the option of preventing a captured value from being overwritten
before it was read or transferred to the holding register. This option is controlled by the Input
Control Overwrite (ICOVW) register. The contents of this register are shown in Figure 8.34. This
capability will be useful when external events occur at a rate that the CPU cannot read quickly
enough.
7
6
5
4
3
2
1
0
NOVW7 NOVW6 NOVW5 NOVW4 NOVW3 NOVW2 NOVW1 NOVW0
Reset value
= 0x00
NOVWn: no input-capture overwrite
0 = the contents of the related capture register or holding register can be overwritten
when a new input-capture or latch occurs.
1 = the related capture register or holding register cannot be written by an event
unless it is empty. This will prevent the captured value to be overwritten until it
is read or latched in the holding register.
Figure 8.34 ■ Input Control Overwrite register (ICOVW)
Four 8-bit pulse accumulators are associated with the four IC buffered channels. Each pulse
accumulator has a holding register to memorize its value by an action on its external input.
Each pair of pulse accumulators can be used as a 16-bit pulse accumulator.
8.9.2 Why the Enhanced Capture Timer Module?
There are applications that require the capture of two consecutive edges (could be both rising or both falling, or one rising and the other falling) at very high frequencies. In Example 8.2,
the following instructions (faster than movw TCx,edge_ist by one E cycle) are executed before we
have time to wait for the arrival of the second edge (after we detect the first edge):
ldd
std
TCx
...
It takes five E-clock cycles to execute these two instructions, which set the upper limit on the
signal frequency that can be dealt with. By providing the capability of setting the interrupt flag
(or interrupting the CPU) after two signal edges have been captured, the upper limit of the signal frequency that can be handled can be significantly improved.
The input-capture function of the original standard timer module allows the newly captured
value to overwrite the old one even if the CPU has not read the old value yet. This can cause a
problem when the event frequency is very high. The enhanced input-capture function allows the
user to prevent the overwriting of captured values by enabling the nonoverwrite feature.
Chapter 8 ■ Timer Functions
8.9.3 The Operation of the Enhanced Input-Capture Function
The enhanced input-capture function has all the registers in the input-capture function of
the standard timer module. New registers are added to implement the additional features. An
input-capture register is empty if its value has been read or latched into its associated holding
register. A holding register is empty if its value has been read. An enhanced input-capture channel can be configured to operate in either latch mode or queue mode.
Figures 8.35 and 8.36 illustrate the registers related to the operation of the latch mode
and queue mode, respectively. In both diagrams, channels IC0 to IC3 are identical and IC4 to
IC7 are identical. Only one channel in each group is shown in the figure. The latch mode and
queue mode are selected by setting and clearing the LATQ bit of the ICSYS register (shown in
Figure 8.31).
÷1,2, . . . ,128
E-clock
PTx
Prescaler
Pin logic
16-bit free-running
E-clock
main timer
Delay
counter
÷1, 4, 8, 16
Prescaler
16-bit load register
16-bit modulus
down counter
Comparator
EDG x
TCx capture/compare
register
One IC channel
(IC0:IC3)
TCxH hold register
Latch
412
ICLAT, LATQ, BUFEN
(force latch)
To other IC channels
Write $0000 to
modulus counter
LATQ
(MDC latch enable)
PTi
Pin logic EDG i
MUX
EDG j
j=8–i
Comparator
TCx capture/compare
register
One IC channel
(IC4:IC7)
Figure 8.35 ■ Enhanced input-capture function block diagram in latch mode
413
8.9 ■ Enhanced Capture Timer (ECT) Module
÷1,2, . . . ,128
E clock
PTx
Prescaler
16-bit free-running
main timer
Pin logic
Delay
counter
÷1, 4, 8, 16
E clock
Prescaler
16-bit load register
16-bit modulus
down counter
Comparator
EDG x
TCx capture/compare
register
One IC channel
(IC0:IC3)
TCxH hold register
To other IC channels
Comparator
PTi
Pin logic EDGi
MUX
EDGj
TCx capture/compare
register
One IC channel
(IC4:IC7)
j =8–i
Figure 8.36 ■ Enhanced input-capture function block diagram in queue mode (channels
IC0...IC3 block diagram)
In the latch mode, the latching function occurs when the modulus down counter reaches 0
or a zero is written into the count register MCCNT. With a latching event, the contents of the
IC registers and 8-bit pulse accumulators are transferred to their holding registers. After this,
the 8-bit pulse accumulators are cleared. In the queue mode, the main timer value is copied
into the IC register (TCx register) by a valid input pin transition. With a new occurrence of a
capture, the value of the IC register is transferred to its holding register and the IC register holds
the new timer value. The queue mode and latch mode can be entered only when the holding
registers are enabled; this can be done by setting the BUFEN bit of the ICSYS register.
The HCS12 is often used in noisy environments such as automotive applications. Due to
the noise, false signal edges often occur in input-capture pins. To distinguish between true edge
and false edge (usually very short), a delay counter is added to the enhanced input-capture function. Any detected edge with a duration shorter than the preprogrammed value is ignored. The
duration of the delay is controlled by the DLYCT register. The contents of the DLYCT register
are shown in Figure 8.37. If enabled, after detection of a valid edge on the input-capture pin, the
delay counter counts the preselected number of E-clock cycles; then it generates a pulse to latch
the TCNT value into the input-capture register (TCx). The pulse is generated only if the level of
414
Chapter 8 ■ Timer Functions
7
6
5
4
3
2
1
0
0
0
0
0
0
0
DLY1
DLY0
Reset value
= 0x00
DLYx: delay counter select bits
0 0 − disabled (bypassed)
0 1 − 256 P clock cycles (P clock has the same period as E-clock)
1 0 − 512 P clock cycles
1 1 − 1024 P clock cycles
Figure 8.37 ■ Delay Counter Control register (DLYCT)
input signal, after the preset delay, is the opposite of the level before the transition. This avoids
reaction to narrow pulses caused by noise. After counting, the counter is cleared automatically.
Example 8.19
▼
Modify the program in Example 8.2 to take advantage of the queue mode of the enhanced
input-capture function.
Solution: The modified program is as follows:
#include “c:\miniide\hcs12.inc”
org
$1000
period
ds.w
1
org
$1500
start
movb
#$90,TSCR
bclr
TIOS,IOS0
movb
#$04,TSCR2
movb
movb
#$01,TCTL4
#$0A,ICSYS
clr
bset
ldd
ldd
brclr
ldd
subd
std
swi
end
DLYCT
ICOVW,NOVW0
TC0
TC0H
TFLG1,$FE,*
TC0
TC0H
period
; memory to store the period
; enable timer counter and fast timer flag clear
; select input-capture 0
; disable TCNT overflow interrupt, set prescale
; factor to 16
; choose to capture the rising edge of PT0 pin
; enable timer flag-setting mode, IC buffer, and queue
; mode
; disable delay counter
; no input-capture overwrite for IC0
; empty the input-capture register TC0
; empty the holding register TC0H
; wait for the arrival of the second rising edge
; subtract the first edge from the second edge
The C language version of the program is as follows:
#include “c:\cwHCS12\include\hcs12.h”
void main(void)
{
unsigned int
period;
TSCR1
5 0x90;
/* enable timer counter, enable fast timer flag clear*/
TIOS
&5 ,IOS0;
/* enable input-capture 0 */
8.9 ■ Enhanced Capture Timer (ECT) Module
TSCR2
5 0x04;
/* set prescale factor to 16 */
TCTL4
5 0x01;
/* capture the rising edge of PT0 pin */
/* enable timer flag-setting mode, IC buffer, and queue mode */
ICSYS
5 0x0A;
DLYCT
5 0x00;
/* disable delay counter */
ICOVW
|5 NOVW0;
/* disable input-capture overwrite */
period
5 TC0;
/* empty TC0 and clear the C0F flag */
period
5 TC0H;
/* empty the TC0H register */
/* wait for the arrival of the second rising edge */
while (!(TFLG1 & C0F));
period
5 TC0 2 TC0H;
while(1);
}
415
▲
Example 8.20
▼
Suppose we want to measure the pulse width of a signal connected to the PT0 pin in a
noisy environment. Write a program to perform the operation. Ignore any noise pulse shorter
than 256 E-clock cycles.
Solution: Since the range of the pulse width is unknown, we need to consider the timer overflow. The program in Example 8.3 is modified to perform the measurement as follows:
#include
edge1
overflow
pulse_width
“c:\miniide\hcs12.inc”
org
$1000
rmb
2
rmb
2
rmb
2
org
$1500
movw
#tov_isr,UserTimerOvf ; set up timer overflow interrupt vector
ldd
#0
std
overflow
movb
#$90,TSCR1
; enable TCNT and fast timer flag clear
movb
#$04,TSCR2
; set prescaler to TCNT to 16
bclr
TIOS,IOS0
; enable input-capture 0
movb
#$01,DLYCT
; set delay count to 256 E cycles
movb
#$01,ICOVW
; prohibit overwrite to TC0 register
movb
#$0,ICSYS
; disable queue mode
movb
#$01,TCTL4
; capture the rising edge on PT0 pin
movb
#C0F,TFLG1
; clear the C0F flag
brclr
TFLG1,C0F,*
; wait for the arrival of the first rising edge
movb
#$80,TFLG2
; clear the TOF flag
bset
TSCR2,TOI
; enable TCNT overflow interrupt
cli
;
“
movw
TC0,edge1
; clear the C0F flag and save the captured first edge
movb
#$02,TCTL4
; capture the falling edge on PT0 pin
“
brclr
TFLG1,C0F,*
; wait for the arrival of the falling edge
ldd
TC0
416
Chapter 8 ■ Timer Functions
subd edge1
std
pulse_width
bcc
next
; second edge is smaller, so decrement overflow count by 1
ldx
overflow
dex
stx
overflow
next
swi
tov_isr movb #TOF,TFLG2
; clear the TOF flag
ldx
overflow
; increment TCNT overflow count
inx
;
“
stx
overflow
;
“
rti
end
▲
The modification to the C program in Example 8.3 is also minor and hence is left as an
exercise problem.
8.10 Pulse-Width Modulation (PWM) Function
There are many applications that require the generation of digital waveforms. The output-compare function has been used to generate digital waveforms with any duty cycle in Section 8.6. However, the generation of waveforms using the output-compare function requires
frequent attention from the MCU. Most microcontrollers designed in the last few years have
incorporated the Pulse-Width Modulation (PWM) function to simplify the task of waveform
generation.
The MC9S12DG256 and many other HCS12 members implement an 8-channel, 8-bit PWM
function. As shown in Figure 8.38, each channel has a period register, a duty cycle register, a
control register, and a dedicated counter to support the waveform generation. The clock signal
is critical to the setting of the frequency of the generated waveform. The clock source of the
counter is programmable through a two-stage circuitry.
The two most important characteristics of a PWM waveform are the period (or frequency)
and the duty cycle of the waveform. The clock source and the period register together determine the period of the generated waveform whereas the clock select chain sets the frequency
of the clock source to the PWM counter. The period of the PWM waveform is set by placing
an appropriate value into the period register and setting the clock select block properly. The
duty cycle is determined by the ratio of the duty register and the period register.
8.10.1 PWM Clock Select
There are four possible clock sources for the PWM function: clock A, clock B, clock SA
(scaled A), and clock SB (scaled B). These four clocks are derived from the E-clock. Clocks A and
B are derived by dividing the E-clock by a factor of 1, 2, 4, 8, 16, 32, 64, or 128. Clock SA (SB) is
derived by dividing clock A (B) by an 8-bit reloadable counter.
Each PWM channel has the option of selecting one of two clocks, either the prescaled clock
(clock A or B) or the scaled clock (clock SA or SB). Figure 8.39 illustrates the block diagram of
the four different clocks and how the scaled clocks are created.
417
8.10 ■ Pulse-Width Modulation (PWM) Function
PWM module
PWM channels
Channel 7
Period and duty
E-clock
Clock select
PWM
clock
Counter
PWM7
Counter
PWM6
Counter
PWM5
Counter
PWM4
Counter
PWM3
Counter
PWM2
Counter
PWM1
Counter
PWM0
Channel 6
Period and duty
Control
Channel 5
Period and duty
Channel 4
Enable
Polarity
Period and duty
Channel 3
Period and duty
Alignment
Channel 2
Period and duty
Channel 1
Period and duty
Channel 0
Period and duty
Figure 8.38 ■ HCS12 PWM block diagram
PRESCALE
The input clock to the PWM prescaler is the E-clock. It can be disabled whenever the MCU
is in freeze mode by setting the PFRZ bit in the PWMCTL register. If this bit is set, whenever the MCU is in freeze mode the active clock to the prescaler is disabled. This is useful for
emulation in order to freeze the PWM. The input clock can also be disabled when all eight
PWM channels are disabled (PWME7–0 5 0). This is useful for reducing power consumption by
disabling the prescale counter.
Chapter 8 ■ Timer Functions
Clock A
M
U
X
Clock to
PWM ch 0
Clock A/2,A/4,A/6, . . . , A/512
PCLK0
8-bit down
counter
Count = 1
Load
PWMSCLA
DIV 2
Clock SA
M
U
X
Clock to
PWM ch 1
PCLK1
M
M
U
X
Clock to
PWM ch 2
16 32 64 128
X
PCLK2
8
M
U
X
Clock to
PWM ch 3
4
PCLK3
M
U
X
2
Divide by prescaler taps
U
M
U
PCLK4
X
M
U
X
PWMSCLB
DIV 2
Clock SB
Clock to
PWM ch 4
Clock to
PWM ch 5
PCLK5
Load
PWME7-0
PFRZ
Freeze mode signal
8-bit down
counter
E-clock
418
Count = 1
Clock B/2,B/4,B/6, . . . , B/512
Clock B
M
U
X
PCLK6
M
U
X
PCLK7
Prescale
Scale
Figure 8.39 ■ PWM clock select block diagram
Clock to
PWM ch 6
Clock select
Clock to
PWM ch 7
419
8.10 ■ Pulse-Width Modulation (PWM) Function
Clock A and clock B are scaled values of the input clock. The possible values for clock A
and clock B are derived by dividing the E-clock by 1, 2, 4, 8, 16, 32, 64, and 128. The value selected for clock A and clock B is determined by the PCKA2,PCKA0 and PCKB2,PCKB0 bits
in the PWMPRCLK register, respectively. The contents of the PWMCTL and PWMPRCLK registers are shown in Figure 8.40 and Figure 8.41, respectively. Tables 8.3 and 8.4 show the available prescale values for clock A and clock B.
Reset:
7
6
5
4
3
2
1
0
CON67
CON45
CON23
CON01
PSWAI
PFRZ
0
0
0
0
0
0
0
0
0
0
CONjk: concatenate channels j and k ( j = 0, 2, 4, or 6; k = j + 1)
0 = channels j and k are separate 8-bit PWMs.
1 = channels j and k are concatenated to create one 16-bit PWM channel. Channel j
becomes the high-order byte and channel k becomes the low-order byte. Channel
k output pin is used as the output for this 16-bit PWM. Channel k clock select bit
determines the clock source, channel k polarity bit determines the polarity,
channel k enable bit enables the output,and channel k center-aligned enable bit
determines the output mode.
PSWAI: PWM stops in wait mode
0 = allow the clock to the prescaler to continue while in wait mode.
1 = stop the input clock to the prescaler whenever the MCU is in wait mode.
PFRZ: PWM counters stop in freeze mode
0 = allow PWM to continue while in freeze mode.
1 = disable PWM input clock to the prescaler whenever the part is in freeze mode.
Figure 8.40 ■ PWM Control register (PWMCTL)
Reset:
7
6
5
4
3
2
1
0
0
PCKB2
PCKB1
PCKB0
0
PCKA2
PCKA1
PCKA0
0
0
0
0
0
0
0
0
Figure 8.41 ■ PWM Prescale Clock Select register (PWMPRCLK)
PCKB2 PCKB1 PCKB0 Value of Clock
B
0
0
0
0
1
1
1
1
0
0
1
1
0
0
1
1
0
1
0
1
0
1
0
1
E-clock
E-clock/2
E-clock/4
E-clock/8
E-clock/16
E-clock/32
E-clock/64
E-clock/128
Table 8.3 ■ Clock B prescaler selects
PCKA2 PCKA1 PCKA0
0
0
0
0
1
1
1
1
0
0
1
1
0
0
1
1
0
1
0
1
0
1
0
1
Value of Clock
A
E-clock
E-clock/2
E-clock/4
E-clock/8
E-clock/16
E-clock/32
E-clock/64
E-clock/128
Table 8.4 ■ Clock A prescaler selects
CLOCK SCALE
The SA clock takes clock A as one of its inputs and divides it further with a user-programmable
value (from 1 to 256) and then divides it by 2. The SB clock is derived similarly, but with clock B as
its input.
420
Chapter 8 ■ Timer Functions
In Figure 8.39, clock A is an input to an 8-bit down counter. This down counter loads a
user-programmable scale value from the scale register (PWMSCLA). When the down counter is
decremented to 1, two things happen: A pulse is output and the 8-bit counter is reloaded. The
output signal from this circuit is further divided by 2. In other words, the clock SA is derived by
the following equation:
Clock SA 5 clock A/(2 * PWMSCLA)
When PWMSCLA equals $00, the PWMSCLA value is considered a full-scale value of 256.
Similarly,
Clock SB 5 clock B/(2 * PWMSCLB)
CLOCK SELECT
Each PWM channel has a choice of two clock signals to use as the clock source for that
channel. The clock source selection is done by the PWMCLK register. The contents of this register are shown in Figure 8.42.
Reset:
7
6
5
4
3
2
1
0
PCLK7
PCLK6
PCLK5
PCLK4
PCLK3
PCLK2
PCLK1
PCLK0
0
0
0
0
0
0
0
0
PCLKx: PWM channel x clock select (x = 7, 6, 3, 2)
0 = clock B as the clock source
1 = clock SB as the clock source
PCLKy: PWM channel y clock select (y = 5, 4, 1, 0)
0 = clock A as the clock source
1 = clock SA as the clock source
Figure 8.42 ■ PWM Clock Select register (PWMCLK)
8.10.2 PWM Channel Timers
The main part of the PWM module consists of the timers. Each of the PWM channels has
an 8-bit counter, an 8-bit period register, and an 8-bit duty cycle register.
The waveform output period is controlled by a match between the period register and the
value in the counter. The duty cycle is controlled by a match between the duty cycle register
and the counter value that causes the state of the output to change during the period. The starting polarity of the output is selectable on a per-channel basis and is selected by programming
the PWMPOL register. The contents of the PWMPOL register are shown in Figure 8.43.
Reset:
7
6
5
4
3
2
1
0
PPOL7
PPOL6
PPOL5
PPOL4
PPOL3
PPOL2
PPOL1
PPOL0
0
0
0
0
0
0
0
0
PPOLx: PWM channel x polarity
0 = PWM channel x output is low at the start of a period, then goes high when the
duty count is reached.
1 = PWM channel x output is high at the start of a period, then goes low when the
duty count is reached.
Figure 8.43 ■ PWM Polarity register (PWMPOL)
421
8.10 ■ Pulse-Width Modulation (PWM) Function
The block diagram of a PWM channel is shown in Figure 8.44. A PWM channel must be
enabled to work. It is enabled by setting a bit in the PWME register. The contents of the PWME
register are shown in Figure 8.45. There is an edge-synchronizing circuit (labeled as GATE in
Figure 8.44) to guarantee that the clock will only be enabled or disabled at an edge.
Clock
source
8-bit counter
GATE
PWMCNTx
up/
down
From port PTP
data register
Reset
(Clock edge sync)
8-bit compare =
T
Q
R
Q
PWMDTYx
M
U
X
M
U
X
To pin
driver
PPOLx
8-bit compare =
PWMPERx
Q
T
Q
R
CAEx
PWMEx
Figure 8.44 ■ PWM channel block diagram
Reset:
7
6
5
4
3
2
1
0
PWME7
PWME6
PWME5
PWME4
PWME3
PWME2
PWME1
PWME0
0
0
0
0
0
0
0
0
PWMEx: PWM channel x enable
0 = PWM channel x disabled.
1 = PWM channel x enabled.
Figure 8.45 ■ PWM Enable register (PWME)
8.10.3 PWM Waveform Properties
There are four major properties in a PWM waveform: polarity, alignment, period, and duty cycle.
422
Chapter 8 ■ Timer Functions
PWM POLARITY
The polarity of a PWM waveform refers to the voltage level (high or low) that a PWM waveform starts with in each period. Each PWM channel has a polarity bit to allow starting a waveform cycle with a high or low level. This is shown in Figure 8.44 as a MUX select of either the
Q or Q output of the PWM output flip-flop. When the PPOLx bit is 0, the Q output is selected
and the PWM output will start with a low. Otherwise, the output Q is selected and the PWM
output starts with a high.
PWM PERIOD AND DUTY
Dedicated period and duty registers exist for each channel and are double buffered so that
if they change while the channel is enabled, the change will not take effect until one of the following occurs:
• The effective period ends.
• The counter is written (counter is reset to $00).
• The channel is disabled.
In this way, the output will always be either the old waveform or the new waveform, not some
variation in between. If the channel is not enabled, then writes to the period and duty registers
will go directly to the latches and the buffer. A change in duty or period can be forced into effect immediately by writing the new values to the duty and/or period registers and then writing
to the counter. This forces the counter to reset and the new duty and/or period values to be
latched.
PWM COUNTERS
Each channel has a dedicated 8-bit up-and-down counter that runs at the rate of the selected clock source. The counter is compared to the duty and period registers in each clock
cycle. When the counter matches the duty register, the output flip-flop changes state, causing
the PWM waveform to also change state. A match between the PWM counter and the period
register behaves differently depending on what output mode is selected.
Any value written to the counter causes the counter to reset to $00 and start to count
up, both the duty and period registers to be loaded with values from their buffers, and the
output to change according to the polarity bit. When the channel is disabled, the counter
stops.
P W M W AV E F O R M A L I G N M E N T
The PWM timer provides the choice of two types of outputs: left aligned and center
aligned. A left-aligned waveform has two line segments in each period whereas a centeraligned waveform has three line segments in one period. They are selected with the CAEx bits
in the PWMCAE register. The contents of the PWMCAE register are shown in Figure 8.46. If
the CAEx bit is 0, the corresponding PWM output will be left aligned.
Reset:
7
6
5
4
3
2
1
0
CAE7
CAE6
CAE5
CAE4
CAE3
CAE2
CAE1
CAE0
0
0
0
0
0
0
0
0
CAEx: center-aligned enable bit for channel x
0 = PWM channel x output is left aligned.
1 = PWM channel x output is center aligned.
Figure 8.46 ■ PWM Center Align Enable register (PWMCAE)
8.10 ■ Pulse-Width Modulation (PWM) Function
423
LEFT-ALIGNED OUTPUT
In the left-aligned output mode, the 8-bit counter is configured as an up counter only. When
the PWM counter equals the duty register, the output flip-flop changes state; this causes the PWM
waveform to also change state. A match between the PWM counter and the period register resets the
counter and the output flip-flop. The counter counts from 0 to the value in the period register – 1.
The waveform of the left-aligned mode is shown in Figure 8.47. The frequency of the PWM
output is given by the following equation:
PWMx frequency 5 clock (A, B, SA, or SB) 4 PWMPERx
The duty cycle of the waveform depends on the selected polarity.
Polarity 5 0,
PWMx duty cycle 5 [(PWMPERx 2 PWMDTYx) 4 PWMPERx] 3 100%
Polarity 5 1,
PWMx duty cycle 5 [PWMDTYx 4 PWMPERx] 3 100%
PPOLx = 0
PPOLx = 1
PWMDTYx
Period = PWMPERx
Figure 8.47 ■ PWM left-aligned output waveform
CENTER-ALIGNED MODE
In this mode, the 8-bit PWM counter operates as an up-and-down counter and is set to count
up whenever the counter is equal to $00. The counter compares with two registers, a duty register and a period register, in each clock cycle. When the counter matches the duty register, the
output flip-flop changes state, causing the PWM waveform to also change state. A match between
the PWM counter and the period register changes the counter direction from an up count to a
down count. When the PWM counter decrements and matches the duty register again, the output
flip-flop changes state, causing the PWM output to also change state. When the PWM counter
decrements and reaches zero, the counter direction changes from a down count back to an up
count and the period and duty registers are reloaded from their buffers. Since the PWM counter
counts from 0 up to the value in the period register and then back down to 0, the effective period
is PWMPERx 3 2.
The output waveform of the center-aligned mode is shown in Figure 8.48. The frequency of
the center-aligned PWM output can be calculated using the following expression:
PWMx frequency 5 clock(A, B, SA, or SB) 4 (2 × PWMPERx)
The duty cycle of the waveform depends on the selected polarity.
Polarity 5 0,
PWMx duty cycle 5 [(PWMPERx 2 PWMDTYx) 4 PWMPERx] 3 100%
Polarity 5 1,
PWMx duty cycle 5 [PWMDTYx 4 PWMPERx] 3 100%
PWM 16-BIT FUNCTIONS
Two 8-bit PWM modules can be concatenated into one 16-bit PWM module. The concatenation of the PWM channels is controlled by the CON bits of the PWMCTL register. The 16-bit
mode PWM system is illustrated in Figure 8.49.
424
Chapter 8 ■ Timer Functions
PP0Lx = 0
PP0Lx = 1
PWMDTYx
PWMDTYx
PWMPERx
PWMPERx
Period = PWMPERx * 2
Figure 8.48 ■ PWM center-aligned output waveform
Clock source 7
High
Low
PWMCNT6
PWMCNT7
PWM7
Period/Duty compare
Clock source 5
High
Low
PWMCNT4
PWMCNT5
Period/Duty compare
PWM5
Clock source 3
High
Low
PWMCNT2
PWMCNT3
PWM3
Period/Duty compare
Clock source 1
High
Low
PWMCNT0
PWMCNT1
Period/Duty compare
PWM1
Figure 8.49 ■ PWM 16-bit mode
When PWM channels k and k 1 1 are concatenated, channel k is the high-order channel,
whereas channel k 1 1 is the low-order channel. When using the 16-bit concatenated mode, the
clock source is determined by the low-order 8-bit channel clock select control bits. That is, it is
determined by channel k 1 1 (k 5 0, 2, 4, or 6) when channel k and k 1 1 are concatenated. The
resulting PWM is output to the pin of the corresponding low-order 8-bit channel. The polarity
425
8.10 ■ Pulse-Width Modulation (PWM) Function
of the resulting PWM output is controlled by the PPOLx bit of the corresponding low-order
8-bit channel as well.
Once concatenated mode is enabled, then enabling or disabling the corresponding 16-bit
PWM channel is controlled by the low-order PWMEx bit. In this case, the PWMEx bits of the
high-order bytes have no effect and their corresponding PWM outputs are disabled. In concatenated mode, writes to the 16-bit counter by using a 16-bit access or writes to either the low- or
high-order byte of the counter will reset the 16-bit counter. Reads of the 16-bit counter must be
made by a 16-bit access to maintain data coherency. Either left-aligned or center-aligned output
mode can be used in concatenated mode and is controlled by the low-order CAEx bit. The highorder CAEx bit has no effect.
PWM BOUNDARY CASES
Table 8.5 summarizes the boundary conditions for the PWM regardless of the output mode
(applicable to both the 8-bit and 16-bit PWM modes).
PWMDTYx
PWMPERx
PPOLx
PWMx Output
$00
>$00
1
Always low
>$00
0
Always high
$001
1
Always high
0
Always low
(indicates no duty)
$00
(indicates no duty)
xx
(indicates no period)
$001
xx
(indicates no period)
.5 PWMPERx
xx
1
Always high
.5 PWMPERx
xx
0
Always low
Note: 1. Counter 5 $00 and does not count.
Table 8.5 ■ PWM boundary cases
EMERGENCY PWM SHUTDOWN
The PWM system can be shut down under emergency conditions. The emergency shutdown
is controlled by the PWMSDN register. The contents of this register are shown in Figure 8.50.
Example 8.21
▼
Write an instruction sequence to program the PWM channel 0 to output a waveform with
50 percent duty cycle and 100-kHz frequency. Assume that the E-clock is 24 MHz.
Solution: To achieve the 50 percent duty cycle, 100-kHz PWM output, we use the following
parameters:
• Clock source prescale factor set to 2
• Clock A selected as the clock input to PWM channel 0
426
Chapter 8 ■ Timer Functions
• Left-aligned mode
• The value 120 written into the PWMPER0 register (frequency is 24 MHz 4 120 4 2 5
100 kHz)
• The value 60 written into the PWMDTY0 register
The following instruction sequence will perform the configuration:
#include “c:\miniide\hcs12.inc”
...
movb
#0,PWMCLK
movb
#1,PWMPRCLK
movb
#1,PWMPOL
movb
#0,PWMCAE
movb
#$0C,PWMCTL
movb
#120,PWMPER0
movb
#60,PWMDTY0
movb
#0,PWMCNT0
bset
PWMEN,PWME0
Reset:
; select clock A as the clock source for PWM0
; set clock A prescaler to 2
; channel 0 output high at the start of the period
; select left-aligned mode
; 8-bit mode, stop PWM in wait and freeze mode
; set period value
; set duty value
; reset the PWM0 counter
; enable PWM channel 0
▲
7
6
5
4
3
2
1
0
PWMIF
PWMIE
PWMRSTRT
PWMLVL
0
PWM7IN
PWM7INL
PWM7ENA
0
0
0
0
0
0
0
0
PWMIF: PWM interrupt flag
0 = no change on PWM7IN input.
1 = change on PWM7IN input.
PWMIE: PWM interrupt enable
0 = PWM interrupt is disabled.
1 = PWM interrupt is enabled.
PWMRSTRT: PWM restart
The PWM can only be restarted if the PWM channel input 7 is de-asserted. After writing a logic 1 to
this bit, the PWM channels start running after the corresponding counter passes the next "counter == 0"
phase.
PWMLVL: PWM shutdown output level
0 = PWM outputs are forced to 0.
1 = PWM outputs are forced to 1.
PWM7IN: PWM channel 7 input status
This bit reflects the current status of the PWM7 pin.
PWM7INL: PWM shutdown active input level for channel 7
0 = active level is low.
1 = active level is high.
PWM7ENA: PWM emergency shutdown enable
If this bit is logic 1, the pin associated with channel 7 is forced to input and the emergency shutdown
feature is enabled. All the other bits in this register are meaningful only if PWM7ENA = 1.
0 = PWM emergency feature is disabled.
1 = PWM emergency feature is enabled.
Figure 8.50 ■ PWM Shutdown register (PWMSDN)
8.10 ■ Pulse-Width Modulation (PWM) Function
427
Example 8.22
▼
Assume that the E-clock frequency is 24 MHz. Write an instruction sequence to generate a
square wave with a period of 20 μs and 60 percent duty cycle using PWM channel 0. Use centeraligned mode.
Solution: Select clock A with prescale factor 2 as the clock source to PWM0, then
20 μs 5 2/24 μs 3 PWMPER0 3 2 ⇒ PWMPER0 5 120
Select the PWM0 waveform to start with high level. Then the value to be loaded into the
PWMDTY0 is computed as follows:
60% 5 (PWMDTY0 4 PWMPER0) 3 100% ⇒ PWMDTY0 5 72
We need to choose the following parameters for this waveform:
• Clock source prescale factor set to 2
• Clock A selected as the clock input to PWM channel 0
• Center-aligned mode with PPOL0 equal to 1
• The value 120 written into the PWPER0 register
• The value 72 written into the PWDTY0 register
The following instruction sequence will configure the PWM0 properly:
movb
movb
movb
movb
movb
movb
movb
bset
#0,PWMCLK
#1,PWMPOL
#1,PWMPRCLK
#1,PWMCAE
#$0C,PWMCTL
#120,PWMPER0
#72,PWMDTY0
PWME,PWME0
; select clock A as the clock source
; set PWM0 output to start with high level
; set the PWM0 prescaler to clock A to 2
; select PWM0 center-aligned mode
; select 8-bit mode, stop PWM in wait mode
; set period value
; set duty value
; enable PWM channel 0
The following C language statements will configure PWM0 properly:
PWMCLK
PWMPOL
PWMCTL
PWMCAE
PWMPRCLK
PWPER0
PWDTY0
PWEN
5 0;
// select clock A as the clock source to PWM0
5 1;
// PWM0 output start with high level
5 0x0C;
// select 8-bit PWM0
5 1;
// PWM0 center aligned
5 1;
// set clock A prescaler to 2
5 120;
5 72;
|5 PWME0; // enable PWM0
▲
Example 8.23
▼
Assume that the E-clock frequency is 24 MHz. Write an instruction sequence to generate a 50-Hz digital waveform with 80 percent duty cycle using the 16-bit mode from the
PWM1 pin output.
428
Chapter 8 ■ Timer Functions
Solution: The ratio of 24 MHz and 50 Hz is 480,000. One of the possible breakdowns of this
number is 16 times 30,000. Therefore, one of the possible settings is as follows:
• Select clock A as the clock source.
• Set the prescaler to 16.
• Select left-aligned mode.
• Select polarity of the waveform to be 1.
• Load the value of 30,000 into the PWMPER0:PWMPER1.
• Load the value of 24,000 into the PWMDTY0:PWMDTY1.
The following instruction sequence will achieve the desired setting:
movb #0,PWMCLK
movb #2,PWMPOL
movb #4,PWMPRCLK
movb #$1C,PWMCTL
movb #0,PWMCAE
movw #30000,PWMPER0
movw #24000,PWMDTY0
bset PWME,PWME1
; select clock A as the clock source
; set PWM0:PWM1 output to start with high level
; set prescaler to 16
; concatenate PWM0:PWM1, stop PWM in wait mode
; select left align mode
; set period to 30000
; set duty to 24000
; enable PWM0:PWM1
▲
The PWM function can be used in many applications that require the average value of
output voltages. The lamp dimmers and DC motor speed control are two examples of the PWM
applications.
Example 8.24
▼
Using PWM in dimming the light. Suppose we are using the PWM0 of the HCS12 to control the
brightness of a light bulb. The circuit connection is shown in Figure 8.51. Write a program so that
the light is turned down to 10 percent brightness gradually in 5 s. The E-clock frequency is 24 MHz.
Solution: We dim the light in the following manner: Use the PWM0 output to control the
brightness of the light bulb. Set the duty cycle to 100 percent at the beginning and then dim the
brightness by 10 percent in the first second, and then 20 percent per second in the following 4 s.
Use 100 as the initial duty and period values.
5V
HCS12
R1
Light
bulb
R2
PWM0
Figure 8.51 ■ Using PWM0 function to dim the light
8.10 ■ Pulse-Width Modulation (PWM) Function
429
Since the brightness of the light is proportional to the average current that flows through
the bulb, we can dim the light bulb by reducing the duty cycle of the PWM output from 100
percent down to 10 percent in 5 s. We will reduce the duty cycle in steps. In 1 s, we reduce the
duty value 10 times. Therefore, we reduce the duty value by 1 every 100 ms in the first second
and reduce the duty value by 2 every 100 ms in the following 4 s.
The assembly program that implements this idea is as follows:
#include “c:\miniide\hcs12.inc”
org
$1000
dim_cnt rmb
1
org
$1500
movb
#0,PWMCLK
; select clock A as the clock source
movb
#1,PWMPOL
; make waveform to start with high level
movb
#$0C,PWMCTL
; select 8-bit mode
movb
#2,PWMPRCLK
; set clock prescaler to 4
movb
#0,PWMCAE
; select left-aligned mode
movb
#100,PWMPER0
; set period value (PWM frequency doesn’t matter)
movb
#100,PWMDTY0
; set duty value
bset
PWME,PWME0
; enable PWM channel 0
; The following instruction segment reduces duty count by 1 per 100 ms
movb
#10,dim_cnt
loop1
ldy
#1
jsr
delayby100ms
dec
PWMDTY0
; decrement duty by 1
dec
dim_cnt
bne
loop1
; The following instruction segment reduces duty count by 2 per 100 ms in 4 s
movb
#40,dim_cnt
loop2
ldy
#1
; wait for 100 ms
jsr
delayby100ms
; “
dec
PWMDTY0
; decrement duty cycle by 2%
dec
PWMDTY0
; per 100 ms
dec
dim_cnt
bne
loop2
swi
#include “c:\miniide\delay.asm” ; include delayby100ms here
end
The C language version of this program is as follows:
#include
“c:\cwHCS12\include\hcs12.h”
#include
“c:\cwHCS12\include\delay.h”
void main ()
{
int dim_cnt;
PWMCLK 5 0;
// select clock A as the clock source
PWMPOL 5 1;
// make waveform to start with high level
PWMCTL 5 0x0C;
// select 8-bit mode
PWMPRCLK 5 2;
// set clock prescaler to 4
PWMCAE 5 0;
// select left aligned mode
PWMPER0 5 100;
// set period of PWM0 to 0.1 ms
PWMDTY0 5 100;
// set duty cycle to 100%
430
Chapter 8 ■ Timer Functions
PWME
|5 0x01;
// enable PWM0 channel
// reduce duty cycle 1% per 100 ms in the first second
for (dim_cnt 5 0; dim_cnt < 10 ; dim_cnt11) {
delayby100ms(1);
PWMDTY022;
}
// reduce duty cycle 2% per 100 ms in the next 4 s
for (dim_cnt 5 0; dim_cnt , 40; dim_cnt11) {
delayby100ms(1);
PWMDTY0 25 2;
}
while(1);
}
▲
8.11 DC Motor Control
DC motors are used extensively in control systems as positional devices because their
speeds and torques can be precisely controlled over a wide range. The DC motor has a permanent magnetic field and its armature is a coil. When a voltage and a subsequent current flow are
applied to the armature, the motor begins to spin. The voltage level applied across the armature
determines the speed of rotation.
The microcontroller can digitally control the angular velocity of a DC motor by monitoring
the feedback lines and driving the output lines. Almost every application that uses a DC motor
requires it to reverse its direction of rotation or vary its speed. Reversing the direction is done
by changing the polarity of the voltage applied to the motor. Changing the speed requires varying the voltage level of the input to the motor, and that means changing the input level to the
motor driver. In a digitally controlled system, the analog signal to the driver must come from
some form of D/A converter. However, adding a D/A converter to the circuit increases the chip
count, which means increasing the system cost and power consumption. The other alternative
is to vary the pulse width of a digital signal input to the motor. By varying the pulse width, the
average voltage delivered to the motor changes and so does the speed of the motor. The HCS12
PWM subsystem can be used to control the DC motor.
The HCS12 can interface with a DC motor through a driver, as shown in Figure 8.52. This
circuit takes up only three I/O pins. The pin that controls the direction can be an ordinary I/O pin,
HCS12
PP7
PP3
PT0
Direction
Speed
On/off
Driver
DC motor
Feedback
Figure 8.52 ■ Simplified circuit for DC motor control
431
8.11 ■ DC Motor Control
but the pin that controls the speed must be a PWM pin. The pin that receives the feedback must be
an input-capture pin.
Although some DC motors can operate at 5 V or less, the HCS12 cannot supply the necessary current to drive a motor directly. The minimum current required by any practical DC motor is much higher than any microcontroller can supply. Depending on the size and rating of the
motor, a suitable driver must be selected to take control signals from the HCS12 and deliver the
necessary voltage and current to the motor.
8.11.1 Drivers
Standard motor drivers are available in many current and voltage ratings. Examples are the
L293 from ST microelectronics and SN754410 from TI. These two chips are pin compatible.
The SN754410 has four channels and can output up to 1 A of current per channel with a supply
of 36 V. It has a separate logic supply and takes a logic input (0 or 1) to enable or disable each
channel. The SN754410 also includes clamping diodes needed to protect the driver from the
back electromagnetic frequency (EMF) generated during the motor reversal. The pin assignment
and block diagram of the SN754410 are shown in Figure 8.53. There are two supply voltages:
VCC1 and VCC2. VCC1 is the logic supply voltage, which can be from 4.5 to 36 V (normally 5.0 V).
VCC2 is the analog supply voltage and can be as high as 36 V.
VCC1
SN754410
1,2EN
1
16
VCC1
1A
2
15
4A
1Y
3
14
4Y
4
GND
5
1
0
1
0
12
16
15
2
3
1
6
11
3Y
2A
7
10
3A
13
12
8
9
3,4EN
5
11
6
VCC2
(a) Pin assignment
M
3
4
M
GND
2Y
1
0
14
13
SN754410
1
1
0
VS
7
2
8
4
10
9
1
0
1
0
M
(b) Motor connection
Figure 8.53 ■ Motor driver SN754410 pin assignment and motor connection
8.11.2 Feedback
The DC motor controller needs information to adjust the voltage output to the motor
driver circuit. The most important information is the speed of the motor, which must be fed
back from the motor by a sensing device. The sensing device may be an optical encoder, infrared detector, Hall-effect sensor, and so on. Whatever the means of sensing is, the result is a
signal, which is fed back to the microcontroller. The microcontroller can use the feedback to
determine the speed and position of the motor. Then it can make adjustments to increase or
decrease the speed, reverse the direction, or stop the motor.
432
Chapter 8 ■ Timer Functions
Magnets
Hall-effect
transistor
t
T/2
T is the time for one revolution
Figure 8.54 ■ The output waveform of the Hall-effect transistor
Assume that a Hall-effect transistor is mounted on the armature (stator) of a DC motor and
two magnets are mounted on the shaft (rotor). As shown in Figure 8.54, every time the magnet passes by the Hall-effect transistor, a pulse is generated. The input-capture function of the
HCS12 can capture the passing time of the pulse. The time between two captures is half of a
revolution. Thus the motor speed can be calculated. By storing the value of the capture registers
each time and comparing it with its previous value, the controller can constantly measure and
adjust the speed of the motor. Using this method, a motor can be run at a precise speed or be
synchronized with another event.
VCC
HCS12
SN754410
1
8
2
7
PWM3
PP7
VCC
4
5
9
10
NC
11
VCC
16
3
15
13
12
6.8 μF
0.33 μF
M
6
14
VCC
6.8 μF
1
PT0
30137
10 kΩ
All diodes are the same and could be any one of the 1N4000 series
3
2
Hall-effect
switch
Figure 8.55 ■ Schematic of an HCS12-based motor-control system
The schematic of a motor-control system is illustrated in Figure 8.55. The PWM output
from the PWM3 pin is connected to one end of the motor, and the PP7 pin is connected to the
other end of the motor. The circuit is connected so that the motor will rotate clockwise when
the voltage of the PP7 pin is 0 while the PWM output is nonzero (positive). The direction of
433
8.11 ■ DC Motor Control
PWM (PP3)
SN754410
A
When A = B, torque
applied to motor = 0
A
When A ≠ B, motor runs
Motor
B
Off
Off
Port Pin (PP7) B
Clockwise
Counterclockwise
Figure 8.56 ■ The TI SN754410 motor drive
motor rotation is illustrated in Figure 8.56. By applying appropriate voltages on PP7 and PP3
(PWM3), the motor can rotate clockwise, counterclockwise, or even stop. Input-capture channel
0 is used to capture the feedback from the Hall-effect transistor.
When a motor is first turned on, it cannot reach a steady speed immediately. A certain
amount of startup time should be allowed for the motor to get to speed. A smaller motor can
reach steady speed faster than a larger one. It is desirable for the motor speed to be constant for
many applications. However, when a load is applied to the motor, it will be slowed down. To
keep the speed constant, the duty cycle of the voltage applied to the motor should be increased.
When the load gets lighter, the motor will accelerate and run faster than desired. To slow down
the motor, the duty cycle of the applied voltage should be reduced.
The response time will be slow if the change to the duty cycle is small. However, a large variation in duty cycle tends to cause the motor to overreact and oscillate. There are control algorithms
for motors that you can find in textbooks on control. A DC motor cannot respond to the change of
duty cycle instantaneously because of its inertia. A certain amount of time should be allowed for
the motor to speed up or slow down before the effect of the change of duty cycle is measured.
Example 8.25
▼
Write a C function to measure the motor speed (in rpm) assuming that the HCS12 is running with a 16-MHz E-clock.
Solution: To measure the motor speed, one needs to capture two consecutive rising edges. Let
the difference of two consecutive edges be diff and the period of the timer be set to 1 μs; then
the motor speed (rpm) is
Speed 5 60 3 106 4 (2 3 diff)
The C function that measures the motor speed is as follows:
#include “c:\cwHCS12\include\hcs12.h”
unsigned int motor_speed (void)
{
unsigned int edge1, diff, rpm;
long int temp;
TSCR1
TIOS
TSCR2
5 0x90;
&5 IOS0;
5 4;
/* enable TCNT and fast flag clear */
/* select IC0 function */
/* set TCNT prescale factor to 16 */
434
Chapter 8 ■ Timer Functions
TCTL4
5 0x01;
/* select to capture the rising edge of PT0 */
TFLG1
5 C0F;
/* cleared C0F flag */
while (!(TFLG1 & C0F));
/* wait for the first edge */
edge1
5 TC0;
while (!(TFLG1 & C0F));
/* wait for the second edge */
diff
5 TC0 2 edge1;
temp
5 10000001/(long)(2 * diff);
rpm
5 temp * 60;
return rpm;
}
▲
8.11.3 Electrical Braking
Once a DC motor is running, it picks up speed. Turing off the voltage to the motor does
not make it stop immediately because the momentum will keep it rotating. After the voltage
is turned off, the momentum will gradually wear out because of friction. If the application does
not require an abrupt stop, then the motor can be brought to a gradual stop by removing the
driving voltage.
An abrupt stop may be required by certain applications in which the motor must run a few
turns and stop quickly at a predetermined point. This could be achieved by electrical braking.
Electrical braking is done by reversing the voltage applied to the motor. The length of time that
the reversing voltage is applied must be precisely calculated to ensure a quick stop while not
starting the motor in the reverse direction. A discussion of good motor braking algorithms is
outside the scope of this textbook. In a closed-loop system, the feedback can be used to determine where or when to start and stop braking and when to discontinue. In Figure 8.56, the motor can be braked by (1) reducing the PWM duty count to 0 or (2) setting port pin PP7 output to
high for an appropriate amount of time.
8.12 Summary
Many applications require a dedicated timer. Without a timer the following applications
will become very difficult or even impossible to implement:
• The measurement of pulse width, frequency, period, duty cycle, and phase
difference
• The detection of certain events
• The creation of time delays
• The generation of waveforms
• The generation of a siren and playing of songs
Some HCS12 members implement an Enhanced Capture Timer (ECT) module, which
has the features of the Standard Timer Module (TIM) enhanced by additional functions. The
heart of the timer system is the 16-bit main timer, TCNT. This timer must be enabled in
order to run. Its clock signal is derived by dividing the E-clock by a prescaler. The prescale
factor can be from 1 to 128 for the standard timer module and the enhanced capture timer
module.
8.12 ■ Summary
435
The input-capture function can be programmed to latch the main timer value (TCNT)
into the input-capture register on the arrival of an active edge and optionally generate an
interrupt. The input-capture function is often used to measure the period, pulse width, duty
cycle, and phase shift. It can also be used as a time reference and count the events that occur
within an interval. There can be up to eight channels (IC0 to IC7) of input-capture function.
The input-capture function of TIM has several limitations that make it unsuitable for highfrequency applications and a noisy environment. The ECT module adds a holding register to
each of the four input-capture channels (IC0 to IC3). This enhancement adds the following
capabilities:
• Interrupt the CPU after two edges have been captured instead of each edge. This
capability reduces the software overhead.
• Ignore the short pulse. This capability enables the user to capture event arrival
time and measure pulse width or period in a noisy environment.
• Selective no-overwrite. This capability allows the user to do measurements in very high
frequency and not miss the true events because the CPU is busy with other chores.
There can be up to eight output-compare channels (OC0 to OC7). Each output-compare
channel has a 16-bit register, a 16-bit comparator, and an output-compare action pin. The outputcompare function is often used to create a time delay, to generate a waveform, to activate an
operation at a predetermined future time, and so on. To use the output-compare function, one
makes a copy of the main timer, adds a delay to this copy, and stores the sum into an outputcompare register. The 16-bit comparator compares the contents of the main timer with that of
the output-compare register. When they are equal, the corresponding timer flag will be set and
an optional action on the associated signal pin will be triggered: pull to high, pull to low, or
toggle. An output-compare operation can be forced to take effect immediately by writing a 1 into
the corresponding bit in the FORC register. This action will not set the timer flag and will not
generate an interrupt either.
The output-compare channel 7 can control up to eight output-compare channels at the
same time. This capability allows the user to use two output-compare channels to control the
same signal pin.
The TIM module has a 16-bit pulse-accumulator module (PACA). The PACA function has
two operation modes: event counting and gated time accumulation. This function has been
used to generate an interrupt after N events have occurred, measure the frequency of a signal,
count events, measure the pulse width, and so on.
The ECT module adds four buffered 8-bit pulse accumulators. Each pulse accumulator has
an 8-bit counter that will increment when an active edge arrives. Each pair of the pulse accumulators can be concatenated into a 16-bit pulse accumulator.
A set of delay functions are created and stored in the files delay.asm and delay.c and can
be called by the end user. These functions utilize the modulus down counter to generate time
delays.
Using the output-compare function to generate digital waveforms requires frequent attention from the CPU. This reduces the CPU time available for other applications. The pulse-width
modulation (PWM) is designed to reduce the CPU load from waveform generation. The HCS12
provides eight channels of 8-bit PWM modules. The clock source, prescale factor, the polarity, the waveform alignment, the duty cycle, and the period of the waveform are all programmable. Two adjacent 8-bit PWM channels can be concatenated into one 16-bit PWM channel.
The PWM function is used in motor control, light dimming, and any application that requires
the control of the average voltage level.
436
Chapter 8 ■ Timer Functions
8.13 Exercises
Assume that the E-clock frequency of the HCS12 is 24 MHz for the following questions unless it is specified otherwise:
E8.1 Write a program to configure all Port T pins to be used in capturing event arrival times.
Use the interrupt-driven approach. Stay in a wait loop after completing the configuration. Exit
the wait loop when all eight channels have arrived. Store the arrival times in memory locations
starting from $1000.
E8.2 Use the input-capture channel 1 to measure the duty cycle of a signal. Write an assembly
and a C program (in a subroutine format) to do the measurement.
E8.3 Assume that two signals having the same frequency are connected to the pins PT1 and
PT0. Write an assembly and a C program to measure their phase difference.
E8.4 Write an assembly and a C program to generate a 2-kHz, 70 percent duty cycle waveform
from the PT6 pin.
E8.5 Write an assembly and a C program to generate a 4-kHz, 80 percent duty cycle waveform
from the PT5 pin.
E8.6 Write a subroutine that can generate a time delay from 1 to 100 s using the modulus down
counter. The number of seconds is passed to the subroutine in accumulator A.
E8.7 What would be the output frequency of the PT0 signal generated by the following program
segment?
clr
movb
bset
movb
movb
movb
movb
movb
movb
ldd
std
ldd
std
PTT
#$01,DDRT
TIOS,$81
#$01,OC7M
#$01,OC7D
#$90,TSCR1
#$02,TCTL2
#$0,TIE
#$08,TSCR2
#$0
TC0
#$1
TC7
E8.8 Write a program to generate a 25-Hz digital waveform with a 50 percent duty cycle on the PT0
pin as long as the voltage level on the PP7 pin is high. Your program should consist of two parts:
1. Entry test. As long as the PP7 pin is low, it stays in this loop.
2. Waveform generation body. This part generates a pulse with 20 ms high time and
20 ms low time, and at the end of a period tests the PP7 signal. If PP7 is still high, it
generates the next pulse. Otherwise, it jumps to entry test.
E8.9 Write a program to wait for an event (rising edge) to arrive at the PT0 pin. After that, the
program will wait for 100 ms and trigger a pulse 20 ms wide on the PT6 pin.
E8.10 In Example 8.2, we used the polling method to check for the arrival of edges. Write a
program that uses the interrupt-driven approach to measure the period of an unknown signal.
There will be two interrupts related to the PT0 active edges and zero or more TCNT overflow
interrupts to be dealt with.
E8.11 Write a program to generate 10 pulses from the PT6 pin. Each pulse has 60 μs high time
and 40 μs low time.
8.14 ■ Lab Exercises and Assignments
437
E8.12 Write a program to generate an interrupt to the HCS12 20 ms after the rising edge on the
pin PT2 has been detected.
E8.13 Suppose that the contents of the TCTL1 and TCTL2 registers are $79 and $9B, respectively. The content of the TFLG1 register is $00. What would occur on pins PT7 to PT0 on the
next clock cycle if the value $7F is written into the CFORC register?
E8.14 Write an instruction sequence to configure the modulus down counter so that it generates periodic interrupts to the microcontroller every 40 ms.
E8.15 Modify the programs in Example 8.8 to avoid the drawback in the generated song.
E8.16 Find the score of the song “Home, Sweet Home” and modify the program in Example 8.8
to play it.
E8.17 Write an instruction sequence to generate a 160-kHz digital waveform with 80 percent
duty cycle from the PWM2 pin output. Use left-aligned mode.
E8.18 Write an instruction sequence to generate a 120-kHz digital waveform with 40 percent
duty cycle from the PWM2 pin output. Use center-aligned mode.
E8.19 Write an instruction sequence to generate a 10-kHz digital waveform with 60 percent
duty cycle from the PWM2 pin output. Use left-aligned mode.
E8.20 Write an instruction sequence to generate a 5-kHz digital waveform with 70 percent duty
cycle from the PWM2 pin output. Use center-aligned mode.
E8.21 Write an instruction sequence to generate a 20-Hz digital waveform with 50 percent duty
cycle using the 16-bit mode from the PWM1 pin output. Use left-aligned mode.
E8.22 Write an instruction sequence to generate a 10-Hz digital waveform with 60 percent duty
cycle using the 16-bit mode from the PWM3 pin output. Use center-aligned mode.
E8.23 What is the slowest clock signal that can be generated from the PWM output?
8.14 Lab Exercises and Assignments
L8.1 Frequency measurement. Use the pulse-accumulator function to measure the frequency
of an unknown signal. The procedure is as follows:
Step 1
Set the function generator output to square wave and adjust the output to between 0 and 5 V.
Connect the signal to the PAI (PT7) pin.
Step 2
Also connect the signal to an oscilloscope or a frequency counter. This is for verification
purposes.
Step 3
Output the message “Do you want to continue to measure the frequency? (y/n)”.
Step 4
Set up the frequency of the signal to be measured and enter y or n to inform the microcontroller
if you want to continue the measurement.
Step 5
Your program would read in the answer from the user. If the character read in is n, then
stop. If the answer is y, then repeat the measurement. If the character is something else,
then repeat the same question.
Step 6
Perform the measurement and display the frequency in Hz in decimal format on the screen
and go back to step 3. Use as many digits as necessary. The output format should look like
438
Chapter 8 ■ Timer Functions
The signal frequency is xxxxxx Hz.
Crank up the frequency until the measurement becomes inaccurate. What is the highest frequency that you can measure?
L8.2 Pulse-width measurement. Use the input-capture function to measure the pulse width.
The procedure is as follows:
Step 1
Set the function generator output to be square wave and adjust the output to between 0
and 5 V. Connect the signal to the PAI (PT7) pin.
Step 2
Connect the signal to an oscilloscope or a frequency counter to verify your measurement.
Step 3
Output the message “Do you want to continue to measure the pulse width? (y/n)”.
Step 4
Set up the appropriate period (frequency) of the signal to be measured and enter y or n to
inform the microcontroller if you want to continue the measurement.
Step 5
Your program would read in the answer from the user. If the character read in is n, then
stop. If the answer is y, then repeat the measurement. If the character is something else,
then repeat the same question.
Step 6
Perform the measurement and display the period in μs in decimal format on the screen
and go back to step 3. Use as many digits as necessary. The output format should look like
The signal period is xxxxxx microseconds.
Crank up the frequency until the measurement becomes inaccurate. What is the shortest period
that you can measure?
L8.3 Driving the DC motor and servomotor using the PWM module
Motor kits used
• A cooling fan with DC motor (D24-B10A-04W4-000 from Globe motors) (shown in
Figure L8.3a)
• A hobby servo-motor HS-311 made by Hitec (shown in Figure L8.3b)
A servo is a small device that incorporates a three-wire DC motor, a gear train, a potentiometer,
an integrated circuit, and an output shaft bearing. The shaft of the servomotor can be positioned
to specific angular positions by sending coded signals. As long as the coded signal exists on the
input line, the servomotor will maintain the angular position of the shaft. If the coded signal
changes, then the angular position of the shaft changes.
A common use of servomotors is in radio-controlled models like cars, airplanes, robots, and
puppets. They are also used in powerful heavy-duty sailboats. Servos come in different sizes
but use similar control schemes and are extremely useful in robotics. The motors are small and
extremely powerful for their size. They also draw power proportional to the mechanical load. A
lightly loaded servo, therefore, doesn’t consume much energy.
A typical servo looks like a rectangular box with a motor shaft coming out of one end and
a connector with three wires out of the other end. The three wires are the power, control, and
ground. Servos work with voltages between 4 and 6 V. The control line is used to position the
servo. Inexpensive servos have plastic gears, and more expensive servos have metal gears which
are much more rugged but wear faster.
439
8.14 ■ Lab Exercises and Assignments
Figure L8.3a, b ■ Photos of DC and servomotors
Servos are constructed from three basic pieces: a motor, a potentiometer that is connected
to the output shaft, and a control board. The potentiometer allows the control circuitry to
monitor the current angle of the servomotor. The motor, through a series of gears, turns the
output shaft and the potentiometer simultaneously. The potentiometer is fed into the servo
control circuit, and when the control circuit detects that the angle is not correct, it turns the
motor the correct direction until the angle is correct. Normally a servo is used to control an
angular motion between 0 and 180 degrees. It is not mechanically capable (unless modified) of
turning any farther due to the mechanical stop build on the main output gear.
Servos are controlled by sending them a pulse of variable width. The control wire is used to
send this pulse. As shown in Figure L8.3c, the pulse has a minimum pulse, a maximum pulse,
and a repetition rate. Given the rotation constraints of the servo, neutral is defined to be the
position where the servo has exactly the same amount of potential rotation in the clockwise direction as it does in the counterclockwise direction. It is important to note that different servos
will have different constraints on their rotation but they all have a neutral position, and that
position is always 1.5 ms.
20 ms
Pulse width 1 ms (min.) to 2 ms (max.)
Figure L8.3c ■ Pulse pattern of a servomotor
The angle (indicated by the pointer on the white plate of the servomotor) is determined by
the duration of a pulse that is applied to the control line. The servo expects to see a pulse every
20 ms. The length of the pulse will determine how far the motor turns. For example, a 1.5-ms
pulse will make the motor turn to the 90-degree position (neutral position).
When a servo is commanded to move, it will move to the position and hold that position.
If an external force pushes against the servo while the servo is holding a position, the servo will
440
Chapter 8 ■ Timer Functions
resist moving out of that position. The maximum amount of force the servo can exert is the
torque rating of the servo. Servos will not hold their position forever though; the position pulse
must be repeated to instruct the servo to stay in position.
As shown in Figure L8.3d, when a pulse is sent to a servo that is less than 1.5 ms, the servo
rotates to a position and holds its output shaft some number of degrees counterclockwise from
the neutral point. When the pulse is wider than 1.5 ms, the opposite occurs. The minimal width
and the maximum width of pulse that will command the servo to turn to a valid position are
functions of each servo. Different brands, and even different servos of the same brand, have different maximum and minimums. Generally the minimum pulse will be about 1 ms wide and
the maximum pulse will be 2 ms wide.
Another parameter that varies from servo to servo is the turn rate. This is the time it takes
for the servo to change from one position to another. The worst-case turning time is when the
servo is holding at the minimum rotation and it is commanded to go to maximum rotation.
This can take several seconds for very high torque servos.
0°
Minimum pulse
Pulse width 1 ms
90°
Neutral position
Pulse width 1.5 ms
180°
Maximum pulse
Pulse width 2 ms
Figure L8.3d ■ Servo control pulse width and motor position
The diagram of the HS-311 servomotor to be used in this lab is shown in Figure L8.3b.
There are three terminals connected to the control circuit (see its left).
• Black wire: ground
• Red wire: connected to VCC
• Yellow wire: connected to signal (PWM output)
The HCS12 PWM cannot supply enough current to drive the DC motor or the servomotor directly.
A motor driver chip such as the SN754410 from TI is needed to supply the current needed by the
motor. The pin assignment of SN754410 is shown in Figure 8.53. The lab procedure is as follows:
Step 1
Connect the circuit properly. Connect the PWM0 and PWM1 pins to the 1A and 2A pins of
the SN754410, respectively. Connect the 1Y and 2Y pins to the DC motor and servomotor
control input, respectively. The servomotor needs the driving signal to be 50 Hz.
Step 2
Write a program that enables and configures the PWM function properly. The frequency
of the PWM output for this lab is fixed (at what frequency?), but the duty cycle will be
changed in response to the user input.
8.14 ■ Lab Exercises and Assignments
441
Step 3
Enter, assemble, and download the program onto the demo board for execution.
Step 4
Run the program. When the program runs, it outputs the message “duty cycle for DC:”
to the UART port (displayed on the HyperTerminal window). You enter from 00 to 99
(using the PC keyboard) to set the duty cycle for the DC motor and the program reads.
After that, the MCU will update the duty cycle for the DC motor immediately. The
HCS12 next outputs the message “duty cycle for servo:” to the HyperTerminal window.
The user will enter the new duty cycle for the digital waveform that drives the servomotor. Remember that the duty cycle for the servomotor is between 5 and 10 percent only.
(See Figure L8.3d.) You can figure out a set of convenient values for specifying duty cycles
for the servomotor. The program reads in the duty cycle and then performs the required
computation to derive the appropriate value that should be written into the duty cycle
registers. Your program will echo the new duty cycle on the LCD screen for the DC motor
and servomotor, respectively. If you are using the serial monitor to communicate with the
PC, then you will need two COM ports from the PC.
After that, the speed of the DC motor and the position of the servomotor would be changed.
Your program will wait for 5 s and then ask you to enter the new duty cycles and repeat the
same operation.
This page intentionally left blank
9
Serial Communication
Interface (SCI)
9.1 Objectives
After completing this chapter, you should be
able to
• Explain the four aspects of the TIA-232
standard
• Explain the errors that occur in data transmission
• Establish null-modem connection
• Explain the operation of the SCI subsystem
• Wire the SCI pins to the TIA-232 connector
• Program the SCI subsystem to perform data
transmission and reception
444
Chapter 9 ■ Serial Communication Interface (SCI)
9.2 Fundamental Concept of Serial Communications
The need to exchange data between the MCU and peripheral devices can be satisfied by using
parallel data transfer (multiple bits in one transfer operation). However, there are a few drawbacks.
• Parallel data transfer requires many I/O pins. This requirement prevents the microcontroller from interfacing with as many devices as desired in the application.
• Many I/O devices do not have a high enough data rate to justify the use of parallel
data transfer.
• Data synchronization for parallel transfer is difficult to achieve over a long distance. This
requirement is one of the reasons that data communications always use serial transfer.
• Higher cost.
The serial communication interface (SCI) was designed to transfer data in an asynchronous
mode that utilizes the industrial standard TIA-232 protocol. The TIA-232 was originally called
RS-232 because it was a recommended standard. You have been using this interface to communicate with and download programs onto the demo board for execution. Only two wires are used
by the SCI function.
9.3 The RS-232 Standard
The RS-232 standard was established in 1960 by the Electronic Industry Association (EIA) for
interfacing between a computer and a modem. It has experienced several revisions since then. The
latest revision, TIA-232F, was published in July 1997. TIA stands for Telecommunication Industry Association. In this revision, the prefix has been changed to TIA. This change represents no
change in the standard, but was made to allow users to identify the source of the standard. In data
communication terms, both computers and terminals are called data terminal equipment (DTE),
whereas modems, bridges, and routers are referred to as data communication equipment (DCE).
There are four aspects to the TIA-232 standard.
1. Electrical specifications. Specify the voltage level, rise time and fall time of each
signal, achievable data rate, and the distance of communication.
2. Functional specifications. Specify the function of each signal.
3. Mechanical specifications. Specify the number of pins and the shape and dimensions
of the connectors.
4. Procedural specifications. Specify the sequence of events for transmitting data, based
on the functional specifications of the interface.
9.3.1 TIA-232E Electrical Specification
The following electrical specifications of the TIA-232E are of interest to us:
1. Data rates. The TIA-232 standard is applicable to data rates of up to 20,000 bits per
second (the usual upper limit is 19,200 baud). Fixed baud rates are not set by the
TIA-232E standard. However, the commonly used values are 300, 1200, 2400, 9600,
and 19,200 baud. Other accepted values that are not often used are 110 (mechanical
teletype machines), 600, and 4800 baud.
2. Signal state voltage assignments. Voltages of 23 to 225 V with respect to signal
ground are considered logic 1 (the mark condition), whereas voltages of 13 to 125 V
445
9.3 ■ The RS-232 Standard
are considered logic 0 (the space condition). The range of voltages between 23 and 13
V is considered a transition region for which a signal state is not assigned.
3. Signal transfer distance. The signal should be able to transfer correctly within 15 m.
Greater distance can be achieved with good design.
9.3.2 TIA-232E Functional Specification
The TIA-232E standard specifies 22 signals. A summary of these signals is given in Table 9.1.
These signals can be divided into six categories.
1. Signal ground and shield.
2. Primary communications channel. This is used for data interchange and includes flow
control signals.
3. Secondary communications channel. When implemented, this is used for control of
the remote modem, requests for retransmission when errors occur, and governance
over the setup of the primary channel.
Pin No.
Circuit
Description
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
–
BA
BB
CA/CJ
CB
CC
AB
CF
–
–
–
SCF/CI
SCB
SBA
DB
SBB
DD
LL
SCA
CD
RL/CG
CE
CH/CI
DA
TM
Shield
Transmitted data
Received data
Request to send/ready for receiving1
Clear to send
DCE ready
Signal common
Received line signal detector
Reserved for testing
Reserved for testing
Unassigned2
Secondary received line signal detection/data rate selector (DCE source)3
Secondary clear to send
Secondary transmitted data
Transmitter signal element timing (DCE source)
Secondary received data
Receiver signal element timing
Local loopback
Secondary request to send
DTE ready
Remote loopback/signal quality detector
Ring indicator
Data signal rate selector (DTE/DCE source)3
Transmitter signal element timing (DTE source)
Test mode
1. When hardware flow control is required, circuit CA may take on the functionality of circuit CJ. This is one
change from the former TIA-232.
2. Pin 11 is unassigned. It will not be assigned in future versions of TIA-232. However, in international standard ISO
2110, this pin is assigned to select transmit frequency.
3. For designs using interchange circuit SCF, interchange circuits CH and CI are assigned to pin 23. If SCF is
not used, CI is assigned to pin 12.
Table 9.1 ■ Functions of EIA-232E signals
446
Chapter 9 ■ Serial Communication Interface (SCI)
4. Modem status and control signals. These signals indicate modem status and provide
intermediate checkpoints as the telephone voice channel is established.
5. Transmitter and receiver timing signals. If a synchronous protocol is used, these
signals provide timing information for the transmitter and receiver, which may
operate at different baud rates.
6. Channel test signals. Before data is exchanged, the channel may be tested for its
integrity and the baud rate automatically adjusted to the maximum rate that the
channel could support.
SIGNAL GROUND
Pins 7 and 1 and the shell are included in this category. Cables provide separate paths for
each, but internal wiring often connects pin 1 and the cable shell or shield to the signal ground
on pin 7. All signals are referenced to a common ground as defined by the voltage on pin 7. This
conductor may or may not be connected to protective ground inside the DCE device.
P R I M A R Y C O M M U N I C AT I O N C H A N N E L
Pin 2 carries the transmit data (TxD) signal, which is active when data is transmitted from
the DTE device to the DCE device. When no data is transmitted, the signal is held in the mark
condition (logic 1, negative voltage).
Pin 3 carries the received data (RxD), which is active when the DTE device receives data
from the DCE device. When no data is received, the signal is held in the mark condition.
Pin 4 carries the request to send (RTS) signal, which is asserted (logic 0, positive voltage) to
prepare the DCE device for accepting transmitted data from the DTE device. Such preparation
might include enabling the receive circuits or setting up the channel direction in half-duplex
applications. When the DCE is ready, it acknowledges by asserting the CTS signal.
Pin 5 carries the clear to send (CTS) signal, which is asserted (logic 0) by the DCE device
to inform the DTE device that transmission may begin. RTS and CTS are commonly used as
handshaking signals to moderate the flow of data into the DCE device.
S E C O N D A R Y C O M M U N I C AT I O N C H A N N E L
Pin 14 is the secondary transmitted data (STxD). Pin 16 is the secondary received data
(SRxD). Pin 19 carries the secondary request to send (SRTS) signal. Pin 13 carries the secondary clear to send (SCTS) signal. These signals are equivalent to the corresponding signals in
the primary communications channel. The baud rate, however, is typically much slower in the
secondary channel, for increased reliability.
M O D E M S TAT U S A N D C O N T R O L S I G N A L S
This group includes the following signals:
Pin 6—DCE ready (DSR). When originating from a modem, this signal is asserted (logic
0) when all the following three conditions are satisfied:
1. The modem is connected to an active telephone line that is off-hook.
2. The modem is in data mode, not voice or dialing mode.
3. The modem has completed dialing or call setup functions and is generating an answer tone.
If the line goes off-hook, a fault condition is detected, or a voice connection is established, the DCE ready signal is de-asserted (logic 1).
Pin 20—DTE ready (DTR). This signal is asserted (logic 0) by the DTE device when it
wishes to open a communications channel. If the DCE device is a modem, the assertion of
DTR prepares the modem to be connected to the telephone circuit and, once connected,
9.3 ■ The RS-232 Standard
447
maintains the connection. When DTR is de-asserted, the modem is switched to on-hook
to terminate the connection (same as placing the phone back on the telephone socket).
Pin 8—Received line signal detector, also called carrier detect (CD). This signal is relevant when the DCE device is a modem. It is asserted (logic 0) by the modem when the
telephone line is off-hook, a connection has been established, and an answer tone is being received from the remote modem. The signal is de-asserted when no answer tone
is being received or when the answer tone is of inadequate quality to meet the local
modem’s requirements.
Pin 12—Secondary received line signal detector (SCD). This signal is equivalent to the
CD (pin 8) signal but refers to the secondary channel.
Pin 22—Ring indicator (RI). This signal is relevant when the DCE device is a modem
and is asserted (logic 0) when a ringing signal is being received from the telephone line.
The assertion time of this signal is approximately equal to the duration of the ring signal,
and it is de-asserted between rings or when no ringing is present.
Pin 23—Data signal rate selector. This signal may originate in either the DTE or the
DCE devices (but not both) and is used to select one of two prearranged baud rates. The
assertion condition (logic 0) selects the higher baud rate.
TRANSMITTER AND RECEIVER TIMING SIGNALS
This group consists of the following signals:
Pin 15—Transmitter signal element timing, also called transmitter clock (TC). This
signal is relevant only when the DCE device is a modem and is operating with a synchronous protocol. The modem generates this clock signal to control exactly the rate at
which data is sent on TxD (pin 2) from the DTE device to the DCE device. The logic 1–
to–logic 0 (negative to positive transition) transition on this line causes a corresponding
transition to the next data element on the TxD line. The modem generates this signal
continuously, except when it is performing internal diagnostic functions.
Pin 17—Receiver signal element timing, also called receiver clock (RC). This signal is
similar to TC, except that it provides timing information for the DTE receiver.
Pin 24—Transmitter signal element timing, also called external transmitter clock (ETC),
with timing signals provided by the DTE device for use by a modem. This signal is used
only when TC and RC (pins 15 and 17) are not in use. The logic 1–to–logic 0 transition
indicates the time center of the data element. Timing signals will be provided whenever
the DTE is turned on regardless of other signal conditions.
CHANNEL TEST SIGNALS
This group consists of the following signals:
Pin 18—Local loopback (LL). This signal is generated by the DTE device and is used to
place the modem into a test state. When LL is asserted (logic 0, positive voltage), the modem redirects its modulated output signal, which is normally fed into the telephone line,
back into its receive circuitry. This enables data generated by the DTE to be echoed back
through the local modem to check the condition of the modem circuitry. The modem asserts its test mode signal on pin 25 to acknowledge that it has been placed in LL condition.
Pin 21—Remote loopback (RL). This signal is generated by the DTE device and is used
to place the remote modem into a test state. When RL is asserted (logic 0), the remote
modem redirects its received data back to its transmitted data input, thereby remodulating
448
Chapter 9 ■ Serial Communication Interface (SCI)
the received data and returning it to its source. When the DTE initiates such a test, transmitted data is passed through the local modem, the telephone line, the remote modem,
and back, to exercise the channel and confirm its integrity. The remote modem signals the
local modem to assert test mode on pin 25 when the remote loopback test is underway.
Pin 25—Test mode (TM). This signal is relevant only when the DCE device is a modem.
When asserted (logic 0), it indicates that the modem is in an LL or RL condition. Other
internal self-test conditions may also cause the TM signal to be asserted, depending on
the modem and the network to which it is attached.
9.3.3 TIA-232E Mechanical Specification
The TIA-232E uses a 25-pin D-type connector, as shown in Figure 9.1a. Since only a small
subset of the 25 signals is actually used, a 9-pin connector (DB9) is used in most PCs. The signal
assignment of DB9 is shown in Figure 9.1b. The DB9 is not part of the TIA-232E standard.
Signal
Direction
to DCE
to DTE
to DTE
to DTE
to DCE
to DCE
to DTE
to DTE
Both
to DCE
Signal Name
Signal Name
1
14
2
15
3
16
4
17
5
18
6
19
7
20
8
21
9
22
10
23
11
24
12
25
13
Secondary transmitted data
Transmit clock
Secondary received data
Receiver clock
Unassigned
Secondary request to send
Data terminal ready
Signal quality detect
Ring indicator
Data rate select
Transmit clock
Unassigned
Protective ground
Transmitted data
Received data
Request to send
Clear to send
Data set ready
Signal ground
Carrier detect
Reserved
Reserved
Unassigned
Secondary carrier detect
Secondary clear to send
Figure 9.1a ■ EIA-232E DB25 connector and pin assignment
Ring indicator
9
Clear to send
8
Request to send
7
DCE ready
6
5
Ground
4
DTE ready
3
Transmitted data
2
Received data
1
Received line signal detect
Figure 9.1b ■ EIA-232 DB9 connector and signal assignment
Signal
Direction
Both
to DCE
to DTE
to DCE
to DTE
to DTE
Both
to DTE
to DTE
to DTE
449
9.3 ■ The RS-232 Standard
9.3.4 TIA-232E Procedural Specification
The sequence of events that occurs during data transmission using the TIA-232E is easier
to understand by studying examples. Two examples are used to explain the procedure.
In the first example, two DTEs are connected with a point-to-point link using a modem.
The modem requires only the following circuits to operate:
• Signal ground (AB)
• Transmitted data (BA)
• Received data (BB)
• Request to send (CA)
• Clear to send (CB)
• Data set ready (CC)
• Carrier detect (CF)
Before the DTE can transmit data, the DSR circuit must be asserted to indicate that the
modem is ready to operate. This signal should be asserted before the DTE attempts to make a
request to send data. The DSR pin can simply be connected to the power supply of the DCE to
indicate that it is switched on and ready to operate. When a DTE is ready to send data, it asserts
the RTS signal. The modem responds, when ready, with the CTS signal asserted, indicating that
data may be transmitted over circuit BA. If the arrangement is half-duplex, then the assertion
of the RTS signal also inhibits the receive mode. The DTE sends data to the local modem bit
serially. The local modem modulates the data into the carrier signal and transmits the resultant
signal over the dedicated communication lines. Before sending out modulated data, the local
modem sends out a carrier signal to the remote modem so that it is ready to receive the data.
The remote modem detects the carrier and asserts the CD signal. The assertion of the CD signal tells the remote DTE that the local modem is transmitting. The remote modem receives the
modulated signal, demodulates it to recover the data, and sends it to the remote DTE over the
RxD pin. The circuit connections are illustrated in Figure 9.2.
The next example involves two computers exchanging data through a public telephone line.
One of the computers (initiator) must dial the phone (automatically or manually) to establish
Computer
(DTE)
Modem
(DCE)
TxD
TxD
RxD
RxD
Modem
(DCE)
Direct link
Computer
(DTE)
TxD
TxD
RxD
RxD
CD
CD
CD
CD
CTS
CTS
CTS
CTS
RTS
RTS
RTS
RTS
DSR
DSR
DSR
DSR
GND
GND
GND
GND
Figure 9.2 ■ Point-to-point asynchronous connection
450
Chapter 9 ■ Serial Communication Interface (SCI)
the connection, just like people talking over the phone. Two additional leads are required for
this application.
• Data terminal ready (DTR)
• Ring indicator (RI)
The data transmission in this setting can be divided into three phases.
PHASE 1
Establishing the connection. The following events occur in this phase:
1. The transmitting computer asserts the DTR signal to indicate to the local modem
that it is ready to make a call.
2. The local modem opens the phone line and dials the destination number. The number
can be stored in the modem or transmitted to the modem by the computer via the
TxD pin.
3. The remote modem detects a ring on the phone line and asserts the RI signal to
inform the remote computer that a call has arrived.
4. The remote computer asserts the DTR signal to accept the call.
5. The remote modem answers the call by sending a carrier signal to the local modem
via the phone line. It also asserts the DSR signal to inform the remote computer that
it is ready for data transmission.
6. The local modem asserts both the DSR and CD signals to indicate that the connection
is established and it is ready for data communication.
7. For full-duplex data communication, the local modem also sends a carrier signal to
the remote modem. The remote modem then asserts the CD signal.
PHASE 2
Data transmission. The following events occur during this phase:
1. The local computer asserts the RTS signal when it is ready to send data.
2. The local modem responds by asserting the CTS signal.
3. The local computer sends data bits serially to the local modem over the TxD pin.
The local modem then modulates its carrier signal to transmit the data to the remote
modem.
4. The remote modem receives the modulated signal from the local modem, demodulates
it to recover the data, and sends it to the remote computer over the RxD pin.
PHASE 3
Disconnection. Disconnection requires only two steps.
1. When the local computer has finished the data transmission, it drops the RTS signal.
2. The local modem then de-asserts the CTS signal and drops the carrier (equivalent to
hanging up the phone).
The circuit connection for this example is shown in Figure 9.3. A timing signal is not
required in an asynchronous transmission.
451
9.3 ■ The RS-232 Standard
Computer
(DTE)
Modem
(DCE)
Modem
(DCE)
Computer
(DTE)
TxD
TxD
TxD
TxD
RxD
RxD
RxD
RxD
RI
RI
RI
RI
CD
CD
CD
CD
CTS
CTS
CTS
CTS
RTS
RTS
RTS
RTS
DSR
DSR
DSR
DSR
DTR
DTR
DTR
DTR
GND
GND
GND
GND
Phone line
Figure 9.3 ■ Asynchronous connection over public phone line
9.3.5 Data Format
In asynchronous data transfer, data is transferred character by character. Each character is
preceded by a start bit (a low), followed by 8 or 9 data bits, and terminated by a stop bit. The
data format of a character is shown in Figure 9.4.
Start
bit
0
1
2
3
4
5
6
7
Stop
bit
Figure 9.4 ■ The format of a character
As shown in Figure 9.4, the least significant bit is transmitted first, and the most significant bit is transmitted last. The stop bit is high. The start bit and stop bit identify the start and
end of a character.
Since there is no clock information in the asynchronous format, the receiver uses a clock
signal with a frequency that is a multiple (usually 16) of the data rate to sample the incoming
data in order to detect the arrival of the start bit and determine the logical value of each data
bit. A clock, with a frequency that is 16 times the data rate, can tolerate a frequency difference
in the clocks slightly over 3 percent at the transmitter and receiver.
To detect the arrival of a start bit, the SCI waits for the falling edge after the RxD pin has
been idle (high) for at least three sampling times. It will then look at the third, fifth, and seventh samples after the first low sample (these are called verification samples) to determine if a
valid start bit has arrived. This process is illustrated in Figure 9.5. If the majority of these three
samples are low, then a valid start bit is detected. Otherwise, the SCI will restart the process.
After detecting a valid start bit, the SCI will start to shift in the data bits.
To determine the data bit value, the SCI uses a clock with a frequency about 16 times
(most often) that of the data rate to sample the RxD signal. If the majority of the eighth, ninth,
and tenth samples are 1s, then the data bit is determined to be 1. Otherwise, the data bit is
determined to be 0.
452
Chapter 9 ■ Serial Communication Interface (SCI)
Actual start bit
RxD pin
RT16
RT15
RT13
RT14
RT12
RT12
RT11
RT9
RT10
RT8
RT7
0
RT6
0
[2]
RT5
RT clock state
0
0
RT4
1
RT3
1
[1]
RT2
RT clock
(16 × baud
rate)
1
RT1
Samples
1. A 0 following three 1s.
2. Majority of samples 3, 5, and 7 are 0s.
Figure 9.5 ■ Detection of start bit (ideal case)
The stop bit is high. Using this format, it is possible to transfer data character by character
without any gap.
The term baud rate is defined as the number of signal changes per second. Since the RS-232
standard uses a non-return-to-zero (NRZ) encoding method, baud rate is identical to bit rate. In
an NRZ code, a logic 1 bit is sent as a high value and a logic 0 bit is sent as a low value. When
a logic 1 follows another logic 1 during a data transfer, the voltage does not drop to zero before
it goes high.
Example 9.1
▼
Sketch the output of the letter g when it is transmitted using the format of 1 start bit, 8 data
bits, and 1 stop bit.
Solution: Letters are represented in ASCII code. The ASCII code of letter g is $67 (5 01100111).
Since the least significant bit goes out first in the TIA-232 protocol, the format of the output of
letter g is as shown in Figure 9.6.
0
1
1
1
0
0
1
1
0
1
(a) Output waveform on microcontroller interface
0
1
1
1
0
0
0
1
0
1
(b) Output waveform on TIA-232E interface
Figure 9.6 ■ Data format for letter g
▲
453
9.4 ■ The HCS12 Serial Communication Interface
9.3.6 Data Transmission Errors
The following errors may occur during the data transfer process using asynchronous serial
transmission:
• Framing error. A framing error occurs when a received character is improperly
framed by the start and stop bits; it is detected by the absence of the stop bit. This
error indicates a synchronization problem, faulty transmission, or a break condition.
A break is defined as the transmission or reception of a logic 0 for one frame or more.
• Receiver overrun. One or more characters in the data stream were received but
were not read from the buffer before subsequent characters were received.
• Parity error. A parity error occurs when an odd number of bits change value. It can
be detected by a parity error detecting circuit.
9.3.7 Null Modem Connection
When two DTE devices are located side by side and use the TIA-232E interface to exchange
data, there is really no reason to use two modems to connect them. However, the TIA-232E standard does not allow the direct connection of two DTEs. In order to make this scheme work, a null
modem is needed. The null modem interconnects leads in such a way as to fool both DTEs into
thinking that they are connected to modems. The null modem connection is shown in Table 9.2.
DTE 1
Signal Name
FG (frame ground)
TD (transmit data)
RD (receive data)
RTS (request to send)
CTS (clear to send)
SG (signal ground)
DSR (data set ready)
CD (carrier detect)
DTR (data terminal ready)
DTR (data terminal ready)
DTE 2
DB25 Pin
DB9 Pin
DB9 Pin
DB25 Pin
1
2
3
4
5
7
6
8
20
20
–
3
2
7
8
5
6
1
4
4
–
2
3
8
7
5
4
4
1
6
1
3
2
5
4
7
20
20
8
6
Signal Name
FG
RD
TD
CTS
RTS
SG
DTR
DTR
CD
DSR
Table 9.2 ■ Null modem connection
In Table 9.2, the signals of DTE1 and DTE2 that are to be wired together are listed in the
same row. The transmitter timing and receiver timing signals are not needed in asynchronous
data transmission. A ring indicator is not needed either because the transmission is not through
a public phone line.
9.4 The HCS12 Serial Communication Interface
An HCS12 device may have one or two serial communication interfaces. These two SCI
modules are referred to as SCI0 and SCI1, respectively. The SCI0 module shares the use of the
Port S pins PS1 (TxD0) and PS0 (RxD0); the SCI1 shares the use of the Port S pins PS3 (TxD1)
and PS2 (RxD1). The block diagram of the HCS12 SCI module is shown in Figure 9.7.
454
Chapter 9 ■ Serial Communication Interface (SCI)
SCI data register
RxD
Receive shift register
Interrupt
generation
Idle
IRQ
RDRF/OR
IRQ
Eclock
Baud
generator
Receive and wake-up control
16
O
R
I
N
G
Data format control
IRQ
to CPU
Transmit control
Transmit shift register
Interrupt
generation
TDRE
IRQ
TC
IRQ
SCI data register
TxD
Figure 9.7 ■ HCS12SCI block diagram
The operation of an SCI module involves the following registers:
• Two baud rate registers. SCIxBDH and SCIxBDL (x 5 0 or 1)
• Two control registers. SCIxCR1 and SCIxCR2 (x 5 0 or 1)
• Two status registers. SCIxSR1 and SCIxSR2 (x 5 0 or 1)
• Two data registers. SCIxDRH and SCIxDRL (x 5 0 or 1)
The HCS12 SCI interface uses a data format of 1 start bit, 8 or 9 data bits, and 1 stop
bit. When the SCI is configured to use 9 data bits, one of the bits can be used as the parity
bit. The collection of the start bit, data bits, and the stop bit is called a frame. The SCI function has the capability to send a break to attract the attention of the other party of the data
communication. The SCI function supports hardware parity for transmission and reception.
When enabled, a parity bit is generated in hardware for transmitted data and received data. Received parity errors are flagged in hardware. The SCI module supports two wake-up methods,
idle line wake-up and address mark wake-up; this allows the HCS12 to operate in a multiplenode environment.
9.5 SCI Baud Rate Generation
The HCS12 SCI modules use a clock signal that is 16 times the data rate to detect the arrival
of the start bit and determine the logic value of data bits. The HCS12 SCI modules use a 13-bit
counter to generate this clock signal. This circuit is called a baud rate generator. To set the baud
rate to a certain value, one needs to write an appropriate value to the SCIxBDH:SCIxBDL register
455
9.6 ■ The SCI Operation
Reset:
7
6
5
4
3
2
1
0
0
0
0
SBR12
SBR11
SBR10
SBR9
SBR8
0
0
0
0
0
0
0
0
(a) SCI baud rate control register high (SCI0BDH/SCI1BDH)
Reset:
7
6
5
4
3
2
1
0
SBR7
SBR6
SBR5
SBR4
SBR3
SBR2
SBR1
SBR0
0
0
0
0
0
1
0
0
(b) SCI baud rate control register low (SCI0BDL/SCI1BDL)
Figure 9.8 ■ SCI baud rate control register
pair. The upper 3 bits of the SCIxBDH register are tied to zeros. The contents of these two registers are shown in Figure 9.8.
This baud rate generator divides down the E-clock to derive the clock signal for reception
and transmission. The value (referred to as SBR) to be written into the SCIxBDH:SCIxBDL register pair can be derived by rounding the following expression to an integer value:
SBR 5 fE 4 16 4 baud rate
The divide factors for the baud rate generator for the 16-MHz and 24-MHz E clocks are
listed in Table 9.3.
Desired SCI
Baud Rate
Baud Rate Divisor for
fE = 16 MHz
Baud Rate Divisor for
fE = 24 MHz
300
600
1200
2400
4800
9600
14,400
19,200
38,400
3333
1667
833
417
208
104
69
52
26
5000
2500
1250
625
313
156
104
78
39
Table 9.3 ■ Baud rate generation
9.6 The SCI Operation
The operation of the SCI module is controlled by two control registers: SCI0CR1 (SCI1CR1)
and SCI0CR2 (SCI1CR2). Their contents are shown in Figures 9.9 and 9.10, respectively. The
SCI module allows full duplex, asynchronous, non-return-to-zero (NRZ) serial communication
between the CPU and remote devices, including other CPUs. The SCI transmitter and receiver
456
Chapter 9 ■ Serial Communication Interface (SCI)
7
6
LOOPS SCISWAI
5
4
3
2
1
0
RSRC
M
WAKE
ILT
PE
PT
Reset value
= 0x00
LOOPS: loop select bit
0 = loop operation disabled.
1 = loop operation enabled.
SCISWAI: SCI stop in wait mode
0 = SCI enabled in wait mode.
1 = SCI disabled in wait mode.
RSRC: receiver source bit
When LOOPS = 1, the RSRC bit determines the source for the receiver shift register.
0 = receiver input connected to the transmitter internally (not TxD pin).
1 = receiver input connected externally to the transmitter (TxD pin).
M: data format mode bit
0 = 1 start bit, 8 data bits, 1 stop bit.
1 = 1 start bit, 9 data bits, 1 stop bit.
WAKE: wake-up condition bit
0 = idle line wake-up.
1 = address mark wake-up (last data bit set).
ILT: idle line type bit
0 = idle character bit count begins after start bit.
1 = idle character bit count begins after stop bit.
PE: parity enable bit
0 = parity disabled.
1 = parity enabled.
PT: parity type bit (for both transmit and receive)
0 = even parity selected.
1 = odd parity selected.
Figure 9.9 ■ SCI control register 1 (SCI0CR1/SC1CR1)
operate independently, although they use the same baud rate generator. The CPU monitors the
status of the SCI, writes the data to be transmitted, and processes received data.
9.6.1 Character Transmission
The block diagram of the SCI transmitter is shown in Figure 9.11. To transmit data, the
MCU writes the data bits to the SCI data registers (SCIxDRH/SCIxDRL), which in turn are
transferred to the transmit shift register. The transmit shift register then shifts a frame out
through the TxD pin, after it has prefaced the bits with a start bit and appended a stop bit. The
SCI data registers are the write-only buffers between the internal data bus and the transmit
shift register.
The SCI also sets a flag, the transmit data register empty flag (TDRE), every time it transfers data from the buffer (SCIxDRH/L) to the transmit shift register. The transmit driver routine
may respond to this flag by writing another byte to the transmit buffer, while the shift register
is still shifting out the first byte.
There are three major steps in the SCI transmission.
1. Configure the SCI transmission.
a. Select a baud rate. The user must write an appropriate value to the SCI baud registers to set the baud rate. Writing to the SCIxBDH register has no effect without
also writing to the SCIxBDL register.
457
9.6 ■ The SCI Operation
7
6
5
4
3
2
1
0
TIE
TCIE
RIE
ILIE
TE
RE
RWU
SBK
Reset value
= 0x00
TIE: transmit interrupt enable bit
0 = TDRE interrupt disabled.
1 = TDRE interrupt enabled.
TCIE: transmit complete interrupt enable bit
0 = TC interrupt disabled.
1 = TC interrupt enabled.
RIE: receiver full interrupt enable bit
0 = RDRF and OR interrupts disabled.
1 = RDRF and OR interrupt enabled.
ILIE: idle line interrupt enable bit
0 = IDLE interrupt disabled.
1 = IDLE interrupt enabled.
TE: transmitter enable bit
0 = transmitter disabled.
1 = transmitter enabled.
RE: receiver enable
0 = receiver disabled.
1 = receiver enabled.
RWU: receiver wake-up bit
0 = normal SCI receiver.
1 = enables the wake-up function and inhibits further receiver
interrupts. Normally, hardware wakes up the receiver by
automatically clearing this bit.
SBK: send break bit
0 = no break characters.
1 = generate a break code, at least 10 or 11 contiguous 0s. As long
as SBK remains set, the transmitter sends 0s.
Figure 9.10 ■ SCI control register 2 (SCI0CR2/SCI1CR2)
b. Write to the SCIxCR1 register to configure the word length, parity, and other configuration bits (LOOPS, RSRC, M, WAKE, ILT, PR, PT).
c. Enable the transmitter, interrupt, receive, and wakeup as required by writing to the
SCIxCR2 register bits (TIE, TCIE, RIE, ILIE, TE, RE, RWU, and SK). A preamble will
now be shifted out of the transmitter shift register.
2. Set a transmit procedure for each character.
a. Poll the TDRE flag by reading the SCIxSR1 register or responding to the TDRE
interrupt.
b. If the TDRE flag is set, write the data to be transmitted to SCIxDRH/L, where the
ninth bit is written to the T8 bit in the SCIxDRH register if the SCI is in
9-bit data format. A new transmission will not result until the TDRE flag has
been cleared.
3. Repeat step 2 for each subsequent transmission.
The contents of the SCIxSR1 and SCIxSR2 registers are shown in Figures 9.12 and 9.13,
respectively.
Setting the TE bit from 0 to 1 automatically loads the transmit shift register with a preamble of 10 logic 1s (if M 5 0) or 11 logic 1s (if M 5 1). After the preamble shifts out, control
logic transfers the data from the SCI data register into the transmit shift register. A logic 0
Chapter 9 ■ Serial Communication Interface (SCI)
Internal bus
÷16
SCI data register
SBR12-SBR0
Breaks (all 0s)
Parity
generation
Preamble (all 1s)
T8
Shift enable
H 8 7 6 5 4 3 2 1 0 L
Load from SCIDR
M
PE
PT
START
Baud divider
STOP
Eclock
MSB
458
TxD
Loop
control
To RxD
Loops
RSRC
Transmitter control
TDRE interrupt request
TDRE
TIE
TC interrupt request
TC
TCIE
TE
SBK
Figure 9.11 ■ SCI transmitter block diagram
start bit automatically goes into the most significant bit position. Hardware supports odd or
even parity. When parity is enabled, the most significant bit of the data character is the parity
bit. The transmit data register empty flag, TDRE, in the SCIxSR1 register becomes set when
the SCI data register transfers a byte to the transmit shift register. The TDRE flag indicates
that the SCI data register can accept new data from the internal data bus. If the transmit interrupt enable bit, TIE, in the SCIxCR2 register is also set, the TDRE flag generates a transmit
interrupt request.
When the transmit shift register is not transmitting a frame, the TxD output signal goes
to the idle state, logic 1. If at any time software clears the TE bit in the SCIxCR2 register, the
TxD signal goes idle. If software clears the TE bit while a transmission is in progress, the frame
in the transmit shift register continues to shift out. To avoid accidentally cutting off the last
frame in a message, always wait for TDRE to go high after the last frame before clearing the
TE bit.
Use the following procedure if it is desirable to separate messages with preambles with
minimum idle line time:
1. Write the last byte of the first message to the transmit data register.
2. Wait for the TDRE flag to go high, indicating the transfer of the last frame to the
transmit shift register.
3. Queue a preamble by clearing and then setting the TE bit.
4. Write the first byte of the second message to the transmit data register.
459
9.6 ■ The SCI Operation
7
6
5
4
3
2
1
0
TDRE
TC
RDRF
IDLE
OR
NF
FE
PF
Reset value
= 0x00
TDRE: transmit data register empty flag
0 = no byte was transferred to the transmit shift register.
1 = transmit data register is empty.
TC: transmit complete flag
0 = transmission in progress.
1 = no transmission in progress.
RDRF: receiver data register full flag
0 = SCIxDR empty.
1 = SCIxDR full.
IDLE: idle line detected flag
0 = RxD line active.
1 = RxD line becomes idle.
OR: overrun error flag
0 = no overrun.
1 = overrun detected.
NF: noise error flag
Set during the same cycle as the RDRF bit but not set in the case.
of an overrun (OR).
0 = no noise.
1 = noise.
FE: framing error flag
Set when a 0 is detected where a stop bit was expected.
0 = no framing error.
1 = framing error.
PF: parity error flag
0 = parity correct.
1 = incorrect parity detected.
Figure 9.12 ■ SCI status register 1 (SCI0SR1/SCI1SR1)
7
6
5
4
3
2
1
0
0
0
0
0
0
BK13
TXDIR
RAF
Reset value
= 0x00
BK13: break transmit character length
0 = break character is 10- or 11-bit long.
1 = break character is 13- or 14-bit long.
TXDIR: transmit pin data direction in single-wire mode
0 = TxD pin to be used as an input in single-wire mode.
1 = TxD pin to be used as an output in single-wire mode.
RAF: receiver active flag
RAF is set when the receiver detects a logic 0 during the RT1 time
period of the start bit search. RAF is cleared when the receiver detects
an idle character.
0 = no reception in progress.
1 = reception in progress.
Figure 9.13 ■ SCI status register 2 (SCI0SR2/SCISR2)
The TDRE bit in the SCIxSR1 register is cleared by reading the SCIxSR1 register and
followed by writing a byte into the SCIxDRL register. All status flags related to reception are
cleared by reading the SCI status register followed by reading the SCIxDRL register.
460
Chapter 9 ■ Serial Communication Interface (SCI)
9.6.2 Break Characters
Whenever one party in the data communication discovers an error, it can send break characters to discontinue the communication and start over again. To send a break character, the
user sets the SBK bit in the SCIxCR1 register to 1. As long as the SBK bit is 1, transmitter logic
continuously loads break characters into the transmit shift register. After software clears the
SBK bit, the shift register finishes transmitting the last break character and then transmits at
least one logic 1. The automatic logic 1 at the end of a break character guarantees the recognition of the start bit of the next frame.
The SCI module recognizes a break character when a start bit is followed by 8 or 9 logic 0
data bits and a logic 0 where the stop bit should be. Receiving a break character has these effects on SCI registers.
• Sets the framing error flag FE
• Sets the receive data register full flag RDRF
• Clears the SCI data registers (SCIxDRH/L)
• May set the overrun flag OR, noise flag NF, parity error flag PE, or receiver active
flag RAF
9.6.3 Idle Characters
An idle character contains all 1s and has no start, stop, or parity bit. The length of the idle
character depends on the M bit in the SCIxCR1 register. The preamble is a synchronizing idle
character that begins the first transmission initiated after setting the TE bit from 0 to 1.
If the TE bit is cleared during a transmission, the TxD signal becomes idle after the completion of the transmission in progress. Clearing and then setting the TE bit during a transmission
queues an idle character to be sent after the frame currently being transmitted.
9.6.4 Character Reception
The block diagram of the SCI receiver is shown in Figure 9.14. The SCI receiver can accommodate either 8-bit or 9-bit data characters. The state of the M bit in the SCI control register 1
determines the length of data characters. When receiving 9-bit data, the R8 bit of the SCIxDRH
register holds the ninth bit.
During an SCI reception, the receive shift register shifts in a frame from the RxD pin.
The SCI data register is the read-only buffer between the internal bus and the receive shift
register. After a complete frame is shifted into the receive shift register, the data portion of
the frame is transferred to the SCI data register. The receive data register full flag, RDRF, in
the SCIxSR1 register becomes set, indicating that the receive byte can be read. If the receive
interrupt enable bit RIE in the SCIxCR2 register is also set, then an interrupt is requested to
the MCU.
The receiver uses the method illustrated in Figure 9.5 to detect the arrival of the start bit
and uses the majority function of the samples RT8, RT9, and RT10 to determine the logic value
of a bit.
9.6.5 Receiver Wake-Up
The SCI module supports the HCS12 to operate in a multiple-receiver system. When a
message is not intended for this MCU, the SCI module will put itself in a standby state to
ignore the rest of the message. This is done by setting the RWU bit of the SCIxCR2 register. In
the standby state, the SCI module will still load the receive data into the SCIxDRH/L registers,
but it will not set the RDRF flag.
461
9.6 ■ The SCI Operation
Internal bus
STOP
Eclock
Baud divider
RxD
Data
recovery
Loop
control
11-bit receive shift register
8
7
6
5
4
3
2
1
0
L
All 1s
MS B
From
TxD
H
START
SCI data register
SBR12-SBR0
RE
RAF
LOOPS
RSRC
FE
M
WAKE
ILT
PE
PT
IDLE interrupt request
NF
Wake-up
logic
PE
Parity
checking
R8
RWU
IDLE
ILIE
RDRF
RDRF/OR interrupt request
RIE
OR
Figure 9.14 ■ SCI receiver block diagram
The transmitting device can address messages to selected receivers by including addressing
information in the initial frame or frames of each message. One can choose to use idle line or
address mark to wake up the receiver to compare the address information of the incoming frame.
IDLE LINE WAKE-UP
When the idle line wake-up method is chosen, an idle condition on the RxD pin clears the
RWU bit in the SCIxCR2 register and wakes up the SCI. The initial frame or frames of every message contain addressing information. All receivers evaluate the addressing information, and receivers for which the message is addressed process the frames that follow. Any receiver for which a
message is not addressed can set its RWU bit and return to the standby state. The RWU bit remains
set and the receiver remains on standby until another idle character appears on the RxD signal.
Idle line wake-up requires that messages be separated by at least one idle character and that
no message contain idle characters. The idle character that wakes a receiver does not set the
receiver idle bit, IDLE, or the receive data register full flag, RDRF.
The idle-line-type bit, ILT, determines whether the receiver begins counting logic 1s as idle
character bits after the start bit or after the stop bit. The ILT bit is in the SCIxCR1 register.
462
Chapter 9 ■ Serial Communication Interface (SCI)
ADDRESS MARK WAKE-UP
In this wake-up method, a logic 1 in the most significant bit (msb) position of a frame clears
the RWU bit and wakes up the SCI. The logic 1 in the msb position marks a frame as an address
frame that contains addressing information. All receivers evaluate the addressing information,
and the receivers for which the message is addressed process the frames that follow. Any receiver
for which a message is not addressed can set its RWU bit and return to the standby state.
The logic 1 of an address frame clears the receiver’s RWU bit before the stop bit is received
and sets the RDRF flag. Address mark wake-up allows messages to contain idle characters but
requires that the msb be reserved for use in address frames.
9.6.6 Single-Wire Operation
Normally, the SCI uses two pins for transmitting and receiving. In single-wire operation,
the RxD pin is disconnected from the SCI module. The SCI module uses the TxD pin for both
receiving and transmitting, as illustrated in Figure 9.15.
Single-wire operation is enabled by setting the LOOPS and the RSRC bits in the SCIxCR1
register. Setting the LOOPS bit disables the path from the RxD pin to the receiver. Setting the
RSRC bit connects the receiver input to the output of the TxD pin driver. Both the transmitter
and receiver must be enabled. The TXDIR bit determines whether the TxD pin is going to be
used as an input (TXDIR 5 0) or an output (TXDIR 5 1) in this mode of operation.
Transmitter
TxD
Receiver
RxD
Figure 9.15 ■ Single-wire operation
9.6.7 Loop Operation
In this operation, the transmitter output goes to the receiver input. As shown in Figure
9.16, the RxD signal is disconnected from the SCI. The loop operation is enabled by setting the
LOOPS bit and clearing the RSRC bit in the SCIxCR1 register. Setting the LOOPS bit disables
the path from the RxD pin to the receiver. Clearing the RSRC bit connects the transmitter output to the receiver input. Both the transmitter and receiver must be enabled.
Transmitter
TxD
Receiver
RxD
Figure 9.16 ■ Loop operation
9.8 ■ Flow Control of USART in Asynchronous Mode
463
9.7 Operation of the SCI in Different MCU Modes
When the MCU is in normal run mode, the SCI module performs normal operation.
When the MCU enters the wait mode, the SCI’s operation depends on the state of the
SCISWAI bit in the SCIxCR1 register.
1. If the SCISWAI bit is 0, the SCI operates normally when the CPU is in wait mode.
2. If the SCISWAI bit is 1, the SCI clock generation ceases and the SCI module enters
a power-conservation state when the CPU is in wait mode. Setting the SCISWAI
bit does not affect the state of the receiver enable bit (RE) or the transmitter enable
bit (TE).
If the SCISWAI bit is set, any transmission or reception in progress stops at wait mode
entry. The transmission or reception resumes when either an internal or external interrupt
brings the CPU out of wait mode. Exiting wait mode by reset aborts any transmission or reception in progress and resets the SCI module.
When the MCU enters the stop mode, the SCI becomes inactive. The stop instruction
does not affect the SCI register states, but the SCI module clock will be disabled. The SCI
operation resumes from where it left off after an external interrupt brings the CPU out of
stop mode. Exiting stop mode by reset aborts any transmission or reception in progress and
resets the SCI.
9.8 Flow Control of USART in Asynchronous Mode
The SCI module will transmit data as fast as the baud rate allows. In some circumstances,
the software that is responsible for reading the data from the SCIxDRH/L register may not be
able to do so as fast as the data is being received. In this case, there is a need for the HCS12
MCU to tell the transmitting device to suspend transmission of data temporarily. Similarly, the
HCS12 MCU may need to be told to suspend transmission temporarily. This is done by means
of flow control. There are two common methods of flow control, XON/XOFF and hardware.
Here, X stands for transmission.
The XON/XOFF flow control can be implemented completely in software with no external hardware, but full-duplex communication is required. When incoming data need to be suspended, an XOFF byte is transmitted back to the other device that is transmitting the data being
received. To start the other device transmitting again, an XON byte is transmitted. XON and
XOFF are standard ASCII control characters. This means that when sending raw data instead of
ASCII text, care must be taken to ensure that XON and XOFF characters are not accidentally
sent with the data. The ASCII codes of XON and XOFF are 0x11 and 0x13, respectively. The
XON character is called the Device Control 1 (DC1) character; the XOFF character is called the
Device Control 3 (DC3) character.
Hardware flow control uses extra signals to control the flow of data. To implement hardware flow control on an HCS12 device, extra I/O pins must be used. Generally, an output pin
is controlled by the receiving device to indicate that the transmitting device should suspend
or resume transmissions. The transmitting device tests an input pin before a transmission to
determine whether data can be sent.
464
Chapter 9 ■ Serial Communication Interface (SCI)
Example 9.2
▼
Write an instruction sequence to configure the SCI0 to operate with the following
parameters:
• 9600 baud (E-clock is 24 MHz)
• 1 start bit, 8 data bits, and 1 stop bit format
• No interrupt
• Address mark wake-up
• Disable wake-up initially
• Long idle line mode
• Enable receive and transmit
• No loop back
• Disable parity
• SCI stops in wait mode
Solution: The following instruction sequence will achieve the desired configuration:
movb
#$00,SCI0BDH
; set up baud rate to 9600
movb
#156,SCI0BDL
;
movb
#$4C,SCI0CR1
; select 8 data bits, address mark wake-up
movb
#$0C,SCI0CR2
; enable transmitter and receiver
“
The equivalent C statements are as follows:
SCI0BDH 5 0x00;
SCI0BDL 5 0x9C;
SCI0CR1 5 0x4C;
SCI0CR2 5 0x0C;
▲
9.9 Interfacing SCI with TIA-232
Because the SCI circuit uses 0 and 5 V (or other lower voltages) to represent logic 0
and 1, respectively, it cannot be connected to the TIA-232 interface circuit directly.
A voltage translation circuit, called the TIA-232 transceiver, is needed to translate the
voltage levels of the SCI signals (RxD and TxD) to and from those of the corresponding
TIA-232 signals.
TIA-232 transceiver chips are available from many vendors. The LT1080/1081 from Linear
Technology, ST232 from ST Microelectronics, ICL232 from Intersil, MAX232 from MAXIM,
and DS14C232 from National Semiconductor are TIA-232 transceiver chips that can operate
with a single 5-V power supply and generate TIA-232 compatible outputs. These chips are also
pin compatible. In this section, we discuss the use of DS14C232 from National Semiconductor
to perform the voltage translation. The pin assignment and the use of each pin are shown in
Figure 9.17.
465
9.9 ■ Interfacing SCI with TIA-232
+5 V
6.3 V
– +
1.0 μF
C4
16
1
VCC
C1+
V+ 2
C1 1.0 μF
3
C1–
4
C2+
V–
C2 1.0 μF
C3
DC-to-DC Converter
5
6
1.0 μF
C2–
+5 V
TTL/CMOS
inputs
11
T1IN
T1OUT
D1
14
+5 V
EIA-232E
outputs
TTL/CMOS
inputs
10 T2IN
TTL/CMOS
outputs
12
D2
R1OUT
R1
T2OUT
7
R1IN
13
5 kΩ
TTL/CMOS
outputs
9
R2OUT
R2
R2IN
EIA-232E
inputs
8
5 kΩ
GND
15
Figure 9.17 ■ Pin assignments and connections of the DS14C232
Adding a TIA-232 transceiver chip allows the HCS12 to use the SCI interface to communicate with the TIA-232 interface circuit. An example of such a circuit is shown in
Figure 9.18. A null-modem wiring is followed in this circuit.
466
Chapter 9 ■ Serial Communication Interface (SCI)
DS14C232
1
DCD
6
PS1/TxD
CTS1
PS0/RxD
RTS1
11
10
12
9
T1IN
T1OUT
T2IN
R2IN
R1OUT
R1IN
R2OUT
T2OUT
14
2
8
7
13
3
7
8
DSR
RxD
RTS
TxD
CTS
4
DTR
Note:
1. Both CTS and RTS are
jumpered to an I/O pin in case
hardware handshake is needed.
9
RI
5
GND
DB9 connector
Figure 9.18 ■ Diagram of SCI and EIA-233DB9 connector wiring in SSE256 demo board
Example 9.3
▼
Write a subroutine to send a break to the communication port controlled by the
SCI0 interface. The duration of the break is approximately 24,000 E-clock cycles, or 1 ms
at 24 MHz.
Solution: A break character is represented by 10 or 11 consecutive 0s and can be sent out by
setting the bit 0 of the SCI0CR2 register. As long as the bit 0 of the SCI0CR2 register remains
set, the SCI will keep sending out the break character. The following assembly subroutine will
perform the desired operation:
#include
sendbrk
#include
“c:\miniide\hcs12.inc”
bset
SCI0CR2,SBK
ldy
#1
jsr
delayby1ms
bclr
SCI0CR2,SBK
rts
“c:\miniide\delay.asm”
; turn on send break
; turn off send break
467
9.9 ■ Interfacing SCI with TIA-232
The C language version of the function is as follows:
#include
“c:\cwHCS12\include\hcs12.h”
#include
“c:\cwHCS12\include\delay.h”
void send_break (void)
{
SCI0CR2 |5 SBK; // start to send break
delayby1ms(1);
SCI0CR2 &5 ,SBK; // stop sending break
}
▲
Example 9.4
▼
Write a subroutine to output the character in accumulator A to the SCI0 module using the
polling method.
Solution: A new character should be sent out only when the transmit data register is empty. When
the polling method is used, the subroutine will wait until bit 7 of the SCI0SR1 register is set
before sending out the character in accumulator A. The assembly language function is as follows:
#include
putcSCI0
“c:\miniide\hcs12.inc”
brclr
SCI0SR1,TDRE,*
staa
SCI0DRL
rts
; wait for TDRE to be set
; output the character
The C language version of the function is as follows:
void putcSCI0 (char cx)
{
while (!(SCI0SR1 & TDRE));
SCI0DRL 5 cx;
}
▲
Example 9.5
▼
Write a subroutine to read a character from the SCI0 using the polling method. The character will be returned in accumulator A.
Solution: Using the polling method, the subroutine will wait until the RDRF bit (bit 5) of the
SCI0SR1 register becomes set and then read the character held in the SCI0DRL register. The
subroutine that reads a character from SCI0 is as follows:
#include
“c:\miniide\hcs12.inc”
getcSCI0
brclr
SCI0SR1,RDRF,*
; wait until RDRF bit is set
ldaa
SCI0DRL
; read the character
rts
468
Chapter 9 ■ Serial Communication Interface (SCI)
The C language version of the function is as follows:
char getcSCI0 (void)
{
while(!(SCI0SR1 & RDRF));
return (SCI0DRL);
}
▲
Example 9.6
▼
Write a subroutine that outputs a NULL-terminated string pointed to by index register X
to the SCI0 using the polling method.
Solution: The subroutine will call putcSCI0() repeatedly until all characters of the string have
been outputted.
putsSCI0
done
ldaa
beq
jsr
bra
rts
1,x1
done
putcSCI0
putsSCI0
; get a character and move the pointer
; is this the end of the string?
The C language version of the program is as follows:
void
putsSCI0 (char *cx)
{
while (!(*cx)) {
putcSCI0(*cx);
cx11;
}
}
▲
Example 9.7
▼
Write a subroutine that inputs a string from the SCI0 module. The string is terminated by
the carriage return character and must be stored in a buffer pointed to by index register X. This
subroutine must allow the user to backspace to erase errors.
Solution: The subroutine will call getsSCI0() repeatedly until the carriage return character is
inputted.
getsSCI0
jsr
cmpa
beq
staa
jsr
cmpa
getch
#CR
qi
0,x
putch
#BS
; is it a carriage return?
; save the character and increment the pointer
; echo it back to SCI0
; is it a backspace character?
469
9.10 ■ Summary
nc
qi
bne
dex
ldaa
jsr
ldaa
jsr
bra
inx
bra
clr
rts
nc
#WS
putch
#BS
putch
gets
gets
0,x
; no, continue
; decrement the input buffer pointer
; wipe out the previous character
; terminate the string by a NULL character
The C language version of the function is as follows:
int gets(char *ptr)
{
char cx;
while ((cx 5 getch()) !5 0x0D) {
*ptr 5 cx;
putch(cx);
if (cx 55 0x08) {
// is it a backspace character?
ptr22;
// move back the string pointer
putch(0x20);
// output a space
putch(0x08);
// output another backspace character
}
ptr11;
// move buffer pointer
}
*ptr 5 0;
// terminate the string with a NULL character
return 0;
}
▲
9.10 Summary
When high-speed data transfer is not necessary, using serial data transfer enables the chip
designer to make the most use of the limited number of I/O pins. Serial data transfer can be performed asynchronously or synchronously. The SCI interface is an asynchronous interface that
is designed to be compatible with the TIA-232 standard. The TIA-232 standard has four aspects:
electrical, mechanical, procedural, and functional. For a short distance, modems are not needed
for two computers to communicate. A connection called null modem is used instead. This is
achieved by connecting signals in such a way as to fool two computers into thinking that they
are connected through modems.
Since there is no common clock signal for data transfer synchronization, devices participating in communication must agree on a common data rate before data exchange is started. Users
of the TIA-232 standard follow a common data format: Each character is framed with a start bit
and a stop bit. A character can have 8 or 9 data bits. The ninth bit is often used as a parity bit
for error checking or an address mark to alert the receiver to perform address comparison. The
receiver uses a clock signal with a frequency that is 16 times the data rate to sample the RxD
pin to detect the start bit and determine the logic values of data bits.
470
Chapter 9 ■ Serial Communication Interface (SCI)
Errors could happen during the data transmission process. The most common errors
include framing, receiver overrun, and parity errors. A framing error occurs when the start and
stop bits improperly frame a received character. A framing error is detected by a missing stop
bit. A receiver-overrun error occurs when one or multiple characters are received but not read
by the processor. A parity error occurs when an odd number of bits change value.
Some HCS12 members have two identical serial communication interface (SCI) subsystems. Since the TIA-232 standard uses a voltage level different from those to represent logic 1
and logic 0, a transceiver is required to do the voltage translation so that the SCI subsystem can
interface with the TIA-232 circuit. Due to the widespread use of the TIA-232 standard, transceiver chips are available from many vendors. The DS14C232 from National Semiconductor is
used as an example to illustrate the SCI hardware interfacing.
9.11 Exercises
E9.1 Sketch the output of the letters k and p at the TxD pin when they are transmitted using
the format of 1 start bit, 8 data bits, and 1 stop bit.
E9.2 Write an instruction sequence to configure the SCI1 to operate with the following
parameters:
• 19200 baud (E-clock is 24 MHz)
• One start bit, 8 data bits, and 1 stop bit format
• Enable both transmit and receive interrupts
• Idle line wake-up
• Disable wake-up initially
• Long idle line mode
• Enable receive and transmit
• No loop back
• Enable parity
E9.3 Write a subroutine to send a break to the communication port controlled by SCI1. The
duration of the break must be approximately 480,000 E-clock cycles.
E9.4 Write a subroutine to output the contents of accumulator A as two hex digits to channel
SCI0.
E9.5 Modify Example 9.4 so that the putcSCI0 routine will expand the CR character into the
CR/LF pair and expand the LF character into the LF/CR pair. You can add a flag to indicate
whether the expansion should be performed.
E9.6 Add an echo flag (1 byte) and modify Example 9.5 so that the received character will be
echoed back to the SCI0 when the flag is 1. Otherwise, no echo will be performed.
E9.7 Write a subroutine to input two hex digits from the SCI0 module and echo them back to
SCI0. When this routine is run on a demo board, it will allow you to input two hex digits from
the keyboard and echo them on the terminal screen.
E9.8 Modify the getcSCI0 routine in Example 9.5 so that it will handle the backspace character
like this:
• Check if the input character is the backspace character (ASCII code $08).
• If the entered character is the backspace character, then echo it, output a space
character, and output another backspace character. Why would you do this?
9.12 ■ Lab Exercises and Assignments
471
9.12 Lab Exercises and Assignments
L9.1 Write a program to be run on your demo board. This program and the user interact as
follows:
1. The program outputs the message “Please enter your age:” and waits for the user to
enter his or her age.
2. The user enters his or her age (followed by a carriage return) and the program reads it.
3. The program outputs the message “Please enter your height in inches:” and waits for
the user to enter his or her height.
4. The user enters his or her height in inches (followed by a carriage return) and the
program reads it.
5. The program outputs the message “Please enter your weight in lbs:” and waits for the
user to enter his or her weight.
6. The user enters his or her weight in lbs (followed by a carriage return) and the program
reads it.
7. The program outputs the following messages on the terminal screen and exits:
You are xxx years old.
You are kk ft mm inches tall.
You weigh zzz lbs.
L9.2 If your demo board has two TIA-232 connectors, then perform this experiment.
1. Connect one of the TIA-232 connectors to the PC so that you can download your
program onto the demo board.
2. Connect the second TIA-232 connector to another demo board.
3. Write a program to be run on two demo boards. The program outputs one character
per second to the SCI port that is connected to another demo board and then reads in
characters from the same SCI port.
4. The program displays the SCI activities on the LCD screen as follows:
The first row displays what has been sent out: snd:xxxxx . . .
The second row displays what has been received: rvd:yyyyy . . .
Only the last 16 characters sent and received will be displayed on the LCD screen if a
16 3 2 LCD screen is used.
This page intentionally left blank
10
The SPI Function
10.1 Objectives
After completing this chapter, you should be
able to
• Describe the HCS12 SPI module
• Configure the SPI operation parameters
• Interface with peripheral devices with SPI
interface
• Use the SPI function to interface with the
shift register 74HC595
• Use SPI to interface with the digital
temperature sensor TC72
• Use SPI to interface with the 12-bit D/A
converter MCP4922
• Use SPI to interface with the matrix LED
display driver MAX6952
474
Chapter 10 ■ The SPI Function
10.2 Introduction to the SPI Function
The serial peripheral interface (SPI) allows the HCS12 to communicate synchronously with
peripheral devices and other microcontrollers. The SPI system in the HCS12 can operate as a
master or as a slave. When the SPI module is configured as a master, it is responsible for generating the clock signal (SCK) during an SPI data transfer. The SPI subsystem is mainly used
in interfacing with peripherals such as TTL shift registers, LED/LCD display drivers, phaselocked-loop (PLL) chips, memory components with serial interface, or A/D and D/A converter
chips that do not need a very high data rate.
The SPI must be enabled to operate. When the SPI module is enabled, the four associated
SPI port pins are dedicated to the SPI function as
• Slave select (SS)
• Serial clock (SCK)
• Master out/slave in (MOSI)
• Master in/slave out (MISO)
The main element of the SPI system is the SPI data register. The 8-bit data register in the
master and the 8-bit data register in the slave are linked by the MOSI and the MISO pins to
form a distributed 16-bit register. When a data transfer is performed, this 16-bit register is serially shifted 8 bit positions by the SCK clock from the master; data is exchanged between the
master and the slave. Data written to the master SPI data register becomes the output data for
the slave, and data read from the master SPI data register after a transfer operation is the input
data from the slave.
A write to the SPI data register puts data into the transmit buffer if the previous transmission was complete. When a transfer is complete, received data is moved into a receiver data
register. Data may be read from this double-buffered system any time before the next transfer is
completed. This 8-bit register acts as the SPI receive data register for reads and as the SPI transmit data register for writes. A single SPI register address is used for reading data from the read
data buffer and for writing data to the shifter.
There are four possible clock formats to choose from. The user selects one of these four
clock formats for data transfer by programming the CPOL and CPHA bits of the SPI control register 1. The CPOL bit simply selects a noninverted (idle low) or inverted (idle high) clock. The
CPHA bit is used to accommodate two fundamentally different protocols by shifting the clock
a half-cycle or by not shifting the clock.
The SPI function on the HCS12 has been modified slightly from that in the 68HC11 with
the expectation of improving its applicability. An HCS12 device may have from one to three
identical SPI modules (SPI0, SPI1, and SPI2).
10.2.1 SPI Signal Pins
The SPI0 function shares the use of the upper four Port S pins: MISO0 (PS4), MOSI0 (PS5),
SCK0 (PS6), and SS0 (PS7). Out of reset, the SPI1 and SPI2 share the use of the lower four Port
P pins and upper four Port P pins, respectively. However, the SPI1 and SPI2 pins can also be
rerouted to the lower four Port H pins and upper four Port H pins by programming the MODRR
register. The assignment of SPI1 and SPI2 signal pins is described in Section 7.5.6. There is no
need to configure the pin directions when the SPI function is enabled.
• MISOx (x 5 0, 1, or 2): master in slave out (serial data input). This pin is used to
transmit data out of the SPI module when it is configured as a slave and receive
data when it is configured as a master.
475
10.3 ■ Registers Related to the SPI Subsystem
• MOSIx (x 5 0, 1, or 2): master out slave in (serial data output). This pin is used to
transmit data out of the SPI module when it is configured as a master and receive
data when it is configured as a slave.
• SCKx (x 5 0, 1, or 2): serial clock. This pin is used to carry the clock signal that
synchronizes SPI data transfer. It is an output if the SPI is configured as a master
but an input if the SPI is configured as a slave.
• SSx (x 5 0, 1, or 2): slave select. When configured as a slave, this pin must be pulled
low for the SPI module to operate.
10.3 Registers Related to the SPI Subsystem
Most of the SPI operational parameters are set by two SPI control registers: SPIxCR1 (x 5 0,
1, or 2) and SPIxCR2 (x 5 0, 1, or 2). Their contents are shown in Figures 10.1 and 10.2, respectively. The SPI must be enabled before it can start data transfer. Setting bit 6 of the SPIxCR1
register will enable the SPI subsystem. Bit 1 (SSOE) of the SPIxCR1 register allows the user to
use the SS pin to select the slave device for data transfer automatically. However, this feature is
not useful if the user wants to use the SPI to interface with multiple slave devices at the same
time. The selection of SS for input and output is shown in Table 10.1. Some slave devices may
7
6
5
4
3
2
1
0
SPIE
SPE
SPTIE
MSTR
CPOL
CPHA
SSOE
LSBFE
Reset value
= 0x04
SPIE: SPI interrupt enable bit
0 = SPI interrupts are disabled.
1 = SPI interrupts are enabled.
SPE: SPI system enable bit
0 = SPI disabled.
1 = SPI enabled and pins PS4–PS7 are dedicated to SPI function.
SPTIE: SPI transmit interrupt enable
0 = SPTEF interrupt disabled.
1 = SPTEF interrupt enabled.
MSTR: SPI master/slave mode select bit
0 = slave mode.
1 = master mode.
CPOL: SPI clock polarity bit
0 = active high clocks selected; SCK idle low.
1 = active low clocks selected; SCK idle high.
CPHA: SPI clock phase bit
0 = The first SCK edge is issued one-half cycle into the 8-cycle transfer operation.
1 = The SCK edge is issued at the beginning of the 8-cycle transfer operation.
SSOE: slave select output enable bit
The SS output feature is enabled only in master mode by asserting the
SSOE bit and the MODFEN bit of the SPIxCR2 register.
LSBF: SPI least significant bit first enable bit
0 = data is transferred most significant bit first.
1 = data is transferred least significant bit first.
Figure 10.1 ■ SPI control register 1 (SPIxCR1, x 5 0, 1, or 2)
476
Chapter 10 ■ The SPI Function
MODFEN
SSOE
Master Mode
Slave Mode
0
0
1
1
0
1
0
1
SS not used by SPI
SS not used by SPI
SS input with MODF feature
SS output
SS input
SS input
SS input
SS input
Table 10.1 ■ SS input/output selection
7
6
5
0
0
0
4
3
MODFEN BIDIROE
2
1
0
0
SPSWAI
SPC0
Reset value
= 0x08
MODFEN: mode fault enable bit
0 = disable the MODF error.
1 = enable setting the MODF error.
BIDIROE: output enable in the bidirectional mode of operation
0 = output buffer disabled.
1 = output buffer enabled.
SPSWAI: SPI stop in wait mode
0 = SPI clock operates normally in stop mode.
1 = stop SPI clock generation in wait mode.
SPC0: serial pin control bit 0
With the MSTR bit in the SPIxCR1 register, this bit enables bidirectional pin
configuration, as shown in Table 10.2.
Figure 10.2 ■ SPI control register 2 (SPIxCR2, x 5 0, 1, or 2)
Pin Mode
A
B
C
D
Normal
Bidirectional
SPC0
MSTR
MISO1
0
0
Slave out
Slave in
SCK in
SS in
1
Master in
Master out
SCK out
SS I/O
0
Slave I/O
—
SCK in
SS in
1
—
Master I/O
SCK out
SS I/O
1
MOSI2
SCK3
SS4
1 Slave output is enabled if BIDIROE bit = 1, SS = 0, and MSTR = 0 (C).
2 Master output is enabled if BIDIROE bit = 1 and MSTR = 1 (D).
3 SCK output is enabled if MSTR = 1 (B,D).
4 SS output is enabled if MODFEN = 1, SSOE = 1, and MSTR = 1 (B,D).
Table 10.2 ■ Bidirectional pin configurations
transfer data with the least significant bit first. Bit 0 (LSBF) of the SPIxCR1 register allows the
user to have this flexibility.
The SPI data shift rate (also called baud rate) is programmable. The baud rate is set by
programming the SPIxBR register to appropriate values. The contents of the SPIxBR register are
shown in Figure 10.3. The method for computing the baud rate is also given in Figure 10.3.
477
10.3 ■ Registers Related to the SPI Subsystem
7
6
5
4
3
2
1
0
0
SPPR2
SPPR1
SPPR0
0
SPR2
SPR1
SPR0
Reset value
= 0x00
SPPR2,SPPR0: SPI baud rate preselection bits
SPR2,SPR0: SPI baud rate selection bits
BaudRateDivisor = (SPPR + 1) × 2(SPR + 1)
Baud Rate = E-Clock ÷ BaudRateDivisor
Figure 10.3 ■ SPI baud rate register (SPIxBR, x 5 0, 1, or 2)
Example 10.1
▼
Give a value to be loaded into the SPIxBR register to set the baud rate to 2 MHz for a
24-MHz E-clock.
Solution: 24 MHz 4 2 MHz 5 12. By setting SPPR2,SPPR0 and SPR2,SPR0 to 010 and 001,
respectively, we can set the baud rate to 2 MHz. The value to be loaded to the SPIxBR register
is $21.
▲
Example 10.2
▼
What is the highest possible baud rate for the SPI with 24-MHz E-clock?
Solution: The highest SPI baud rate occurs when both SPPR2,SPPR0 and SPR2,SPR0 are set to 000.
Under this condition, the BaudRateDivisor is 2, and hence the baud rate is 24 MHz/2 5 12 MHz.
▲
The SPI has a status register that records the progress of data transfer and errors, as shown
in Figure 10.4. The application program can check bit 7 of the SPIxSR register or wait for the
SPI interrupt to find out if the SPI transfer has completed. When transferring data in higher
frequency using the SPI format, using the polling method is more efficient due to the overhead
involved in interrupt handling.
The setting of the SPIF flag may request an interrupt to the CPU if the SPIE bit of the
SPIxCR1 register is also set to 1.
The SPTEF bit is set when there is room in the transmit data buffer. It is cleared by reading
the SPIxSR register with SPTEF set, followed by writing a data value into the SPI data (SPIxDR)
register. The SPTEF bit is set whenever the byte in the SPIxDR register is transferred to the
transmit shift register. The setting of the SPTEF flag may request an interrupt to the CPU if the
SPTIE bit of the SPIxCR1 register is also set to 1.
The MODF bit is set if the SS input becomes low while the SPI is configured as a master.
The flag is cleared automatically by a read of the SPIxSR register followed by a write to the
SPIxCR1 register. The MODF bit is set only if the MODFEN bit of the SPIxCR2 register is set.
The 8-bit SPIxDR register is both the input and output register for SPI data. A write to this
register allows a byte to be queued and transmitted. For an SPI configured as a master, a queued
478
Chapter 10 ■ The SPI Function
7
6
5
4
3
2
1
0
SPIF
0
SPTEF
MODF
0
0
0
0
Reset value
= 0x20
SPIF: SPI interrupt request bit
SPIF is set after the eight SCK cycles in a data transfer, and it is
cleared by reading the SP0SR register (with SPIF set) followed by
a read access to the SPI data register.
0 = transfer not yet complete.
1 = new data copied to SPIxDR.
SPTEF: SPI data register empty interrupt flag
0 = SPI data register not empty.
1 = SPI data register empty.
MODF: mode error interrupt status flag
0 = mode fault has not occurred.
1 = mode fault has occurred.
Figure 10.4 ■ SPI status register (SPIxSR)
data byte is transmitted immediately after the previous transmission has completed. Do not
write to the SPIxDR register unless the SPTEF bit is 1.
10.4 SPI Operation
Only a master SPI module can initiate transmission. A transmission begins by writing to
the master SPI data register. Data is transmitted and received simultaneously. The serial clock
(SCK) synchronizes shifting and sampling of the information on the two serial data lines. The
SS line allows selection of an individual slave SPI device; slave devices that are not selected do
not interfere with SPI bus activities. Optionally, on a master SPI device, the SS signal can be
used to indicate multiple-master bus contention.
10.4.1 Transmission Formats
The CPHA and CPOL bits in the SPIxCR1 register allow the user to select one of the four
combinations of serial clock phase and polarity. The clock phase control bit (CPHA) selects one
of two fundamentally different transmission formats. Clock phase and polarity should be identical for the master SPI device and the communicating slave device.
When the CPHA bit is set to 0, the first edge on the SCK line is used to clock the first data bit
of the slave into the master and the first data bit of the master into the slave. In some peripheral
devices, the first bit of the slave’s data is available at the slave data out pin as soon as the slave is
selected. In this format, the first SCK edge is not issued until a half-cycle into the 8-cycle transfer operation. The first edge of SCK is delayed a half cycle by clearing the CPHA bit.
The SCK output from the master remains in the inactive state for a half SCK period before
the first edge appears. A half SCK cycle later, the second edge appears on the SCK pin. When this
second edge appears, the value previously latched from the serial data input is shifted into the
least significant bit of the shifter. After this second edge, the next bit of the SPI transfer data is
transmitted out of the MOSI pin of the master to the serial data input pin of the slave device.
This process continues for a total of 16 edges on the SCK pin, with data being latched on oddnumbered edges and shifted (to the shift register) on even-numbered edges. Data reception is
double-buffered. Data is shifted serially into the SPI shift register during the transfer and is
transferred to the parallel SPI data register after the last bit is shifted in.
479
10.4 ■ SPI Operation
After the 16th (last) SCK edge, the SPIF flag in the SPIxSR register is set, indicating that the
transfer is complete. The timing diagram for this transfer format (CPHA bit 5 0) is shown in
Figure 10.5.
Begin
Transfer
SS (O)
master only
End
SS (I)
SCK (CPOL = 0)
SCK (CPOL = 1)
Sample I
MOSI/MISO
Change O
MOSI Pin
Change O
MISO Pin
tL
MSB first (LSBF = 0)
LSB first (LSBF = 1)
MSB
LSB
tT
Bit 6
Bit 1
Bit 5
Bit 2
Bit 4
Bit 3
Bit 3
Bit 4
Bit 2
Bit 5
Bit 1
Bit 6
tI
tL
LSB Minimum 1/2 SCK
MSB
for tT, tI, tL
Figure 10.5 ■ SPI clock format 0 (CPHA 5 0)
Some peripheral devices require the first SCK edge to appear before the first data bit becomes available at the data out pin; the second edge clocks data into the system. In this format,
the first SCK edge is issued by setting the CPHA bit at the beginning of the 8-cycle transfer
operation. The first edge of SCK occurs immediately after the half SCK clock cycle synchronization delay. This first edge commands the slave to transfer its most significant data bit to the
serial data input pin of the master. A half SCK cycle later, the second edge appears on the SCK
pin. This is the latching edge for both the master and slave. When the third edge occurs, the
value previously latched from the serial data input pin is shifted into the least significant bit
of the SPI shifter. After this edge, the next bit of the master data is shifted out of the serial data
output pin of the master to the serial input pins on the slave.
This process continues for a total of 16 edges on the SCK line with data being latched
on even-numbered edges and shifting taking place on odd-numbered edges. Again, the SPIF
flag is set after the 16th SCK edge. The timing diagram of this transmission format is shown in
Figure 10.6.
480
Chapter 10 ■ The SPI Function
Begin
Transfer
SS (O)
master only
End
SS (I)
SCK (CPOL = 0)
SCK (CPOL = 1)
Sample I
MOSI/MISO
Change O
MOSI Pin
Change O
MISO Pin
tL
MSB first (LSBF = 0)
LSB first (LSBF = 1)
tT
MSB
LSB
Bit 6
Bit 1
Bit 5
Bit 2
Bit 4
Bit 3
Bit 3
Bit 4
Bit 2
Bit 5
Bit 1
Bit 6
tI
tL
LSB Minimum 1/2 SCK
MSB
for tT, tI, tL
Figure 10.6 ■ SPI clock format 1 (CPHA 5 1)
10.4.2 Bidirectional Mode (MOMI or SISO)
The SPI subsystem can be used in bidirectional mode. In this mode, the SPI uses only one
serial data pin for the interface with external device(s). The MSTR bit of the SPIxCR1 register
decides which pin is to be used. The MOSI pin becomes a serial data I/O (MOMI) pin for the
master mode, and the MISO pin becomes a serial data I/O (SISO) pin for slave mode. The MISO
pin in the master mode and the MOSI pin in the slave mode are not used by the SPI in bidirectional mode. The possible combinations are shown in Figure 10.7.
The direction of each serial I/O pin depends on the BIDIROE bit. If the pin is configured
as an output, serial data from the shift register is driven out on the pin. The same pin is also
the serial input to the shift register. If we want to read data from a peripheral device, then the
BIDIROE bit should be cleared to 0.
The use of bidirectional mode is illustrated in Exercise 10.8.
10.4.3 Mode Fault Error
If the SSx input becomes low while the SPIx is configured as a master, it indicates a system
error in which more than one master may be trying to drive the MOSIx and SCKx lines simultaneously. This condition is not permitted in normal operation. The MODF bit in the SPIxSR
register is set automatically, provided that the MODFEN bit in the SPIxCR2 is set.
481
10.4 ■ SPI Operation
Master mode
MSTR = 1
When SPE = 1
Serial Out
Normal
mode
SPC0 = 0
Slave mode
MSTR = 0
MOSI
SPI
MISO
Serial Out
SPI
MOSI
SPI
Serial In
SWOM enables open-drain output
Bidirectional
mode
SPC0 = 1
Serial In
MOMI
BIDIROE
Serial In
Serial Out
MISO
SWOM enables open-drain output
Serial In
SPI
Serial Out
BIDIROE
SISO
Figure 10.7 ■ Normal mode and bidirectional mode
In the special case where the MODFEN bit is cleared, the SSx pin is a general-purpose I/O
pin for the SPI system configured in master mode. In this special case, the mode fault function is prohibited and the MODF flag remains cleared. In case the SPI system is configured as a
slave, the SSx pin is a dedicated input pin. Mode fault error does not occur in slave mode.
When a mode fault error occurs, the MSTR bit in the SPIxCR1 is cleared, the MODF bit in
the SPIxSR register is set, and the output enable for the SCKx, MISOx, and MOSIx pins are deasserted. If the mode fault error occurs in the bidirectional mode for an SPI system configured in
master mode, output enable of the MOMI pin is cleared if it was set but SISO is not affected. No
mode fault error occurs in the bidirectional mode for the SPI system configured in slave mode.
10.4.4 Low-Power Mode Options
The HCS12 has two low-power modes and the SPI module behaves differently in these two
modes.
SPI IN WAIT MODE
SPI operation in wait mode depends on the state of the SPISWAI bit in the SPIxCR2
register.
• If the SPISWAI bit is cleared, the SPI operates normally when the CPU is in wait
mode.
• If the SPISWAI bit is set, the SPI clock generation ceases and the SPI module
enters a power conservation state when the CPU is in the wait mode. If the SPI is
configured as a master, any transmission and reception in progress stops at wait
mode entry. The transmission and reception resumes when the SPI exits wait mode.
482
Chapter 10 ■ The SPI Function
If the SPI is configured as a slave, any transmission and reception in progress
continues if the SCK continues to be driven from the master. This keeps the slave
synchronized to the master and the SCK.
SPI IN STOP MODE
The stop mode is dependent on the system. The SPI enters stop mode when the module
clock is disabled. If the SPI is in master mode and exchanging data when the CPU enters stop
mode, the transmission is frozen until the CPU exits stop mode. After exiting stop mode, data
to and from the external SPI device is exchanged correctly. In slave mode, the SPI will stay synchronized with the master. The stop mode is equivalent to the wait mode with the SPISWAI
bit set except that the stop mode is dependent on the system and cannot be controlled with the
SPISWAI bit.
10.5 SPI Circuit Connection
In a system that uses the SPI subsystem, one device (normally a microcontroller) is configured as the master and the other devices are configured as slaves. Either a peripheral chip or a
HCS12 can be configured as a slave device. The master SPI device controls the data transfer and
can control one or more SPI slave devices simultaneously.
In a single-slave configuration, the circuit would be connected as shown in Figure 10.8. If
the SSx output enable feature is set, then the SSx pin will automatically go low to enable the
slave device before the data transfer is started. This feature is not used in Figure 10.8.
Master SPI
Shift register
MISO
MISO
MOSI
MOSI
SCK
SCK
Slave SPI
Shift register
VDD
Baud rate
generator
SS
SS
Figure 10.8 ■ Master/slave transfer block diagram
There could be several connection methods in a multislave SPI environment. One possibility is shown in Figure 10.9. In this connection method, the HCS12 can choose any peripheral
device for data transfer. In Figure 10.9, Port P pins are used to drive the SS inputs of peripheral
devices. Any other unused general-purpose I/O pins can be used for this purpose.
If we don’t need the capability of selecting an individual peripheral device for data transfer,
then the connection shown in Figure 10.10 can be used, which will save quite a few I/O pins.
Figure 10.10 differs from Figure 10.9 in the following ways:
1. The MISO pin of each slave is wired to the MOSI pin of the slave device to its right.
The MOSI pins of the master and slave 0 are still wired together.
483
10.5 ■ SPI Circuit Connection
+5 V
SPI master
(HCS12)
Slave 0
Slave 1
Slave k
Shift
register
Shift
register
Shift
register
MOSI SCK MISO SS
SS
MOSI SCK MISO SS
MOSI SCK MISO SS
SCKx
MOSIx
MISOx
PP0
PP1
PPk
Figure 10.9 ■ Single-master and multiple-slave device connection (method 1)
SPI master
(HCS12)
+5 V
Slave 0
Slave 1
Slave k
Shift
register
Shift
register
Shift
register
MOSI SCK MISO SS
MOSI SCK MISO SS
MOSI SCK MISO SS
SS
SCKx
MOSIx
MISOx
Figure 10.10 ■ Single-master and multiple-slave device connection (method 2)
2. The MISO pin of the master is wired to the same pin of the last slave device.
3. The SS inputs of all slaves are tied to ground to enable all slaves.
Thus the shift registers of the SPI master and slaves become a ring. The data of slave k is
shifted to the master SPI, the data of the master is shifted to slave 0, the data of slave 0 is shifted
to slave 1, and so on. In this configuration, a minimal number of pins control a large number of
peripheral devices. However, the master does not have the freedom to select an arbitrary slave
device for data transfer without going through other slave devices.
484
Chapter 10 ■ The SPI Function
This type of configuration is often used to extend the capability of the SPI slave. For example, suppose there is an SPI-compatible seven-segment display driver/decoder that can drive only
four digits. By using this configuration, up to 4 3 k digits can be displayed when k driver/decoders
are cascaded together.
Depending on the capability and role of the slave device, either the MISO or MOSI pin
may not be used in the data transfer. Many SPI-compatible peripheral chips do not have the
MISO pin.
10.6 Configuration of and Data Transfer in SPI
Most peripheral chips with the SPI interface have either a chip-select (CS) or chip-enable
(CE) signal to enable data exchange with the chip. One needs to assert this signal in order to perform data exchange with the device. Before data transfer can be started, the SPI function must
be properly configured. The designer needs to deal with four registers: the WOMS, SPIxBR,
SPIxCR1, and SPIxCR2 registers. In addition, the Port S pins pull-up must be enabled so that no
external pull-up device is needed.
Example 10.3
▼
Configure the SPI0 module to operate with the following settings assuming that the E-clock
is 24 MHz:
• Baud rate at 6 MHz
• Interrupt disabled
• SPI enabled
• SCK idle low
• Data to be latched on the rising edge of SCK
• Master mode
• Most significant bit of a data byte is transferred first
• Mode fault and SS output disabled
• Normal SPI operation
• SPI stopped in wait mode
Solution: Since 24 MHz/6 MHz 5 4, we need to set SPPR2,SPPR0 and SPR2,SPR0 to 001 and
000, respectively. Therefore, the value to be written into the SPI0BR register would be $10. For
other parameters, we need to write $50 and $02 into SPI0CR1 and SPI0CR, respectively. The
WOMS register must also be cleared to enable the Port S pull-up.
The following instruction sequence will configure the SPI as desired:
movb
movb
#$10,SPI0BR
#$50,SPI0CR1
movb
movb
#$02,SPI0CR2
#0,WOMS
; set baud rate to 6 MHz (the value of 0x01 will also work)
; disable interrupt, enable SPI, SCK idle low,
; data latched on rising edge, data transferred msb first
; disable bidirectional mode, stop SPI in wait mode
; enable Port S pull-up
10.6 ■ Configuration of and Data Transfer in SPI
485
The same setting can be achieved by the following C statements:
SPI0BR
SPI0CR1
SPI0CR2
WOMS
5 0x10;
5 0x50;
5 0x02;
5 0;
/* set baud rate to 6 MHz */
/* enable SPI, data shift on SCK’s rising edge, master mode */
/* disable bidirectional mode, SPI stops in wait mode */
/* enable Port S pull-up */
▲
Sending and reading data using the SPI interface is very straightforward. The following four
functions are used most often:
1. putcSPIx (x 5 0, 1, or 2). This function sends out a character through the SPI interface.
In assembly language, the character to be output is passed in accumulator A.
2. putsSPIx (x 5 0, 1, or 2). This function outputs a NULL-terminated string through the
SPI interface. In assembly language, the string is pointed to by index register X.
3. getcSPIx (x 5 0, 1, or 2). This function reads in a character from the SPI interface. In
assembly language, the character is returned in accumulator A.
4. getsSPIx (x 5 0, 1, or 2). This function reads in a string from the SPI interface. The
buffer to hold the string is pointed to by index register X, whereas the number of
characters to read is specified in accumulator B.
Example 10.4
▼
Write the four common SPI data transfer functions in the assembly and C languages.
Solution: Before writing a byte to the SPI data register, the user must make sure that the SPTEF
flag is 1. To make sure that the SPI transfer is completed, the program must wait until the SPIF
flag is set to 1.
The following assembly function outputs the character in accumulator A to SPI0:
putcspi0 brclr
staa
brclr
ldaa
rts
SPI0SR,SPTEF,*
SPI0DR
SPI0SR,SPIF,*
SPI0DR
; wait until write operation is permissible
; output the character to SPI0
; wait until the byte is shifted out
; clear the SPIF flag
The following assembly function outputs a NULL-terminated string pointed to by index
register X:
putsspi0 ldaa
beq
jsr
bra
doneps0 rts
1,x1
doneps0
putcspi0
putsspi0
; get 1 byte to be output to SPI port
; reach the end of the string?
; call subroutine to output the byte
; continue to output
To read a byte from the SPI interface, a byte must be written into the SPIxDR register to
trigger eight clock pulses to be sent out from the SCK pin. The following assembly function
reads a character from the SPI0 interface and returns the character in accumulator A:
getcspi0 brclr
staa
brclr
ldaa
rts
SPI0SR,SPTEF,*
SPI0DR
SPI0SR,SPIF,*
SPI0DR
; wait until write operation is permissible
; trigger eight clock pulses for SPI transfer
; wait until a byte has been shifted in
; return the byte in A and clear the SPIF flag
486
Chapter 10 ■ The SPI Function
The following assembly function reads a string from the SPI0 interface:
getsSPI0 tstb
beq donegs0
jsr getcspi0
staa 1,x1
decb
bra getsspi0
donegs0 clr 0,x
rts
; check the byte count
; return when byte count is zero
; call subroutine to read a byte
; save the returned byte in the buffer
; decrement the byte count
; terminate the string with a NULL character
These four assembly functions are stored in the file spi0util.asm and are included on the
complementary CD.
The C language versions of the previous four functions are as follows:
void putcspi0 (char cx)
{
char
temp;
while(!(SPI0SR & SPTEF));
// wait until write is permissible
SPI0DR 5 cx;
// output the byte to the SPI
while(!(SPI0SR & SPIF));
// wait until write operation is complete
temp 5 SPI0DR;
// clear the SPIF flag
}
void putsspi0(char *ptr)
{
while(*ptr) {
// continue until all characters have been outputted
putcspi0(*ptr);
ptr11;
}
}
char getcspi0(void)
{
while(!(SPI0SR & SPTEF));
// wait until write is permissible
SPI0DR 5 0x00;
// trigger eight SCK pulses to shift in data
while(!(SPI0SR & SPIF));
// wait until a byte has been shifted in
return SPI0DR;
// return the character
}
void getsspi0(char *ptr, char count)
{
while(count) {
/* continue while byte count is nonzero */
*ptr11 5 getcspi0(); /* get a byte and save it in buffer */
count22;
}
*ptr 5 0;
/* terminate the string with a NULL */
}
These four C functions are stored in the file spi0util.c and are included in the complementary CD.
▲
487
10.8 ■ The 74HC595 Shift Register
10.7 SPI-Compatible Chips
The SPI is a protocol proposed by Freescale to interface peripheral devices to a microcontroller. As long as a peripheral device supports the SPI interface protocol, it can be used with
any microcontroller that implements the SPI subsystem. Many semiconductor manufacturers
are producing SPI-compatible peripheral chips. The Freescale SPI protocol is compatible with
the National Semiconductor Microwire protocol. Therefore, any peripheral device that is compatible with the SPI can also be interfaced with the Microwire protocol.
10.8 The 74HC595 Shift Register
As shown in Figure 10.11, the 74HC595 consists of an 8-bit shift register and an 8-bit
D-type latch with three-state parallel outputs. The shift register accepts serial data and provides
a serial output. The shift register also provides parallel data to the 8-bit latch. The shift register
and the latch have different clock sources. This device also has an asynchronous reset input.
The frequency of the shift clock can be as high as 100 MHz.
The functions of the pins in Figure 10.11 are as follows:
• DS: Serial data input. The data on this pin is shifted into the 8-bit shift register.
• SC: Shift clock. A low-to-high transition on this signal causes the data at the serial
input pin to be shifted into the 8-bit shift register.
• Reset. A low on this pin resets the shift register portion of this device only. The
8-bit latch is not affected.
DS
14
15
1
2
3
Shift
register
Latch
4
5
SC
11
6
7
Reset
LC
OE
10
12
9
QA
QB
QC
QD
QE
QF
QG
QH
SQH
13
VCC = Pin 16
GND = Pin 8
Figure 10.11 ■ The 74HC595 block diagram and pin assignment
488
Chapter 10 ■ The SPI Function
• LC: Latch clock. The rising edge of this signal loads the contents of the shift
register into the output latch.
• OE: Output enable. A low on this pin allows the data from the latch to be
presented at the output pins QA, . . . , QH.
• QA to QH: Noninverted, tri-state latch outputs.
• SQH: Serial data output. This is the output of the eighth stage of the 8-bit shift
register. This output does not have tri-state capability.
The 74HC595 is designed to shift in 8-bit data serially and then transfer it to the latch to
be used as parallel data. The 74HC595 can be used to add parallel output ports to the HCS12.
Both the connection methods shown in Figures 10.9 and 10.10 can be used in appropriate
applications.
Example 10.5
▼
Describe how to use two 74HC595s to drive eight common-cathode seven-segment displays, assuming that the E-clock frequency of the HCS12 is 24 MHz.
Solution: Two 74HC595s can be cascaded using the method shown in Figure 10.10. One
74HC595 is used to hold the seven-segment pattern, whereas the other 74HC595 is used to
carry digit-select signals. The circuit connection is shown in Figure 10.12.
Since there are only seven segments, the QH bit of the segment-control 74HC595 is not
needed. The PK7 pin is used to control the LC input of the 74HC595. The time-multiplexing
technique illustrated in Example 4.14 will be used to display multiple digits in Figure 10.12.
5V
Reset
OE
300 Ω
QG
QF
(7)
(6)
(0)
a
b
a
b
a
b
g
g
g
74HC595
DS
SC LC SQH
MOSI0
SCK0
reset
DS QH
SC
R
Common
cathode
2N2222
R
QG
PK7
Common
cathode
5V
2N2222
LC
OE
R
QA
HCS12
74HC595
Figure 10.12 ■ Two 74HC595s together drive eight seven-segment displays
Common
cathode
/MAX = 70 mA
300 Ω
QA
2N2222
10.8 ■ The 74HC595 Shift Register
489
To light the digit on display 7, the voltage at QH of the digit-select 74HC595 must be driven
to high. To light the digit on display 6, the voltage at QG of the digit-select 74HC595 must be
driven to high, and so on.
There are four parts in the program to be written.
Part 1. Display table setup
The segment patterns and digit-select values are stored in a table so that the table lookup
method can be used to display the desired pattern.
Part 2. SPI module initialization
Configure the SPI module to shift data at 12 MHz on the rising clock edge and most
significant data bit first.
Part 3. Timer delay function
Include the library function delayby1ms to multiplex the digit per millisecond.
Part 4. SPI data transfer
Include the library function putcspi0.asm (or putcspi0.c) to output the display patterns and
digit-select data.
The following program will display 87654321 on display 7 through display 0:
#include
“c:\miniide\hcs12.inc”
org
$1000
icnt
ds.b
1
; loop count
org
$1500
lds
#$1500
; set up stack pointer
bset
DDRK,$80
; configure the PK7 pin for output
jsr
openspi0
; configure SPI0
forever
ldx
#disp_tab
; use X as a pointer to the table
movb
#8,icnt
; set loop count to 8
loop
ldaa
1,x1
; send the digit-select byte to the 74HC595
jsr
putcspi0
;
“
ldaa
1,x1
; send segment pattern to 74HC595
jsr
putcspi0
;
“
bclr
PTK,BIT7
; transfer data from shift register to output
bset
PTK,BIT7
; latch
ldy
#1
; display the digit for 1 ms
jsr
delayby1ms
;
“
dec
icnt
;
bne
loop
; if not reach digit 1, then next
bra
forever
; start from the start of the table
; ********************************************************************************
; The following function configures the SPI module properly:
; ********************************************************************************
openspi0 movb
#0,SPI0BR
; set baud rate to 12 MHz
movb
#$50,SPI0CR1
; disable interrupt, enable SPI, SCK idle low,
; latch data on rising edge, transfer data msb first
movb
#$02,SPI0CR2
; disable bidirectional mode, stop SPI in wait mode
movb
#0,WOMS
; enable Port S pull-up
rts
#include
“c:\miniide\delay.asm”
#include
“c:\miniide\spi0util.asm”
490
Chapter 10 ■ The SPI Function
; *********************************************************************************
; Each digit consists of 2 bytes of data. The first byte is
; digit select; the second byte is the digit pattern.
; *********************************************************************************
disp_tab
dc.b
$80,$7F,$40,$70,$20,$5F,$10,$5B
dc.b
$08,$33,$04,$79,$02,$6D,$01,$30
end
The C language version of the program is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\spi0util.h”
// include spi0util.c into the project
#include “c:\cwHCS12\include\delay.h”
// include delay.c into the project
void openspi0(void);
void main (void)
{
unsigned char disp_tab[8][2] 5 {{0x80,0x7F},{0x40,0x70},{0x20,0x5F},{0x10,0x5B},
{0x08,0x33},{0x04,0x79},{0x02,0x6D},{0x01,0x30}};
char i;
openspi0();
/* configure the SPI0 module */
DDRK ⏐5 BIT7;
/* configure pin PK7 as output */
while(1) {
for (i 5 0; i , 8; i11) {
putcspi0(disp_tab[i][0]);
/* send out digit select value */
putcspi0(disp_tab[i][1]);
/* send out segment pattern */
PTK &5 ,,BIT7;
/* transfer values to latches of 74HC595s */
PTK ⏐5 BIT7;
/* “ */
delayby1ms(1);
/* display a digit for 1 ms */
}
}
}
void openspi0(void)
{
SPI0BR 5 0;
/* set baud rate to 12 MHz */
SPI0CR1 5 0x50; /* disable SPI interrupt, enable SPI, SCK idle low, shift
data on rising edge, shift data msb first */
SPI0CR2 5 0x02; /* disable bidirectional mode, disable SPI in wait mode */
WOMS 5 0;
/* enable Port S pull up */
}
▲
This example illustrates one of the applications of the shift register 74HC595. The main
drawback of the example program is that the CPU spends all its time on the task of multiplexing the displays. The program can be modified to be interrupt-driven to free the CPU from idle
waiting. Several manufacturers produce LED display drivers to eliminate the display multiplexing task altogether. The MC14489 from Freescale and the MAX7221 from Maxim are two
examples.
491
10.9 ■ The TC72 Digital Thermometer
10.9 The TC72 Digital Thermometer
The TC72 from Microchip is a digital temperature sensor with the SPI interface. The TC72
has a 10-bit resolution; that is, it uses 10 bits to represent the ambient temperature. The pin
assignment and functional block diagram are shown in Figure 10.13.
TC72
VDD
NC
1
CE
2
Internal
diode
temperature
sensor
8 VDD
7
NC
SCK 3
6
SDI
GND 4
5 SDO
10-bit
sigma-delta
A/D
converter
TC72
Manufacturer
ID register
CE
Serial
port
interface
SDI
Temperature
register
GND
SCK
SDO
Control
register
Figure 10.13 ■ TC72 pin assignment and functional block diagram
10.9.1 Functioning of TC72
The TC72 is capable of reading temperatures from 255°C to 1125°C. The TC72 can be
used either in the continuous temperature conversion mode or the one-shot conversion mode.
The continuous conversion mode measures temperature approximately every 150 ms and stores
the data in temperature registers. The TC72 has an internal clock generator that controls the
automatic temperature conversion sequence. In contrast, the one-shot mode performs a single
temperature measurement and returns to the power-saving shutdown mode.
10.9.2 Temperature Data Format
Temperature data is represented by a 10-bit two’s complement word with a resolution
of 0.25°C per least significant bit. The analog-to-digital converter is scaled from 2128°C to
1127°C with 0°C represented as 0x0000, but the operating range of the TC72 is specified from
255°C to 1125°C. The 10-bit temperature value is stored in two 8-bit registers. Because the
TC72 A/D converter is scaled from 2128°C to 1127°C and the conversion is represented in
10 bits, the temperature value is equal to the A/C conversion result divided by 4. The lowest
2 bits of the temperature are stored in the highest 2 bits of the lower byte of the temperature
register. The temperature value is represented in the two’s complement format. Whenever the
most significant bit of the upper byte of the temperature register is 1, the temperature is
492
Chapter 10 ■ The SPI Function
negative. The magnitude of a negative temperature can be found by taking the two’s complement of the temperature reading. After this is done, the upper 8 bits become the integer part
of the temperature, whereas the least significant 2 bits become the fractional part of the temperature. A sample of the temperature readings and their corresponding temperature values are
shown in Table 10.3.
Binary
High Byte/Low Byte
Hex
Temperature
0010 0001/0100 0000
2140
33.25°C
0100 1010/1000 0000
4A80
74.5°C
0001 1010/1100 0000
1AC0
26.75°C
0000 0001/1000 0000
0180
1.5°C
0000 0000/0000 0000
0000
0°C
1111 1111/1000 0000
FF80
20.5°C
1111 0010/1100 0000
F2C0
213.25°C
1110 0111/0000 0000
E700
224°C
1100 1001/0100 0000
C900
255°C
Table 10.3 ■ TC72 Temperature output data
10.9.3 Serial Bus Interface
The serial interface consists of chip enable (CE), serial clock (SCK), serial data input (SDI),
and serial data output (SDO). The CE input is used to select TC72 when there are multiple SPI
slaves connected to the microcontroller. TC72 can operate as a SPI slave only.
The SDI input writes data into the TC72’s control register, while the SDO output pin reads
the temperature data from the temperature register and the status of the shutdown bit of the
control register. The TC72 can shift data in or out using either the rising or the falling edge of
the SCK input. The CE signal is active high. The SCK idle state is detected when the CE signal
goes high. As shown in Figure 10.14, the clock polarity (CP) of SCK determines whether data is
shifted on the rising or the falling edge. The highest SCK frequency (fSCK) is 7.5 MHz.
CP = 0
CP = 1
CE
SCK
Shift
edge
(a) Data shift on rising edge
Figure 10.14 ■ Serial clock polarity
Shift
edge
(b) Data shift on falling edge
493
10.9 ■ The TC72 Digital Thermometer
Data transfer to and from the TC72 consists of one address byte followed by one or multiple data (2 to 4) bytes. The most significant bit (A7) of the address byte determines whether
a read (A7 5 0) or a write (A7 5 1) operation will occur. A multiple-byte read operation starts
from a high address toward lower addresses. The user needs send in only the temperature result
high byte address and read the temperature result high byte, low byte, and the control register.
The timing diagrams for the single-data-byte write, single-data-byte read, and three-data-byte
read are shown in Figure 10.15a, b, and c, respectively.
The procedure for reading the temperature result is as follows:
Step 1
Pull the CE pin to high to enable SPI transfer.
Step 2
Send the temperature result high-byte-read address (0x02) to the TC72. Wait until the SPI
transfer is complete.
Step 3
Read the temperature result high byte. Write a dummy byte into the SSPBUF register to
trigger eight pulses to be sent out from the SCK pin so that the temperature result high
byte can be shifted in.
CE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
A7 = 1
A
A
7
6
A
5
A
4
A
3
A
2
A
1
A
0
D
7
D
6
D
5
D
4
D
3
D
2
D
1
D
0
9
10
11
12
13
14
15
16
D
7
D
6
D
5
D
4
D
3
D
2
D
1
D
0
SCK
SDI
High Z
SDO
Figure 10.15a ■ Single-data-byte write operation
CE
1
2
3
4
5
6
7
8
A7 = 0
A
A
7
6
A
5
A
4
A
3
A
2
A
1
A
0
SCK
SDI
SDO
High Z
Figure 10.15b ■ Single-data-byte read operation
High Z
494
Chapter 10 ■ The SPI Function
CE
Write operation
SCK
Address byte = 0x80
A
7
SDI
Control byte
A
0
D
7
D
0
High Z
SDO
Read operation
Address byte = 0x02
SDI
A
7
A
0
D
7
SDO
D
0
D
7
D
0
D
7
D
0
Figure 10.15c ■ SPI multiple-data-byte transfer
Step 4
Read the temperature result low byte. Likewise, write a dummy byte into the SSPBUF
register to shift in the temperature low byte.
Step 5
Pull the CE pin to low so that a new transfer can be started.
10.9.4 Internal Register Structure
The TC72 has four internal registers: control register, LSB temperature, MSB temperature,
and manufacturer ID. Their contents are shown in Table 10.4.
Read
Address
Write
Address
Bit 7
Bit 6
Bit 5
Bit 4
Bit 3
Bit 2
Bit 1
Bit 0
Value on
POR/BOR
Control
0x00
0x80
0
0
0
OS1
0
0
0
SHDN2
0x05
LSB temperature
0x01
N/A
T1
T0
0
0
0
0
0
0
0x00
MSB temperature
0x02
N/A
T9
T8
T7
T6
T5
T4
T3
T2
0x00
Manufacturer ID
0x03
N/A
0
1
0
1
0
1
0
0
0x54
Register
1 OS is one-shot
2 SHDN is shutdown
Table 10.4 ■ Register for TC72
CONTROL REGISTER
The control register is read-/writable and is used to select the shutdown, continuous, or
one-shot conversion operating mode. The temperature conversion mode selection logic is
shown in Table 10.5.
At power-up, the SHDN bit is 1. Thus, the TC72 is in the shutdown mode at startup. The shutdown mode disables the temperature conversion circuitry; however, the serial I/O communication
495
10.9 ■ The TC72 Digital Thermometer
Operation Mode
One-Shot Bit
Shutdown Bit
Continuous temperature conversion
Shutdown
Continuous temperature conversion
One-shot
0
0
1
1
0
1
0
1
Table 10.5 ■ Control register temperature conversion mode selection
port remains active. If the SHDN bit is 0, the TC72 will perform a temperature conversion approximately every 150 ms. In normal operation, a temperature conversion will be initialized by a
write operation to the control register to select either the continuous temperature conversion or
the one-shot operation mode. The temperature data will be available in the upper byte and lower
byte of the temperature register approximately 150 ms after the control register is written into.
The one-shot mode performs a single temperature measurement and returns to the powersaving mode. After completion of the temperature conversion, the one-shot bit is reset to 0. The
user must set the one-shot bit to 1 to initiate another temperature conversion.
T E M P E R AT U R E R E G I S T E R
The temperature register is a read-only register and contains a 10-bit two’s complement
representation of the temperature measurement. Bit 0 through bit 5 are always read as 0. After
reset, the temperature register is reset to 0.
M A N U FA C T U R I N G I D R E G I S T E R
This register is read-only and is used to identify the temperature sensor as a Microchip
component.
Example 10.6
▼
Describe the circuit connection between the HCS12 MCU and the TC72 for digital temperature reading, and write a C function to read the temperature every 200 ms. Convert the
temperature value into a string so that it can be displayed on an appropriate output device.
A pointer to the buffer to hold the string will be passed to this function. The E-clock frequency
of the demo board is assumed to be 24 MHz.
Solution: A possible circuit connection is shown in Figure 10.16. A 0.1- to 1.0-μF capacitor
should be added between the VDD and the GND pins to filter out power noise.
The C function (read_temp()) that starts a temperature measurement and converts the temperature reading into a string is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\spi0util.h”
#include “c:\cwHCS12\include\delay.h”
void openspi0(void);
void read_temp (char *ptr);
char buf[10];
void main (void)
{
DDRM |5 BIT1;
// configure the PM1 pin for output
openspi0();
// configure SPI0 module
read_temp(&buf[0]);
}
496
Chapter 10 ■ The SPI Function
TC72
VDD
HCS12 MCU
VDD
0.1 μF
CE
PM1
SCK
SCK0
SDO
MISO0
SDI
MOSI0
GND
Figure 10.16 ■ Circuit connection between the TC72 and the HCS12
void openspi0(void)
{
SPI0BR 5 0x10;
SPI0CR1 5 0x50;
SPI0CR2 5 0x02;
WOMS 5 0x00;
}
void int2alpha(unsigned int xx, char *ptr) {
int quo;
*(ptr12) 5 xx % 10 1 0x30;
quo 5 xx/10;
if(quo !5 0){
*(ptr11) 5 quo % 10 1 0x30;
quo 5 quo/10;
}
if(quo !5 0)
*ptr 5 quo 1 0x30;
}
void read_temp (char *ptr)
{
char hi_byte, lo_byte, temp, *bptr;
unsigned int result;
*ptr
5 0x20;
*(ptr11) 5 0x20;
*(ptr12) 5 0x30;
*(ptr13) 5 ‘.’;
*(ptr14) 5 0x30;
bptr
5 ptr;
PTM
|5 BIT1;
putcspi0 (0x80);
putcspi0 (0x11);
PTM
&5 ,BIT1;
delayby100ms(2);
// set baud rate to 6 MHz
// enable Port S pull-up
// tens digit
// initialize the temperature buffer to 0.0
// and place two space characters before
// 0.0
//
“
//
“
// enable TC72 data transfer
// send out TC72 control register write address
// perform one-shot conversion
// disable TC72 data transfer
// wait until temperature conversion is complete
10.9 ■ The TC72 Digital Thermometer
497
PTM |5 BIT1;
// enable TC72 data transfer
putcspi0(0x02);
// send MSB temperature read address
hi_byte 5 getcspi0();
// read the temperature high byte
lo_byte 5 getcspi0();
// save the temperature low byte and clear SPIF
PTM &5 ,BIT1;
// disable TC72 data transfer
lo_byte &5 0xC0;
// make sure the lower 6 bits are 0s
result 5 (int)hi_byte * 256 1 (int)lo_byte;
if (hi_byte & 0x80) {
// temperature is negative
result 5 2result;
// take the two’s complement of result
result ..5 6;
temp 5 result & 0x0003;
// place the lowest 2 bits in temp
result ..5 2;
// get rid of fractional part
*ptr115 0x2D;
// store the minus sign
int2alpha(result, ptr);
}
else {
// temperature is positive
result ..5 6;
temp 5 result & 0x0003;
// save fractional part
result ..5 2;
// get rid of fractional part
int2alpha(result, ptr);
// convert to ASCII string
}
while(*bptr){ // search the end of the string
bptr11;
};
switch (temp){
// add fractional digits to the temperature
case 0:
break;
case 1:
// fractional part is 25
*bptr115 0x2E;
// add decimal point
*bptr115 0x32;
*bptr115 0x35;
*bptr 5 ‘\0’;
break;
case 2:
// fractional part is 5
*bptr115 0x2E;
// add decimal point
*bptr115 0x35;
*bptr 5 ‘\0’;
break;
case 3:
// fractional part is 75
*bptr115 0x2E;
// add decimal point
*bptr115 0x37;
*bptr115 0x35;
*bptr 5 ‘\0’;
break;
default:
break;
}
}
▲
498
Chapter 10 ■ The SPI Function
10.10 The D/A Converter MCP4922
The MCP4922 from Microchip is a 12-bit voltage output digital-to-analog converter (DAC)
with a flexible four-wire serial interface. The four-wire serial interface allows glueless interface
to SPI, QSPI, and Microwire serial ports.
The MCP4922 has an output settling time of 4.5 μs. A D/A conversion operation is started
by writing a 16-bit serial string that contains 4 control and 12 data bits to the MCP4922. Designed for a wide range of supply voltages, the MCP4922 can operate from 2.7 to 5.5 V and is
available in several types of packages including PDIP, SOIC, MSOP, and TSSOP.
10.10.1 Signal Pins
The pin assignment and functional block of the MCP4922 are shown in Figure 10.17. The
MCP4922 device utilizes a resistive string architecture, which has the inherent advantages of
low differential nonlinearity (DNL) error, low ratio metric temperature coefficient, and fast
settling time. The MCP4922 includes double-buffered inputs, allowing simultaneous updates
using the LDAC pin.
CS
SDI
SCK LDAC
VOUTA
NC
2
7
VREFA
CS
3
6
AVSS
4
5
VREFB
SDI
5
10
VOUTB
NC
6
9
SHDN
NC
7
8
LDAC
DACB
register
String
DACA
String
DACB
Buffer
Gain
logic
Buffer
Gain
logic
Output
logic
SCK
DACA
register
VOUTA SHDN VOUTB
(a) Pin assignment
(B) Functional block diagram
Figure 10.17 ■ The MCP4922 DAC pins and block diagram
VREFB
8
Input
register B
AVSS
1
VREFA
VDD
Input
register A
Power-on
reset
Interface logic
VDD
499
10.10 ■ The D/A Converter MCP4922
The functions of the MCP4922 signals are as follows:
AVSS: analog ground.
CS: chip select. This signal enables/disables inputs.
SDI: serial data input.
LDAC: latch DAC input. When this signal is low, the input latch registers’ contents are
transferred to the DAC registers. This signal can be tied low if data transfer on the
rising edge of the CS signal is desired.
SHDN: Shut down input. When this signal is low, the MCP4922 is in the low-power
standby mode.
DACx outputs (VOUTA, VOUTB): VOUTA and VOUTB are DAC outputs. The DAC output amplifiers
drive these pins with a range of AVSS to VDD.
SCK: serial clock input. The MCP4922 can shift data with the SCK frequency up to
20 MHz.
VDD: positive power supply.
10.10.2 Data Format
The microprocessor or microcontroller sends in a 16-bit frame that consists of 4 bits of
command and 12 bits of data to the MCP4922 to start a D/A conversion. The contents of the
16-bit data frame are shown in Figure 10.18. The control bit SHDN allows the user to shut
down the MCP4922 to save power when it is not needed.
15
14
13
A/B
BUF
GA
12
11
10
9
8
7
6
5
4
3
2
1
0
SHDN D11
D10
D9
D8
D7
D6
D5
D4
D3
D2
D1
D0
A/B: DACA or DACB select
0 = write to DACA.
1 = write to DACB.
BUF: VREF input buffer control
0 = unbuffered.
1 = buffered.
GA: output gain select bit
0 = gain is 2 (VOUT = 2 × VREF × D/4096).
1 = gain is 1 (VOUT = VREF × D/4096).
SHDN: output power down control bit
0 = output buffer is disabled. Output is in high impedance.
1 = output buffer is enabled.
D11:D0: DAC data bits
Figure 10.18 ■ Input data format for the MCP4922 DAC
10.10.3 MCP4922 Output Voltage
The output voltage of the MCP4922 is given by the following expression:
VOUT 5 GA × VREF × code ÷ 212
Where, GA is the selected gain (can be 1 or 2). Code is the value (represented by D11, . . . , D0)
to be converted to voltage.
500
Chapter 10 ■ The SPI Function
10.10.4 Format Data to Be Sent to the MCP4922
According to Figure 10.18, the user needs to represent the value to be converted in 12 bits
and set the most significant 4 bits properly to select the channel, shutdown mode, gain, and
reference voltage buffer mode.
Assume we want to generate a 3-V output from the VOUTA pin, the value to be sent to the
MCP4922 can be calculated as follows:
• Select channel A (set bit 15 to 0)
• Select unbuffered mode (set bit 14 to 0)
• Set gain to 1 (set bit 13 to 1)
• Enable output buffer (set bit 12 to 1)
• 3 V corresponds to the digital value (lowest 12 bits) of 212 * 3/5 5 2458 5 0x99A
• The value to be sent to MCP4922 is 0011 1001 1001 1010 5 0x399A
10.10.5 Interfacing the MCP4922 with the HCS12
A typical circuit connection for interfacing the HCS12 with the MCP4922 is shown in
Figure 10.19. Two unused I/O port pins must be used to drive the CS and FS pins.
HCS12
MOSI
MCP4922
5V
SDI
VDD
SCK
SCK
VREFA
PK7
CS
SHDN
LDAC
VOUTA
AVSS
Figure 10.19 ■ Circuit connection between the
HCS12 and MCP4922
Example 10.7
▼
Write a program to generate the waveform from the VOUTA pin shown in Figure 10.20 using
the circuit shown in Figure 10.19 assuming that the E-clock frequency for the HCS12 is 24 MHz.
4.5 V
3.0 V
1.5 V
1 ms 1 ms 1 ms 1 ms 1 ms 1 ms 1 ms 1 ms 1 ms
Figure 10.20 ■ Waveform to be generated
501
10.10 ■ The D/A Converter MCP4922
Solution: The values (bit 11 to bit 0 in Figure 10.18) corresponding to 1.5 V, 3.0 V, and 4.5 V are
1229 (0x4CD), 2458 (0x99A), and 3686 (0xE66), respectively.
The assembly program that can generate the specified waveform is as follows:
#include “c:\miniide\hcs12.inc”
org
$1500
lds
#$1500
bset
DDRK,BIT7
; configure PK0 pin for output
jsr
openSPI
forever ldd
#$34CD
jsr
sendMCP4922
; output 1.5 V from OUTA pin
ldy
#1
; wait for 1 ms
jsr
delayby1ms
;
“
ldd
#$39AA
jsr
sendMCP4922
; output 3.0 V from OUTA pin
ldy
#1
jsr
delayby1ms
ldd
#$3E66
jsr
sendMCP4922
; output 4.5 V from OUTA pin
ldy
#1
jsr
delayby1ms
bra
forever
; ********************************************************************************
; The following subroutine sends out the value to be converted by MCP4922.
; The value to be sent is passed in double accumulator D.
; ********************************************************************************
sendMCP4922
bclr
PTK,BIT7
jsr
putcSPI0
tfr
B,A
jsr
putcSPI0
bset
PTK,BIT7
rts
; ********************************************************************************
; The following subroutine enables SPI to shift data on the rising edge of SCK and
; force SCK to be idle low, and shift data at 6 MHz for the 24-MHz E-clock.
; ********************************************************************************
openSPI
movb
#$10,SPI0BR
; set SPI 0 baud rate to 6 MHz
movb
#$50,SPI0CR1
; disable interrupt, enable SPI, SCK idle low,
; data latched on the rising edge, msb first
movb
#$02,SPI0CR2
; disable bidirectional mode, stop SPI in wait mode
movb
#0,WOMS
; enable Port S pull-up (push-pull)
rts
#include “c:\miniide\delay.asm”
#include “c:\miniide\spi0util.asm”
end
The C language version of the program to be compiled by CodeWarrior for generating the
specified waveform is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\delay.h”
// include delay.c in the project
502
Chapter 10 ■ The SPI Function
#include “c:\cwHCS12\include\spi0util.h”
void sendMCP4922(char x1, char x2);
void openSPI0(void);
void main(void) {
DDRK |5 BIT7;
openSPI0();
while(1) {
sendMCP4922(0x34,0xCD);
delayby1ms(1);
sendMCP4922(0x39,0xAA);
delayby1ms(1);
sendMCP4922(0x3E,0x66);
delayby1ms(1);
}
}
void openSPI0(void) {
SPI0BR 5 0x10;
SPI0CR1 5 0x50;
SPI0CR2 5 0x02;
WOMS
5 0;
}
void sendMCP4922(char x1, char x2) {
PORTK
&5 0x7F;
putcspi0(x1);
putcspi0(x2);
PTK
|5 BIT7;
}
// include spi0util.c in the project
// configure PK7 pin for output
// generate 1.5 V from VOUTA pin
// wait for 1 ms
// generate 3.0 V from VOUTA
// generate 4.5 V from VOUTA
// Set SPI0 baud rate to 6 MHz
// enable SPI0, SCK idle low & shift on rising edge
// disable bidirectional mode, stop SPI in wait mode
// enable port S pull-up
// enable SPI transfer to MCP4922
// send out upper byte
// send out lower byte
// start DAC operation
▲
Example 10.8
▼
Write a program to generate the waveform shown in Figure 10.21 using the DAC circuit
shown in Figure 10.19.
VOUTA
Time
2 ms
2 ms
2 ms
2 ms
VOUTB
1 ms
1 ms
1 ms
1 ms
1 ms
Figure 10.21 ■ Waveform to be generated
1 ms
1 ms
1 ms
503
10.10 ■ The D/A Converter MCP4922
Solution: The procedure for generating the specified waveform is as follows:
Step 1
Configure SPI0 module properly.
Step 2
Output a high (5 V) from VOUTA and also output a high from VOUTB.
Step 3
Wait for 1 ms.
Step 4
Output a low from VOUTB.
Step 5
Wait for 1 ms.
Step 6
Output a low from VOUTA and output a high from VOUTB.
Step 7
Wait for 1 ms.
Step 8
Output a low from VOUTB.
Step 9
Wait for 1 ms.
Step 10
Go to step 2.
The value to be sent to MCP4922 to output a high from VOUTA is 0x3FFF whereas the value
to be sent to generate a low from the VOUTA pin is 0x3000.
The value to be sent to MCP4922 to output a high from VOUTB is 0xBFFF whereas the value
to be sent to generate a low from the VOUTB pin is 0xB000.
The assembly program for generating the specified waveform is as follows:
#include “c:\miniide\hcs12.inc”
org
$1500
lds
#$1500
jsr
openSPI
bset
DDRK,BIT7
forever
ldd
#$3FFF
jsr
sendMCP4922
ldd
#$BFFF
jsr
sendMCP4922
ldy
#1
jsr
delayby1ms
ldd
#$B000
jsr
sendMCP4922
ldy
#1
jsr
delayby1ms
ldd
#$3000
jsr
sendMCP4922
ldd
#$BFFF
jsr
sendMCP4922
ldy
#1
; set up the stack pointer
; configure the SPI0
; configure PK7 pin for output
; output a high from VOUTA
;
“
; output a high from VOUTB
;
“
; wait for 1 ms
;
“
504
Chapter 10 ■ The SPI Function
jsr
delayby1ms
ldd
#$B000
jsr
sendMCP4922
ldy
#1
jsr
delayby1ms
lbra
forever
; ********************************************************************************
; The following subroutine sends out the value to be converted by MCP4922.
; The value to be sent is passed in double accumulator D.
; ********************************************************************************
sendMCP4922
bclr
PTK,BIT7
jsr
putcSPI0
tfr
B,A
jsr
putcSPI0
bset
PTK,BIT7
rts
; ********************************************************************************
; The following subroutine enables SPI to shift data on the rising edge of SCK and
; force SCK to be idle low, and shift data at 6 MHz for the 24-MHz E-clock.
; ********************************************************************************
openSPI movb
#$10,SPI0BR
; set SPI 0 baud rate to 6 MHz
movb
#$50,SPI0CR1
; disable interrupt, enable SPI, SCK idle low,
; data latched on the rising edge, msb first
movb
#$02,SPI0CR2
; disable bidirectional mode, stop SPI in wait mode
movb
#0,WOMS
; enable Port S pull-up (push-pull)
rts
#include “c:\miniide\delay.asm”
#include “c:\miniide\spi0util.asm”
end
▲
This program can be modified to be interrupt-driven so that the MCU can still perform
other operations. The C language version of the program is straightforward and hence is left as
an exercise problem.
10.11 Matrix LED Displays
Many organizations have the need to display important information at the entrance or
some corners of their buildings. The information need not be displayed all at once but can be
rotated. Temperature, date, humidity, and other data in turn are displayed at many crossroads.
Schools display their upcoming games, the result of games yesterday, or other important events
using large display panels. Many of these display panels use the matrix LED displays because of
their brightness and versatility.
10.11.1 The Organization of Matrix LED Displays
Matrix LED displays are denoted by the number of their columns and rows. The most popular matrix LED display has five columns and seven rows (5 × 7). Other configurations, such
as 5 × 8 and 8 × 8, are also available. It is obvious that the more rows and columns of LEDs are
used, the better the resolution will be.
505
10.11 ■ Matrix LED Displays
10.11.2 Colors of Matrix LED Displays
Like seven-segment displays, there are green, yellow, red, and bicolor (red/green) matrix LED
displays.
10.11.3 Connection Method
Matrix LED displays can be organized as cathode row (anode column) or anode row (cathode column). In a cathode-row organization, the LEDs in a row have a common cathode. In an
anode-row organization, the LEDs in a row have a common anode. The cathode-row organization and anode-row organizations are shown in Figures 10.22 and 10.23, respectively.
Pin
Column
13
3
4
10
6
1
2
3
4
5
Row
9
1
14
22
8
33
12
44
1
55
7
66
2
7
Figure 10.22 ■ Cathode-row matrix LEDs (Fairchild GMC8X75C)
506
Chapter 10 ■ The SPI Function
Pin
Column
13
3
4
10
6
1
2
3
4
5
Row
9
1
14
2
8
3
12
4
1
5
7
6
2
7
Figure 10.23 ■ Anode-row matrix LEDs (Fairchild GMA8X75C)
10.11.4 Dimension of Matrix LED Displays
The dimension of a matrix LED display is often indicated by its height. Common heights
for matrix LED displays include 0.7, 1.2, 1.7, and 2.3 in. Larger matrix LED displays are also
available.
10.12 ■ The MAX6952 Matrix LED Display Driver
507
10.11.5 Method of Driving Matrix LED Displays
Because of the internal connection of a matrix LED display, two parallel ports are needed to
drive it. The matrix LED displays need to be scanned one row at a time, from top to bottom (or
bottom to top). Usually, multiple matrix LED displays are needed in the application, so a timemultiplexing technique needs to be used, which is quite demanding on CPU time.
Because of the popularity of matrix LED displays, a few companies have produced driver
chips for them. Among them, Maxim provides driver chips with an SPI (MAX6952) or I2C
(MAX6953) interface for cathode-row matrix LEDs.
10.12 The MAX6952 Matrix LED Display Driver
The MAX6952 is designed to drive cathode-row matrix LED displays with a 5 × 7 organization. This chip can operate with a power supply from 2.7 to 5.5 V and can drive four monocolor
or two bicolor cathode-row matrix LED displays. It has a built-in ASCII 104-character font and
24 user-definable characters. The built-in characters are in the Arial font, with the addition of
the following common symbols: |, ;, ¥, °, μ, 6, ↑, and ↓. The 24 user-definable characters are uploaded by the user into on-chip RAM through the serial interface and are lost when the device
is powered down. It allows automatic blinking control for each segment and provides 16-step
digital brightness control. Both the 36-pin SSOP and 40-pin packages are available. The device
includes a low-power shutdown mode, segment blinking (synchronized across multiple drivers,
if desired), and a test mode that forces all LEDs on.
10.12.1 Pin Functions
The pin functions are described in Table 10.6. The connection for four monocolor digits
and two bicolor digits are shown in Tables 10.7 and 10.8, respectively. The typical circuit connection that drives four monocolor digits using the MAX6952 is shown in Figure 10.24.
The MAX6952 uses four pins to interface with the MCU: CLK, CS, DIN, and DOUT. The
CS signal must be low to clock data in and out of the device, and the DIN signal is shifted in
on the rising edge of CLK. The MAX6952 can shift data at 26 MHz. When the MAX6952 is not
being accessed, DOUT is not in high impedance, contrary to the SPI standard.
Multiple MAX6952 can be daisy-chained by connecting the DOUT pin of one device to the
DIN pin of the next and driving the CLK and CS lines in parallel (Figure 10.25). Data at DIN
propagates through the internal shift registers and appears at DOUT 15.5 clock cycles later,
clocked out on the falling edge of CLK. When sending commands to daisy-chained MAX6952s,
all devices are accessed at the same time. An access requires 16 × n clock cycles, where n is the
number of MAX6952s connected together. To update just one device in a daisy chain, the user
can send the no-op command (0x00) to the others.
10.12.2 Internal Registers
The block diagram of the MAX6952 is shown in Figure 10.26. The MAX6952 contains
a 16-bit shift register (in the serial interface block in Figure 10.26) into which DIN data are
shifted on the rising edge of the CLK signal, when the CS signal is low. The 16 bits in the shift
register are loaded into a latch on the rising edge of the CS signal. The 16 bits in the latch are
then decoded and executed. The CLK input must be idle low. This signal must be taken low
before data transfer is started.
508
Chapter 10 ■ The SPI Function
Pin
Name
Function
SSOP
PDIP
O0 to O13
1, 2, 3, 6–14,
23, 24
1, 2, 3, 7–15,
26, 27
LED cathode drivers. O0 to O13 output sink current from the displays’s
cathode rows.
GND
4, 5, 6
4, 5, 6, 18
Ground
ISET
15
17
Segment current setting. Connect ISET to GND through series resistor RSET
to set the peak current.
BLINK
17
19
Blink clock output. Output is open-drain.
DIN
18
20
Serial data input. Data is loaded into the internal 16-bit shift register on the
rising edge of the CLK.
CLK
19
21
Serial-clock input. On the rising edge of CLK, data is shifted into the internal
shift register. On the falling edge of CLK, data is clocked out of DOUT. CLK
input is active only when CS is low.
DOUT
20
22
Serial data output. Data clocked into DIN is output to DOUT 15.5 clock
cycles later. Data is clocked out on the falling edge of CLK. Output is
push-pull.
DOUT
21
23
Chip-select input. Serial data is loaded into the shift register while CS
is low. The last 16 bits of serial data are latched on CS’s rising edge.
OSC
22
24
Multiplex clock input. To use the internal oscillator, connect capacitor CSET
from OSC to GND. To use the external clock, drive OSC with a 1-MHz to 8-MHz
CMOS clock.
O14 to O23
25–31, 34,
35, 36
28–34, 38,
39, 40
LED anode drivers. O14 to O23 output source current to the display’s anode
columns.
V+
32, 33
35, 36, 37
Positive supply voltage. Bypass V+ to GND with a 47-μF bulk capacitor and a
0.1-μF ceramic capacitor.
Table 10.6 ■ MAX6952 4-digit matrix LED display driver pin functions
Digit
O0,O6
O7,O13
O14,O18
O19,O23
—
Digit 0 columns
(anodes) C1 to C5
Digit 1 columns
(anodes) C6 to C10
Digit 2 columns
(anodes) C1 to C5
Digit 3 columns
(anodes) C6 to C10
O14,O18
O19,O23
Digit 0 rows (cathodes) R1 to R7
1
Digit 1 rows (cathodes) R1 to R7
Digit 2 rows (cathodes) R1 to R7
2
—
Digit 3 rows (cathodes) R1 to R7
Table 10.7 ■ Connection scheme for four monocolor digits
Digit
O0,O6
O7,O13
1
Digit 0 rows (cathodes) R1 to R14
—
2
—
Digit 1 rows (cathodes) R1 to R14
Table 10.8 ■ Connection scheme for two bicolor digits
Digit 0 columns (anodes) C1 to C10
The 5 green anodes
The 5 red anodes
Digit 1 columns (anodes) C1 to C10
The 5 green anodes
The 5 red anodes
509
10.12 ■ The MAX6952 Matrix LED Display Driver
V+
O0
O14
C1
O19
C1
V+
O1
O2
O3
O15
C2
O20
C2
O16
O21
C3
O22
O4
O5
O18
O6
O1
O7
O2
C3
C4 Cathoderow
C5
5x7
R1
matrix
R2
LED
R3 display
O2
C4 Cathoderow
C5
5x7
R1
matrix
R2
LED
R3 display
O3
R4
O3
R4
O4
R5
O5
R5
R1
O4
BLINK
O8
O9
O10
O5
R1
CLK
O11
O6
R2
O6
R2
DIN
O12
O13
O14
C1
O19
C1
CS
O14
O15
C2
O20
C2
O15
O16
C3
O21
C3
O16
O17
O18
O17
O22
O19
O8
O20
O21
O9
O10
C4 Cathoderow
C5
5x7
R1
matrix
R2
LED
R3 display
R4
O10
C4 Cathoderow
C5
5x7
R1
matrix
R2
LED
R3 display
R4
O11
R5
O11
R5
O12
R1
O12
R1
O13
R2
O13
R2
0.1 μF
47 μF
GND
5V
4.7 kΩ
Interface
with
MCU
DOUT
OSC
CSET
26 pF
ISET
RSET
53.6 kΩ
O22
O23
Figure 10.24
O17
O0
O18
O7
Digit 0
Digit 2
O23
O0
O1
O23
O7
O8
O9
■ MAX6952 driving four matrix LED displays
HCS12
MAX6952
MAX6952
MOSI0
DIN
DIN
SCK0
CLK
CLK
CLK
SS0
CS
CS
CS
DOUT
MISO0
Figure 10.25
■ MAX6952 daisy-chain connection
DOUT
MAX6952
DIN
DOUT
Digit 1
Digit 3
510
Chapter 10 ■ The SPI Function
ISET
OSC
Current
source
PWM
brightness
control
Divider/
counter
network
Row
multiplexer
LED
drivers
Charactergenerator
RAM
Blink
O0
to
O23
Charactergenerator
ROM
Blink
speed
select
Configuration
register
RAM
CLK
CS
Serial interface
DIN
DOUT
Figure 10.26 ■ MAX6952 functional diagram
The upper 8 bits of the shift register select the destination register to which the lower 8 bits of
the shift register are to be transferred. The address map of the MAX6952 is shown in Table 10.9.
The procedure for writing the MAX6952 is as follows:
Step 1
Pull the CLK signal to low.
Step 2
Pull the CS signal to low to enable the internal 16-bit shift register.
Step 3
Shift in 16 bits of data from the DIN pin with the most significant bit first. The most
significant bit (D15) must be low for a write operation.
Step 4
Pull the CS signal to high.
Step 5
Pull the CLK signal to low.
511
10.12 ■ The MAX6952 Matrix LED Display Driver
Register
No op
Intensity10
Intensity32
Scan limit
Configuration
User-defined fonts
Factory reserved (do not write into)
Display test
Digit 0 plane P0
Digit 1 plane P0
Digit 2 plane P0
Digit 3 plane P0
Digit 0 plane P1
Digit 1 plane P1
Digit 2 plane P1
Digit 3 plane P1
Write digit 0 plane P0 and plane P1 with
same data (reads as 0x00)
Write digit 1 plane P0 and plane P1 with
same data (reads as 0x00)
Write digit 2 plane P0 and plane P1 with
same data (reads as 0x00)
Write digit 3 plane P0 and plane P1 with
same data (reads as 0x00)
Address (command byte)
D15
D14
D13
D12
D11
D10
D9
D8
Hex
Code
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
R/W
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
0
0
0
0
0
0
0
0
1
1
1
1
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
0
0
0
0
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
0x00
0x01
0x02
0x03
0x04
0x05
0x06
0x07
0x20
0x21
0x22
0x23
0x40
0x41
0x42
0x43
0x60
R/W
1
1
0
0
0
0
1
0x61
R/W
1
1
0
0
0
1
0
0x62
R/W
1
1
0
0
0
1
1
0x63
Table 10.9 ■ MAX6952 register address map
Any register data within the MAX6952 may be read by setting the D15 bit to 1. The procedure to read a register is as follows:
Step 1
Pull CLK to low.
Step 2
Pull the CS signal to low to enable the internal shift register.
Step 3
Clock 16 bits of data into the DIN pin with bit 15 first. Bit 15 must be 1. Bit 14 to bit 8
contain the address of the register to be read. Bits 7 through 0 contain dummy data.
Step 4
Pull the CS signal to high. Bits 7 to 0 of the serial shift register will be loaded with the
data in the register addressed by bits 15 through 8.
Step 5
Pull CLK to low.
Step 6
Issue another read command (which can be a no-op), and examine the bit stream at the
DOUT pin. The second 8 bits are the contents of the register addressed by bits 14 to 8 in
step 3.
512
Chapter 10 ■ The SPI Function
DIGIT REGISTERS
The MAX6952 uses eight digit registers to store the characters that the user wishes to
display on the four 5 × 7 LED digits. These digit registers are implemented with two planes of
4 bytes, called P0 and P1. Each LED digit is represented by 2 bytes of memory, 1 byte in plane
P0 and the other in plane P1. The digit registers are mapped so that a digit’s data can be updated
in plane P0 or plane P1 or both at the same time, as shown in Table 10.10.
Segment’s
Bit Setting in
Plane P1
Segment’s
Bit Setting in
Plane P10
0
0
Segment off
0
1
Segment on only during the first
half of each blink period
1
0
Segment on only during the
second half of each blink period
1
1
Segment on
Segment Behavior
Table 10.10 ■ Digit register mapping with blink globally enabled
If the blink function is disabled through the blink enable bit E in the configuration register, then the digit register data in plane P0 is used to multiplex the display. The digit register
data in P1 is not used. If the blink function is enabled, then the digit register data in both plane
P0 and P1 are alternately used to multiplex the display. Blinking is achieved by multiplexing
the LED display using data planes P0 and P1 on alternate phases of the blink clock (shown in
Table 10.10).
The data in the digit registers does not control the digit segments directly. Instead, the
register data is used to address a character generator, which stores the data of a 128-character
font. The lower 7 bits of the digit data (D6 to D0) select the character font. The most significant
bit of the register data (D7) selects whether the font data is used directly (D7 5 0) or whether
the font is inverted (D7 5 1). The inversion feature can be used to enhance the appearance of
bicolor displays by displaying, for example, a red character on a green background.
C O N F I G U R AT I O N R E G I S T E R
The configuration register is used to enter and exit shutdown, select the blink rate, globally
enable and disable the blink function, globally clear the digit data, and reset the blink timing.
The contents of the configuration register are shown in Figure 10.27.
INTENSITY REGISTERS
Display brightness is controlled by four pulse-width modulators, one for each display digit.
Each digit is controlled by a nibble of one of the two intensity registers, Intensity10 and Intensity32. The upper nibble of the Intensity10 register controls the intensity of the matrix display 1,
whereas the lower nibble of the same register controls the intensity of the matrix display 0.
Matrix displays 3 and 2 are controlled by the upper and lower nibbles of the Intensity32 register,
respectively. The modulator scales the average segment current in 16 steps from a maximum
of 15/16 down to 1/16 of the peak current. The minimum interdigit blinking time is, therefore,
1/16 of a cycle. The maximum duty cycle is 15/16.
513
10.12 ■ The MAX6952 Matrix LED Display Driver
7
6
5
4
3
2
1
0
P
x
R
T
E
B
x
S
P: blink phase read back select
0 = P1 blink phase.
1 = P0 blink phase.
R: global clear digit data
0 = digit data on both planes P0 and P1 are not affected.
1 = clear digit data on both planes P0 and P1.
T: global blink timing synchronization
0 = blink timing counters are unaffected.
1 = blink timing counters are reset on the rising edge of CS.
E: global blink enable/disable
0 = blink function is disabled.
1 = blink function is enabled.
B: blink rate selection
0 = select slow blinking (refreshed for 1 s by plane P0, then 1 s
by P1 at 4 MHz).
1 = select fast blinking.
S: shutdown mode
0 = shutdown mode.
1 = normal operation.
Figure 10.27 ■ The MAX6952 configuration register
SCAN-LIMIT REGISTER
The scan-limit register sets how many monocolor digits are displayed, either two or four. A
bicolor digit is connected as two monocolor digits. The multiplexing scheme drives digits 0 and
1 at the same time, then digits 2 and 3 at the same time. To increase the effective brightness
of the displays, the MAX6952 drives only two digits instead of four. By doing this, the average
segment current doubles, but this also doubles the number of MAX6952s required for driving a
given number of digits.
The contents of the scan-limit register are shown in Figure 10.28. This register has only
1 bit implemented. When this bit is 0, only digits 0 and 1 are displayed. Otherwise, all four
digits are displayed.
7
6
5
4
3
2
1
0
X
X
X
X
X
X
X
2 or 4
2 or 4: scan two digits (0 and 1) or all four digits.
0 = display digits 0 and 1 only.
1 = display digits 0, 1, 2, and 3.
Figure 10.28 ■ The MAX6952 scan-limit register
D I S P L AY T E S T R E G I S T E R
The display test register switches the drivers between one of two modes: normal and display test. Display-test mode turns on all LEDs by overriding, but not altering, all control and
digit registers (including the shutdown register). In display-test mode, eight digits are scanned
514
Chapter 10 ■ The SPI Function
7
6
5
4
3
2
1
0
X
X
X
X
X
X
X
Test
Test: test bit
0 = normal operation.
1 = display test.
Figure 10.29 ■ The MAX6952 display test register
and the duty cycle is 7/16 (half power). The contents of the display test register are shown in
Figure 10.29. Only bit zero of this register is implemented.
C H A R A C T E R - G E N E R AT O R F O N T M A P P I N G
The character font is a 5 3 7 matrix. The character generator comprises 104 characters in
ROM and 24 user-definable characters. The selection from a total of 128 characters is represented by the lower 7 bits of the 8-bit digit registers. The character map is in the Arial font for
96 characters in the range from 0x28 to 0x7F. The first 32 characters map the 24 user-defined
positions (RAM00 to RAM23), plus 8 extra common characters in ROM. When the most significant bit is 0, the device will display the font normally. Otherwise, the chip will display the
font inversely.
USER-DEFINED FONT REGISTER
The 24 user-definable characters are represented by 120 entries of 7-bit data, five entries
per character, and are stored in the MAX6952’s internal RAM. The 120 user-definable font data
are written and read through a single register at the address 0x05. An auto-incrementing font
address register pointer in the MAX6952 indirectly accesses the font data. The font address
pointer can be written, setting one of 120 addresses between 0x00 and 0xF7, but cannot be
read back. The font data is written to and read from the MAX6952 indirectly, using this font
address pointer. Unused font locations can be used as general-purpose scratch RAM. Font registers are only 7 bits wide.
To define new fonts, the user first needs to set the font address pointer. This is done
by placing the address in the font address pointer register and setting bit 7 to 1. After this,
the user can write the font data in the lower 7 bits (to the font address pointer position) and
clear bit 7.
The font address pointer autoincrements after a valid access to the user-definable font data.
Auto-incrementing allows the 120 font data entries to be written and read back very quickly
because the font pointer address needs to be set only once. When the last data location, 0xF7,
is written into, the font address pointer increments to 0x80 automatically. If the font address
pointer is set to an out-of-range address by writing data in the range from 0xF8 to 0xFF, then the
address is set to 0x80 instead.
The memory mapping of user-defined font register 0x05 is detailed in Table 10.11. The
behavior of the font pointer address is shown in Table 10.12. To display the font defined by the
user, one must send in the RAM address from 0x00 through 0x17, corresponding to the font address pointer value that is 5 × RAM address (one character needs 5 bytes).
515
10.12 ■ The MAX6952 Matrix LED Display Driver
Address Code
(hex)
Register
Data
SPI Read
or Write
Function
0x85
0x00–0x7F
Read
Read 7-bit user-definable font data entry from current font
address. MSB of the register data is clear. Font address
pointer is incremented after the read.
0x05
0x00–0x7F
Write
Write 7-bit user-definable font data entry to current font
address. Font address pointer is incremented after the write.
0x05
0x80–0xFF
Write
Write font address pointer with the register data.
Table 10.11 ■ Memory mapping of user-defined font register 0x05
Font Pointer
Address
Action
0x80–0xF6
Valid range to set the font address pointer. Pointer autoincrements after a
font data read or write, while pointer address remains in this range.
0xF7
Font address resets to 0x80 after a font data read or write to this pointer
address.
0xF8 to 0xFF
Invalind range to set the font address pointer. Pointer is set to 0x80.
Table 10.12 ■ Font pointer address behavior
10.12.3 Blinking Operation
The display blinking facility, when enabled, makes the LED drivers flip automatically
between displaying the digit register data in planes P0 and P1. If the digit register data for
any digit is different in two planes, then that digit appears to flip between two characters.
To make a character appear to blink on or off, write the character to one plane and use the
blank character (0x20) for the other plane. Once blinking has been configured, it continues
automatically without further intervention. Blinking is enabled by setting the E bit of the
configuration register.
The blink speed can be programmed to be fast or slow and is determined by the frequency
of the multiplex clock, OSC, and by setting the B bit of the configuration register. The blink
rate selection bit B of the configuration register sets either fast or slow blink speed for the
whole display.
B L I N K S Y N C H R O N I Z AT I O N
When multiple digits are displayed, one can choose to synchronize the blinking operation
of these digits. Internally, blink synchronization is achieved by resetting the display multiplexing sequence. As long as all MAX6952s are daisy-chained with one device’s DOUT connected
to the DIN of the next device, global synchronization is achieved by toggling the CS pin for
each device, either together or in quick succession.
516
Chapter 10 ■ The SPI Function
BLINK OUTPUT
The blink output (the BLINK pin) indicates the blink phase and is high during the P0 period
and low during the P1 period. Blink phase status can be read back as the P bit in the configuration register. Typical uses for this output are
• To provide an interrupt to the processor so that segment data can be changed
synchronously to the blinking. For example, a clock application may have
colon segments blinking every second between hour and minute digits, and
the minute display is best changed in step with the colon segments. Also,
if the rising edge of blink is detected, there is half a blink period to change
the P1 data (P0 data drives the displays during this interval). Similarly, if the
falling edge of blink is detected, there is half a blink period to change the
P0 digit data.
• If OSC is driven with an accurate frequency, blink can be used as a seconds
counter.
10.12.4 Choosing Values for RSET and CSET
The RC oscillator uses an external resistor, RSET, and an external capacitor, CSET, to set the
oscillator frequency, fOSC. The allowed range of fOSC is 1 to 8 MHz. RSET also sets the peak segment current. The recommended values for RSET and CSET set the oscillator to 4 MHz, which
sets the slow and fast blink frequencies to 0.5 Hz and 1 Hz. The recommended value of RSET
also sets the peak current to 40 mA, which makes the segment current adjustable from 2.5 to
37.5 mA in 2.5-mA steps:
ISEG 5 KI/RSET mA
fOSC 5 KF/(RSET × CSET 1 CSTRAY) MHz
where
KI 5 2144
KF 5 6000
RSET 5 external resistor in kΩ
CSET 5 external capacitor in pF
CSTRAY 5 stray capacitance from OSC pin to GND in pF, typically 2 pF
The recommended value for R SET is 53.6 kΩ and the recommended value for C SET is
26 pF. The recommended value for RSET is the minimum allowed value since it sets the display driver to the maximum allowed segment current. RSET can be set to a higher value to
set the segment current to a lower peak value whenever it is desirable. The effective value of
CSET includes not only the actual external capacitor used but also the stray capacitance from
OSC to GND.
Example 10.9
▼
Daisy-chain two MAX6952s to drive eight of the cathode-row, monocolor, matrix LED
displays GMC8975C made by Fairchild, assuming that the PM5 pin is used to drive the CS
input of two MAX6952s. The connection of two MAX6952s with the HCS12 (24-MHz E-clock)
is shown in Figure 10.30. Each MAX6952 is driving four matrix LED displays. Write a program
to configure the SPI module to operate in master mode, and transfer data at 12 MHz, shift data
in/out on the rising edge of the SCK clock. Display MSU ECET on the matrix LED displays
without blinking.
517
10.12 ■ The MAX6952 Matrix LED Display Driver
HCS12
MOSI0
MAX6952
DIN
MAX6952
DOUT
DIN
OSC
DOUT
OSC
CSET
CSET
SCK0
CLK
PM5
CS
CLK
ISET
ISET
RSET
CS
RSET
MISO0
Figure 10.30 ■ HCS12 driving two MAX6952s
Solution: The SPI0 module should be configured with the following features:
• 12-MHz baud rate
• Master mode with interrupts disabled
• Shift data on the rising edge with clock idle low
• Shift data out most significant bit first
• Disable mode fault
• Stop SPI0 on wait mode
Two MAX6952 chips should be configured as follows:
Intensity10 register
We set the intensity of both MAX6952s to maximum by sending out the following value:
0x01, 0xFF, 0x01, 0xFF
Sixteen bits need to be written to each MAX6952, of which the upper 8 bits are the address to
select the Intensity10 register.
Intensity32 register
Set the second and third displays to maximum intensity by sending the same data as to
Intensity10:
0x02, 0xFF, 0x02, 0xFF
Scan-limit register
Configure the MAX6952 to drive four monocolor displays by writing the following value to the
MAX6952:
0x03, 0x01, 0x03, 0x01
; drive four displays
Configuration register
Configure this register to
• Select P1 blink phase
• Not to clear digit data on both plane P0 and P1
518
Chapter 10 ■ The SPI Function
• Reset blink timing counter on the rising edge of CS
• Disable blink function
• Select slow blinking (“don’t care”)
• Select normal mode
Send the following values to the MAX6952s:
0x04, 0x11, 0x04, 0x11
Display test register
Disable display test as the start by writing the following values to the MAX6952:
0x07, 0x00, 0x07, 0x00
Digit 0 (rightmost digit) plane P0
Display space character and letter T, respectively, on display 0 of the first and second MAX6952
by sending the following data:
0x20, 0x54, 0x20, 0x20
Digit 1 (second rightmost digit) plane P0
Display letter U and letter E, respectively, on display 1 of the first and second MAX6952 by
sending the following data:
0x21, 0x45, 0x21, 0x55
Digit 2 (second leftmost digit) plane P0
Display letter S and letter C, respectively, on display 2 of the first and second MAX6952 by
sending the following data:
0x22, 0x43, 0x22, 0x53
Digit 3 (leftmost digit) plane P0
Display letter M and letter E, respectively, on the display 3 of the first and second MAX6952 by
sending the following data:
0x23, 0x45, 0x23, 0x4D
The following C program configures the SPI and MAX6952 properly:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\spi0util.c”
void sendtomax(char x1, char x2, char x3, char x4);
void openspi0(void);
void main (void)
{
openspi0();
DDRM |5 BIT5; // configure PM5 pin for output
sendtomax(0x01, 0xFF, 0x01, 0xFF);
// set intensity for digits 0 and 1
sendtomax(0x02, 0xFF, 0x02, 0xFF);
// set intensity for digits 2 and 3
sendtomax(0x03, 0x01, 0x03, 0x01);
// set scan limit to drive four digits
sendtomax(0x04, 0x11, 0x04, 0x11);
// set configuration register
sendtomax(0x07, 0x00, 0x07, 0x00);
// disable test
sendtomax(0x20, 0x54, 0x20, 0x20);
// value for digit 0
sendtomax(0x21, 0x45, 0x21, 0x55);
// value for digit 1
sendtomax(0x22, 0x43, 0x22, 0x53);
// value for digit 2
sendtomax(0x23, 0x45, 0x23, 0x4D);
// value for digit 3
}
10.12 ■ The MAX6952 Matrix LED Display Driver
void sendtomax (char c1, char c2, char c3, char c4)
{
char
temp;
PTM &5 ,BIT5;
/* enable SPI transfer to MAX6952 */
putcspi0(c1);
/* send c1 to MAX6952 */
putcspi0(c2);
/* send c2 to MAX6952 */
putcspi0(c3);
/* send c3 to MAX6952 */
putcspi0(c4);
/* send c4 to MAX6952 */
PTM |5 BIT5;
/* load data from shift register to latch */
}
void openspi0(void)
{
SPI0BR 5 0x00; /* set baud rate to 12 MHz */
SPI0CR1 5 0x50; /* disable interrupt, set master mode, shift data on
rising edge, clock idle low */
SPI0CR2 5 0x02; /* disable mode fault, disable SPI in wait mode */
WOMS
5 0;
/* enable Port S pull-up */
}
519
▲
Example 10.10
▼
Modify the previous example to blink the display at a slow rate.
Solution: We need to change the setting of the configuration register and also send the space
character (0x20) to the four digits in plane P1. New data to be sent to the configuration registers
are as follows:
0x04, 0x19, 0x04, 0x19
The main program should be modified as follows:
void main (void)
{
openspi0();
DDRM |5 BIT5;
sendtomax(0x01, 0xFF, 0x01, 0xFF);
sendtomax(0x02, 0xFF, 0x02, 0xFF);
sendtomax(0x03, 0x01, 0x03, 0x01);
sendtomax(0x04, 0x19, 0x04, 0x19);
sendtomax(0x07, 0x00, 0x07, 0x00);
sendtomax(0x20, 0x54, 0x20, 0x20);
sendtomax(0x21, 0x45, 0x21, 0x55);
sendtomax(0x22, 0x43, 0x22, 0x53);
sendtomax(0x23, 0x45, 0x23, 0x4D);
sendtomax(0x40, 0x20, 0x40, 0x20);
sendtomax(0x41, 0x20, 0x41, 0x20);
sendtomax(0x42, 0x20, 0x42, 0x20);
sendtomax(0x43, 0x20, 0x43, 0x20);
}
// configure PM5 pin for output
// configuration register, blink at phase P1
// disable test
// value for digit 0 on plane P0
// value for digit 1
// value for digit 2
// value for digit 3
// value for digit 0 on plane P1 (space)
// value for digit 1 “
// value for digit 2 “
// value for digit 3 “
▲
520
Chapter 10 ■ The SPI Function
Example 10.11
▼
For the circuit shown in Figure 10.30, write a program to display the following message and
shift the information from right to left every second and enable blinking:
08:30:40 Wednesday, 72°F, humidity: 60%
Solution: We use plane P0 to shift the message once every half a second. The message in plane
P0 is used to multiplex the display in half a second and the message sent to plane P1 is used to
multiplex the display in the next half of a second. We display the space character in each matrix
display using plane P1 and display the normal characters using plane P0. A delay function is
invoked once every second to shift the message to plane P0. The program is as follows:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\spi0util.h”
#include “c:\cwHCS12\include\delay.h”
void send2max (char x1, char x2, char x3, char x4);
void openspi0 (void);
char msgP0[41] 5 “08:30:40 Wednesday, 72°F, humidity: 60% “;
void main (void)
{
char i1, i2, i3, i4;
char j1, j2, j3, j4;
char k;
openspi0();
DDRM |5 BIT5; // configure PM5 pin for output
send2max(0x01, 0xFF, 0x01, 0xFF);
send2max(0x02, 0xFF, 0x02, 0xFF);
send2max(0x03, 0x01, 0x03, 0x01);
send2max(0x04, 0x1D, 0x04, 0x1D); // configuration register
send2max(0x40, 0x20, 0x40, 0x20); // send space character to plane P1
send2max(0x41, 0x20, 0x41, 0x20);
send2max(0x42, 0x20, 0x42, 0x20);
send2max(0x43, 0x20, 0x43, 0x20);
k 5 0;
while (1) {
i1 5 k;
i2 5 (k11)%40;
i3 5 (k12)%40;
i4 5 (k13)%40;
j1 5 (k14)%40;
j2 5 (k15)%40;
j3 5 (k16)%40;
j4 5 (k17)%40;
sendtomax(0x20, msgP0[i1], 0x20, msgP0[j1]);
sendtomax(0x21, msgP0[i2], 0x21, msgP0[j2]);
sendtomax(0x22, msgP0[i3], 0x22, msgP0[j3]);
sendtomax(0x23, msgP0[i4], 0x23, msgP0[j4]);
delayby100ms(10); /* wait for 1 s */
k 5 (k11)%40;
}
}
521
10.12 ■ The MAX6952 Matrix LED Display Driver
void sendtomax (char c1, char c2, char c3, char c4)
{
PTM &5 ,BIT5;
// enable SPI transfer to MAX6952
putcspi0(c1);
// send c1 to MAX6952
putcspi0(c2);
// send c2 to MAX6952
putcspi0(c3);
// send c3 to MAX6952
putcspi0(c4);
// send c4 to MAX6952
PTM |5 BIT5;
// load data from shift register to latch
}
void openspi0(void)
{
SPI0BR 5 0x00; // set baud rate to 12 MHz
SPI0CR1 5 0x50; // disable interrupt, set master mode, shift data on
rising edge, clock idle low
SPI0CR2 5 0x02; // disable mode fault, disable SPI in wait mode
WOMS
5 0;
// enable Port S pull-up
}
▲
Example 10.12
0x49
0x49
0x7F
0x49
0x49
0x44
0x24
0x1F
0x24
0x44
0x70
0x40
0x7F
0x40
6
5
4
3
2
1
0
0x70
Write a program to define fonts for three special characters as shown in Figure 10.31. Store
the font of these three special characters at locations from 0x00 to 0x0E of the MAX6952.
7
▼
Figure 10.31 ■ User-definable font example
Solution: The program that sets fonts is very straightforward. The following program will set up
the fonts as specified:
#include “c:\cwHCS12\include\hcs12.h”
#include “c:\cwHCS12\include\spi0util.h”
char fonts [15] 5 {0x70,0x40,0x7F,0x40,0x70,0x48,0x38,0x0F,0x38,0x48,0x49,
0x49,0x7F,0x49,0x49};
void send_font (char xc);
void openspi0 (void);
522
Chapter 10 ■ The SPI Function
void main (void)
{
char i;
DDRM |5 BIT5;
// configure PM5 pin for output
openspi0();
// configure SPI module properly
send_font(0x80);
// set font address pointer address to 0x00
for (i 5 0; i , 15; i11)
send_font(fonts[i]);
}
void send_font(char xx)
{
PTM &5 ,BIT5;
// enable SPI transfer
putcspi0(0x05);
// specify font address pointer
putcspi0(xx);
// send a font value
PTM |5 BIT5;
// load data in shift register to destination
}
void openspi0(void)
{
SPI0BR 5 0x00; // set baud rate to 12 MHz
SPI0CR1 5 0x50; // disable interrupt, set master mode, shift data on
// rising edge, clock idle low
SPI0CR2 5 0x02; // disable mode fault, disable SPI in wait mode
WOMS
5 0;
// enable Port S pull-up
}
▲
To display user-defined fonts, we need to send in the appropriate addresses in the range from
0x00 to 0x17. In Example 10.12, the addresses 0x00,0x02 should be used to access those three
special characters. The following C statements will display those three Chinese characters followed by letters A, B, C, D, and E from left to right on the matrix displays shown in Figure 10.30:
sendtomax(0x20, 0x42, 0x20, 0x00); // 0x00 is the address of the first character font
sendtomax(0x21, 0x43, 0x21, 0x01); // 0x01 is the address of the second character font
sendtomax(0x22, 0x44, 0x22, 0x02); // 0x02 is the address of the third character font
sendtomax(0x23, 0x45, 0x23, 0x41);
10.13 Summary
When high-speed data transfer is not needed, using serial data transfer enables us to make
the most of the limited number of I/O pins available on the MCU device. Serial data transfer
can be performed asynchronously or synchronously. The SPI is a synchronous protocol created
by Freescale for serial data exchange between peripheral chips and microcontrollers.
In the SPI format, a device (must be a microcontroller) is responsible for initiating the data
transfer and generating the clock pulses for synchronizing data transfer. This device is referred
to as the SPI master. All other devices in the same system are referred to as SPI slaves. The
master device needs three signals to carry out the data transfer.
• SCK: a clock signal for synchronizing data transfer
• MOSI: serial data output from the master
• MISO: serial data input to the master
10.14 ■ Exercises
523
To transfer data to one or more SPI slaves, the MCU writes data into the SPI data register,
and eight clock pulses are generated to shift out the data in the SPI data register from the MOSI
pin. If the MISO pin of the MCU is also connected (to the slave), then 8 data bits are also shifted
into the SPI data register. To read data from the slave, the MCU also needs to write data into
the SPI data register to trigger clock pulses to be sent out from the SCK pin. However, the value
written into the SPI data register is unimportant in this case.
When configured as a slave device, the HCS12 MCU also needs the fourth signal called
Slave Select (SS). The SS signal enables the HCS12 slave to respond to an SPI data transfer. Most
slave peripheral devices have signals called CE (chip enable) or CS (chip select) to enable/disable
the SPI data transfer.
Multiple peripheral devices with an SPI interface can be interfaced with a single MCU
simultaneously. There are many different methods for interfacing multiple peripheral devices
(with SPI interface) to the MCU. Two popular connection methods are
1. Parallel connection. In this method, the MISO, MOSI, and SCK signals of all the
peripheral devices are connected to the same signals of the HCS12 device. The
HCS12 MCU also needs to use certain unused I/O pins to control the CS (or CE)
inputs of each individual peripheral device. Using this method, the HCS12 MCU can
exchange data with any selected peripheral device without affecting other peripheral
devices.
2. Serial connection. In this method, the MOSI input of a peripheral device is
connected to the MISO pin of its predecessor, and the MISO output of a peripheral
device is connected to the MOSI input of its successor. The MOSI input of the
peripheral device that is closest (in terms of connection) to the MCU is connected
to the MOSI output of the MCU. The MISO output of the last peripheral device
(in the loop) is connected to the MISO input of the MCU. The SCK inputs of all
peripheral devices are tied to the SCK pin of the MCU. Using this method, the data
to be sent to the last device in the loop will need to go through all other peripheral
devices. The CE (or CS) signals of all peripheral devices are controlled by the same
signal.
In all of the peripheral devices with the SPI interface, shift registers such as 74HC595 and
74HC589 can be used to add parallel output and input ports to the MCU. The widely used digital temperature sensors are quite useful for displaying the ambient temperature. The TC72 and
TC77 from Microchip and LM74 from National Semiconductor are digital temperature sensors
with the SPI interface. The SPI interface is also often added to LED and LCD display drivers.
The MAX7221 from Dallas-Maxim can drive up to eight seven-segment displays. The MC14489
from Freescale can drive up to five seven-segment displays. Both driver chips can be cascaded to
drive more displays. Many A/D and D/A converters also have an SPI interface, for example, the
12-bit D/A converter MCP4922. Matrix displays have been widely used in recent years. They
use the time-multiplexing method to display multiple digits. Due to their high demand on CPU
time in performing time
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )