- •1 Introduction to C
- •1.1 Some Simple Programs
- •1.2 Names
- •1.3 Types and Type Declarations
- •1.4 Storage Classes, Linkage, and Scope
- •1.5 Character Constants
- •1.6 Arrays
- •1.7 Other types
- •1.8 Operators and Expressions
- •1.9 Increment and Decrement Operators
- •1.10 Precedence and Associativity
- •1.11 Program Flow and Control
- •1.12 Functions
- •1.13 Recursion
- •1.14 Summary
- •2 Advanced C Topics
- •2.1 Pointers
- •2.2 Multidimensional Arrays
- •2.3 Structures
- •2.4 More Structures
- •2.5 Input and Output
- •2.6 Memory Management
- •2.7 Miscellaneous Functions
- •2.8 Summary
- •3 What Are Microcontrollers?
- •3.1 Microcontroller Memory
- •3.3 Programming Microcontrollers
- •3.4 Coding Tips for Microcontrollers
- •4.1 Microcontroller Memory
- •4.2 Timers
- •4.4 Pulse Width Modulator System
- •4.5 Other Program Items
- •4.6 Summary
- •5.1 Header File
- •5.2 Sorting Programs
- •5.3 Data Compression
- •5.4 Timer Operations
- •5.5 Summary
- •6 Large Microcontrollers
- •6.3 A Pulse Width Modulation Program
- •6.4 Cosmic MC68HC16 Compiler
- •6.6 Digital Signal Processor Operations
- •6.7 Other MC68HC16 Considerations
- •7.1 Numeric Encoding
- •7.2 Numeric Decoding
- •7.3 Coding the alpha data
- •7.4 The Monitor Program
- •7.5 The SAVEIT() Routine
- •7.6 The printout() and the printafter() Functions
- •7.7 Reset
- •7.9 Putting It All Together
- •7.10 Summary
- •8 MCORE, a RISC Machine
- •8.1 Delay Routine
- •8.2 Delays Revisited
- •8.4 Handling Interrupts
- •8.5 A Clock Program
- •8.6 Keyboard
- •8.7 Integrating Keyboard and Clock
- •8.8 Adding a Display
- •8.9 Summary
Other MC68HC16 Considerations 345
The data array starts out empty, and the coefficients are each 0x100. The output is as you would expect—it starts and increases linearly from 0 to 64 in the first 32 samples. The output then remains at 64 until the end of the test. This linear ramp is exactly what one would expect when sending a step function into the constant weighting function.
There is one important point to be found in this program. It was stated earlier that the programmer should attempt to keep the code in C whenever possible. In the above case, we introduced a rather simple function in assembly language. The fact that the program was a func tion, and it was necessary to use a large memory model, the code required to handle the data as it was passed into the function is much longer than you would expect. Also, the implied C code in preparing the machine for the function call and the handling of the extension registers in the main program makes the overall program larger than expected. Unfortunately, it is not possible to access the DSP with C, so if you wish to do DSP operations, assembly language access is all that you can use.
Other MC68HC16 Considerations
The discussion in this chapter has been dedicated to the MC68HC16 microcontroller. Of course, there are components of this microcontroller that are not covered in this chapter. No attempt was made to outline the access to the analog-to-digital converter, several features of the general purpose timer, the serial peripheral interface, or the static RAM. However, the programs shown here do cover enough of the part to demonstrate that the on-board peripherals can be accessed from the C language. In the approach used, the header files contain all of the bit field definitions needed to access any bit or bit field in any control register in the part.
Chapter 7
Advanced Topics in Programming
Embedded Systems (M68HC12)
During the past few years, we have seen an unbelievable proliferation of embedded systems products. Devices that could only be imagined ten years ago are commonplace today and their very existence demonstrates the importance of C programming for small microcontrollers. Let’s take a look at one such application and see how easily you can develop rather complicated applications on microcontrollers.
Throughout this text, much emphasis has been placed on the construction of small functions and then integrating these functions into a working package. This approach is about the only way that you can really hope to create a complicated piece of firmware in a sensible time. My early projects would always start with careful design of the whole project, partitioning of the project into sensible modules, design of each module, writing the code for each module, integration of the whole project and then, after cleaning up syntax errors, I’d begin to test the whole program. What a disaster! These programs would never work and there would be no hope of ever getting the package to run as a unit.
As I gained experience, I found that the top-down approach I used was probably satisfactory if I tempered the integration of the system. Today, I start with a careful design for the whole system. This design is partitioned into constituent components. At that point, another look at the design is in order to see if the existing components can be further reduced into sensible components. At that point, the lowest-level components are coded. Often these functions are so simple that they work when first tested. Whenever there is a required
347
348 Chapter 7 Advanced Topics
interface for a component, I take the time when it is being created to write stub functions to provide any necessary interface to test the function. Every small function is tested as it is written.
The hierarchy of these functions is built upward. Those functions that make use of the lower-level functions are coded and tested, and so forth until the whole project is completed. Every function is written and tested as it is created so that, as the program builds upward, it is always based on known working modules. And this approach is used to complete the whole program.
At every phase in the construction of the project the key words are test, test, test. Every function is tested at its creation and, therefore, the whole project is built upon relatively simple, small functions that have been tested and work. Does this approach guarantee that no bugs will be built into the program? Does it guarantee that the final integrated version of the program will work as desired? In both cases, the answer is a resounding NO. However, you have at least some chance of creating a program that can be debugged and will meet the desired specification. Also, you will find that writing and testing a series of small functions always requires less time than writing and testing the aggregate, more complicated function.
In summary, make your functions small, test them until there is no possibility of hidden errors, revise them and retest them always with the intent of reducing the function size. Then, when you have completed the function, parade it in front of your peers and see if they can suggest ways to improve the functions. Build your project with such blocks and you will have a reasonable chance of meeting your deadlines.
In this chapter on the M68HC12, we will discuss programming into the chip a part of the features of a telephone, or perhaps of an electronic phone book. Note that the problem discussed here is but a small part of the control of a telephone. A telephone book function is one complete module that is a part of the telephone control.
As it stands, the HC12 has a small amount of EEPROM into which a phone book can be stored. The chip also has a large amount of FLASH ROM in which the program is stored. The FLASH memory has some disadvantages that discourage its use to store the telephone book data. On these chips, the FLASH memory is broken into two parts. The smaller portion of the FLASH is called BOOT FLASH and the larger portion is specified for general program storage. If you need to erase any memory, the whole block, either the BOOT or
Advanced Topics in Programming Embedded Systems (M68HC12) 349
the general program storage, must be completely erased. Therefore, if you should want to change the contents of the phone book, the whole block would have to be erased and rewritten. Another approach that could be used is just to assume that you will have enough FLASH to write over and discard the memory used and move the entry to new memory whenever it is changed. This approach, however, is wasteful of the memory.
The EEPROM does not suffer this problem. Any individual byte in the EEPROM memory block can be written, read, or erased without a problem. Also, the EEPROM can be erased and rewritten at least 10,000 times without deterioration. FLASH, on the other hand, is specified to be able to withstand 100 erase/write cycles during its lifetime. All in all, FLASH makes a good program storage memory and EEPROM makes a better changeable nonvolatile memory.
The C compiler used in this chapter is that provided by Cosmic. This compiler is essentially the same as that seen in Chapters 5 and 6 except, of course, it creates code for the M68HC12 family of parts. One very nice extension of the compiler is the ability to identify EEPROM in the code. The approach followed here uses the following #pragma:
#pragma data[768] @eeprom
It identifies data as an array 768 bytes long that is stored in EEPROM. At link time a command like
+seg .eeprom –b 0xd00 –m768
indicates that the EEPROM will be found at the address 0xd00 and it will be 768 bytes long. The nice feature derived from this extension to the C language is that any assignment to the data array will first erase the byte and then store the data into the specified location automatically.
The fact that the HC12 component that is to be used for this project contains a large amount of FLASH and little RAM leads to some difficulty in writing code for use on this part. There are no development environments that contain sufficient RAM to test a significant program. Therefore, prior to executing the first byte of code on the target chip, you must be more certain than normal that the code will work as intended. I solve this problem by developing most of the code for the final configuration to execute in a DOS environment and only after I have a complete working program is there any attempt to move it into the target system. Code developed in this manner was discussed in the
350 Chapter 7 Advanced Topics
previous paragraphs. The main difference is that the development flow will be for a DOS-based system and the microcontroller-based code will be very carefully designed and tested and integrated into the program as it is developed. The final tests after the code has been transferred to the target system will be limited to those items that are specific to the target system only.
Let us look now at what we would like our phone book code to
do.
The purpose of the program is to allow storage of names and telephone numbers in the EEPROM section of an M68HC912B32. This chip has 768 bytes of EEPROM and 32K bytes of FLASH EEPROM. It has an on-board UART through which all of the communications with the chip are conducted. One of four singleletter commands can be entered into the system:
Command |
Response |
n |
Receive a NAME terminated by an <enter> |
|
followed by a phone number also terminated by |
|
an <enter>. |
s |
Display the entire directory contents. |
a |
Display the next directory entry. |
r |
Delete the entire contents of the directory. |
Nonvolatile storage is at a premium. Therefore, all data stored in EEPROM will be encoded to compress the data as much as practical. All numbers will be stored in BCD form. This approach requires 4 bits per stored number when, in fact, 3.32 bits per digit is required if it is assumed that the use of each number is equally likely. Confusion can result when an empty number is stored, so the value stored for the number 0 will be 0xa rather than 0x0.
Alpha, or letter, data will be compressed using a Huffman code as was shown in Chapter 5. This code will be written specifically to compress data from the names found in a telephone directory. Fre quency of letter usage here is different from that found with general English text. The decode scheme to be used here will follow the general approach given in Chapter 5.
As a first estimate, the following functions will be required in putting this program together:
Advanced Topics in Programming Embedded Systems (M68HC12) 351
Monitor |
This function executes all of the time and receives |
|
data from the keyboard. It interprets the entries and |
|
passes control to the appropriate function to execute. |
Encode |
Encodes the alpha data read from the keyboard |
|
with a Huffman code. |
Decode |
Decodes the Huffman encoded data stored in |
|
FLASH when needed. |
Numdup |
Converts numeric data passed in an array to the |
|
modified BCD format and saves these data in the |
|
FLASH array, also a passed parameter. |
Putbcd |
Converts the encoded numeric data contained in |
|
the passed array to ASCII form. Places the con |
|
verted data in an array that is passed to the function. |
Getchar |
Reads in a character from the serial port. This func |
|
tion and putchar() below work with the |
|
standard library input/output functions that will |
|
be used by the program. |
Putchar |
Sends a character to the serial port. |
Get |
Reads in a character string, either numeric or al |
|
pha from the serial input. |
Printout |
Prints the contents of the phone book stored in |
|
EEPROM. |
Printafter |
Prints the next entry in the phone book. |
Saveit |
Saves the phone book entry in the proper |
|
EEPROM location. |
Reset |
Erases the contents of the EEPROM. |
Most of these functions have nothing to do with the underlying computer. Therefore, we will write code that is completely indepen dent of the computer. If there is ever a potential modification in this code when changing to the embedded microcontroller, standard com piler control commands will be used.
