192
C What Happens USING PIC® MICROCONTROLLERS AND THE CCS C COMPILER DAVID BENSON VERSION 1.0

c What Happens

Embed Size (px)

Citation preview

Page 1: c What Happens

C What Happens

USING PIC® MICROCONTROLLERS

AND THE CCS C COMPILER

DAVID BENSON

VERSION 1.0

Page 2: c What Happens

NOTICE

The material presented in this book is for the education and amusement of students, hobbyists, technicians and engineers. Every effort has been made to assure the accuracy of this infor­mation and its suitability for this purpose. Square 1 Electronics and the author assume no responsibility for the suitability of this information for any application nor do we assume any liability resulting from use of this information. No patent liability is assumed for use of the information contained herein.

All rights reserved. No part of this manual shall be reproduced or transmitted by any means (including photo copying) without written permission from Square 1 Electronics and the author.

Copyright © 2008 David Benson

Registered trademarks of Microchip Technology, Inc:

ICSPIn-Circuit Serial Programming

Registered trademarks of Microsoft Corporation: Microsoft Windows Hyper Terminal

Registered trademarks of Hilgraeve, Inc.: HyperTerminal Private Edition

PUBLISHER

Square 1 ElectronicsP.O. Box 1414Hayden, ID 83835 U.S.A.Voice (208)664-4115 FAX (208)772-8236 EMAIL [email protected] http://www.sq-1.com

TRADEMARKS

PICPICkit 2 ICD 2

PICmicro PICSTART Plus MPLAB

Page 3: c What Happens

C What Happens

INTRODUCTION 1

PIC MICROCONTROLLER PRODUCT OVERVIEW 4

SELECTING A DEVICE FOR EXPERIMENTS 6

PIC16F818 8

Pins and functions 8Package 8Clock oscillator 9Reset 9Ports 10Special Features 10PIC microcontroller architecture 11Code and data protection 13Configuration bits 13

CIRCUIT FOR PIC16F818 EXPERIMENTS 14

CHOOSING DEVELOPMENT TOOLS 18

CCS compiler 18 Device programming methods

Device programmers and ease of running code 18 examples

Device programmer 18In-circuit serial programmer 18

Choosing a device programmer 19Microchip PICSTART Plus 19

Choosing an in-circuit programmer/debugger 20CCS ICD-U40 (or -S40) 20Microchip PICkit 2 20Microchip ICD 2 20

PROGRAMMING A DEVICE USING THE ICD-U40 (or -S40) 22

PROGRAMMING A DEVICE USING THE PICkit 2 26

PROGRAMMING A DEVICE USING THE ICD 2 28

PROGRAMMING A DEVICE USING THE PICSTART Plus 31

CCS COMPILER 32

Page 4: c What Happens

C SOURCE CODE 34

What it looks like 34Typing accuracy 34Comments 34Text And Formatting 34

BITS, BYTES, ETC. 36

Bit 36Nibble 36Byte 36Binary 36Hexadecimal 38

CONSTANTS 40

VARIABLES 41

DATA 42

Data types 43ASCII characters 44

NAMING CONSTANTS AND VARIABLES 45

Reserved words in C 45OPERATORS - SHORT LIST 47

TRUE vs. FALSE 48

DEVICE FILES 48

PRE-PROCESSOR DIRECTIVES - SHORT LIST 49

INs AND OUTS OF DIGITAL I/O 51

CONFIGURATION REGISTER(S) FUSES 53

FUNCTIONS 54

main() function 55Functions 56Built-in functions - short list 57

STATEMENTS 58

Executable statements 58Blocks 58

Conditional statements 58Semicolon use rules 59

Page 5: c What Happens

PROGRAM DESIGN 60

Program design - control flow 61if 61if/else 62while loop 64do/while loop 65for loop 66switch/case 67break 67continue 68return 68goto 68

Rule 68Modular programming 69

WRITING PROGRAMS (With Experiments) 70

Programming concepts 70Programming examples 72Simple data transfers 75Loop - endless 76

While loop 77Do/while loop 78

Port registers accessed as variables 79Port addresses defined using 79

#byte directivesPort addresses defined using 80

user-created include filePort addresses defined using 81

get environment built-in functionLoop with a counter 82

For loop 82Loop until 84

While loop 84Comparisons 86

Relational operators 86If/else 86

Switch/case 87Function calls and time delays 89Bit-level I/O using built-in functions 92

Bit toggle 93If statement - read switch position 94

! logical operator 96&& logical operator (two switches) 97

logical operator (two switches) 97if/else, else, else 98

Read input bit, write output bit 100Event counting 102

Page 6: c What Happens

Bit manipulation using bit manipulation functions 104Bit set/clear 104Bit testing 104Flags 105

#bit pre-processor directive example 106typedef example 106

Bit manipulation using bitwise operators 107Shift bits right or left 107Change specific bit to "1" 108Change specific bit to "0" 108Change specific bit to its compliment 108

Goto 110Function library 112Cut and paste 112

TALKING TO A PIC MICROCONTROLLER WITH A PC VIA A WINDOWS 113 TERMINAL PROGRAM

"U"-turn experiment 114PC-to-PC "2-lane highway" experiment 117PC/PIC microcontroller 118

PC baud rates 118RS-232 interface for a PIC microcontroller 119PIC microcontroller-to-PC serial communication 121Formatting PIC microcontroller data on a PC screen 122

STRINGS 125

ARRAYS 127

Index to an array 127Step through array elements 128Extract n**1 element from array 129Add offset to index 130Lookup tables 131

7-segment LED display 131STRUCTURES 133

Structures and ports - bit fields 137MATH AND MANIPULATING NUMBERS 140

Mathematical operators 140Operator precedence 140Data type selection considerations 142 Formatting variables such as math results for printing 143

PASSING VARIABLES 146

Passing variables Returning variables Prototyping functions

146147 147

Page 7: c What Happens

OPERATORS 149

Assignment operator 149Relational operators 149Logical operators 149Increment and decrement 149Mathematical operators 150Bitwise operators 150Pointer operators 150Structure operators 150Operators that don't fit the categories 150

INTERRUPTS 151

External interrupt sources 152Internal interrupt sources 152

Timer 0 interrupt 152Port B interrupt on change - bits 7, 6,5,4 152Interrupts generated by other peripherals 153

Global interrupt enable flag (GIE) 153Return from interrupt 153 Where to put the interrupt service routine in program 153

memoryInterrupt latency 153Multiple external interrupt sources 153Interrupts in C 154

Functions - Built-in 154Pre-processor directives used to identify 154

interrupt service routinesExample - external interrupt 155

TIMING AND COUNTING USING TIMER 0 158

Digital output waveforms 158Using timer 0 159Prescaler 160Putting timer 0 to work 161

Setting up timer 0 Starting timer 0 CounterHow do we know timer 0 is doing something? Timer 0 will keep on counting as long as: Timer 0 must be reloaded after each overflow

for repeating time intervals Stopping timer 0

Timer 0 experiments 162Digital output waveform using timer 0 - internal 162

clockSingle time interval - internal clock 163Free running mode - internal clock - 0.1 second 166

periodSingle time interval - external clock 169Free running mode - internal clock 170Counting events (pulses) 174

Going further 175

Page 8: c What Happens

ANALOG TO DIGITAL CONVERSION 176

INSERTING ASSEMBLY CODE IN C CODE 179

APPENDIX A - PULSER 180

APPENDIX B - SOURCES 181

APPENDIX C - HEXADECIMAL NUMBERS 182

APPENDIX D - PROGRAM LISTINGS vs. PAGE NUMBERS 183

Page 9: c What Happens

INTRODUCTION

The internal operation of a microcontroller is all about reading and writing to registers, or some­times a bit in a register. This is done to:

• Control the operation of the microcontroller itself.• To communicate with the outside world via input or output pins (lines).• To move data from one register to another.• To perform mathematical calculations.• And more.

Assembly language programmers select and use instructions from the microcontroller's instruc­tion set and put them in the proper order to make the desired (hopefully) things happen.

C does a lot of this byte and bit level stuff for you. C is a high level language. You, the pro­grammer, create the overall plan in the form of C source code. The C compiler generates an assembly level program and the file containing the l's and 0's that get programmed into the microcontroller (device) itself.

C does not have an instruction set. Functions and executable statements get the job done. Some functions are built-in to the compiler, and some you write yourself. The C compiler that we will be using has a lot of built-in functions specific to PIC ® microcontrollers which will make your job much easier. You will write the executable statements.

You will find that there are lots of ways to do the same thing in C (that work). I have two goals which conflict. The most important one is to give you the basics in the simplest, cleanest, most consistent wray possible to minimize confusion and move you toward writing your own pro­grams (that work) as soon as possible. The secondary goal is to show' you enough about other ways to do things that you will be able to understand other people's code, especially the exam­ples and drivers that come with the compiler which have been created by various authors over time.

Reading this book will not give you an encyclopedic knowledge of C. It is not meant to be erudite. It is meant to be informal and user friendly. My goal is to help you be successful.

No matter what programming language or specific device you use, you will need to know some­thing about the internal workings/layout of the device you choose to work w'ith and the electrical connections to the outside world. In this book, we will use the PIC 16F818 for running the examples. The information that you need to understand the examples is provided so you won't have to look elsew'here for it (w ith the exception of some specific details listed in the CCS C Compiler Reference Manual). When you move on to projects of your ow n using other devices, you will need a copy of the Microchip data sheet for each device. It will be book-length and available for download at microchip.com in PDF format.

1

Page 10: c What Happens

The PIC16F818, like the majority of PIC microcontrollers, has program memory made using flash technology, which means it can be erased electrically. It can be programmed, tested in-cir­cuit and reprogrammed if necessary in a matter of a few minutes and without the need for an ultraviolet (UV) EPROM eraser. It is a small device (18-pins), readily available to all including hobbyists and students at a cost of $4.00 (at this writing) in single quantity.

Think of the PIC16F818 as a custom I/O handler. It looks at its inputs and, based on what it sees, it sends signals out its outputs. You can customize it to do what you w'ant via program­ming. It is not a heavy duty number cruncher or data manipulator.

A variety of device programmers arc available for the PIC16F818 from independent program­mer manufacturers for as little as $40.

Learning how PIC microcontrollers work and how to apply them involves study in three areas:

• Use of a computer running Microsoft Window's (tm) (as needed).• C programming language and C compiler.• PIC microcontroller itself.

The use of "pow'er tools" for programming PIC microcontrollers is essential. This means learn­ing to use an IBM compatible computer if you haven't already done so. It also means learning to use a C compiler which converts English-like readable instructions into machine language understood by the PIC microcontroller itself.

Finally, learning about the PIC microcontroller's inner workings is possible once use of the power tools is understood.

The usual approach used by others to teach the use of PIC microcontrollers has been to get into all of the theory and details of the programming language and then show advanced examples using one of the more complicated parts. As usual, only 5 percent of this information is needed to get started, but which 5 percent. The approach taken here will be to give you the 5 percent you need to get going using the P1C16F818 masquerading as a relatively simple part.

The object is to make this process as easy and enjoyable as possible. Once you get through this and you have programmed a PIC16F818 for the first time, a wrhole newr world awaits. You will be able to create more interesting projects and have more fun!

2

Page 11: c What Happens

The assumption is made that you know how' to do the following on a computer running Microsoft Window's:

• Create a new folder.• Copy part of the contents of a CD to the folder.

• Use a simple text editor to create a text file, save it, make a copy of it, print it, andcopy it to the folder.

As a beginner, you need to type the code examples in this book yourself, make the typing mis­takes we all make, and learn what the error messages generated by C compiler mean. That is why the code in this book is not on our web site. The code for our intermediate and advanced level books is on our web site.

GENERAL INFORMATION

See our web site at http://www.sq-l.com for updates and for errata.

Let's C what happens!

Page 12: c What Happens

PIC MICROCONTROLLER PRODUCT OVERVIEW

This book is not about one device. It is about the whole Microchip microcontroller product line. We need a place to start, a simple (relatively speaking) device which will be available for an extended period. I feel that the best device for initial learning purposes is the PIC16F818. What you learn in the process of using it is applicable, in varying degrees, to the entire Microchip product line.

In a simplistic way, Microchip's 8-bit microcontroller line may be classified or categorized in three groups as follows:

• 12-bit core base-line - 10, 12 and 16 series part numbers• 14-bit core mid-range - 12 and 16 series part numbers• 16-bit core - 18 series part numbers

A different set of rules applies to each group. The number of bits in the instruction words corre­spond to the core width (in bits). We don't really care how' many bits are in an instruction word or how' wide the core is. We merely need to know what category a given device belongs in so we know' which set of rules apply to it.

All of these microcontrollers arc classified as 8-bit devices because the data and data bus are 8 bits wide.

The most popular segment of the 8-bit product line is the 14-bit core mid-range parts. Our atten­tion will be focused there.

The 12-bit core base-line parts are less sophisticated than the 14-bit core parts and are a step backwards (dumbed down). They still have plenty of capability for many low-end applications and are widely used where production volume is high and unit cost must be very low. They are not used much by experimenters.

The 18 series parts have many similarities with the mid-range devices, but require a separate dis­cussion. This group is growing.

Microchip has introduced a new' 16-bit microcontroller family. The data and data bus are 16 bits w ide. The core is 24 bits wide. These devices have a lot of computation capability and on­board peripherals. Once you learn the fundamentals of using PIC microcontrollers, there is plen­ty of room to grow.

So, the information in this book applies directly to the 14-bit core parts.

4

Page 13: c What Happens

Chances are that when you choose a part for a project or product, it won't be the one you begin your learning experience with. Learn with the PIC16F818 and branch out later to learn about other devices that interest you. You will need an PIC16F818 data sheet (really a book) as a ref­erence as well as a data sheet for each device you become interested in later on. This informa­tion is available on Microchip's web site.

Microchip has product family information available on their web site. As I am writing this, you can select 8-bit PIC Microcontrollers, then PIC16MCU to arrive at a matrix of information con­sisting of devices going down and features going across the matrix. Their web site is constantly changing, so you may have to poke around a little to get there.

Compare the features and layout of devices of interest with the soon to be familiar PIC16F818 as a reference point.

5

Page 14: c What Happens

SELECTING A DEVICE FOR EXPERIMENTS

This book is not about one or two specific PIC microcontrollers. The information presented will serve as a foundation for working with all Microchip microcontrollers. In order to do experi­ments and to create code that works, we must select a specific device to work with. By making minor changes, the code examples in this book will run on many other PIC microcontrollers in the 14-bit core product line. One of the great things about C is that code can be ported from one device to another easily.

I have chosen the PIC16F818 is the example because:

• It has 18 pins (small).• It has an internal clock oscillator with 8 speeds selectable via software. The 8 speed

clock oscillator is a relatively new design used in many new devices currently being introduced by Microchip.

• It has the best mix of on-board peripherals (timer/counters, A/D converter) forbeginner and intermediate level experimentation.• It has in-circuit debugging capability built into the device which you will find is a bigadvantage as you move forward to more complex projects.

There are lots of features and their associated registers inside the PIC16F818 which I will keep hidden from you so you don't have to worry about them until some time down the road (next book). The PIC 16F818 will appear to you as a simple device with a few pins which will go unused for now.

When choosing a dcvice for an application, one would look at factors such as program memory size, on-board peripherals such as A/D, timer/counters, etc.

6

Page 15: c What Happens

When firing up a device you have not used before for the first time, you must do the following:

• Determine whether or not there are analog peripherals (A/D and/or comparators)which must be turned off if not used. The CCS C compiler will do this for you as a default.• If there is a multi-speed internal clock oscillator (software selectable speed), you mustdetermine what speed it will run at when the device is powered-up and whether or not the speed must be changed during initialization of the device to suit your application.• Determine what features are controlled by the configuration word(s) as the device isprogrammed by the device programmer and what selections should be made. Using C will greatly simplify this process for you.

• Determine how many configuration registers there are and how to write to them.Using C will greatly simplify this process for you.

Explanations of these things follow as appropriate. I have made the choices for you when using the PIC16F818 as your example for the experiments. However, I will show you how to do this on your own.

Page 16: c What Happens

PIC16F818

PINS AND FUNCTIONS

The PIC 16F818 is fabricated using CMOS technology. It consumes very little power and is fully static meaning that the clock can be stopped and all register contents will be retained. The maximum sink or source current for a port at any given time is as follows:

Any I/O Pin Port A Port B

Sink current 2 5 mA 100 raA 100 mASource current 2 5 mA 100 mA 100 mA

Supply current is primarily a function of operating voltage, frequency and I/O pin loading and is typically 2 mA or so for a 4MHz clock oscillator and 5 volts. This drops to less than 100 microamps (even a few microamps) in the power-managed modes (see data sheet).

Because the device is CMOS, all inputs must go somewhere. All unused inputs should be pulled up to the supply voltage (usually I 5 VDC) via a 10 K resistor.

PACKAGE

The PIC16F818 is available in an 18-pin DIP package suitable for the experimenter. The current part number is PIC 16F818-I/P.

8

Page 17: c What Happens

CLOCK OSCILLATOR

The internal clock oscillator may be used (most common), as is done in this book, or an external clock oscillator may be used. The details of various external oscillator circuits and components as well as internal clock oscillator use options are given in the Microchip data sheet. At pro­gramming time, the part must be told via configuration bits which clock oscillator option will be used. This will be explained as we go along.

For experimentation, we will use the internal clock oscillator as follows:

• Default frequency (31.25 KFIz) for most applications.• 4 MHz for applications using a time delay (built-in function or timer 0).

RESET

The PIC16F818 has built-in power-on reset which works as long as the power supply voltage comes up quickly. Commonly the MCLR pin is merely tied to the power supply using a pull-up resistor. A switch may be used to regain control if things run away.

For our experiments, we will use pin 4 as MCLR which stands for Master Clear (reset). It will be pulled up to +5volts via a 47 K resistor to keep the device out of reset unless the MCLR pin is pulled low by some external device.

If you choose to use one of the ICD-type in-circuit serial programmers, the programmer will use pin 4 for the programming threshold voltage, Vpp, which puts the device in programming mode. After programming is completed, you may bring the device out of reset to test your program using the ICD control interface running on the PC. This makes programming and testing your codc fast and easy.

9

Page 18: c What Happens

PORTS

Port A, as we will be using it, has 7 bits/lines. Port B is 8 bits/lines wide or byte-wide. Each port line may be individually programmed as an input line or output line. This is done using a special function which matches a bit pattern with the port lines in registers callcd "tristate" or "tris" registers. A "0" associated with a port line makes it an output, a "1" makes it an input. Examples follow.

Pins which may have analog functions in use are analog functions when the microcontroller comes out of reset. For the PIC16F818, the CCS compiler will generate an instruction which changes those pins to digital I/O as part of the initialization process unless the compiler encoun­ters a call to setup_adc_ports ( ) ; . This is a default and it is very important to keep in mind.

The Port B lines have weak pullup resistors on them which may be enabled or disabled under software control. All 8 resistors are enabled/disabled as a group via a built-in function in the compiler (not used in this book). The pullup resistor on an individual port line is automatically turned off when that line is configured as an output. The pullups are disabled on power-on reset.

Port A, bit 4 is shared with the external timer/counter input called TOCKI. As a digital input line, the input is Schmitt trigger. As a digital output line, it is open drain, so a pullup resistor is required. The output cannot source current, it can only sink current.

For experimenting, all unused port lines should be tied to the power supply via 10 K pullup resistors (CMOS rule - all inputs must go somewhere). On reset, all port lines are inputs.

SPECIAL FEATURES

Watchdog Timer

The watchdog timer is useful in some control applications where a runaway program could cause a safety problem. We will not deal with it exccpt to say that it is important to select "watchdog timer off' when programming the configuration bits.

Power-up Timer (PWRT)

The power7up timer holds the device in reset for a time which allows the power to come up to a level wjiich will provide reliable operation of the device at which time it is allowed to come out of resetV The power-up timer should be selected "enabled" when programming the configuration bits.

Brown-out Reset (BOR)

If Vdd falls below approximately 4 volts for about 100 microseconds, the device will be reset. The brown-out reset feature should be selected "enabled" when programming the configuration bits.

Sleep Mode

The feature of the "sleep mode" is drastically reduced power consumption achieved by turning off the main clock oscillator.

10

Page 19: c What Happens

In-Circuit Debugging

The PIC16F818 is designed so that an in-circuit debugger may be connected to it (advanced topic).

Low-Voltage ICSP Programming

Low voltage ICSP will not be used.

Peripherals

Peripherals such as timer/counters and A/D converters will be discussed in chapters devoted to the subjects.

Special Feature Selection

Details follow.

PIC MICROCONTROLLER ARCHITECTURE

PIC microcontrollers have two separate blocks of memory, program memory and data memory.

Program Memory

The PIC16F818 program memory is 14 bits wide and 1K words long. Program memory is flash which means it can be erased electrically. Program memory is read-only at run time for the purposes of this book. PIC microcontrollers can only execute code contained in program memory.

Pointed To By ■Reset Vector

Pointed To By Interrupt Vector

0x3FF _______________________

A limited amount of data may be stored in program memory (see Writing Programs chapter).

11

Page 20: c What Happens

Weird Hex Notation

The "Ox" means hexadecimal. The Ox notation comes from the C programming language. The main thing is, when you see OxOF, it means hexadecimal OF. 0x004 means hexadecimal 004.

Some of the newer Microchip literature uses "h" to denote hexadecimal numbers. 3FFh means 3FF hexadecimal. h'3FF' has the same meaning.

Data Memory

Data memory consists of register files containing registers of two types:

• General purpose registers which hold your data.• Special Function Registers (SFRs).

The register files are 8 bits wide (with the exception of the PCLATH register which is 5 bits wide). The P1C16F818 has a 512 register file address space (0x000 - Ox IFF) divided into four banks, but not all addresses are used.

Register File

0x000102030405060708 09 0A 0B 0C

IF2 0

0x7F

Indirect AddressTMR0PCLStatusFile SelectPort A DataPort B Data

PCLATHINTCON

Bank 0

Indirect Address Pointer *Timer/CounterProgram Counter Low Order 8 Bits Status Register - Flags Indirect Pointer Port A Port B

Program Counter Latch High Order 5 Bits Interrupt Control

General Purpose RegistersThink. Of This Area As RAM (Data Memory)

Not Physically Implemented

Note: Bank Switching AndBanks 2 And 3 Ignored

64 file registers have specific dedicated purposes and are called Special Function Registers (SFRs). For the most part, the C compiler knows what to do with the SFRs and the address of each. Wc will discuss the very few situations in which the compiler needs help finding address­es as the need arises. 128 registers arc there for the C compiler to use and may be thought of as RAM or data memory for storing data during program execution.

12

Page 21: c What Happens

Data EEPROM Memory

Data EEPROM memory is not directly mapped into the data memory register file address space described earlier. Instead, it is addressed using six special function registers and some built-in C functions. This topic is beyond the scope of this book.

Oh yes, I neglected to spell out what the acronym means. It stands for memory that can be read, written to, or even erased electronically. It is usually used to store data acquired during program execution (think data logger).

CODE AND DATA PROTECTION

The code protection bits in the configuration register may be set so as to protect the code in pro­gram memory, the data in data memory, and the data in EEPROM memory from examination by the outside world (so your code can't be ripped off). Your program can still access and change the contents of data EEPROM memory with the code protection bits set.

CONFIGURATION BITS

The configuration bits are loeated in flash memory outside the main part of flash memory used for program storage. They are used to determine things like clock oscillator type, functions of some of the pins, etc. There is a chapter devoted to this subject. The device programmer accesses these bits during the device programming procedure. By doing this, the device will be in the correct configuration when it comes out of reset.

13

Page 22: c What Happens

CIRCUIT FOR EXPERIMENTS

One simple circuit may be used for all but one of the experiments in this book. The exception utilizes a 7-segment LED digital display.

TOCKI ANO INTRA4 RA1 RAO RBO

14

Page 23: c What Happens

Looking at what is included may give you some ideas about how you would like to construct it. My suggestions on how to proceed follow.

A simple circuit board may be assembled which includes a socket for a PIC16F818, power supply terminal block, power supply decoupling capacitors, three 3-pin headers and shorting blocks for use in selecting functions for pins RAO, RA1 and RA4, screw terminal blocks as a means of connecting RAO/ANO, RA I, RA4/T0CKI, and RBO/INT to the off-board components used in the experiments, and DIP switches for pins RAO, 1, 2, 3. A modular jack is included for easy connection to any of the three ICD/programmers described in the book.

If you decide to use a device programmer rather than use an ICD as a programmer, I would defi­nitely use a ZIF socket for the PIC microcontroller to avoid bending or damaging the pins.18-pin ZIF sockets are becoming expensive and difficult to find. The once common part made by the TEXTOOL division of 3M is still available from Digi-Key. It is the gold plated (literally) version (3M part number 218-3341-00-0602J) and the cost is around $18.

A 24-pin Aries socket is available from JAMECO, Digi-Key and others. The Aries part number is 24-6554-10 (tin plated contacts) and the price will be in the $8 range. Simply ignore the extra 6 pins.

Pullup resistors are used in the experiments primarily for the purpose of preventing unused inputs from floating. There are pullups on port B built into the PIC16F818. I decided not to use them because they just add confusion to the program examples and detract from explanation ofthe applications themselves. So........... as a beginner, when you use the circuit module, rememberthere are 10 K pullup resistors on all unused port lines. You can save refinements for later.

15

Page 24: c What Happens

A modular phone jack is used to connect to the ICD via cable. A printed circuit board style jack is shown. One manufacturer is tyco Electronics AMP. The part number is 5204703.The Digi-Key part number is A9049-ND.

The modular phone jack is connected as shown:

Modular Jack 6-Conductor □tyco Electronics / AMP □Mfgr. P/N 520470-3 Digi-Key P/N A9049-ND

16

Page 25: c What Happens

If you arc not able to find the small modular phone jack and you want one NOW, you may pur­chase a wall-mount phone jack and whack it down to size using a saw. It will have screw termi­nals on the back. Short wires may be used to connect it to your board or a solderless bread­board.

The example in the photos has one end cut off to show the concept. The one that I have used for some time has all four sides of the mounting plate portion cut off.

CCS has a nice PIC16F818 board available which includes what I have described above plus a little more. See Appendix B - Sources.

Page 26: c What Happens

CHOOSING DEVELOPMENT TOOLS

CCS C COMPILER

One of the great things about the CCS C compiler is that it includes tots of built-in functions (think subroutines if you are not familiar with C) which control the PIC microcontroller on­board peripherals. You will not have to learn about or concern yourself with many of the inter­nal workings (think control registers) of the microcontroller. If you want to read the A/D con­verter, simply use the READ ADC() function. Precision time delay built-in functions are avail­able for your use. CCS also supplies drivers to control popular external devices so you don't have to work so hard figuring out how to do it on your own. The CCS C compiler is a power tool for creating the code for your applications.

DEVICE PROGRAMMING METHODS

Device Programmers And Ease Of Running Code Examples

There are two choices.

• lise a device programmer, program the device, remove it from the socket on theprogrammer, place it in the socket on the test circuit, power-up the test circuit, and run the program. This is easier than it sounds.• Use an in-circuit debugger (ICD) as a programmer This is done using a method calledin-circuit serial programming (tm) (ICSP) (tm) and the device is programmed in the experiment board. Immediately after programming, the circuit may be exercised by clicking on a "run" (or similarly named) button on-screen. Moving the device is not necessary. This is the easiest method. ICDs are the least expensive way to go these days unless you already own a device programmer.

Device Programmer

A device programmer is used to load code into a device and that's it. The device is usually clamped in the programmer's zero insertion force (ZIF) socket during programming. After pro­gramming is completed, the device is removed from the ZIF socket and inserted in the socket on the experiment board. The board is powered-up and the code is tested.

In-Circuit Serial Programmer

An in-circuit debugger (ICD) may be used to download code into a device, run the code, and debug the code. Debugging is not covered in this book, however, a debugger may be used to load the example programs in this book into a device followed by bringing the device out of reset so the code will run. The device is part of the example circuit when the programming takes place, a process called in-circuit serial programming (ICSP).

18

Page 27: c What Happens

In-circuit serial programming requires the use of two port lines, generally port B, pins 7 and 6.To keep things simple, the examples in this book do not use these two pins for the test circuit. This allows in-circuit programming followed immediately by running the program using the ICD on-screen controls to bring the microcontroller out of reset which allows the program to run.

In an industrial/commercial product development environment, port B, pins 7 and 6 would be used in the application and special means (a special substitute device or a header containing one) would be used to gain access to the part for debugging purposes while leaving port B, pins 7 and 6 free. This is an advanced topic and wc won't concern ourselves with the details here.

The PIC16F818 may be programmed using an ICD as an in-circuit serial programmer followed, immediately, by running the code.

Available ICD's include the CCS ICD-U40 (or ICD-S40), the PICkit 2 (tm), and the Microchip ICD 2 (tm). Three advantages of using this method over using a device programmer (only) are:

• The convenience of running the code immediately following programming.• The low cost of the tool.

• The ICD will be available for debugging when you move up to more advancedprojects.

CHOOSING A DEVICE PROGRAMMER

Selecting a programmer (only, not an ICD) for PIC microcontroller development is a personal choicc based on the following criteria:

• Range of parts which can be programmed.• Support (level, long-term).• Price.

Simple, inexpensive device programmers are available which run under Microsoft Windows. As product offerings are continually changing in this fast-paced market, I suggest you contact the programmer manufacturers directly for the latest information. Note that some programmers are connected to the PC's parallel (printer) port, some arc connected to a serial port (usually COM2), and some are USB devices.

Microchip PICSTART Plus (tm)

Microchip's PICSTART Plus will program their 5 volt dual in-line (DIP) packagc devices. The latest version of the PICSTART Plus contains a PIC 18 scries microcontroller which converts information received from the PC's serial port to the appropriate signals to program each device. Since these devices have differing requirements, the code in the PIC 18 microcontroller must be updated to cover the requirements of new devices as they become available. The latest code (firmware) may be downloaded from Microchip's web site at no charge.

The PICSTART Plus operates under Windows and is connectcd to one of the PC's serial ports (usually COM2).

19

Page 28: c What Happens

The control software for the PICSTART Plus is incorporated in MPLAB (tm) which is Microchip's development software. MPLAB is also updated frequently to incorporate support for new devices as they are introduced.

The PICSTART Plus currently sells for about $200.

CHOOSING AN IN-CIRCUIT PROGRAMMER/DEBUGGER

CCS ICD-U40 (or -S40)

The 1CD-U40 uses a USB interface and the ICD-U40 uses an RS-232 serial interface. They each use different control software.

The CCS ICD will program the devices used as examples used in this book using ICSP and will allowr the program to run after the device is programmed.

The ICD Control Software is separate from the compiler software.

The ICD-U40 or -S40 currently costs approximately $75.

Microchip PICkit 2 (tm)

The PICkit 2 uses a USB interface.

The PICkit 2 will program the devices used as examples used in this book using ICSP and will allow the program to run after the device is programmed.

Control software comes with the PICkit 2 and will handle the tasks we need to perform with the examples in this book. The PICkit 2 may be used under the control of MPLAB for many devices. We will use the PICkit 2 control software in our examples.

The PICkit 2 MCU programmer (PG164120) currently costs about $35. I suggest purchasing a RJ-11 to ICSP adapter (AC 164110) ($ 10) to go with it. It has a header at one end to interface with the PICkit 2 and a short 6-conductor cable with a modular plug on the other to interface w ith the modular jack on your board.

Microchip ICD 2 (tm)

The ICD 2 will also program the device used as an example in this book using ICSP and will allow the program to run after the device is programmed.

MPLAB is used to control the ICD 2.

An ICD 2 with a USB cable and powrer supply sells for approximately SI90.

20

Page 29: c What Happens

ProgrammingSoftware

Programming (only) Connections

CCS ICDControlSoftware

PICkit 2 Application Software (MPLAB for some devices)

MPLAB

MPLAB

PICSTART Plus

21

Page 30: c What Happens

PROGRAMMING A DEVICE USING THE ICD-U40 (or -S40)

INSTALLING THE ICD CONTROL SOFTWARE

Follow the CCS directions on CD.

USING THE ICD AS A PROGRAMMER

To program a C object file (.cot) into a PIC microcontroller:

Connect the 1CD-U40 to your PC using the USB cable included with the ICD module.Connect the ICD to your target board using the short modular phone cable (6-conductor)

supplied with the ICD.The target board should be powered by your +5 volt logic power supply.Power-up the PC which will supply power to the ICD via the USB interface.Power-up the Target board.Open the ICD control software. Note that this program is separate from the compiler software.

22

Page 31: c What Happens

The ICD Control Program window appears.

Targets Supported: All

Page 32: c What Happens

To program and exercisc an example program, click on the Advanced button.

The ICD Advanced window appears.

Our objectives are:

• Halt the device (hold the MCLR line low). _____• Run example programs (put the target device in run mode by allowing the MCLR lineto be pulled high).

• Program (write) example programs into a device.

We will not concern ourselves with debugging as that is an advanced topic.

Halt vs. Run can be selected by clicking on the appropriate button in the Target State area in the ICD Advanced window.

To program a dcvicc, a .cof file must have been created previously using the compiler.

24

Page 33: c What Happens

Halt the target device.In the Write Device area, click on From Hex/Cof file.The Download To Target window will appear.

Navigate to the object file you wish to write to the target device and select it (will be highlighted when you have selected it).

Click Open. This will initiate programming.In the message area in the lowrer left comer of the ICD Advanced window, a message

should appear indicating that your source file has been written to the target device.

Click on the Run button. There will be a slight delay.Observe your program running (look at LEDs or whatever depending on what the code

is supposed to do).

When you have finished your celebration (you did do everything correctly didn't you!), click Halt.

You may repeat the programming process to test another program.

When you are finished:

Power-off the target board first (always!).Power-off the PC last.Disconnect the USB cablc.

When using the CCS ICD to program a PIC microcontroller, the software also supports .hex files (not just .cof files). Since .hex files are much smaller and do not contain the C source, .hex files are more often used in a production setting.

With the ICD Advanced window open:

Page 34: c What Happens

PROGRAMMING A DEVICE USING THE PICkit 2

To program a .cof file into a PIC microcontroller:

The PICkit 2 comes with its own programming software which we will use here. Some devices may be programmed using MPLAB and accessing the PICkit 2 as one of the programmer options under MPLAB.

The PICkit 2 has a 6-pin header socket as the interface to a user board. Products are often designed with a 6-pin header on them to allow in-circuit serial programming (ICSP) after the product is assembled and just before it is shipped allowing the latest firmware version to be pro­grammed into the product. Firmware may also be changed by the customer in the field after the product is in use.

for experimenting and development, Microchip offers an adapter consisting of a very small board with a header to mate with the socket on the PICkit 2 plus a modular phone jack. A short 6-conductor phone cable is included to conncct the adapter to a modular phone jack on your board. The Microchip part number is AC 164110 (ICD 2 to ICSP adapter).

Connect the PICkit 2 , adapter, modular phone cable, and your board together. Conncct your board to your +5 volt logic power supply (power supply off). Connect the PICkit 2 to your PC using the USB cable supplied with it. Power-up the PC.Power-up your board.Open the PICkit 2 programming software.

26

Page 35: c What Happens

The PICkit 2 software will come up recognizing the device on your board (assuming thedevicc family you are using was selected the last time the software was used). If not, on the Menu Bar, select Devicc Family>Midrange.

Check /MCLR in the small Vdd PICkit 2 box.Load your .cof file into MPLAB’s Program Memory (zone).

File>Import Hex Navigate to your .cof file.Select the file.Click Open.A message will appear indicating that the file has been imported.

Click on the Write button to program the device.A message should appear reporting success.

Uncheck /MCLR in the small Vdd PICkit 2 box.Your program should run.

To halt program execution check /MCLR. This will hold the device in reset.

To program and run another program, load another .cof file and repeat the programming procedure.

When you have finished experimenting, power-down using the reverse sequence: Power-down the target board.Power-down the PC.

27

Page 36: c What Happens

PROGRAMMING A DEVICE USING THE ICD 2

DESCRIPTION

The Microchip in-circuit debugger (ICD 2) consists of software which runs on a PC (included in MPLAB) and hardware which looks like a colorful hockey puck (two of the colors are shown here).

For the purposes of this book, the ICD 2 will be used as an In-Circuit Serial Programmer (tm).

The unit has a USB connector for serial communication with a host PC and a 6-conductor modu­lar phone jack for communication with a flash PIC microcontroller. The PIC microcontroller resides on a so-called "target" board which is the user's (your) board.

Microchip strongly recommends using USB and NOT the RS-232 connection which is built into the ICD 2 for communications between the host PC and the ICD 2.

Following the current version of the Microchip USB Port Setup instructions is a MUST. Refer to the "Readme for MPLAB ICD 2" contained in the Readmes folder in the MPLAB folder.

I recommend purchasing the ICD 2 full kit with power supply (DV164007) and storing the RS-232 serial cable somewhere.

28

Page 37: c What Happens

• Debugger mode (not discussed here).• Programmer mode.

In the programmer mode, your code is programmed into the device for stand alone (without the ICD 2 connected) operation. The code can, however, be run with the ICD 2 connected.

Selecting the best option for powering the ICD 2 and the target is critical. Based on my experi­ence and that of others, I recommend powering the ICD 2 using Microchip's wall transformer power supply and NOT via the USB port. I also recommend powering the target (your circuit) with your own power supply.

The ICD 2 can operate in two operating modes:

USER BOARD = TARGET BOARD

For the purposes of this book, the PIC16F818 experiment board described earlier is set up to be used with the ICD 2. You can easily set up another board of your own in a similar way by pro­viding the modular phone jack and using the 47 K pull-up resistor on MCLR (reset).

SETTING UP THE ICD 2

Connect the ICD 2 to your PIC16F818 board using a short 6-conductor modular phone cable. Connect your PIC 16F818 board to a suitable +5 volt DC power supply.

When powering-up the ICD 2 and PIC16F818 board the first time or for use with a new project, use the following procedure:

1. At start-up, NO power should be applied to the PIC 16F818 board.2. Power-up the host PC.3. Power the ICD 2 . The green "Power" LED in the ICD 2 should be on.

4. Start MPLAB. At this point, it is assumed that you have a .cof file suitable to beprogrammed into the PIC16F818 on your board.

5. Configure>Select Devicc.Select PIC16F818.

6. Select ICD 2 as the programmer to be used.Programmer>Select Programmer>MPLAB ICD 2.

29

Page 38: c What Happens

7. A Setup wizard will appear the first time the ICD 2 is connected.Select USB as the communications method.Select target has owrn power supply.Select auto connect to ICD 2.Select ICD 2 automatically downloads the required operating system.

8. Select Programmer>Settings.9. The MPLAB ICD 2 Settings dialog box will open. Click the Power tab and ensure

that the check box for "Power target circuit from ICD 2" is NOT checked.Click OK. This is important!

10. Power-up your PIC 16F818 board.11. Open the output window.

View>Output.12. Select Programmer>Connect.

13. Observe the activity in the output window. On completion, the next to the last twotext lines should read "Running ICD Self Test... Passed" and the last text line should read "MPLAB ICD 2 Ready".

14. You should now be able to debug and erase the PIC16F818 on your board.

Reverse the procedure to power-down (PIC16F818 board off, ICD 2 off, PC off).

PROGRAMMING PROCEDURE

Soooooo........once the setup has been done, use the following procedure to program a .cof fileinto a PIC microcontroller:

1. Connect the ICD 2 to your board (target board).2. Connect the ICD 2 to your PC.3. Power-up the PC.4. Power-up the ICD 2 using the ICD 2 power supply.5. Power-up the target board using your own power supply.6. Open MPLAB.7. Configure>Select Device.8. Programmer>Select Programmer.

Select MPLAB ICD 2.9. Programmer>Connect (if you don't have auto-connect selected).10. Import your .cof file into MPLAB's Program Memory (zone).

File>Import.Navigate to your .cof file.Select the file.Click Open.A message will appear indicating that the file has been imported.

11. Programmer>Program.A message in the Output window should report success.

12. Programmer>Release from Reset to run your program.13. Import another .cof file and repeat the procedure.

14. Programmer>Disable Programmer (assuming you are finished programming devicesfor a while).

15. Power-down using the reverse sequence:Power-down the target board.Power-down the IC’D 2 by disconnecting the power cable.Power-down the PC.

30

Page 39: c What Happens

PROGRAMMING A DEVICE USING THE PICSTART Plus

To program a .cof file into a PIC microcontroller:

Connect the PICSTART Plus to your PC (as usual).Power-up the PICSTART Plus.Open MPLAB.Configure>Select Device.Programmcr>Se 1 ect Programmer.

Select PICSTART Plus.Programmer>Enable Programmer.

A message in the Output window will indicate your PICSTART Plus firmware version. Load your .cof file into MPLAB's Program Memory (zone).

File>Import.Navigate to your .cof file.Select the file.Click Open.A message will appear indicating that the file has been imported.

Insert the device in the ZIF socket. Be careful about pin orientation.Programmer>Program.

A message in the Output window should report success.Remove the programmed device from the ZIF socket.Programmer>Disable Programmer (assuming you are finished programming devices

for a while).Power-down the PICSTART Plus.

NEVER power-up the PICSTART Plus with a device in it as the device may be damaged!

31

Page 40: c What Happens

CCS COMPILER

INSTALLING CCS COMPILER - PCWH

Follow the CCS directions on the CD.

USING THE COMPILER

Create a new' folder for your C stuff.Create a source file.

File>New>SourceCsl The compiler will add .c file name extension.

Type your first source code. Call it Csl as an example.Create a new project.

Project>Create.The Select main source file window appears.Select device.Select your file (Csl or whatever).Click Open.The Project options window appears.

Your file name has been selected automatically (if you had one open).

Click Apply.Compile your code.

Click on the Compile tab on the menu bar at the top of the screen.The Compile menu ribbon will appear.Click on the Compile icon on the Compile menu ribbon.Your source code will be compiled successfully, we hope.The Output window will appear indicating how the compile process

turned out.For the examples in this book, a warning message will appear:

»>Warning 208 Csl.c Line 6 (i,5): Function not void and does not return a value main

Ignore the warning.This is what we wanted, so celebrate!!

A C object file (.cof file) was created as part of the process of compiling your source code (done in the background). It is located in the same folder as your source code (Csl .cof). This is the file that will be programmed into the PIC microcontroller.

32

Page 41: c What Happens

If you have PIC microcontroller assembly language programming expcricnce, you may want to take a look at the assembly language listing of the code generated by the compiler. To do this, click on the Output files icon on the right end of the Compile menu ribbon. Click on the C/ASM List icon.

: in proper places critical!!

One missing semicolon can result in a lot of compiler error messages of various kinds. I am cer­tain that you will never experience this.

Page 42: c What Happens

C SOURCE CODE

This is what C source code looks like:

////first C program ala pictl.asm Csla#include <16F818.h>#fuses INTRC_IO,NOWDT,PUT,NOPROTECT,BROWNOUT,MCLR,NOLVP,NOCPD,NOWRT,NODEBUG #fuses CCPB2

//internal clock osc with I/O on RA7, RA6//watchdog timer disabled power-up timer enabled//code protection off brown-out reset enabled//mclr enabled low-voltage programming disabled//no EE data protection write protection off//no debug CCPl function pin RB2 (arbitrary)

main(){

setuposcillator(0SC4MHZ); //4 MHz clock oscillatoroutputb (OxOf); //bit pattern to port Bwhile (TRUE); //circle, always

}

This is a very simple program which runs on a PIC16F818. We will assume that there are 8 LEDs connected to port B. Executing this program causes 4 LEDs to be off and 4 LEDs to be on. I am sure this program looks very cryptic to you now. When you have finished reading this book and doing the experiments, this program and much more complex ones will no longer seem cryptic.

As you read the next few chapters, you can refer to this program to see how the topics relate to this program. For now, let's make some observations about the overall look of the program.

TYPING ACCURACY

Typing accuracy is very important when creating C source code. A punctuation mark, either typed by mistake or omitted, can cause a lot of head scratching (or worse) because your "per­fect" program won't compile. The compiler sees exactly what you type.

COMMENTS

Comments help anyone (including you) who reads your code understand what it does. There arc two styles.

/* */ Comments between /* and */// Comments between // and end of line

The compiler ignores all comments.

34

Page 43: c What Happens

TEXT AND FORMATTING

Formatting such as spaces, tabs, carriage returns, etc. are ignored by the compiler. Formatting makes code readable to us.

White space resulting from laying out a program so it appears better organized is a good thing. This comes from using spaces, tabs, and blank lines. The compiler will ignore white space.

Use tabs for indentation instead of several spaces. The number of spaces per tab is usually adjustable. Three spaces per tab works well.

ANSI (or standard) C is case sensitive, but CCS C is not.

35

Page 44: c What Happens

BITS, BYTES, ETC.

BIT

One bit in memory or a register can represent 1 of 2 possible states, "0" or "1".

In the world of digital electronics, it is convenient to build circuit elements which have two states, off or on. These states can be represented by "0" or "1".

POSITIVE LOGIC

Binary Number Voltage

0 0 volts (approximately)1 5 volts (approximately)

in a 5 volt system

The exact voltage ranges which represent 0 and 1 vary depending on the logic supply voltage and the integrated circuit logic chip family used (TTL, CMOS, etc.).

The choice of using binary 0 to represent 0 volts is arbitrary. Positive logic is shown above. It can be done the opposite way which is called negative logic.

NIBBLE

A nibble consists of 4 bits and can represent 16 possible states. A nibble is typically the upper or lower half of a byte (most significant or least signi (leant nibble).

BYTE

A byte consists of 8 bits and is said to be 8 bits wide. 8-bit microcontrollers move bytes around on an 8-bit data buss (8 conductors wide).

BINARY

A binary' number with more than one bit can represent numbers larger than " I". How much larg­er depends on the number of bits. An 8-bit binary number (byte) can represent 256 possible numbers (0 to 255). A 16-bit binary number can represent numbers from 0 to 65,535. If we use a byte to transmit information, we can transmit 256 possible combinations, enough to represent the 10 decimal digits, upper and lower case letters, and more. A commonly used code used to represent these characters is called ASCII (American Standard For Information Interchange).

36

Page 45: c What Happens

Binary numbers arc based on powers of 2. The value of bit 0 is 2° = 1 if it contains a 1, or 0 if it contains 0. The value of bit 1 is 21 = 2 if it contains a 1, or 0 if it contains 0. The value of bit 3 is 23 = 8 if it contains a 1, or 0 if it contains 0, and so on.

For a 16-bit binary number, bit 0 is the least significant bit, and bit 15 is the most significant bit.

The following table shows the value of each bit position if it contains a " I

Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Most Significant Least Significant

Bit Value

0 11 22 43 84 165 326 647 1288 2569 512

10 102411 204812 409613 819214 1638415 32768

The value of a binary number contained in a 16-bit timer/counter would be determined by multiplying the contents of each bit by the value of each bit.

1 0 0 1 1 0 0 1 0 0 1 0 0 01 1 1Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

L L

37

Page 46: c What Happens

Counting up in binary goes like this:

00000001 0010 0011 0100 0101 etc.

You can use this information when you observe what happens to some LEDs used to display the count in a binary count up example program.

PIC on-board timer/counters count in binary.

A binary number may be used to represent byte-wide bit patterns sent to output ports.

HEXADECIMAL

Binary numbers which are two bytes long are difficult to recognize, remember and write without errors, so the hexadecimal numbering system is sometimes used instead. Think of hex as a kind of shorthand notation to make life easier rather than some kind of terrible math.

Hexadecimal Binary Decimal

0 0000 01 0001 12 0010 23 0011 34 0100 45 0101 56 0110 67 0111 78 1000 89 1001 9A 1010 10B 1011 11C 1100 12D 1101 13E 1110 14F 1111 15

Hex is sometimes used in this book to represent addresses in memory and is sometimes used to represent bytes of data. Using hex is not difficult. All you need is a little practice.

One byte requires two hex digits. Note that the bits representing a byte are sometimes shown in groups of four. Note that the most significant hex digit is on the left.

Hex numbers are denoted by "Ox" in this book. Some of the more recent Microchip literature uses the "h" to designate a number as hexadecimal.

38

Page 47: c What Happens

To summarize:

Nibbles

Bytes

Two Bytes

1Binary

1 Hex1 1 Decimal i i1

0000

1 t

0

t 1

00001 1 10101 5 51111 F 15

0000 0000 00 00000 0001 01 10000 1111 OF 150001 0000 10 161111 1111 FF 255

0000 0000 0000 0000 0000 00000 0000 0000 0001 0001 10000 0001 0000 0000 0100 2561111 1111 1111 1111 FFFF 65535

Hexadecimal OxFFFF (the very top of the program memory address space in some microcon­trollers) is much easier to write or remember than either 1111 1111 1111 1111 or 65535.

39

Page 48: c What Happens

CONSTANTS

A literal constant may be defined as part of a built-in function such as:

output_b (OxOf); //defines hex byte OxOf and sends bit pattern// to port B

A symbolic constant may be defined using the key word const which is a modifier that can be applied to any numeric declaration.

const int LEVEL = 1 0 ; // defines integer type constant named// level with a value of 10

After the symbolic constant is defined, it is referred to by name in the program.

The #define pre-processor directive may be used for defining constants.

#define MAX_SAMPLES 32

This method for defining constants is probably used more often than the key word const method.

A convention in C is to use upper case letters in all constant names.

Constants are stored in program memory and cannot be changed during program execution.

40

Page 49: c What Happens

VARIABLES

Variables are memory locations used to store data. A variable must be defined prior to use in a program by using the assignment operator.

variable = data

'---assignment operator

Variables are named according to the data type that will be stored in them, ie. an integer variable holds integer data, etc. (see next chapter, Data). The variable definition process tells the compiler how much memory space should be allocated for the variable being defined.

A variable, as the name implies, may be changed during program execution and will be stored in a general purpose file register (RAM location) in the PIC microcontroller.

Variables are either global or local.

• Global variables are defined outside a function and can be used by any function.• Local variables are defined inside a function (after an opening brace) and used within

that function (before the corresponding closing brace).

It is considered good practice to use local variables wherever possible so you can control access to them.

If a function needs to use another function's local variable, that variable can be passed to the function that needs it. Access to local variables is controlled, a good thing.

Passing variables (arguments) will be discussed in a later chapter on the subject.

Declaration of a variable will be demonstrated in the following chapter after data types have been explained. Naming of variables is explained in the chapter after that.

Page 50: c What Happens

DATA

Basic data types used in this book are (see note below):

Character (ASCII)A character is a single ASCII character which may be represented

by 8 bits. There are 256 ASCII characters. The characters most commonly used in microcontroller applications are contained in a table later in this chapter.

Apostrophes indicate a character 'A' 'a' ' 1 '6'More than one character is called a string and is designated

by quotation marks " " (see Strings chapter)Bit

One bitCalled inti, sometimes called short or boolean, can be called bit

with use of typedef declarator (details follow)Can represent one of two numbers, "0" or "1"

Byte8 bitsCalled int8, byte Can represent 0 - 255 Hex OxOfBinary ObOOOOllll

int 1616-bit number Can represent 0 - 65535

IntegerWhole numbers No decimal pointCalled int - same as int8 by default Can represent 0 - 255

FloatReal numbersMay have a decimal point Not used in this book

VoidIndicates no specific type

These are called data "types" and are used in type declarations to allocate the space required in memory for data storage.

42

Page 51: c What Happens

Type qualifiers may be added to further delineate these basic number types, but we won't use them.

Variables with negative values will not be discussed.

Note: The definitions of C data types depends heavily on the source of the information. The degree of inconsistency is huge! My goal is to present what is needed to get going in a simpli­fied way while still giving you a representative look at the wrorid of C. Using the CCS names (inti, int8, inti6) exclusively would simplify things, but you would not be able to read code found elsewhere. You will have some difficulty anyway, but I hope to keep it to a minimum.

The following table lists the data types used in this book plus inti 6.

DATA TYPES

How I CCSThink Name Bits Range

character char 8 N/Abit inti 1 0, 1byte int8 8 0 - 25516-bit binary intl6 16 0 - 65535integer int 8 0 - 255

The integer data type may be assigned as follows

int a; //declares variable a as in integer typea = 2; //assigns initial value 2 to variable aint a = 2; //combined declaration, declares variable a and assigns

// and initial value 2

The = sign is the assignment operator.

Assignment examples arc sprinkled through the code examples that follow'.

typedef is a declarator which can be used to create a new type name that can be used in declarations.

[type-qualifier] [type-specifier] [declarator]

typedef inti bit; //use bit instead of inti

An example appears in the Writing Programs chapter in the Flags section.

Page 52: c What Happens

ASCII CHARACTERS

The transmission of text requires that the text characters be encoded using a combination of binary bits. The most widely accepted text code is ASCII, the American Standard Code for Information Interchange, supported by the American National Standards Institute (ANSI).ASCII includes 26 upper case letters, 26 lower ease letters, the numerals 0 through 9, plus some punctuation characters and special characters. The following table includes the most commonly used characters.

The table shows how each character may be represented by a hex byte as is sometimes necessary in microcontroller applications (driving an alphanumeric LCD for example).

UPPER NIBBLEJIBBLE 0x2 3 4 5 6 7

0x0 0 P P1 i 1 A Q a q2 " 2 B R b r3 # 3 C S c s4 $ 4 D T d t5 % 5 E U e u6 Sc 6 F V f V7 ' 7 G W g w8 ( 8 H X h x9 ) 9 I Y i yA * J Z j zB + / K kC , < L 1D - = M mE > N /\ nF / O o

Example: 0x41 = A

Note: 0x20 = Blank

44

Page 53: c What Happens

NAMING CONSTANTS AND VARIABLES

Names for constants and variables are also referred to as identifiers (id).

Rules:

1) All names used in a program must be unique.2) Names must begin with a letter of the alphabet or an underscore.3) After the first letter, names can be made up of letters, numbers, and underscores in any

combination. An occasional capital letter may be used for clarity.4) Names may be of any length up to a maximum of 32 characters.5) ANSI (standard) C is ease sensitive, but CCS C is not.6) The following key words are reserved for special uses in C. Do not use

them for names.

auto enum signedbreak extern sizeofcase float staticchar for structconst goto switchcontinue if typedefdefault int uniondo long unsigneddoubl register voidelse return volatileentry short while

7) In addition, the CCS compiler has the following non-standard reserved key words:

addressmod inti int32_fixed int8 int48float32 intl6 int64float48 float64

45

Page 54: c What Happens

A nice thing about C is that port pins may be named for their functions while writing programs which makes referring to them easy.

The #define pre-processor directive may be used for defining constants.

#define GREEN LED PIN_B0

When it is time to write a line of code to toggle the green LED, the "green one" is easier to remember than the fact that the green LED is wired to port B, pin 0.

output toggle(GREEN_LED);

Another example is:

#define TD PIN_A1

RS-232 data is transmitted via pin I’D rather than port A, pin 1.

46

Page 55: c What Happens

OPERATORS - SHORT LIST

An operator is a symbol that instructs the C compiler to perform a specific operation on one or more operands. An operand is an expression that the operand acts on. This will become clear through usage.

We will need only a few operators to get started writing C programs. They are:

Assignment operator

equal signvariable = expression;

Assign the value of expression to variable = not normally used in math, but it is legal to do so

Relational operators

== Equal to< Less than<= Less than or equal to1= Not equal to

Used for comparison

Increment operator Array operator

Increment [ ] Used to index an element in an array

Used to increment a counter

Mathematical operator Structure operator

+ AdditionSubtraction

* Multiplication/ Division

Dot operator or structure member operator

Logical operators Grouping operator

ANDORNOT

() Grouping

47

Page 56: c What Happens

TRUE vs. FALSE

A C expression is anything that evaluates to a numeric value. Expressions using relational oper­ators evaluate to a value of either TRUE (1) or FALSE (0). Relational expressions are often used within if and while statements.

if (a<b) //compare a to b

If the expression evaluates as TRUE, the statement(s) following this line of code are executed.If the expression evaluates as FALSE, the statement(s) following this line of code are not executed.

As another example:

while (input(PIN_A2)==1); //wait for switch to close

If the expression evaluates as TRUE, execution loops back. If the expression evaluates as FALSE, the while statement is terminated and execution passes to the statement(s) following the w'hile (condition).

DEVICE FILES

The CCS compiler contains a device file for each of the devices supported by the specific com­piler you are using. The device file for the PIC16F818 (file 16F818.h) may be found in the com­piler files (\PICC\Devices\16F818.h). I suggest printing a copy for reference.

In this book, we will be mainly interested in the configuration fuses, port pin definitions, inter­rupt functions, timer 0 functions, and A/D functions.

48

Page 57: c What Happens

PRE-PROCESSOR DIRECTIVES - SHORT LIST

C programs are processed by the compiler in two distinct steps. The first pass is a pre-processor step and the compiler will process lines of code that begin with #. The pre-processor lines may affect compiler settings or may control how the following text is interpreted. #defines will cause textural replacements. #include will be replaced with the contents of an entire file. Be aware that the preprocessor variables (identifiers) are not the same as normal C variables. When the preprocessor is done, there will be no pre-proccssor directives or identifiers left for the normal processor.

All pre-processor directives begin with # and are used to convey information to the compiler. Following are the pre-processor directives used in this book:

#define

#defme id textid is the name you wish to define.In use, text replaces id everywhere it appears as the program is compiled.

#define id constant#defme pre-processor directive may be used for defining constants. id is the name of the constant you wish to define.

#define MAXSAMPLES 32

#include

#inelude <filename> or #includc "filename"A devicc file (.h file) is always included in a PIC microcontroller program.

Device files are in the CCS compiler CD in the Devices directory.Print one for PIC 16F818 for reference.You may create include files of your own.

#use delay (clock^s/w*/)

#use delay (c\ock=speed)speed is a constant 1-100000000 (1 hz to 100 mhz)

See CCS Reference Manual for details.

#use delay (type=speed)

#use delay (type=speed)type defines clock typespeed is a constant 1 -100000000 (I hz to 100 mhz)

See CCS Reference Manual for details.

49

Page 58: c What Happens

#fiises

#fuses optionsoptions vary depending on the device and are listed in the device .h file for the

device of interest.See Configuration Register(s) Fuses chapter.

#bit id = x.yid is a valid C identifier x is a C variable or constant y is a constant 0-7

The I-bit variable is placed in memory at byte x, bit y. This is useful for referring to bits in special function registers.

#bit INTEDG = 0x81.6 //interrupt edge select

intedg = 1 //interrupt on rising edge on RBO/INT pin

#byte

#byte id = xid is a valid C identifier x is a C variable or constant

#byte portb = 0x06 //port B address

#use rs232

#use rs232 (options)See CCS manual for a long list of options. options are separated by commas.We will use baud rate and transmit pin designation.

#use rs232 (baud = 4800, xmit = PIN_A1)

The use of pre-processor directives will be explained in the Writing Programs chapter as they are needed.

50

Page 59: c What Happens

INs AND OUTs OF DIGITAL I/O

PORT DATA DIRECTION

Port data direction (input vs. output) is controlled using a special register called the tristate or TRIS register.

A TRIS register has 8 bits. There is a TRIS register for each port.

TRIS Register

Bit 7 6 5 4 3 2 1 0

Loading a "0" in a TRIS register bit makes the corresponding port bit an output. Conversely, a ”1" results in an input.

For most applications, built-in functions are used for input and output and TRIS register opera­tions arc taken care of automatically by the CCS C compiler.

PORT READ/WRITE

Digital I/O operations using a PIC microcontroller and the CCS C compiler may be carried out in two ways based on:

• Using built-in functions provided in the CCS compiler for the purpose of accessingthe ports.

• Working with variables stored in the port registers located in the PIC microcontroller'sdata memory. The variable controls the logic level of each port pin. In order to do this, we must tell the compiler where the port registers are located in data memory (port addresses), as this information is not included in the compiler in the form required to work this way.

A person w'ith a hardware background will gravitate to the first method wrhile a person with a computer science background will gravitate toward the second method. Depending on your mind set and the application, you may find one of the methods easier/better. You can make up your own mind after being exposed to both philosophies.

This will serve as a reminder that there is more than one wray to write a C program.

Page 60: c What Happens

Built-In I/O Functions Method

The built-in functions of interest are:

INPUT_x()OUTPUTxQ

value = input_x() x is port ID Inputs byte from port output_x (value) x is port ID Outputs byte to port

OUTPUT_BIT() OUTPUT HIGH() OUTPUT LOW()

INPUT() value = input (pin) output bit (pin, value)

Reads pinOutputs 0 or 1 to pin Outputs 1 to pin Outputs 0 to pin Outputs 0 or 1 to pinOUTPUT_TOGGLE()

output high (pin) output low (pin) output toggle (pin)

x - is port designation letter - a, b, etc.pin - comes from device .h file - PINB1 for example

When using the built-in functions method, the port addresses are understood by the compiler and do not have to be defined by the user.

Port Register Accessed As A Variable (Port Address In Memory) Method

Using this method is based on treating the port registers themselves as variables. This makes it possible to do things like:

portb = porta; //make contents of port B = contents of port A

The poit addresses may be defined for use in a program in several different ways as shown in the examples in the chapter on Writing Programs.

Using a structure to control a port is described in the chapter on Structures.

When a port register is accessed as a variable, data direction must be specified in the program by writing to the port's TRIS register. The built-in function used for this purpose is:

SET TRIS x() set tris_x (value) x is port ID I/O port direction

Don't try to understand all of this now. The subject will become clearer as you wrork through the programming examples in the chapter on Writing Programs.

52

Page 61: c What Happens

CONFIGURATION REGISTER(S) FUSES

The configuration bits are located in flash memory outside the main part of flash memory used fo program storage. There are 14 or more configuration bits located in one or more configuration registers. The device programmer accesses these bits during the device programming procedure.

The configuration options for each device (PIC16F818, etc.) are listed in pneumonic form in the devicc file for that devicc. You won't be concerned with which bit does what as the C compiler will take care of that for you.

Fuses?? What fuses? In the olden days (not so long ago), memory chips wrcre made using fuse- able links which were melted away (blasted) using a programmable read only memory (PROM) blaster. This terminology was used for the early PIC microcontrollers. For some reason, it is still used in the name for the directive which tells the C compiler what to do with the configuration bit (now in a flash memory register).

The #fuses pre-processor directive is used to determine what is written to the configuration regis­ters) during device programming.

#fuses options

options vary depending on the device and arc listed in the device .h file for the device of interest.

Later, when you are writing a program and you have a C project open (the device will have been selected), you may easily view the configuration options for the selected device by using:

View>Valid Fuses

Pre-proccssor directives are discussed in a chapter on the subject.

The kinds of things determined when making configuration selections arc:

Clock oscillator type selectionWatchdog timer enabled/disabledPowcr-up timer enabled/disabledRA5/MCLR/Vpp pin function selectionBrown-out reset enabled/disabledLowr voltage programming cnabledMisabledData code protection enabied/disabledFlash program memory write protection on/offIn-circuit debugging enabled/disabledCCP module pin selectionFlash program memory codc protection on;off

RC RCDisabled NOWDTEnabled PUTMCLR MCLREnabled BROWNOUTDisabled NOLVPDisabled NOCPDOff NOWRTDisabled NODEBUGCCP 2 CCP2Off NOPROTECT

53

Page 62: c What Happens

The CCS C compiler has default fuse selections. The defaults for the PIC16F818 are listed in the second and third columns in the list above.

As it turns out, the default selections are what I would use, with the exception of the clock oscil­lator. Using the internal clock oscillator is most convenient. We will use the default 31.25 KHz internal clock oscillator frequency for all examples except those involving a time delay or the use of timer 0. In those instances, we will use 4MHz as the internal clock oscillator frequency. The clock oscillator frequency is divided by 4 inside the microcontroller resulting in an instruction clock frequency of 1 MHz when the clock oscillator frequency is 4 MHz. This makes the math easy for calculating timer 0 intervals.

In this book, clock oscillator type and frequency will be selected in one of three ways:

1) For programs with no time delay or use of timer 0:

#fuses intrc io

The pre-processor directive selects the internal clock oscillator option.By default, the frequency will be 31.25 KHz.

2) For programs using a time delay, we will use the internal clock oscillator operating at 4 MHz.

#use_delay(internal=4mhz)

The pre-processor directive selects the internal clock oscillator operating at 4 MHz.In addition to the delay, the compiler gives you:

#fuses intr_io setuposcillator(0SC4MHZ);

These two lines do not need to appear in your code.

The pre-processor directive syntax is:

#use delay(type=speed) See the CCS compiler manual for details.

type is the clock oscillator type. speed is a constant.

The compiler takes care of the clock oscillator fuses for us.

3) For programs using timer 0, we will use:

#fuses intrc_io

and

setup_osciliator(0SC_4MHz);

This will be illustrated by the programming examples that follow.

54

Page 63: c What Happens

FUNCTIONS

A function is a routine that performs a task. A function may come with the C compiler you are using or you may write one yourself.

All C programs contain a "main" function which embodies the program that you create,

main(){

program

main() FUNCTION

The parentheses that follow a function name indicate that it is a function. Parameters are some­times passed to functions as arguments contained within the parenthesis. In this example, there are none.

Braces { } indicate what is included in the function, which is the whole program in this case.

void main (void) is commonly used in place of main() to begin C programs.

function

void main (void)

VN------- main has no

arguments

main does not return data

I choose not to use this method because in microcontroller applications, arguments are not required for the main program and no data is returned. The CCS compiler generates a warning when I do this. I choose to ignore it because using main() makes the code simpler and more readable. Simple is my goal.

55

Page 64: c What Happens

FUNCTIONS

Functions are the basis of programming microcontrollers in C. The CCS C compiler comes with lots of functions designed to do specific tasks such as writing to a port, creating a time delay, set­ting up an A/D converter, etc. The fact that these built-in functions are provided will save a lot of work and your projects will be completed sooner.

Parameters may be passed to functions as arguments.

funct ion ( ________ , ________ , ________ )

Calling a function may or may not return a variable.

You may write a function to perform a specific task, test it, and store it away so it can be retrieved and used the next time you are writing code which includes that task.

56

Page 65: c What Happens

BUILT-IN FUNCTIONS - SHORT LIST

The CCS C Compiler Reference Manual contains descriptions of the built-in functions contained in their compilers. Following is an abbreviated list of some functions we will need to get started writing programs.

Some built-in functions require a special/specific include file(s) to work. Refer to the descrip­tion of the built-in functions you are interested in using in your program. If a pre-processor directive file is needed, its name will appear under the "Requires:" heading in the built-in func­tion description in the manual.

FUNCTION SYNTAX PRE_PROCESSOR DIRECTIVE (if required)

BIT_CLEAR() BIT_SET() BIT_TEST DELAY_CYCLES() DELAY_MS() DELAY US()

bit_clear(var,bit) bitset(var,bit) value=bit test(var,bit) delay cycles(count) delay_ms(time) delay_us(time)

#use delay #use delay

//clock frequency //clock frequency

For ports where x is port identification letter (ie. "a", "b", etc.):

SET_TRIS x() INPUT_x()OUTPUT x(), etc.

set_tris_x(value) value = input_x() output_x(value)

For individual port pins:

INPUT() OUTPUT_LOW() OUTPUT_HIGH() OUTPUTBIT() OUTPUT TOGGLE(

value = input(pin) outputlow(pin) output high(pin) output_bit(pin, value) output_toggle(pin)

For interrupts:

CLEAR_INTERRUPT() clearinterrupt(level)DISABLE_INTERRUPTS() disable_interrupts(level) #int xxxx ENABLE_INTERRUPTS() enable_interrupts(level) #int_xxxx EXT_INT EDGE() ext_int_edge(source, edge)

edge H_TO_L high to low transition at INT pin

edge L_TO H low to high transition at INT pin

For RS-232:

PUTC() PRINTF()

putc(cdata) printf(string)

#use_rs232 and #use delay #use_rs232 and #use_delay

57

Page 66: c What Happens

For controlling timer 0:

SETUP TIMER 0() setuptimer_0(mode)SETTIMERO() set_timerO(value)GET_TIMER0() value=get_timer0()

For controlling the A/D conversion process:

SETUP_ADC(mode) setup adc(mode) SETUP_ADC_PORTS() setup_adc_ports (value) SET_ADC_CHANNEL() setup adc channel(chan)READADC() value=read_adc([mode])

STATEMENTS

EXECUTABLE STATEMENTS

An executable statement is a line of code that does something. Executable statements always end with a semicolon which indicates that the statement is executable. means "execute".

Blocks

A block is a group of one or more executable statements enclosed by braces.

{statementl;statement2; //block of statements

statementn;}

CONDITIONAL STATEMENTS

Conditional statements control program flow (see next chapter),

ifwhiledoforswitch

58

Page 67: c What Happens

SEMICOLON USE RULES

Again, executable statements always end with a semicolon wrhich indicates that the statement is executable. means "execute".

Conditional statements are not followed by a semicolon.

ifdoforswitchwhile has exceptions and has its own rules - see below,

while

do/while loop

Semicolon required after w'hile (condition),

while loop

No semicolon after while (condition).Exception: a semicolon is required after while(TRUE) used in a

while loop acting as a halt at the end of a program (executed).

Remember that there is no semicolon after a function name such as main() or del().

The programming examples in the chapter on Writing Programs will serve to illustrate.

59

Page 68: c What Happens

PROGRAM DESIGN

This chapter will serve as an outline or overview of program design possibilities. Chunks of this information will be reprinted as appropriate in the following chapter on Writing Programs with accompanying programming examples. Showing the possibilities in one place will serve now as an overview and later as a reference.

You may want to skim through this chapter the first time and then reread it a time or two as you go through the next chapter (Writing Programs).

60

Page 69: c What Happens

PROGRAM DESIGN - CONTROL FLOW

i f

or

if (condition) statement;

if (condition){

statementl; statement2;

//single statement

//block of statements

statementn;

If the condition is true, statement(s) is executed.If the condition is false, statement(s) is not executed.In either case, execution continues with the line of code

following statement(s).

Note that if(condition) and statement(s) are all part of the if statement.

The relational operators may be used as the basis of conditions in if and if/else conditional state­ments to direct program flow.

Relational Operators Comparisons

== Equal to> Greater than< Less than>= Greater than or equal to <= Less than or equal to!= Not equal to

61

Page 70: c What Happens

The logical operators && (and), || (or), and ! (not) may also be used as the basis of conditions in if and if/else conditional statements to direct program flow. The logical operators evaluate to TRUE or FALSE.

Logical Operators

&& andI I or ! not

if/else

An if conditional statement may, as an option, contain an else clause.

if (condition) statement(s)1;

else statement statement(s)2;

//single statement or block

//single statement or block

If the condition is true, statement(s)1 is executed and statement(s)2 is not executed.

If the condition is false, statement(si) is not executed and statement(s)2 is executed.

In either case, execution continues with the line of code following statement(s)2.

if/else statements may be cascaded to create a hierarchy for decision making.

if (conditionl) statement(s)1;

else if (condition2) statement(s)2;

else if (condition3) statement(s)3;

elsestatement(s) 4;

//single statement or block

//single statement or block

//single statement or block

//single statement or block

For this sequence of if/else statements, only one will be executed and all that follow will be skipped over. The first if/else statement in the sequence that evaluates as TRUE will be executed. If conditions 1, 2 and 3 all evaluate as FALSE, statement 4 will be executed.

62

Page 71: c What Happens

63

Page 72: c What Happens

while loop

while (condition)statement(s); //single statement or block

Repeat code until condition becomes false.

1) Condition is evaluated first.2) If condition is true, statement(s) is executed and execution

loops back.3) If condition is false, while statement is terminated and

execution passes to the first statement following statement(s).

4) The statement(s) may not be executed at all if the conditionremains true.

We will use the while loop for simple situations where a simple condition such as a switch being open or closed governs program flow.

while (TRUE); while (1) both work for circle = always

1 is true0 is falsewhile (1==1) also works

64

Page 73: c What Happens

do/while loop

dostatement(s); //single statement or block

while (condition);

1) The statement(s) is executed first.2) Condition is evaluated.3) If condition is true, execution loops back and the

statement(s) is executed again.4) If condition is false, while statement is terminated and

execution passes to the first statement following while (condition).

5) The statement(s) in braces will be executed at least once.

while (TRUE); may be used

65

Page 74: c What Happens

for loop

for (start expression; test expression; count expression) statement(s); //single statement or block

start expression i = 0test expression i <= ncount expression i ++ //increment i

66

Page 75: c What Happens

switch/case

When a variable can be one of several values, switch/case may be used instead of several if/else statements. As an option, a default may be used which takes care of any cases not otherwise included. If a default is not used and there is no match between expression and the cases given, execution passes to the first statement following the switch statement's closing brace.

switch (expression){

case 0: statement(s);break;

case 1: statement(s);break;

default: statement(s);break;

}

Expression must evaluate to an int8, int16, or a char.

break statement - causes control to skip past the end of a loop or switch statement

While (){

--- break;

Page 76: c What Happens

continue statement - causes control go back to the beginning of a loop

--*- While ( ){

--- continue;

}

return statement - used to return from a function call (see modular programming section which follows)

goto

goto label;

label: statement;

Execution branches to location identified by label within the same function.

if (conditionl) goto labell;

elsestatement;

labell: statement;

If you want the label to appear on a line by itself, you can place a semicolon after the label. A semicolon by itself is a null statement (does nothing).

label: ;statement;

C purists don't like the use of goto statements and go all out to avoid using them.

RULE

Variables must be declared at the beginning of a block where a block is an open { to a close }. This is a syntax rule.

Variables can be initialized at any time.

Failure to do this will result in a lot of error messages being generated!

68

Page 77: c What Happens

MODULAR PROGRAMMING

C is designed to encourage (force) modular or structured programming. Programs are construct­ed using modules called functions. Each function performs a specific task. In assembly language, the modules are called subroutines.

The main function calls functions to perform tasks. Further, any function can call any other function.

The functions that you crcate must appear ahead of main () in your source code as they must be defined prior to use.

functl(){

return;}

funct2() {

return;}

funct3() {

//return to calling function, main

//return to calling function, main

return;}

main(){

functl(); funct2(); funct3();

}

//return to calling function, main

//calls function 1 //calls function 2 //calls function 3

The return statement sends execution back to the calling function.

For other than very simple programs, the main function is used solely to call each of the func­tions designed to perform the tasks that the program is required to accomplish.

69

Page 78: c What Happens

WRITING PROGRAMS

PROGRAMMING CONCEPTS

The PIC16F818 microcontroller will respond to a series of coded instructions stored in program memory.

When a designer (which may be you) thinks of something which he or she would like to control, the natural thing to do is to think through the control process in logical steps. Creating a How chart is a good way to visualize these steps. The control process might consist of sensing out­side world events such as light vs. darkness, cows passing through a gate, temperature, a key stroke etc., testing the data w'hich has come from the sensors followed by taking one of two pos­sible program paths based on the test results, and controlling some outside device such as a digi­tal display, indicator light, motor, heater, etc.

Instructions for repetitive operations can be repeated in long strings, but that wastes valuable memory space. It may even result in a program which is too long to fit in the available memory space. Instead, one sequence of instructions can be used over and over in a loop and the micro­controller goes 'round and 'round until something forces it to stop.

• Loops can go around forever or until the plug is pulled or an anvil is dropped on themicrocontroller chip.

• Or until a counter counts up to a predetermined number.

• Or until a test result says to move on.

Note: The first line of some example programs in this chapter contains "ala pict3 . asm" or similar. This indicates that the C program performs the same function as an assembly language program "pict3. asm" in our books Easvj PIC'n and Easy Microcontrol'n.

70

Page 79: c What Happens

An example of the use of a loop is a program used to read an input port and display the data received (or the status of the lines) at an output port.

Loops arc useful when it is necessary to perform an operation a certain number of times (n).

71

Page 80: c What Happens

The use of a loop prevents having to write the code n times and the requirement for memory space to store it.

Another technique for keeping C programs short and manageable is the use of functions. If the same task is to be performed in two or more places in a program, the function code can be wTit- ten once and stored in one location. When the points in the program arc rcachcd where the func­tion is to be used, it is called. The last statement in a function of this type is always a return statement. The program then continues w'here it left off.

An important concept to keep in mind when using microcontrollers is that they can only do one thing at a time, one very simple thing. They execute many very simple instructions and they do it blindingly fast. The P1C16F818 executes roughly a million instructions every second with a 4MHz clock oscillator.

In situations where events of interest to the microcontroller occur only once in a while, perhaps randomly, it may be desirable to use a microcontroller feanire called the interrupt. In simplified form, if an event occurs in the outside world which demands the microcontroller's attention, the sensor monitoring the event can be wired to direct a signal (pulse) to an interrupt line (pin) on the microcontroller. Wrhen the signal arrives, the microcontroller drops what it was doing (after finishing the instruction it was executing), and then jumps to a special program called an inter­rupt service routine. The purpose of the routine is to do whatever the designer/programmer thinks is required when the outside event occurs.

Microcontrollers do only one thing at a lime, but they can be instructed to drop one task, take care of another, and resume the original task.

Interrupts arc a special area closely tied to the hardware used to make their occurrence known, so they will be discussed in detail in the Interrupts chapter of this book.

PROGRAMMING EXAMPLES

The best way to learn how microcontrollers arc used is to think of applications, write programs to implement them, and C what happens. In this chapter, we will start out with very simple pro­grams to demonstrate the concepts we have just discussed. By the time you have finished this chapter, you will be able to think in microcontroller terms and will see microcontroller applica­tions in your work or hobbies. You will also be able to visualize the methods for implementing microcontroller solutions.

Simple programs used as examples will illustrate the use of the various types of functions and some executable statements.

72

Page 81: c What Happens

The first example program (introduced earlier) turned on 4 of 8 LEDs assumed to be connected to port B and turned the remaining 4 off. It did this by:

• Setting up the clock oscillator.• Writing a byte to the port B register. The hexadecimal byte OxOF is equivalent to

binary 00001111. A " 1" in a bit position of the port register causes that bit/pin to be logic high (+5 volts in this case) which results in current flowing through the LED connected to it. A "0" in a bit position of the port register causes that bit/pin to be logic low (0 volts) which results in zero current through the LED connected to it.

In the following version of the same program, we will use the default clock oscillator frequency (31.25 KHz).

A flow chart will help you visualize what this simple program does.

////first C program ala pictl.asm Csla#include <16F818.h>#fuses INTRCIO //remaining fuses by default

//internal clock osc with I/O on RA7, RA6//watchdog timer disabled power-up timer enabled//code protection off brown-out reset enabled//mclr enabled low-voltage programming disabled//no EE data protection write protection off//no debug CCP1 function pin RB2 (arbitrary)

//clock oscillator frequency is 31.25 KHz by default main( ){

output b (OxOf); //bit pattern to port Bwhile (TRUE); //circle, always

}

Let us examine the various parts of the program.

The comment on the first line provides information about the program.

The #include pre-proccssor directive serves to include the PIC16F818 device header file in the program.

The #fuses pre-processor directive selects the internal clock oscillator option. The default inter­nal clock oscillator frequency is 31.25 KHz.

The main() function is present/required in all C programs.

73

Page 82: c What Happens

Between the two curly braces are two program statements which will be executed. Notice that each statement is followed by a indicating to the compiler that the statement is to be executed.

The literal OxOf is defined as part of the outputb (OxOf) statement. This is the byte written to port B. Hex OxOf is equivalent to binary 00001 111. Bit 0 is the " 1" on the right. Bit 7 is the "0" on the left. The port has 8 lines or pins numbered 0 (least significant bit) through 7 (most significant bit). The port and its register are 8 bits wide.

There is no halt function in C, so we will have to fake one. while (true); is a conditional statement. The while loop repeats until the condition becomes false which will never happen here as the condition is defined as TRUE. The result is an endless or infinite loop. The loop goes 'round and 'round forever, or until power to the PIC microcontroller is turned off. If the while (true); statement were not there, the compiler would create an instruction to put the microcontroller to sleep at the end of main(). A ';' appears at the end because while (true) ; is executed.

The next program is the same as the previous one with the exception that the literal representing the bit pattern sent to port B is defined as a binary number.

////first C program ala pictl.asm Cslb#include <16F818.h>#fuses INTRCIO main(){

outputb (ObOOOO1111) ; while (TRUE);

}

//bit pattern to port B //circle, always

74

Page 83: c What Happens

SIMPLE DATA TRANSFERS

Input ports are read using data transfers. The next example reads the data from port A and writes that data to port B.

DIP switches arc assumed to be located at port A, bits 3, 2, 1, 0 (least significant 4 bits) and LEDs are assumed to be located at port B. Check to be sure that the jumper which connects pin RAO to the pullup resistor and DIP switch is in place . Port A, bits 7,6,5,4 are ignored for this experiment.

We will declare a byte variable "pattern". This must be done at the beginning of the block. A block is an open {to a close }. Failure to do this will generate compiler error messages.

The input_a () built-in function requires a name for the variable obtained by reading port A.

The program stores the data from port A (bit pattern) in the variable "pattern". Notice that this'fs done by making pattern = port A. Then the data in the variable pattern is output on port B.After that, a continuous loop is executed.

////data transfer demo ala pict2.asm Cs2a#include <16F818.h>#fuses INTRC_IO main(){

byte pattern; //declare variable pattern (a byte)pattern = input_a(); //read port A bit patternoutput_b (pattern); //bit pattern to port Bwhile (TRUE); //circle, always

}

Set the switches at the input port and run the program. Compare the resulting bit pattern at the display (least significant 4 bits) with the pattern you set in. Change the input pattern and rerun the program by resetting the microcontroller.

75

Page 84: c What Happens

LOOP - ENDLESS

In the first two examples, we used an endless loop while(true) to fake a halt (the PIC16F818 docs not have a halt instruction). The PIC16F818 sits in an endless loop (circle).

while (TRUE); while (1); both work for circle = always

^--- 1 is true

0 is false

Next we will use a while loop in a different way. The program will read the input port A and continuously display the lowest 4 bits of port A at the lowest 4 bits of port B. Check to be sure that the jumper which connects pin RAO to the pullup resistor and DIP switch is in place .Port A, bits 7,6,5,4 are ignored for this experiment. This time, the statements inside the braces following while ( true ) repeat over and over in a loop.

The program does the same thing as the previous one cxccpt it uses a while loop so that the sta­tus of port A may be displayed via the LEDs on port B on a continuous basis. If a switch posi­tion on one of the 4 low order bits changes, the result will be indicated on port B.

Next we will use a while loop in a different way. The program will read the input port A and continuously display the lowest 4 bits of port A at the lowest 4 bits of port B. Check to be sure

Port A, bits 7,6,5,4 are ignored for this experiment. This time, the statements inside the braces following while (

Page 85: c What Happens

while loop

while (condition)statement(s); //single statement or block

Repeat code until condition becomes false.

1) Condition is evaluated first.2) If condition is true, statement(s) is executed and execution

loops back.3) If condition is false, while statement is terminated and

execution passes to the first statement following statement(s).

4) The statement(s) may not be executed at all if the conditionremains true.

////data transfer demo - while loop #include <16F818.h>#fuses INTRC_IO main( ){

byte pattern; while (TRUE)

{pattern = input_a(); output_b (pattern);}

}

ala pict2.asm Cs2b

//declare variable pattern (a byte)

//read port A bit pattern //bit pattern to port B

Vary the switch settings while the program is running.

This loop will run forever unless you reset the microcontroller or pull the plug (sometimes called "absolute reset"). * *

We will use the while loop for simple situations where a simple condition such as a switch being open or closed governs program flow.

Page 86: c What Happens

Next, we will use a do/while loop which will produce the same results.

do/while loop

dostatement(s); //single statement or block

while (condition)

1) The statement(s) is executed first.2) Condition is evaluated.3) If condition is true, execution loops back and the

statement(s) is executed again.4) If condition is false, while statement is terminated and

execution passes to the first statement following while (condition).

5) The statement(s) in braces will be executed .at least once.

while (TRUE); may be used

////data transfer demo - do/while loop ala pict2.asm Cs2c^include <16F818.h>#fuses INTRC 10 main(){

byte pattern; //declare variable pattern (a byte)do

{pattern = input_a(); //read port A bit patternoutput_b (pattern); //bit pattern to port B

}while (TRUE);

}

J

78

Page 87: c What Happens

Up to this point, built-in compiler functions have been used to access the ports. Sometimes it is advantageous to access the port registers as variables.

portb = porta A

This requires defining the byte variable "porta" address ahead of use in the program. Port addresses are defined using #byte directives ahead of main(). Port A is in data memory and is said to be "memory mapped". The same is true for port B. The ports are Special Function Registers (SFR). Ports may be read or written to like any other data memory (RAM) location. This requires telling the compiler where they are located in data memory.

In the following example, the port memory locations are made known to the compiler using using #byte pre-processor directives ahead of main(). Port addresses may be obtained by using Microchip's device data books or View>Special Registers in the CCS compiler.

Accessing the port registers as variables necessitates specifying the direction (input vs. output) '' of the port pins. This is done using the set_tris_x() built-in function. Recall that a "0" in a bit position in a TRIS register makes the corresponding port pin an output. A " 1" makes the pin an input.

The next example does the same thing as the previous one by accessing the port registers.

////data transfer demo - out = in ala pict2.asm#include <16F818.h>#fuses INTRC_IO #byte porta = 0x05 #byte portb = 0x06 main(){

set_tris_a (Oxff); set_tris_b (0x00); do

{portb = porta;

Cs2d

}while (TRUE);

//port A address //port B address

//port A inputs //port B outputs

//read port A bit pattern, display at // port B

79

Page 88: c What Happens

Port addresses may also be defined using an include file which contains the #byte directives. You can create an include file yourself (named ports.c in this example). The include file looks like this:

////port address definitions ports.c Cs2e#byte porta = 0x05 //port A address#byte portb = 0x06 //port B address

The include file is used in the following program:

////data transfer demo - out = in ala pict2.asm#include <16F818.h> #fuses INTRC_IO#include <ports.c> //port addresses, A & B, 16F818main(){ . , v.

set_tris_a (Oxff); //port A inputsset_tris_b (0x00); //port B outputsdo .

{portb = porta; //read port A bit pattern, display at

// port B}

while (TRUE); //circle, always}

Page 89: c What Happens

Another method is to use a feature of the CCS compiler. View>Special Registers accesses the Device Table Editor.

• Select device.\

• Make include file by clicking on the Make Include File icon.

Of course you will need to use the definitions found in the file.

Another way to define the port addresses is to use the get environment getenvQ built-in function. The getenv() function is used here as part of an assembler directive which appears ahead of main(). "SFR:porta" in the example refers to special function register port A. Port A is one of the special function registers (Microchip terminology). The compiler knows where in the environment (memory map) port A lives by looking in the device header file (always included in the code), so the #byte directive as used here can find the address.

////data transfer demo - out = in #include <16F818.h>#fuses INTRC_IO#byte porta=GETENV("SFR:PORTA") #byte portb=GETENV("SFR:PORTB") main(){

//get port A address //get port B address

ala pict2.asm Cs2f

set_tris_b (0x00);set_tris_a (Oxff); //port A inputs

//port B outputsI

{portb = porta; //read port A bit pattern, display at

// port B}

while (TRUE); //circle, always}

81

Page 90: c What Happens

I iIt is often necessary to perform some operation a specified number of times. To do this, we will use a counter. Each time the operation is performed, a one is added to the counter. This is called incrementing the counter. When the number in the counter becomes equal to the number of times the operation is to be performed, the program can stop or go on to something else. A sim­ple example will illustrate this concept.

LOOP WITH COUNTER

for loop

for (start expression; test expression; count expression) statement(s); //single statement or block

start expression test expression count expression

i = 0 i <= n i ++ //increment i

For this example:

Circle

82

Page 91: c What Happens

Note that 0 counts, so the counter will be incremented 5 times and will contain 4 after it is incre­mented the 5th time.

////loop with counter demo #include <16F818.h>#fuses INTRC_IO main(){

byte i;output_b (0x00); for (i=0; i<=4; i++)

{output_b (i);>

while (TRUE);}

ala pict3.asm Cs3

//declare variable i //initialize port B//display count i 5 times, final count // is 4

//circle, always

Port B will indicate 0x04 in binary (00000100).

This program illustrates a very important concept, the power of microcontrollers to make deci- v sions. A comparison is made to see if the counter contents is less than or equal to a constant. If so, the program loop continues until the counter hits the number we have chosen. If not, the pro­gram ends in a continuous loop. The comparison determines the flow or path of the program. •

* 'Load the program and run it to C what happens. It will be finished in a fewr milliseconds. v Port B should contain 0x04. You may want to try other numbers in the counter. " <

Page 92: c What Happens

Another use for a loop is a loop-until situation.

LOOP UNTIL

while loop

while (condition)statement(s); //single statement or block

Repeat code until condition becomes false

Page 93: c What Happens

Following is a simple while loop:

while (input(PIN_A2)==1) ; //go low? wait for ready switch to close

+5VDC

On reset, the "ready" switch is open and pin A2 is pulled up to +5 volts via a pullup resistor (pin A2 = 1). The microcontroller sits in the loop until the "ready" switch is closed which connects pin A2 to ground (pin A2 = 0). At that point, the condition for while becomes FALSE (input (PIN A2) no longer equals 1) and execution proceeds to presenting a bit pattern to port B. which turns on the LED connected to pin B2.

//clear port B//go low? wait for ready switch to close //indicate ready switch closed //circle, always

Notice that the condition for the while loop (input ( pin_a2 ) ==i ); evaluates as TRUE when port A, pin 2 is high, logic 1. Another way to write this is simply:

while (input(PIN_A2)); //go low? wait for ready switch to close

The condition for the while loop is (input ( pin_a2 )); As before, this evaluates as TRUE when port A, pin 2 is high, logic I.

Writing" == l" makes the code more readily understandable. Both methods work. You will see both as you look at other people's code, so you need to be familiar with both methods.

////simple while loop demo #include <16F818.h>#fuses INTRC_IO main(){

output_b (0x00); while (input(PIN_A2)==1); output_b (ObOOOOO100); while (TRUE);

85

Page 94: c What Happens

COMPARISONS

Two numbers may be compared using the relational operators.

Relational Operators Comparisons

== Equal to> Greater than< Less than>= Greater th^n or equal to <= Less than or equal to!= Not equal to

A number can be compared with a literal value N to determine their relative magnitudes. The direction the program takes depends on the results of the comparison. The if/else statement is used to accomplish this.

if/else

An if conditional statement may, as an option, contain an else clause.

if (condition)statement(s)1; //single statement or block

else statementstatement(s)2; //single statement or block

If the condition is true, statement(s)1 is executed and statement(s)2 is not executed.

If the condition is false, statement(s1) is not executed and statement(s)2 is executed.

In either case, execution continues with the line of code following statement(s)2.

J

86

Page 95: c What Happens

Here is a simple program to demonstrate a method of testing the comparison procedure. LEDs are assumed to be connected to port "B" for use as an indicator.

////comparison demo #include <16F818.h>#fuses INTRC 10 main ( ){

byte less = 0x01; byte nope = 0x02;

int a = 2; int b = 4; output b (0x00); if (a<b)

{output_b (less); }

else{output_b (nope); }

while (TRUE);

ala pict5.asm Cs5

//used to indicate "less" result //used to indicate "nope, not less" // result//declare integer variable a //declare integer variable b //initialize port B //compare a to b and display result // via LEDs ’ <

//circle, always

"00000001" displayed via the LEDs indicates "less". Try changing a to 5 and C what happens. *

SWITCH/CASE

When a variable can be one of several values, switch/case may be used instead of several if/else 1 statements. As an option, a default may be used which takes carc of any cases not otherwise included. If a default is not used and there is no match between expression and the cases given, execution passes to the first statement following the switch statement’s closing brace.

switch (expression){

case 0: statement(s);break;

case 1: statement(s);break;

case 2: statement(s);break;

case 3: statement(s);break;

default: statement(s); break;

}

Expression must evaluate to an int8, int 16, or a char. , <

In this example, expression should evaluate to 0, 1,2, or 3. If not, the default statement(s) will be executed.

87

Page 96: c What Happens

A break statement causes control to skip past the end of the switch statement.

////switch/case demo #include <16F818.h> a#fuses INTRC_IO main(){

int num = 3; output_b (0x00); switch (num){

case 0:output_b (0x00); break;

case 1:output_b (0x01); break;

case 2:output_b (0x02); break;

case 3:output b (0x03); break;

case 4:output_b (0x04); break;

default:output_b (Obi1111111);

}while (TRUE);

}

Cs6

//declare integer = 3 for test //initialize port B

//display 0 via port B LEDs

//display 1 via port B LEDs

//display 2 via port B LEDs

//display 3 via port B LEDs

//display 4 via port B LEDs

//display Obllllllll via port B LEDs

//circle, always

Because I made num = 3, the result of execution will be case 3. Execution of case 3 results in binary 3 being displayed via the LEDs.

88

Page 97: c What Happens

FUNCTION CALLS AND TIME DELAYS

Sometimes it is necessary to wait a specified period of time for something to happen. This might be waiting for switch contacts to stop bouncing or holding an EPROM control line high for a specified time as part of the programming procedure. Time interv als can be measured using hardware (covered later) or by software using a built-in time delay function. The accuracy of the interval depends on the accuracy of the PIC16F818 system clock. For accurate delays, a crystal-controlled clock oscillator is recommended.

The next two examples will illustrate the use of time delays and function calls. The program counts in binary and displays the count at eight LEDs via port B. If the counting were done at full speed, the LEDs would appear to.be all on all of the time. Instead, a built-in time delay function will be used to provide a time delay between counts so that each count will be dis- * played for about 500 milliseconds.

Delays in the milliseconds range may be created easily by using the built-in delay__ms() function.

delayms(time)

Time is the desired interval in milliseconds. Time may be a variable 0 - 65535 (int 16) or a constant 0 - 65535. This delay is created by the compiler in software and does not use a hard­ware timer/counter such as timer 0. If an interrupt occurs during the time interval, the time required to service the interrupt will be added to the time interval.

Page 98: c What Happens

////binary counting demo //// uses time delay #include <16F818.h>#use delay(internal=4mhz) main()

ala pictl2.asm

//clock oscillator internal, frequency

Cs7a

byte count = 0; do

//declare counter, count in binary

output_b (count); delayjms(500); count ++;

//display binary count via LEDs //delay 500 milliseconds //increment counter

while (TRUE);}

For programs using a time delay, we will use the internal clock oscillator operating at 4 MHz.

#use_delay(internal=4mhz)

The pre-processor directive selects the internal clock oscillator operating at 4 MHz.In addition to the delay, the compiler gives you:

#fuses intr_io setup_oscillator(0SC_4MHZ);

These two lines do not need to appear in your code.

The pre-processor directive syntax is:

#use delay(type=speed) See the CCS compiler manual for details.

type is the clock oscillator type. speed is a constant.

The compiler takes care of the clock oscillator fuses for us.

Page 99: c What Happens

We can improve this program by creating a time delay function, placing that function ahead of main (), and calling it from within main(). This is in keeping with our goal of creating modular programs.

////binary counting demo ala pictl2.asm //// uses time delay function call #include <16F818.h>#use delay(internal=4mhz) del ( )

{delay_ms(500); return;

}main(){

byte count = 0; do

{output_b (count); del(); count ++;

}while (TRUE);

}

Cs7b

//clock oscillator internal, frequency //delay function

//delay 500 milliseconds

//declare counter, count in binary

//display binary count via LEDs //call delay function //increment counter

Note that this program loops until the microcontroller is reset.

The "count" register is an 8-bit counter which has 28 = 256 possible combinations of 0's and l's.

91

Page 100: c What Happens

BIT-LEVEL I/O USING BUILT-IN FUNCTIONS

The next example uses the output highQ and output_low() built-in functions in conjunction with the time delay function created in the previous example to toggle bit 0 of port B on and off.

The function dei() is called twice by main(). By wrriting the time delay as a function which can be called saves writing a delay statement twice in main().

////output_high(), output_low() demo I I I I uses time delay via function call #include <16F818.h>#use delay(internal=4mhz) del ( )

{delay_ms(500); return;

}main(){

output_b (0x00); do

{output__high (PIN B0 ) ; del();output_low(PIN_B0);del( ) ;

}while (TRUE);

}

Cs8a

//dock oscillator internal, frequency //delay function

//delay 500 milliseconds

//clear port B

//toggle port B, bit 0 //call delay function //toggle port B, bit 0 //call delay function

Page 101: c What Happens

Next, vve will use the output_toggle() built-in function to accomplish the same thing as in the previous example.

A

I I I /output_toggle() demo 1111 uses time delay via function call#include <16F818.h>#use delay(internal=4mhz) del ()

{delay_ms(500); return;

main(){

output_b (0x00); do

{output_toggle(PIN_B0); del();

}while (TRUE);

//clock oscillator internal, //delay function

//delay 500 milliseconds

//clear port B v

//toggle port B, bit 0 //call delay function

Cs8b

frequency

Page 102: c What Happens

In the comparison example presented earlier, the if/else statement was used. In the following example, if will be used by itself.

if

or

if (condition)' statement;

if (condition){

statementl; statement2;

//single statement

//block of statements

statementn;

If the condition is true, statement(s) is executed.If the condition is false, statement(s) is not executed.In either case, execution continues with the line of code

following statement(s).

Note that if(condition) and statement(s) are all part of the if statement.

The schematic for this experiment is:

+5VDC

J

94

Page 103: c What Happens

An if statement is used by itself to check the position of a switch as the code is executed. If the switch is open, port A, pin 2 will be high and the condition for the if statement will evaluate to "1" or TRUE. The statement following the if statement (really part of the if statement) will be executed and the LED at.port B, pin 2 will be on. If the switch is closed, the if statement will evaluate to "0" or FALSE. The statement following the if statement will not be executed and the LED at port B, pin 2 will be off.

////simple if demo #include <16F818.h>#fuses INTRC_IO main(){ '•

output_b (0x00); if (input(PIN_A2))

output b (ObOOOOO100 ) ; while (TRUE);

}

Cs9a

//clear port B //high?//indicate switch open //circle, always

1) Power-up with switch closed on port A, pin 2 - observe LED.2) Power-up with switch open on port A, pin 2 - observe LED.3) or change switch position and reset.

The logical operators && (and), || (or), and ! (not) may be used as the basis of conditions in if and if/else conditional statements to direct program flow. The logical operators evaluate to TRUE or FALSE.

Each of them will be used in the following three examples.

Page 104: c What Happens

The NOT logical operator negates the logic of the expression it operates on.

! expression will evaluate to FALSE if expression is TRUE.

////simple if demo /include <16F818.h>#fuses INTRC_IO main(){

output_b (0x00); if (1 input(PIN_A2))

output_b (ObOOOOOlOO); while (TRUE);

}

1) Power-up with switch open on port A, pin 2 - observe LED.2) Power-up with switch closed on port A, pin 2 - observe LED.3) or change switch position and reset.

The next two experiments require two switches.

//clear port B //low?//indicate switch closed //circle, always

+5VDC

Cs9b

/

96

Page 105: c What Happens

expression 1 && expression2 will evaluate to TRUE if both expression! and expression? are TRUE.

////simple && logical operator demo CslOa#include <16F818.h>#fuses INTRC_IO main(){

output_b (0x00); //clear port Bif (input(PINAl) && input(PIN_A2)) .//both high?< output_b (ObOOOOO100); //indicate both switches open

while (TRUE); //circle, always}

1) Power-up with switches closed on port A, pin 1 and port A, pin 2.2) Open switch on port A, pin 1 and reset. Observe LED.3) Open switch on port A, pin 2 and reset. Observe LED:

Using the AND logical operator results in the following:

Using the OR logical operator results in the following:

expression 1 || expression2 will evaluate to TRUE if expression 1 or expression2 is TRUE.

////simple || logical operator demo Csl0b*~#include <16F818.h>#fuses INTRC_IO main(){

output b (0x00); //clear port Bif (input(PINAl) || input(PIN_A2)) //high?

outputb (ObOOOOO100); //indicate one switch or other or both// open

while (TRUE); //circle, always

1) Power-up with switches closed on port A, pin 1 and port A, pin 2.2) Open switch on port A, pin 1 and reset. Observe LED.3) Open switch on port A, pin 2 and reset. Observe LED.4) Reset and try other possibilities.

Page 106: c What Happens

if/else statements may be cascaded to create a hierarchy for decision making.

if (conditionl) statement(s)1;

else if (condition2) statement(s)2;

else if (condition3) statement(s)3 ;

elsestatement(s)4 ;

//single statement or block

//single statement or block

//single statement or block

//single statement or block

For this sequence of if/else statements, only one will be executed and all that follow will be skipped over. The first if/else statement in the sequence that evaluates as TRUE will be execut­ed. If conditions 1, 2 and 3 all evaluate as FALSE, statement 4 will be executed.

To try this out, let's assume that we want to make one of three choices known to the microcon­troller by means of three switches. One, and only one switch may be open, indicating our choice. In response to a switch sensed as open (logic 1), a corresponding LED is turned on. Looking at the schematic will make this clearer.

+5VDC

/

98

Page 107: c What Happens

////simple if/else, else, else demo #include <16F818.h>#fuses INTRCIO main(){

output_b (0x00); if (input(PIN_A1))

output_b (ObOOOOOOlO) else if (input(PIN_A2))

output_b (ObOOOOOlOO) else if (input(PIN_A3))

output_b (ObOOOOlOOO) else

output_b (ObOOOlOOOO) while (TRUE);

Csll

//clear port B //high?//indicate port A, pin 1 switch open //high?//indicate port A, pin 2 switch open //high?//indicate port A, pin 3 switch open

//indicate statement 4 //circle, always

1) Power-up with switches closed on port A, pins 1, 2, 3 - observe LEDs.2) Change switch positions, reset - observe LEDs - test program operation.

Page 108: c What Happens

Next we will try a program which uses bit manipulation and port bit-level built-in I/O functions. The objective of the program is to look at an input port line and when it has a 1 on it, output a 1 on an output port line (if sense X, then turn on Y). Bit 3 of port A is arbitrarily chosen as the input line to be sensed and bit 2 of port B is arbitrarily chosen as the output line to be turned on. The input line switch is closed before the program is run. The output port line is cleared to 0 at the beginning of the program so the LED will be off when the program is initiated.

+5VDC

/

100

Page 109: c What Happens

////bit manipulation demo #include <16F818.h>#fuses INTRC_IO main(){

output_b (0x00); while (input(PIN_A3)==0); output_high(PIN_B2); while (TRUE);

}

ala pictlO.asm Cs 12

//clear port B, LEDs off //switch closed?//no, turn on LED //circle, always

whi^e (bit_test (porta, 3) ==0); is ah example of loop until something happens. When the switch is closed (initial condition), port A, pin 3 is connected to ground = logic 0. When the switch is opened, the pin is pulled up to +5 volts and the while condition changes causing pro­gram execution to move to the next statement. • , ..

1) Power-up with switch closed - observe port B, bit 2 LED.2) Open switch - observe port B, bit 2 LED.

The next program makes port B, pin 2 the same as port A, bit 3 using a while loop. Notice the use of the input() function and the use of the output bit() function.

value = input (pin)

output_bit (pin, value)

In the program:

{output_bit (PINB2, input(PINA3));}

The value of port A, pin 3 is output on port B, pin 2 in a while loop.

////output pin = input pin Csl3 #include <16F818.h>#fuses INTRC_I0 main(){

output_b (0x00); //clear port B, LEDs offwhile (TRUE)

{output bit (PIN_B2, input(PIN_A3));} //make port B, pin 2 same as// port A, pin 3

//output bit (pin, value)//value in this case is input(PIN_A3)

>

Page 110: c What Happens

The next example program illustrates an application for bit manipulation in event counting using port bit-level built-in functions. Events represented by a series of switch closures can be counted. The example program will count the number of times the bit 0 switch used in the pre­vious example is opened allowing the port A bit 0 input line to be pulled up to logic 1. The count is displayed in binary on LEDs connected to port B.

+5VDC

102

Page 111: c What Happens

Start with switch closed, bit = 0.

Loop 'til bit = 1 which means a transition from 0 to 1 has occurred (leading edge of pulse detected).

Loop 'til bit = 0 which means a transition from 1 to 0 has occurred (trailing edge of pulse detected).

The pulse is counted when the trailing edge has been detected.

Leading Edge Trailing Edge

////event counting demo #include <16F818.h>#fuses INTRC_IO main(){

byte count; output_b(0); count=0; do

ala pictll.asm Cs 14

{while (input(PIN_A0)==0); while (input(PIN_A0)==1); count ++;

output_b (count);

//declare counter, count in binary //clear port B //clear counter

//go hi?//go lo?//yes, pulse received,// increment counter//display counter contents via LEDs

}while (TRUE);

/

Depending on the switch used, contact bounce may be experienced which will result in multiple pulses being generated and counted for one switch open/close cycle. Switch contacts sometimes bounce when the switch is actuated. This phenomenon is commonplace and can be compensat­ed for in software (not explored here). If you are getting too many counts per switch open/close cycle, contact bounce is the culprit.

A solution to this problem is to use the positive-going output of the pulser circuit described in Appendix A connected directly to pin RAO. It will output one clean pulse per push on the switch lever.

Page 112: c What Happens

BIT MANIPULATION USING BIT MANIPULATION FUNCTIONS

Built-in bit manipulation functions can be used to Set, clear, or test an individual bit.

Bit Set/Clear

The bit set and bit clear functions operate on a selected bit in a selected register.

bit_set (var, bit) bit clear (var, bit)

As an example, bit 3 of a byte variable called reg can be set (made logic 1 or high) by:

bit_set(reg,3); //set reg, bit 3

Bit Testing

A bit in a file register may be tested using the bit test function. , .

value = bit test (var, bit)

The test may be used in conjunction with a while loop or if/else construct as examples.

Bit set, bit clear and bit test will be used in the next example.

104

Page 113: c What Happens

////bit manipulation demo #include <16F818.h>#fuses INTRC_IO main(){

byte reg;byte result;reg = ObOOOOllll;bit_set (reg, 4 ) ;bit_clear (reg,0);result = bit_test (reg,3);if (result == 1)

{bit_set (reg,5);’’ } else

{bit_clear (reg,l);}

output_b (reg); while (TRUE);

Cs 15

//declare "register" to play with //result of bit test //register contents to modify //00011111 //00011110//test bit, result in variable bit

//00111110 will happen

//00011100 won't happen

//circle, .always

You will be able to see the 6 least significant bits via the LEDs.

Flags

A flag is a one-bit register which is set (1) or cleared (0) by execution of one or more types of instructions or by operation of hardware inside the microcontroller.

Some flags arc built into the microcontroller. In the case of a peripheral such as a timer, setting or clearing generally takes place automatically. Others are created in software to indicate results of program execution. After execution of an instruction, the affected Hag may be tested to see if it was set or cleared. The path taken by the program depends on the status of the flag beingtested. In the following example, we will create a 1-bit variable called "flag", change its contents, and display the contents at bit 0 of port B.

The #bit pre-processor directive is used to create a convenient way to access port B, bit 0.

#bit i d = x . yi d is a valid C identifier x is a C variable or constant y is a constant 0-7

The 1-bit variable is placed in memory at byte x, bit y. This is useful for referring to bits in special function registers.

105

Page 114: c What Happens

We will also experiment with a type qualifier called typedef. Suppose I have a personal desire to refer to a 1-bit variable as a "bit" as opposed to the CCS term inti. I can use the typedef qualifier to satisfy my desire.

[type-qualifier] [type-specifier] [declarator]

typedef inti bit; //use bit in place of inti

This will help you understand what the typedef qualifier does when you see it in code that you encounter.

////bit level demo//// uses- time delay via#include <16F818.h>#use delay(internal=4mhz) typedef inti bit;#bit signal = 0x06.0 bit flag; del ( )

{delay_ms( 500) ; return;

}main(){

output_b (0x00); do

Csl6function call

{flag = 1; signal = flag; del(); flag = 0; signal = flag; del();

}

//clock oscillator internal, frequency //use bit instead of inti //port B, bit 0//bit type variable called flag //delay function

I'//delay 500 milliseconds

//clear port B

//set flag//show flag//call delay function//clear flag//show flag//call delay function

while (TRUE);

Page 115: c What Happens

BIT MANIPULATION USING BITWISE OPERATORS

The bitwise operators are:

« Left shift » Right shift <<= Left shift assignment >>= Right shift assignment& Bitwise AND

Bitwise exclusive OR Bitwise inclusive OR

&= Bitwise AND assignment< A= Bitwise exclusive OR assignment

|= Bitwise inclusive OR assignment

Logical operations and shifting bytes sideways may be used to change specific bits in a register'* or variable. These methods are useful when two or more bits in a byte must be changed.

Shift Bits Right or Left

Bits in a register or variable may be shifted right or left one bit position at a time. Each time a shift occurs, the bit position which is vacated is filled with a 0.

« Left shift » Right shift

A shift operator is accompanied by an argument which tells the compiler how many positions to, shift. For example:

new = (high«4); //variable new becomes variable high shifted// left 4 times

We can try out the shift bit manipulation operators by doing the following:

107

Page 116: c What Happens

////shift demo using bitwise operator Csl7a#include <16F818.h>#fuses INTRC_IO main(){

byte datal; byte data2; datal = OblOlOlOlO; data2 = (datal»3);

output_b (data2); while (TRUE);

}

The result is 00010101.

Change Specific Bit To "1"

OR with an 8-bit binary number which is all zeros except the bit to be changed to a "1"

OR with "0" leaves bit unchanged OR with "1" results in "1"

Change Specific Bit To "0"

AND with an 8-bit binary number which is all ones except the bit to be changed to a "0"

AND with "1" leaves bit unchangedAND with "0" results in "0" (called masking)

Change Specific Bit To It's Complement

0 to 1 and 1 to 0 using exclusive OR

XOR with an 8-bit binary number which is all zeros exceptthe bit to be changed to its complement (Exclusive OR)

XOR with "1" changes bit to complement XOR with "0" results in no change

//declare datal//declare data2//declare bit pattern//shift right 3 times, fill 3 ms bits// with 0's//display result via port B LEDs //circle, always

Page 117: c What Happens

We can experiment with the logic bit manipulation instructions by playing with bit 5 (arbitrary choice) in a variable.

Make it a "1" uging inclusive

////inclusive OR demo using bitwise #include <16F818.h>#fuses INTRC_IO main(){

byte datal;byte data2;datal = ObOOOOOOOO;data2 = (datal|ObOOlOOOOO ) ;output__b (data2);while (TRUE);

}

Make it a "0" using AND with

////AND demo using bitwise operator #include <16F818.h>#fuses INTRC_IO main ( ){

byte datal;byte data2;datal = ObOOlOllll;data2 = (datal&ObllOlllll) ;output_b (data2);while (TRUE);

}

OR with 0010 0000

operator Csl7b

//declare datal //declare data2 //declare bit pattern //inclusive OR, make bit 5 a "I" //display result via port B LEDs //circle, always

101 1111

Csl7c

//declare datal //declare data2 //declare bit pattern //AND, make bit 5 a "0"//display result via port B LEDs //circle, always

109

Page 118: c What Happens

Change it using exclusive OR with 0010 0000 (complement or invert)

////exclusive OR demo using bitwise operator #include <16F818.h>#fuses INTRC_IO main(){

byte datal; byte data2; datal = ObOOOOOOOO; data2 = (datal"ObOOlOOOOO); output_b (data2); while (TRUE);

Csl7d

//declare datal //declare data2 //declare bit pattern //exclusive OR, complement bit 5 //display result via port B LEDs //circle, always

GOTO

The goto statement is used as an unconditional jump to code located somewhere else,

goto label;

label: statement;

Execution branches to location identified by label within the same function.

if (conditionl) goto labell;

elsestatement;

labell: statement;

If you want the label to appear on a line by itself, you can place a semicolon after the label. A semicolon by itself is a null statement (docs nothing).

label: ;statement;

C purists don't like the use of goto statements and go all out to avoid using them.

Page 119: c What Happens

In case you just gotta goto:

+5VDC

All LEDs Off

Page 120: c What Happens

Cs 18

//clear port B //high?

//indicate port A, pin 2 switch closed

//indicate port A, pin 2 switch open

//circle, always1 •

A goto cannot jump outside a function and care should be taken to be sure an endless loop is not created.

FUNCTION LIBRARY

I would encourage you to start a function library. This can include functions you wrrite to do various tasks and functions you find in magazine articles, on the internet news groups or wher­ever. The functions can be saved and stored as text files for future use. This saves reinventing them each time they are needed.

Putting reusable functions into include files can be helpful. Note that if a file with several func­tions is "included" and one (or more) of the functions is never called by the program, the compil­er will not include it in the object file and it will not be programmed into the microcontroller.

CUT AND PASTE

Cutting and pasting blocks of text is a very convenient method for creating new programs using portions of existing ones.

////simple goto demo /include <16F818.h>#fuses INTRC_IO main ( ){

output__b (0x00); if (input(PIN_A2))

goto labell; else

output_b (ObOOOOOOOl); goto end;

labell: ;output_b (ObOOOOO100);

end: ;while (TRUE);

112

Page 121: c What Happens

TALKING TO A PIC MICROCONTROLLER WITH A PC VIA A WINDOWS TERMINAL PROGRAM

Having the ability to talk to a PIC microcontroller-controlled black box using a personal com­puter (PC) running a terminal program under Windows (tm) opens up an area of interesting possibilities. The PC can:

• Send data or a command.• Receive status information.

• Receive data - display data on-screen, print data, save data as a file, massage datawith a spreadsheet program, graph data, do math manipulation, etc.

Many PC's running Windows 95 and later have a built-in terminal program called "HyperTerminal" (tm). If HyperTerminal is not installed on your PC or you do not have the ver­sion named "Private Edition", you should download a copy from Hilgraeve at hilgraeve.com. At this writing, HyperTerminal Private Edition (tm) is available at no charge for personal use.

HyperTerminal does not have a "Clear Screen" capability. It you start out using HyperTerminal, you will soon recognize the value of the Clear Screen feature and download HyperTerminal Private Edition. From here on, I will simply use the name "HyperTerminal"

What is a terminal? The term comes from the days of the teletype when a terminal looked like a typewrriter and was used to send and receive messages. For our purposes, HyperTerminal allows sending or receiving single ASCII characters, strings of ASCII charactcrs, or text files.

HyperTerminal can easily set up a serial port such as COM2 and use it to communicate with a PIC microcontroller-based black box.

It is not necessary to learn a programming language for the PC although doing so will enable you to create more sophisticated applications. The PC provides the on-screen user interface, file storage on disk, and printer plus data manipulation, graphing and display capabilities. The PIC microcontroller-based black box provides the controller, data acquisition system, instrument or whatever device you want to dream up.

113

Page 122: c What Happens

"U-TURN" EXPERIMENT

The experiment that follows is an easy way to become familiar with terminal programs and to get a feel for how the PC end of things will work when connected to a PIC microcontroller-con­trolled black box.

You will need an ultra-simple piece of test equipment made from a 9-pin female D-subminiature solder cup connector and a short piece of wire. The wire connects pins 2 and 3.

9-pin Female D-subminiature Solder Cup Connector

The objective is to send stuff out a PC serial port on the transmit data (TD) line and to have it make a U-turn and come right back in on the receive data line (RD) of the same port.

We will discuss hardware later. Now we will work toward understanding the HyperTerminal program and what can be done with it.

The first objective is to open HyperTerminal and select a group of settings which will work for our applications, and save the file (settings) so we don't have to go through the setup procedure each time we wrant to do something.

The second objective is to use the "U-turn" connector and send and receive a single character at a time.

114

Page 123: c What Happens

We will create a communications setup file for use in our experiments.

Open HyperTerminal:

Start>All Programs>wherever it is>HyperTerminal Private Edition.

The Connection Description dialog box will appear.

Create a file name.Choose an icon, but you will not need it.Click OK.

The Connect To dialog box appears.

Select the serial port you wish to use (usually COM2).Connect using: COM2.Click OK.

The COM2 Properties dialog box appears.

Select the following:

Bits per second: 4800Data bits: 8Parity: NoneStop bits: 1Flow control: None

Click Apply.Click OK. '

The "filename" - HyperTerminal window is open.

View>Font.

Font: Courier or Courier New. The courier font is a monospace font. Font Style: Regular Size: 10

File>Properties

The "filename" Properties dialog box appears.

Click on the Settings tab.Emulation: ANSIThe remaining settings should be the default settings.Click OK.

File>Save.

115

Page 124: c What Happens

Strike a letter key. Nothing happens. The screen displays characters received, NOT characters sent.

Turn off your computer. Install the "U-turn" connector at the serial port you are going to use. Generally this will be COM 2 as most systems have the mouse connected to COM 1. Turn on your computer. Open the settings file you just created in HyperTerminal. The HyperTerminal window should now be open, blank, and a cursor should be blinking in the upper left hand cor­ner. Now type any character. The character will appear on-screen w^here the cursor was. The character displayed is actually the character received by the terminal program. If you type the letter "a", it will be transmitted out the COM 2 serial port on the TD line, make a U-turn, come back in the same serial port on the RD line and will be displayed on the screen. Note that the character sent is not displayed, the character received is. They happen to be the same in this case because of the U-turn.

Three ASCII control characters are useful for controlling the placement of ASCII alphanumeric characters on the screen of the PC as they are received. This is important because we want the information to be readable and also because we will want data to be formatted to be saved as a useful text file. The three ASCII control characters are:

As an example, a carriage return is sent when the control and "m" keys are pressed simultane­ously (control m).

As you probably already know, carriage return causes placement of characters on the screen to move to the extreme left side. Line feed causes characters to be placed on the next line down the screen. Horizontal tab means tab over to the right. Theses terms come from the teletype days.

Try experimenting with the first three control characters to get a feel for how they control place­ment of the characters on-screen (formatting).

To clear the screen, Edit>Clear Screen.

For our PIC microcontroller-based experiments, the microcontroller will send data to the PC where it will appear on the HyperTerminal screen. Examples appear in the Strings and Math And Manipulating Numbers chapters.

116

Page 125: c What Happens

If you have two PCs available, you might like to do the following experiment to learn more about serial communication between twro PCs, both running HyperTerminal. A cable wrill be required to conncct the two serial ports. The simplest possible cable which will work consists of two data lines (one for each direction) and a ground line.

PC-TO-PC "2-LANE HIGHWAY" EXPERIMENT

Cable Assembly

Notice that the transmit data (TD) line on computer 1 is connected to the receive data (RD) line on computer 2 and visa versa. You can easily make your own cable assembly using twro 9-pin female D-subminiature connectors and three lengths of wire. Keep the cable as short as possible (8 feet works for me).

Both computers must communicate using the same settings for baud rate, etc. You can start by using the settings used previously in the setup examples.

Baud rate 4800Data bits 8Parity NoneStop bits 1Flow control None

The objective is to establish bi-directional communication between two computers.

We will assume both computers are running HyperTerminal.

To establish bi-directional communication, connect the computers via the serial cable, turn both of them on and bring up HyperTerminal with your settings file in each. When you have the HyperTerminal window open in each computer, type a character in one of the computers. It will appear on the screen of the other computer. Now do the reverse. After you have played a little, clean off the screen in each computer by using Edit>Clear Screen.

117

Page 126: c What Happens

The hardware side of 2-way communications between a PIC microcontroller and a PC will be described next.

PC/PIC MICROCONTROLLER

PC Baud Rates

Baud rate is defined as the number of bits transmitted per second. The baud rates available for serial communication via a PC using a terminal program are:

Baud Rates

118

Page 127: c What Happens

RS-232 INTERFACE FOR A PIC MICROCONTROLLER

My objective here is to give you just enough information about RS-232 to make it possible to build a simple hardware interface between a PC1 serial port and a PIC microcontroller. A MAX233 RS-232 converter IC from MAXIM Dallas will be used to develop the 9 volts or so required to transfer data per the RS-232 standard. Among other things, an RS-232 converter chip is an inverter. There is an RS-232 converter chip inside the PC which inverts data going both ways. It is desirable to use one at the PIC microcontroller end too so that everything comes out right (see diagram on following page).

RS-232 CONVERTER

119

Page 128: c What Happens

The connections between the PC, cable, RS-232 converter and PIC microcontroller are:

RS-232CONVERTER

Note that only the wrircs used between the PC and RS-232 converter board in a particular experi­ment are shown in the drawings that follow in this book. The third wire in the cable described in the "2-lane highwray" experiment will not interfere.

The pin-functions for PC RS-232 serial connectors of interest here are:

Function 9-pin* 25-pin

Transmitted data (TD) 3 2Received data (RD) 2 3Common 5 7

* Shown in this book

The cable is the same one used for the PC-to-PC experiments. Note that transmit on one end goes to receive on the other end.

To test your RS-232 converter, use a wire to connect the PIC microcontroller transmit and receive terminals (Rlout and Tlin) on the converter board. With the converter board connected to the PC via cable, a character sent using the PC terminal program will (should) appear on screen as was the case with the "U-turn'1 experiment.

Up to this point, we have discussed 2-way communication between a PC and a PIC microcon­troller. The RS-232 converter is designed for 2-way communication. The experiments which follow are 1-way with the microcontroller transmitting information to the PC.

120

Page 129: c What Happens

PIC MICROCONTROLLER-TO-PC SERIAL COMMUNICATION

Next, we will get a PIC microcontroller to talk to a PC. We'll sec if the listener (PC) understood w'hat the talker (PIC microcontroller) said.

The circuit for the experiments is:

RS-232SEND CONVERTER RECEIVE

The PIC16F818 uses port A, bit 1 to transmit.

For the purposes of this discussion, it is assumed that you will be using two PC's, one to develop code and program it into the PIC microcontroller using the ICD programmer/lCSP method and the other to display the results transmitted to it via RS-232.

The procedure for firing-up the hardware and running the first example program is:

1) Powrer-up the PC running the CCS compiler and the device programmer.2) Power-up the ICD programmer, the PIC microcontroller board and the RS-232

converter board.3) Program the PIC 16F818.4) Send switch open.5) Bring the PIC 16F818 out of reset.

6) Power-up the PC that will receive the RS-232 communications from the PIC16F818.7) Set up the HyperTerminal program as in previous examples (4800 baud).8) Close send switch.

9) The character or string in the coming experiments should appear on the screen ofthe PC.

To run the second and subsequent examples, life gets simpler.

1) Clear screen on the display PC.2) Send switch open.3) Hold the PIC16F818 in reset.4) Import the next .cof file.5) Program the device.6) Release the PIC 16F818 from reset.7) Close send switch.8) Look at the result on the display PC screen.

121

Page 130: c What Happens

FORMATTING PIC MICROCONTROLLER DATA ON A PC SCREEN

In the next chapter, you will learn about the printfQ built-in function which may be used to send ASCII alphanumeric characters to a PC for display on-screen. Three printf() escape sequences are useful for controlling the placement of ASCII alphanumeric characters on the screen of the PC as they are received. This is important because we want the information to be readable and also because we will want data to be formatted to be saved as a useful text file. The three printfQ escape sequences are:

New line /n (line feed)Carriage return /rHorizontal tab /t

As you probably already know, carriage return causes placement of characters on the screen to move to the extreme left side. Line feed causes characters to be placed on the next line down the screcn. Horizontal tab means tab over to the right. Theses terms come from the teletype days. The binary codes for these functions must be built into PIC microcontroller code and sent to the PC so that the data displayed will make sense to humans.

Sample programs will illustrate how the printf() escape sequences work.

////escape sequence demo 1 Csl9a/include <16F818.h>#use delay (internal = 4mhz)#use rs232 (baud = 4800, xmit = PINAl) main(){

while (input(PIN A2)==l); //go low? wait for ready switch to closeputc ('s'); //start here on-screenprintf ("C what happens!"); //send ASCII character string out a

// serial portwhile (TRUE);

}

Result:

sC wrhat happens!

Notice that the "s" and "C" are run together. In the next example, we will use the \n escape sequence to put "C what happens" on a new line.

Notice, also, that the #use delay() and #use rs232() built-in functions are needed.

As used here, putcQ outputs a single ASCII character and printf() outputs a string of ASCII characters. They are built-in functions. Single quotes are used to indicate a single character which is defined in the putc() function. Quotation marks arc used to indicate a string of charac­ters w'hich is defined in the printf() function.

122

Page 131: c What Happens

////escape sequence demo 2 /include <16F818.h>#use delay {internal = 4mhz)#use rs232 (baud = 4800, xmit = PIN A1) main(){

while (input(PINA2)==1); putc ('s'); printf ("\n"); printf ("C what happens!");

Cs 19b

//go low? wait for ready switch to close //start here on-screen //new line//send ASCII character string out a // serial port

while (TRUE);

Result:

C what happens!

"C what happens!" is on a new line with a blank space one character wide. Adding a carriage return escape sequence will cure that problem.

////escape sequence demo 3 /include <16F818.h>/use delay (internal = 4mhz) /use rs232 (baud = 4800, xmit main(){

while (input(PIN_A2)==1);putc ('s');printf ("\n\r");printf ("C what happens!");

while (TRUE);

Csl9c

PIN_A1)

//go low? wait for ready switch to close//start here on-screen//new line, carriage return//send ASCII character string out a// serial port

Result:

sC what happens!

That's better! Notice that the \n and \r are together (no comma).

123

Page 132: c What Happens

Now let's try out the horizontal tab escape sequence.

////escape sequence demo 4 /include <16F818.h>#use delay (internal = 4mhz)#use rs232 (baud = 4800, xmit = PIN A1) main(){

while (input(PIN_A2)==1); putc (’s'); printf ("\n\r\t"); printf ("C what happens!");

Csl9d

//go low? wait for ready switch to close //start here on-screen//new line, carriage return, horiz tab //send ASCII character string out a // serial port

while (TRUE);

Result:

sC wrhat happens!

124

Page 133: c What Happens

STRINGS

A "string" is a group of characters. C does not have a string data type (string of characters). String data is stored as one character per element in an array called a "string array".

A string literal is enclosed in quotation marks.

A string terminator tells the C compiler where the endvof the string is. It is called the null char­acter or null zero and is added automatically by the compiler. You can't see it, but it is there.

String length is the number of characters in the string including spaces and the null character.The null character must be included in the count even though it is not visible. Examples are:

char msg[6] = "error"

error has 5 charactersAdding room for the null character makes 6

char alarm[15] = "smoke detected";

smoke = 5 characters space = 1 character detected = 8 charactersAdd 1 to make room for the null character Total = 15

The next example program sends an ASCII character to a PC via the serial port. This example is nearly the same as what was done in the previous chapter, but now the emphasis is on explaining strings.

RS-232SEND CONVERTER RECEIVE

125

Page 134: c What Happens

////character demo - rs232 Cs20a/include <16F818.h>#use delay (internal = 4mhz)#use rs232 (baud = 4800, xmit = PIN Al) main(){

while (input(PIN_A2)==1); //go low? wait for ready switch to closeputc ('a'); //send 'a' out a serial portwhile (TRUE);

}

Power-up with the switch open. When you are ready to send, close the switch. Sending data some time after power-up allows time for the serial connection to become stable.

Notice that the #use delay() and #use rs232() built-in functions are needed.

The next example program sends an ASCII character string to a PC via the serial port. The hardware and procedure are the same.

////string demo - rs232 Cs20b/include <16F818.h>/use delay (internal = 4mhz)/use rs232 (baud = 4800, xmit = PIN_A1) main( ){

while (input(PINA2)==1); //go low? wait for ready switch to closeprintf ("C what happens!"); //send ASCII character string out a

// serial portwhile (TRUE);

This is a short introduction to strings. Manipulation of strings can become quite involved. The CCS compiler has 22 built-in functions for working with strings. For now, it is sufficient to be aware that these possibilities exist.

126

Page 135: c What Happens

ARRAYS

Arrays have "elements" and all of the elements are the same data type.

Define array:

data type const array name [size];

number of elements in array

modifier or qualifier (if needed)

"const" is the constant qualifier. Use of const here will cause the constants to be placed in pro­gram memory (ROM).

Again, the elements in an array are all of one data type. The data type is specified in the array definition. Data types have corresponding array types.

As an example, we can define an integer array, which we will call i a, and the initial values:

int const i_a[4] = {1, 2, 3, 4};

Note that the order is int const, not the reverse as in the CCS manual,

i a[3] is the third element of the array which has a value of 4.

The array name is the index to the first clement in the array. We can also declare an index to the array which will be useful in pointing to all elements in the array:

int pi_a; //declare index

The compiler will know that pi_a is an index because when it is used, it is associated with the name of the array.

4 elements

127

Page 136: c What Happens

With an array and an index, we can:

• Step through the array using the index.• Point to the nth element in the array.• Add an offset to the index.

Examples will serve to illustrate.

The index may be incremented by:

pi_a++ //increments index pi_a to next element of array

We can step through array elements using this technique.

////array demo//// uses time delay via function call /include <16F818.h>#use delay(internal=4mhz) del ( )

{delay jus(500); return;

}main(){

int const vals[4] = {0,1,2,3}; int pvals = 0; int i;output b (0x00); for (i=0; i<=3; i++)

{output_b (vals[pvals]);

del(); pvals++;

}while (TRUE);

}

Cs2 la

//clock oscillator internal, frequency //delay function

//delay 500 milliseconds

//declare integer array, initialize //index to array//declare integer i, used for counting //initialize port B //display 4 array values

//display data pointed to by index via // port B LEDs //call delay function //increment index to array

//circle, always

Notice that all the variables used in main() are defined ahead of the first instruction to be exe­cuted output b (0x00);. If you fail to do this, you will get more than a few error messages from the compiler.

Notice, also, that the compiler learns that pvals is an index by observing that it is enclosed in [ J when first encountered.

128

Page 137: c What Happens

We can extract the nth element from an array.

arrayname[n]

////array demo - extract nth element /include <16F818.h>/fuses INTRCIO main(){

int const vals[4] = {0,1,2,3}; outputb (0x00); outputb (vals[3]);

while (TRUE);}

Cs2 lb

//declare integer array, initialize //initialize port B//display data pointed to by index via // port B LEDs //circle, always

129

Page 138: c What Happens

'

An offset may be added to or subtracted from the index.

int offset; //define offset

Later in in the program:

offset = 2; //offset 2 from current index value

outputb (i_a[pi_a + offset]); //offset added to index

If your code requires retrieving the 4th element (remember that "0" counts) in the array (contains "3" in this example), because the result of an operation is 3, make offset = 3 and proceed. This assumes that the pi_a = 0 at the time operation begins.

again:

outputb (i a[pi_a + offset]); //offset added to index

////array demo = add offset to index Cs21c/include <16F818.h>/fuses INTRC 10 main(){

int const vals[4] = {0,1,2,3}; //declare integer array, initializeint pvals = 0; //index to arrayint offset; //declare offsetoutputb (0x00); //initialize port Boffset = 2; //value in offsetoutputb (vals[pvals + offset]); //display data pointed to by index via

// port B LEDswhile (TRUE); //circle, always

130

Page 139: c What Happens

LOOKUP TABLES

A lookup table (array in C) may be used to convert one code to another. Let's say we want to convert numbers ranging from 0 to 9 to 7-segment signals to drive a display.

7-SEGMENT Number CODE DPgfe dcba

0 0x3F 0011 11111 06 0000 01102 5B 0101 10113 4F 0100 11114 66 0110 01105 6D 0110 11016 7D 0111 11017 07 0000 01118 7F 0111 11119 6F 0110 1111

Note: Each port A line must be pulled up to +5 V via a 10 K resistor.

The proper 7-segment code may be pulled from an array of constants by adding an offset to the array index. The offset is the number we want to display. The 7-segment binary code for the number is stored as that array element.

131

Page 140: c What Happens

For demonstration purposes, we will make the offset = 2. The 7-segment equivalent bit pattern will be accessed in the table/array and "2" will be displayed.

////array demo = 7-segment LED display Cs21d/include <16F818.h>/fuses INTRC 10 main ( ){

byte const nums[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//declare integer array, initialize //index to array //declare offset //initialize port B //value in offset//display data pointed to by index via // port B LEDs //circle, always

}

int pnums = 0; int offset; output^b (0x00); offset = 2;output_b (nums[pnums + offset])

while (TRUE);

The processor adds the number to the array index wrhere it will be used as the offset to find the 7-segment code which is then used to drive the display.

Since this program uses all 8 bits of port B, you will need to:

• Program the device in a device programmer followed by putting it in the socket on the7-segment circuit board.

Or

• Provide ICSP connections on your 7-segment circuit board in such a way as to allowprogramming followed by connecting pins RB7 and RB6 to the corresponding display segments.

Run the program. Examine the 7-segment display to see if the program worked.

In an application, the number would result from whatever is going on and would be equated to offset ahead of this code. This code would then display the number.

132

Page 141: c What Happens

STRUCTURES

Structures are similar to arrays. Arrays have "elements" and all of the elements are the same data type. -

Structures have "members". Members may be all of one data type, or a mixture of data types as we shall see by way of examples. A structure is a way to group data which is related, but (usually) of different types,

Think record in a database program.

Name and phone number.Customer, address, phone, accounts receivable balancc.Etc.

First, a structure type must be defined. Think of the definition as a layout, or plan, or template for the structure type you are creating.

An instance of a structure is a structure variable which contains members that are also variables. Think of this as an individual record in a database. Each instance must be named/declared.

Wc will need to load variables into each instance.

Wc will need to be able to access an individual variable in a specific instance.

Let's get started.

Structure type definition:

struct [structure tag]{member definition; member definition;

member definition;}

This definition defines a single structure type. This is the layout.

To refer to the structure type definition, use the structure tag.

133

Page 142: c What Happens

Instances of the structure (think individual records in a database) may be declared in two ways:

1) At the same time the structure is defined,

struct [structtag]{member definition; member definition;

member definition;} instl, inst2, inst3;

"instl" is the name of an individual instance.We can declare one instance or more.

2) Later in the program.

struct structag inst4, inst5, inst6;

As an example, let us create a simple database to help you keep track of the "stuff' that you store in your garage, basement and mini storage. Since you are a very organized person, you store your stuff in boxes of two sizes (large and small) and they are numbered. When looking for some critical stuff, you can look in your database to see which facility it is in. When wandering dow'n the aisle in your mini storage (you neatly organize your stuff) looking for something, you can look for the box with the correct number on it.

This is not a good microcontroller application, obviously, but it is one you can relate to for learn­ing purposes.

struct stuff {int num:5; //box number, 1 thru 512, stored in 5 bitschar desc[40]; //description of the (good) stuff in the box

// 40 bytes allocated for storage char loc[4]; //box location - gar, base, or mini

// 4 bytes allocated for location int size:l; //box size, 1 = large, 0 = small, stored in 1 bit

}

This is the structure type definition.

The number following is the number of bits allocated to the field.

This example has a variety of data types in it. I used one bit for box size. The "large" or "small" box sizes could be spelled out, but the database would take up more memory space. This also provides an example using bits which is more microcontroller oriented (vs. PC oriented).

Members may be a single data type, or a mix of data types.

134

Page 143: c What Happens

Next, wc will declare some instances,

struct stuff boxl, box2, box3;

Variables are loaded into members using the dot operator, the period. This is also known as the structure member operator.

structurevariablename.membername

The structure variable name is the instance. To load the box 1 instance:

box1.num = 1;boxl.desc = "high school diploma"; boxl.loc = "mini";boxl.size =0; // diploma fits in small box

To access data in a member, individual instance, we do the following:

To access the description for box 1, we would refer to it as:

boxl.desc

A simple example program will illustrate how this w'orks. Wc will:

• Define the structure type containing two member definitions. One is an integer andthe other is a character.

• Declare twro instances.• Load data in the two instances.• Display the data in one member of each instance.

135

Page 144: c What Happens

////structure demo1111 uses time delay via function /include <16F818.h>#use delay(internal=4mhz) del ( )

{delay_ms(50 0); return;

}struct test

{int a; char b;

}main(){

struct test iteml, item2;

iteml.a = 1; iteml.b = 'x';

item2.a = 2; item2.b = 'y';

do{

outputb (iteml.a); del();output_b (item2.b); del();

}while (TRUE);

}

call

//clock oscillator internal, frequency //delay function

//delay 500 milliseconds

//declare structure type, assign tag

//declare data type //declare data type

Cs22a

//declare 2 instances of structure // type test//load data - binary 00000001 //load data - ASCII character x // encoded as binary 01111000 //load data - binary 00000010 //load data - ASCII character y // encoded as binary 01111001

//display data via port B LEDs //call delay function //display data via port B LEDs //call delay function

The program displays item 1 .a (00000001), delays 500 milliseconds, displays item2.b (01111001, the binary encoded equivalent of ASCII y), delays 500 milliseconds, and repeats. The LEDs at bits 3, 4, 5, 6 will appear to blink together.

136

Page 145: c What Happens

STRUCTURES AND PORTS - BIT FIELDS

A structure may be created with one instance and assigned to a port to enable:

• Writing bit patterns to the port using a name for each bit pattern.The name may be associated writh a condition or an operation to be performed by the hardware connected to the port.

• Convenient reference to individual or groups of bits for read or write.• Logical manipulation of the port bits.

Compare.Set one bit/pin equal to another, perhaps on a different port.Whatever you might dream up.

Bits arc allocated from low' order up.

The number following is the number of bits allocated to the field,

int bit4:l;

int data:4;

To accomplish this:

• Create a structure type and assign tag.• Assign the structure to a port. The structure is "overlayed on a port" - single instance.

• Use a tag to create predefined/named bit patterns to be sent to the port as a whole.• Assign names to the individual pins.• Write or read pins by name.

This makes it possible to use logic, do comparisons, ctc.using pin names.

Two examples follow. The first is designed for w'riting to the port as a whole as well as writing (or reading) individual bits/pins. The second is simpler and is designed for individual bit access only.

Since built-in I/O functions arc not used, we must take care of the TRIS registers.

137

Page 146: c What Happens

////structure overlayed on a port demo 1111 port as whole or individual bits 1111 uses time delay via function call /include <16F818.h>#use delay(internal=4mhz) del ( )

{delayjms(2000); return;

}

//clock//delay

oscillatorfunction

Cs22b

internal, frequency

//delay 2000 milliseconds

//declare structure type, assign tag

struct main( ){

set_tris_b (0x00) ;□ do

INITl;

TAG

0x06const"~ INITl = {1,0, 1,0, const INIT2 = {1,1,0,0,

0};15};

INIT2;

= 0

= 0;del(); portb.c del(); portb. d del();

= 1

= 1

}

//declare //declare //declare //declare //declare

datadatadatadatadata

typetypetypetypetype

forforforforfor

bitbitbitbitbits 4-7

//name for bits//port B address (overlays struct on B) //initialize 1 00000101 //initialize 2 11110011 (15 = 1111)

//port B outputs

//display init byte 1 via port B LEDs //call delay function//display init byte 2 via port B LEDs

//bit 0=0

//bit 1=0

//bit 2=1

//bit 3=1

while (TRUE);

138

Page 147: c What Happens

Following is an example of a bits only application (no using the structure to write to the port as a whole). Notice that a structure tag is not needed.

////structure overlayed on a port demo Cs22c//// bit identification only//// uses time delay via function call/include <16F818.h>#use delay(internal=4mhz) del( )

{delay_ms(2000); return;

}struct

{inti a iinti b iinti c linti d linti e iinti f iinti g iinti

}portb;h i

#byte portb = 0x06 main(){

set_tris_b (0x00); outputb (ObOOOOOOll); del();portb.a = 0; del();portb.b = 0; del();portb.c = 1; del();portb.d = 1; del();while (TRUE);

//clock oscillator internal, frequency //delay function

//delay 2000 milliseconds

//declare structure type

//declare data type for bit 0//declare data type for bit 1//declare data type for bit 2//declare data type for bit 3//declare data type for bit 4//declare data type for bit 5//declare data type for bit 6//declare data type for bit 7//name for bits //port B address

//port B outputs //initialize port B

//bit 0 = 0

//bit 1 = 0

//bit 2 = 1

//bit 3 = 1

139

Page 148: c What Happens

MATH AND MANIPULATING NUMBERS

MATHEMATICAL OPERATORS

The most useful mathematical operators are (no surprise):

+ AdditionSubtraction

* Multiplication/ Division

We will use them in examples.

You may encounter:

% Modulus

Modulus produces the remainder from division. For example:

15 % 6 evaluates to 3. 15 divided by 6 yields a remainder of 3.

To avoid confusion, note that the symbol % is also used to format variables in a string for print­ing using the printfQ built-in function.

OPERATOR PRECEDENCE

Operator precedence is important. Rules determine which mathematical operation takes place first, ie. takes precedence over others. We will include the increment/decrement operators in this discussion.

Operator Precedence

+ + -- 1 inc, dec* / % 2 mult, div, modulus+ 3 add, subtr

1 has higher precedence than 2which has higher precedence than 3.

A subexpression in parentheses is evaluated first, regardless of the operators involved.

( ( ( ))) Evaluated from the innermost out.

If there are two or more operators having the same precedence, they are evaluated left to right.

140

Page 149: c What Happens

Examples:

x = 2 + 3 * 4 evaluates to 14 (multiplication first, subtraction second),

x = (2 + 3) * 4 evaluates to 20 (inside parentheses first, multiplication second),

x = (2 * (4 + (6 / 2 ))) evaluates to 14 (inside parentheses first, work outward).

x = 4 * 5 / 2 * 5 evaluates to 50 (start left, move right).

HARDWARE BLOCK DIAGRAM FOR MATH EXPERIMENTS

ltS-232SEND CONVERTER RECEIVE

141

Page 150: c What Happens

DATA TYPE SELECTION CONSIDERATIONS

Data types should be appropriate for each value and may be mixed in a calculation as you will soon see in examples. Variable size must be matched to the need. For example, if the result of a calculation is 39203, 16 bits w'ill be needed (int 16).

One data type may be printed as another if appropriate. Define a test number as an int 16 (16-bit binary number). Print it as a long unsigned integer.

////numbers demo Cs23a/include <16F818.h>#use delay (internal = 4mhz)#use rs232 (baud = 4800, xmit = PIN_A1) main(){

intl6 tnum = OblOOllOOlOOlOOOll; //declare test numberwhile (input(PIN_A2)==1); //go low? wait for ready switch to closeprintf ("%5Lu", tnum); //39203, 5 characters, long unsigned

// integerwhile (TRUE);

}

Result:

39203

Unsigned numbers are positive.

My philosophy is: Don't mix data types in heavy math. Do it all in binary. Timers and A/D converters work in binary anyway. Use printf() options to format the result in the desired data type so that humans can relate.

142

Page 151: c What Happens

The result of a mathematical calculation can be printed in a variety of formats,

printf (string);

% is used in the string to indicate that a variable is to be formatted in a certain way.

%nt

n is optional and may be:1-9 to specify the number of characters.01-09 to specify the number of characters and indicate

leading O's.1.9-9.9 to specify the number of characters and indicate

floating point, t is the type and may be one of the following:

u integerLu long integerw unsigned integer with decimal point inserted

n has two numbersfirst is total field widthsecond is desired number of decimal places

And several others - see CCS compiler manual, printf() function description.

In the printfQ argument, the % formatting codes come first in the order that the variables are to be printed. The variables come sccond in the same order. Note the placement of commas and • quotation marks.

printf("%_, %__, %_", x, y, z);

The first %__ is for the x variable, etc.

There may be one or more variables. The number of % and the number of variables must match.

To output a % in a printf, use %%.

Examples follow which will serve to illustrate.

FORMATTING VARIABLES SUCH AS MATH RESULTS FOR PRINTING

143

Page 152: c What Happens

Text such as "Shaft Speed" or "Temperature" may precede the variable to indicate what it is, followed by a space.

Units such as "RPM" or "°F" may be printed following data writh a space between as shown in the example.

////numbers with units demo Cs23b/include <16F818.h>#use delay (internal = 4mhz)#use rs232 (baud = 4800, xmit = PINAl) main(){

intl6 tnum = OblOOllOOlOOlOOOll; //declare test numberwhile (input(PIN_A2)==1); //go low? waitvfor ready switch to closeprintf ("text %5Lu units", tnum); //39203, 5 characters, long unsigned

// integerwhile (TRUE);

}

Notice the blank or space ahead of "units". This serves to separate "units" from the number.

Result:

text 39203 units

The stuff inside the quotes gets printed verbatim (including spaces) except for the % and accom­panying formatting characters. The variable whose name follows the comma is printed where the formatting characters are, per the instructions provided by the formatting characters.

144

Page 153: c What Happens

Division example:

////division demo /include <16F818.h>#use delay (internal = 4mhz)#use rs232 (baud = 4800, xmit = PIN Al) main(){

intl6 num = 40000; int8 denom = 60; intl6 result; result = num/denom; while (input(PINA2)==1); printf ("%Lu", result);

Cs23c

//declare numerator //declare denominator //declare result //division//go low? wait for ready switch to close //666, 3 characters, long unsigned // integer

while (TRUE);

Result:

666

When the C compiler performs division this way, the result is a whole number (no remainder).

Second division example:

////division demo - decimal point /include <16F818.h>/use delay (internal = 4mhz)/use rs232 (baud = 4800, xmit = PIN Al) main ( ){

intl6 num = 40000; int8 denom = 60; int32 result; result = num/denom; while (input(PIN A2)==1); printf ("%3.2w", result);

Cs23d

//declare numerator //declare denominator //declare result //division//go low? wait for ready switch to close //666, 3 characters, long unsigned // integer

while (TRUE);

Result:

6.66

An int32 32-bit data type is required to hold the result.

Some simple math examples appear in the following chapter.

145

Page 154: c What Happens

PASSING VARIABLES

Variables are either global or local.

• Global variables are defined outside a function and can be used by any function.• Local variables are defined inside a function (after an opening brace) and used within

a function (before the corresponding closing brace).

It is considered good practice to use local variables as much as possible so you can control access to them.

If a function needs to use another function's local variable, that variable can be passed to the function that needs it. Access to local variables is controlled, a good thing.

PASSING ARGUMENTS

When passing variables from one function to another, the terms variable, argument and parame­ter mean essentially the same thing. C programmers say that an argument is passed from the first function to the second function and that the second function receives a parameter from the first. A value is returned to the first (calling) function. This helps make understanding more difficult.

Variables may be passed using a method called passing by value, sometimes called passing by copy. The copy of the value of the variable is passed, not the variable itself.

////passing variables demo Cs24a/include <16F818.h>/fuses INTRC_IOsub(int datal) //"subroutine" receives value of datal

{datal = datal / 2; output b (datal); return;

//divides datal by 2//display result via port B LEDs

main() {

while (TRUE);

int datal; datal = 4; sub(datal);

//declare datal variable //initialize datal variable //value of datal is passed //circle, always

}

Notice that the data type is in the receiving function's argument list.

146

Page 155: c What Happens

In this example, the result is sent to the LEDs for display by the "subroutine".

RETURNING VALUES

In the following example, two values are passed to a "subroutine" function w'here they are multi­plied together and the resulting value is returned to the calling function ( main() ) where it is sent to LEDs for display.

Notice that the data types are in the "subroutine" function's argument list.

////returning value demo /include <16F818.h>/fuses INTRC 10 sub(int datal, int data2)

{int subAnswer; subAnswer = datal * data2; return (subAnswer);}

main(){

int answer; int datal = 2; int data2 = 3; answer = sub(datal, data2);

output_b (answer); while (TRUE);

}

The operation of the codc is explained in the comments. Notice that the variable answer is local in main() and subAnswer is local in sub(int datal, int data2).

PROTOTYPING FUNCTIONS

Using a function prototype allows a function to be defined before the actual code is used. If a prototype is used, the function is available for use with different arguments in other parts of the program as the need arises - ala built-in functions.

In a prototype, the variables to be passed are defined.

Cs24b

//"subroutine" receives values of datal // and data2

//declare answer variable in sub //multiplies datal times data2

//declare answer variable in main//declare datal variable//declare data2 variable//values of datal and data2 are passed// answer is returned//display result via port B LEDs//circle, always

147

Page 156: c What Happens

To crcate a prototype, put an exact duplicate of the function's first line somewhere before main( ) .

function prototype; //ahead of mainmain(){

function name (argument list); //calls function

}function header " //same as prototype except no{function definition //function itself}

When the function is called, the correct data types must appear in the proper positions in the function argument list, ie. proper order as defined in the prototype.

////returning value demo - prototype Cs24c/include <16F818.h>/fuses INTRCIO sub(int datal, int data2); main(){

int answer; int datal = 2; int data2 = 3; answer = sub(datal, data2);

output b (answer); while (TRUE);

}sub(int datal, int data2)

{int subAnswer; subAnswer = datal * data2; return (subAnswer);}

A function prototype provides a hint to the compiler as to operation of a function, and allows a function to be defined before the actual code is used. For example, the function can be proto­typed in line 5, it can be called on line 30, and the acUial function written on line 60. If you did not have the prototype on line 5, and you attempted to call the function on line 30, the compiler w'ould not know wrhat the function is because it has not been prototyped.

Prototypes are not needed in small programs, but they are useful in large programs that include lots of linked libraries and include files. Typically, all the important user functions are proto­typed in a .h header file.

//function prototype

//declare answer variable in main//declare datal variable//declare data2 variable//values of datal and data2 are passed// answer is returned//display result via port B LEDs//circle, always

//function header - "subroutine" receives // values of datal and data2

//declare answer variable in sub //multiplies datal times data2

148

Page 157: c What Happens

OPERATORS

An operator is a symbol that instructs the C compiler to perform a specific operation on one or more operands.-An operand is an expression that the operand acts on. All the operators are listed here as a reference.

Assignment operator

equal signvariable = expression;

Assign the value of expression to variable = not used in math, only as assignment operator

Relational operators

== Equal to><>=<=!= Not equal to

Comparisons using relational operators

Logical operators

&& AndI I Or ! Not

Increment and decrement

++ Increment— Decrement

Used with counters

149

Page 158: c What Happens

Mathematical operators

+ Addition- Subtraction* Multiplication/ Division% Modulus - gives division remainder+= Addition assignment- = Subtraction assignment* — Multiplication assignment/= Division assignment%= Modulus assignment

Bitwise operatorsV.

<< Left shift » Right shift «= Left shift assignment »= Right shift assignment & Bitwise AND

Bitwise exclusive OR Bitwise inclusive OR

&= Bitwise AND assignment A= Bitwise exclusive OR assignment |= Bitwise inclusive OR assignment

Pointer operators

& Address-of operator Address unary* Deferencing operator Indirection unary

Structure operators

Dot operator or structure member operator -> Structure pointer

Operators that don’t fit in the categories

( ) Grouping , Sequential evaluationsizeof Size in bytes unary?: Conditional expression

Not an operator

: Used in structure bit fields to indicate the number of bitsallocated to a field

150

Page 159: c What Happens

INTERRUPTS

When an event occurs which demands the microcontroller's attention, an interrupt may be gener­ated which will cause the microcontroller to drop what it is doing, take care of the task that needs to be performed, and go back to what it was doing.

When an interrupt occurs, the instruction currently being executed is completed. Then the PIC 16F818 jumps to address 0x004 in program memory and executes the instruction stored there. This program is called an interrupt service routine. An interrupt service routine may cause (as required) the microcontroller to first take notes on the status of the program it was exe­cuting when the interrupt occurred so that it can find its place when it comes back. Then the interrupt service routine will handle the interrupt by doing whatever needs to be done. On com­pletion, the routine will review its notes, set everything back to the way it was and take up the main program where it left off. The code needed to both preserve and restore the status of the program that was being executed when the interrupt occurred is created automatically by the compiler. This is called context saving.

Interrupts are caused by events which must trigger a response from the microcontroller at the time they occur. For the PIC16F818, interrupts may come from one of several sources:

• External - outside the microcontroller via the RBO/INT pin.

• Timer 0 overflow from OxFF to 0x00.

• Port B logic level change on bits 7,6,5,4.

• A/D conversion complete.

• Data EEPROM write complete.

• And others.

An interrupt flag is a bit in one of the Special Function Registers which, when set, indicates that a specific interrupt has occurred. The flag will remain set until it is eleared by software which is usually done in the interrupt service routine related to that specific interrupt.

Interrupts may be enabled or disabled (masked) at two levels, global (all interrupts regardless of source) or specific (enable/disable specific interrupt sources).

151

Page 160: c What Happens

Using external interrupts requires both hardware and software. Hardware must be provided to sense an event or condition which requires an interrupt to bring about some action. A signal must be generated and fed to the microcontroller INT pin.

RBO/INT is a general puipose interrupt pin. The INT input is connected directly (internally) to a Schmitt trigger whose output is directed to the external interrupt tlag, so the TRJS register is not involved.

The external interrupt is edge-triggered. The INTEDG bit in one of the control registers deter­mines whether a rising or falling edge triggers an interrupt.

The E X T I N T E D G E Q built-in function is used to make this selection.

ext_int_edge(edge)

edge H_TO_L high to low transition at INT pin edge L_TO_H low to high transition at INT pin

An INT interrupt can be disabled using the INT enable (INTE) flag so that interrupts will be ignored until the INTE flag is set. INT interrupts can be serviced or ignored under software control.

If an INT interrupt occurs, the INTF flag is set. The INTF flag must be cleared via software as part of the interrupt service routine before reenabling the interrupt or continuous interrupts will occur. The compiler generates code to take care of this automatically.

• Disable further interrupts (clear INTE flag).• Service the interrupt.• Clear INTF interrupt flag.• Enable interrupts (set INTE flag).

INTERNAL INTERRUPT SOURCES

Timer 0 Interrupt

Timer 0 interrupts occur on overflow from OxFF to 0x00.

Port B Interrupt On Change - Bits 7,6,5,4

An interrupt on logic level change on port B bits 7,6,5,4 sets the RB port change interrupt flag (RB1F). This interrupt is enabled/disabled via the RB port change interrupt enable bit (RBIE). Only port lines 7,6,5,4 configured as inputs are effected.

The pin's value in input mode is compared with the old value latched in the last reading of port B. The "mismatches" of the pins arc OR'cd together to generate the RB1F interrupt.

The interrupt may be cleared by:

• Disabling the interrupt by clearing the RBIE bit.

• Read port B, then clear the RBIF bit. This ends the "mismatch" condition and allowsRBIF to be cleared.

EXTERNAL INTERRUPT SOURCES

152

Page 161: c What Happens

The interrupt on change feature is recommended for wakeup on key depression operation and operations where port B is only used for the interrupt on change feature. Polling of port B is not recommended while using the interrupt on change feature. Reading the port messes up the mis­match.

Interrupts Generated By Other Peripherals

Other peripherals such as a 16-bit timer/counter (timer 1 = TMR1), an A/D converter, a com­parator (not present on the PIC16F818), etc. can generate interrupts specific to their operation. The CCS compiler includes a long list of peripheral interrupt handlers which are available for your use.

GLOBAL INTERRUPT ENABLE FLAG (GIE) v

The occurrence of an interrupt clears the global interrupt enable flag disabling further interrupts while the interrupt is being serviced. As the interrupt service routine is completed, code auto­matically created by the compiler sets the global internipt flag enabling further interrupts.

RETURN FROM INTERRUPT

An inteiTupt service routine must end with a return from interrupt instruction which causes exe­cution to resume where it left off. This instruction is created automatically by the compiler.

WHERE TO PUT THE INTERRUPT SERVICE ROUTINE IN PROGRAM MEMORY

The interrupt vector built into the PIC16F818 is 0x004. When an interrupt occurs, the interrupt vector points to program memory address 0x004 where the first instruction of the interrupt ser­vice routine must be stored. The first instruction is placed there by the compiler. You don't have to think about it.

How to create an interrupt service routine and where to place it in the code will be shown in examples in this chapter and the following one.

INTERRUPT LATENCY

When an interrupt occurs, there will be a delay (latency) before the interrupt service routine is executed. This delay will be 3 or 4 instruction cycles.

MULTIPLE EXTERNAL INTERRUPT SOURCES

Conveniently, the PIC 18 Series devices have three possible external interrupt pins and the interrupts may be prioritized (advanced topic). Interrupt flags indicate which INT pin caused the interrupt. A different software subroutine is needed for each interrupt source.

153

Page 162: c What Happens

The things you need to know (short list) about w'riting code to handle interrupts in C follow':

Functions - Built-in

ENABLE INTERRUPTSQ enable interrupts (level)DISABLE INTERR UPTS() disable interrupts (level)

level is a constant which defines an interrupt source. These constants are found in the device .h file for the device you are using.

EXT INT EDGE() ext_int_edge(edge)

edge H_T0_L high to low transition at INT pin edge L TOH low to high transition St INT pin

Timer 0 interrupt functions - in Timing And Counting Using Timer 0 chapter.

Pre-processor Directives Used To Identify Interrupt Service Routines

#int_xxxx

xxxx refers to the on-board peripheral which is the source of the interrupt.

Example: #int_AD //analog to digital conversion complete

Long list of peripheral interrupt directives appears in the CCScompiler manual in the #int_xxxx pre-processor directive description.

A #int_xxxx directive in the code is followed immediately by the user-written interrupt service routine (a function).

Example:

#int_ext //external interrupti_serv()

" (.... //user interrupt service routine

}

The #int xxxx directive causes the compiler to generate code that will take care of:

• Context saving.• Clearing the interrupt Hag that triggered the

interrupt.• Return from interrupt.

The #int_xxxx directive should be placed ahead of main() (see example).

INTERRUPTS IN C

154

Page 163: c What Happens

EXAMPLE - EXTERNAL INTERRUPT

INT is edge sensitive. It will respond to a rising edge if the INTliDG bit (bit 6 in the option reg­ister) is set, or to a falling edge if the INTliDG bit is clear.

To generate an INT interrupt, the INT pin must detect the edge of a pulse. For demonstration purposes, this can be done using the negative-going output of the pulscr circuit described in Appendix A. The pulser output is normally logic I and the output is a negative-going pulse. We will set up to respond to the falling edge.

When an INT interrupt occurs, the microcontroller:

1) Completes execution of the current instruction.2) Tests the global interrupt enable Hag. If the flag is set, global interrupts are enabled.3) Tests the INT interrupt enable flag. If the flag is j>et, INT interrupts are enabled and the

microcontroller will begin the interrupt sequence.4) If either interrupt enable Hag indicates "disabled", the microcontroller will continue

whatever it was doing when the interrupt signal was received and ignore the interrupt.5) Clears the global interrupt enable flag to prevent further interrupts (automatic on

recognition of interrupt).6) Saves the address of the next instruction on the stack (automatic).7) Jumps to the address pointed to by the INT vector = 0x004. The C compiler automatically

placcs the first instruction of the interrupt service routine at that address.

There is a Schmitt trigger external interrupt (INT) input connected directly (internally) to port B, pin 0, so the TRIS register is not involved.

The following test program used in conjunction with the pulser circuit described above will serve to demonstrate an INT interrupt.

+5VDC

155

Page 164: c What Happens

Main . Program

LEDs Off

InterruptServiceRoutine

Return From Interrupt

Toggle Interrupt LED

The program scans the status of the switch connected to port A, bit 0 and displays its status at port B, bit 1. Its operation serves as something to do for demonstration purposes while waiting for the interrupt to occur. The output is visible, so operation of the program can be verified by changing switch settings w'hile the program runs. Note that the rNT interrupt is enabled (essential).

If an INT interrupt occurs, the microcontroller jumps to 0x004 where the interrupt service rou­tine begins. The interrupt service routine toggles the interrupt indicator LED at port B, bit 2 indicating an interrupt has occurred and then returns to the main program.

The port B pullups are turned off. The program calls for response to a falling edge on the INT line.

156

Page 165: c What Happens

////external interrupt demo /include <16F818.h>/fuses INTRCfO /bit sw = 0x05.0 /bit sw_stat = 0x06.1 /INT_EXT i_serv(){

output_toggle(PIN_B2);}main(){

output_b (0x00); ext_int_edge (H_TO_L); clear_interrupt (INT_EXT); enable_interrupts (GLOBAL); enable interrupts (INT EXT); while (TRUE)

{swstat = sw;}}

ala pictl5.asm Cs25

//port A, bit 0 - switch//port B, bit 1 - switch status LED//external interrupt//interrupt service function

//toggle interrupt LED

//port B bits 7-1 low //interrupt on falling edge //clear external interrupts //enable interrupts

//status LED = switch

The return from interrupt code is generated by the compiler automatically.

The level constants (such as INT EXT) used with the built-in interrupt functions may be found in the device .h file for the device you are using.

Notice that i serv follows immediately after /int ext and ahead of main.

Program a PIC 16F818. Power-up your test circuit. Change the position of the switch on port A, bit 0 to confirm that the main program is running. Pulse the INT line several times and observe the result at the LED connected to port B, bit 2.

Remember that an interrupt should not occur during a software timing loop as it will lengthen the loop by the time required to service that interrupt. The delay_cycles(), delay_us(), and delay_ms() built-in functions use timing loops created in software and do not use a hardware timer such as timer 0 (TMR0). Also, an interrupt which occurs while the PIC 16F818's timer is in use may or may not be serviced before the timer times out.

The use of interrupts greatly enhances the power of the microcontroller. Interrupts may be peri­odic, as determined by a real time clock, or may be related to an event such as a counter count­ing down to 0 or a burglar tripping an alarm. The microcontroller does not have to go around and around in a loop waiting for these things to happen, so it can perform other useful tasks in the meantime.

157

Page 166: c What Happens

TIMING AND COUNTING USING TIMER 0

DIGITAL OUTPUT WAVEFORMS

Digital output waveforms are easy to generate by writing ones and zeros to a port line. A posi­tive going pulse of short or long duration may be output by initializing the line to 0, outputting a 1, using a software timing loop or hardware timer to measure the pulse duration, and then writ­ing a 0 to the port line.

Square waves are easy to generate as you know from some of the examples:

Delays Are Equal

A rectangular wave is produced if the delays are not equal.

If the delays are changed each time around the loop, sweep frequencies or frequency-modulated signals may be generated.

As an alternative, the PIC 16F818 timer 0 timer/counter may be used. An advantage is that the microcontroller is not tied up generating repetitive waveforms.

158

Page 167: c What Happens

USING TIMER 0

PIC microcontrollers have an 8-bit (in most cases) timer/counter called Timer 0, or TMRO, timer 0 (CCS), or RTC’C as it was called in the early days of PIC microcontrollers.

Timer 0's features are:

• 8-bit.• Read/write.• 8-bit software programmable prcscaler.• Internal or external clock.• Edge-rising or falling (external clock).• Increments.• Interrupt on overflow from OxFF to 0x00 with flag output.

Timer 0 has an interrupt on overflow from OxFF to 0x00 and is capable of doing other tasks while timing/counting is going on.

Three built-in functions are used to control timer 0:

• setup timer 0 {mode) where mode options are device dependent and include thingslike clock oscillator internal or external plus prescaler selection.

• set timerO (value) determines the initial value loaded into the counter.• get timerO () reads the timer.

Details are in the CCS compiler manual.

The options for each built-in function are in the device .h file (P1C16F818) for the examples which follow.

The clock source for timer 0 may be either the PIC16F818's internal instruction cycle clock or the T0CKI pin. An external clock may be an oscillator running (much) slower than the PIC 16F818's clock oscillator or it might be a source of pulses to be counted.

The input is either fed directly to the timer/counter (bypassing the prescaler) or through an 8-bit software programmable prescaler.

Input

T0CKI

The prescaler value may be l-of-8 as determined by using the setup timer0() built-in function.

All instructions which write to timer 0 will dear the prescaler.

159

Page 168: c What Happens

If an external clock source is used with no prescaler, synchronization of the external clock input must lake place. This requires a short sampling procedure plus a delay after synchronization occurs and prior to the timer/counter being incremented.

There are some requirements for an external clock signal.

No Prescaler

Input high for at least 2 Tosc.Input low for at least 2 Tosc.

With Prescaler

Input period of at least 4 Tosc divided by the prescaler value.Highs and lows must be of greater than 10 nanoseconds duration.

Tosc is the period of the PIC16F818 clock oscillator.

If there is a write to the timer/counter, incrementing is inhibited for the next 2 instruction cycles. This can be compensated for by adjusting the number loaded in the timer/counter.

External clock pulses may be detected on their rising or falling edge (software selectable via the setup timerOQ function).

The timer 0 outputs are:

• Reading timer 0 using the get_timerO() function.• Interrupt on overflow from OxFF to 0x00.

The timer is incremented by incoming pulses. When the count climbs through OxFF, the count starts over at 0x00. The timer may be incremented over and over if need be and the number of times the counter reaches a certain value may be counted using a file register as a counter.

PRESCALER

There is an 8-bit counter which may be used as a prescaler for timer 0. The prescaler divides the clock input by one-of-eight values which effectively reduces the frequency of the clock. The prescaler is used to divide the input by:

1 (bypass prescaler)2 (default)48163264128256

The prcscaler assignment and ratio are determined by using the setup_timer0() function as will be demonstrated in examples which follow. W'hen the prescaler is assigned to timer 0 using the set timerOQ function, the prescaler will be cleared to prepare it for division of the input signal.

160

Page 169: c What Happens

Setting up timer 0

Assign prescaler value using the setup timcr()() function.

Syntax: setup timer 0 {mode)

mode, in our examples, is two constants. The first defines the clock source. The second defines the prescaler ratio. A prescaler ratio of 1 causes the prescaler to be bypassed. The two mode constants are or'ed together using the bitwise inclusive or operator | per the instructions in the CCS compiler manual. The mode constants may be found in the dcvice .h file.

setup_timer_0(RTCC_INTERNAL|RTCC DIV-256); //internal clock osc,//prescaler divides clock osc by 256

Starting timer 0

Write a value to timer 0 using the set_timer()() function.

Counter

PUTTING TIMER 0 TO WORK

How do we know timer 0 is doing something?

• Successive reads of timer 0.• Interrupt on overflow OxFF to 0x00. This is how we know the timer/counter is

finished counting. The PIC 16F818 is free to do other things while timer 0 is doing it's thing. The timer 0 interrupt flag is the output.

Timer 0 will keep counting as long as:

• It is not cleared or written to by a program function.• The microcontroller is not reset.

Timer 0 must be reloaded after each overflow for repeating time intervals

If this is not done, the count will start at 0x00 each time.

Stopping timer 0

Can't - it just runs.

Experiments follow which will illustrate the use of timer 0.

161

Page 170: c What Happens

For programs using timer 0, we will use:

#fuses intrc_io

and

setuposcillator(OSC_4MHz);

An external or the internal clock oscillator, whichever is used, is divided by 4 internally and becomes the instruction clock. For a 4 MHz clock oscillator, the internal instruction clock fre­quency is 1 MHz. This is the frequency fed into the timer 0 prescaler.

*Digital Output Waveform Using Timer 0 - Internal Clock

Use internal clock divided by 256.Blink an LED at fast rate - delay 8.2 milliseconds (view with oscilloscope).

TIMER 0 EXPERIMENTS

Start Timer 0

162

Page 171: c What Happens

Timer 0 is read continuously. When timer 0 increments to 32, the LED is toggled.

The time interval is roughly 1 microsecond per internal clock cycle divided by 256 (prescaler) equals 256 microseconds per pulse into timer 0 times 32 = 8.2 milliseconds.

The microcontroller is totally occupicd with this timing application when this method is used.

////timer/counter demo ala pict7.asm Cs26a//// free running//// internal clock + 256/include <16F818.h>/fuses INTRCIO main(){

setup oscillator(OSC_4MHZ); //4 MHz clock oscillatorsetup_timer_0(RTCC_INTERNAL|RTCCDIV 256); //internal clock osc,

//prescaler divide clock osc by 256 output b (0x00); //all port B pins lowdo

{outputtoggle(PIN_B0); //toggle port B, bit 0set_timerO(0); //clear and start timer 0while (get_timerO() < 32); //time < 32

}while (TRUE);

Single Time Interval - Internal Clock

Use internal clock divided by 128 and file register counter.Timer 0 increments to OxFF and rolls over.Interrupt is generated and file register used as a counter is incremented. When count reaches 255, LED is turned off.Result is LED blinks once.

163

Page 172: c What Happens

MainProgram

Start Count

InterruptServiceRoutine

Page 173: c What Happens

The on-time is 1 microsecond/internal clock cycle x 128 x 256 x 256 = 8.4 seconds. Timer 0 is incremented 256 times to overflow. The variable "t" is incremented 255 times.

////timer/counter demo //// single time interval //// internal clock -j- 128 //// file register counter /include <16F818.h>/fuses INTRC_IO int t = 0;/INT_TIMER0 i_serv(){

t++;if (t==255) output_low(PIN_B0);

>main(){

setuposcillator(OSC_4MHZ); setup_timer_0(RTCC_INTERNAL|RTCC

outputb (0x00);clear interrupt (INTTIMERO);enableinterrupts (GLOBAL);enable_interrupts (INT_TIMER0);settimerO(0);outputhigh(PINBO);while (TRUE);

pictl6.asm Cs26b

//declare t for counter //enable timer 0 interrupt //interrupt service function

//increment counter //time interval completed? //LED off

//4 MHz clock oscillator DIV_128); //internal clock osc, //prescaler divide clock osc by 128 //all port B pins low //clear timer 0 interrupts //enable interrupts

//clear and start timer 0 //LED on

165

Page 174: c What Happens

We will need an external clock with a period of 0.1 second = 100 milliseconds for the example following this one. It makes sense to use a PIC microcontroller, don't you think?

The time interval between toggle operations (period divided by 2) is roughly 1 microsecond per internal clock cycle times 2 (prescaler divide by 2) times 256 rollovers times 98 = 50,176 microseconds = 50 milliseconds = period -s- 2.

Free Running Mode - Internal Clock

Use internal clock divided by 2 and file register counter.Timer 0 increments to OxFF and rolls over.Interrupt is generated and file register used as a counter is incremented.When count reaches 98, port B, bit 0 is toggled. This occurs at one-half period intervals. Result is a square wave output with period = 100 milliseconds.

166

Page 175: c What Happens

MainProgram

Start Count

InterruptServiceRoutine

Page 176: c What Happens

////timer/counter demo I I I I free running 1111 internal clock + 2 1111 file register counter /include <16F818.h>/fuses INTRC 10 int t = 0;/INT_TIMERO i_serv(){

t++;if (t==98)

{output_toggle(PINBO); t = 0;

}}main(){

setup_oscillator(OSC 4MHZ); setup_timer_0(RTCC INTERNAL|RTCC

output_b (0x00); clear interrupt (INT_TIMER0); enable_interrupts (GLOBAL); enable interrupts (INT_TIMER0); set_timer0(0); while (TRUE);

Cs26c

//declare t for counter //enable timer 0 interrupt //interrupt service function

//increment counter //time interval completed?

//toggle port b, pin 0 //clear counter

//4 MHz clock oscillator DIV_2); //internal clock osc, //prescaler divide clock osc by 2 //all port B pins low //clear timer 0 interrupts //enable interrupts

//clear and start timer 0

To observe the square wave output on port B, pin 0, you will need an oscilloscope.

168

Page 177: c What Happens

Use external 0.1 second clock (another PIC microcontroller running the code in the previous example).

Square wave input - increment counter on rising edge (low to high transition).Bypass prescaler.Blink an LED once.

Note: It takes about 25.6 seconds for the LED to turn off, so wait patiently! 256 clock pulses are needed to overflow timer 0.

Single Time Interval - External Clock

MainProgram

L to HRising Edge

Start Count

InterruptServiceRoutine

169

Page 178: c What Happens

////timer/counter demo ala pictl7.asm Cs26d1111 single time intervalI I 1 1 external clock, bypass prescaler1111 blink LED once/include <16F818.h>/fuses INTRC 10/INT_TIMERO //enable timer 0 interrupti_serv() //interrupt service function{

output_low(PIN_BO); //LED offdisable_interrupts (INT_TIMERO); //disable timer 0 interrupt

}main( ){

setup_oscillator(0SC4MHZ); //4 MHz clock oscillatorsetup_timer_0(RTCC_EXT L_T0_H|RTCC_DIV 1); //external clock osc, low to hi

output_b (0x00); clear_interrupt (INT TIMERO); enable_interrupts (GLOBAL); enable_interrupts (INTTIMERO); set_timerO(0); outputhigh(PINBO); while (TRUE);

Free Running Mode - Internal Clock

Use internal clock divided by 128.Output to port B, bit 0.Interrupts are used. The microcontroller is free to do other things when not servicing

interrupts (not done in this example).

//prescaler bypassed //all port B pins low //clear timer 0 interrupts //enable interrupts

//clear and start timer 0 //LED on

170

Page 179: c What Happens

MainProgram

InterruptServiceRoutine

Start Count

171

Page 180: c What Happens

////timer/counter demo alaI I I I free running 1111 internal clock -j- 128 1111 10 counts to overflow /include <16F818.h>/fuses INTRC 10 /INT_TIMERO i_serv(){

output toggle(PIN_B0); settimerO(Oxf6);

>main(){

setup_oscillator(0SC4MHZ); setup_timer_0(RTCC_INTERNAL|RTCC

output_b (0x00); clear_interrupt (INT TIMER0); enable_interrupts (GLOBAL); enable_interrupts (INTTIMERO); while (TRUE);

pict 20.asm Cs26e

//enable timer 0 interrupt //interrupt service function

//toggle LED//load timer 10 coynts (decimal) to // rollover

//4 MHz clock oscillator DIV_128); //internal clock osc, //prescaler divide clock osc by 128 //all port B pins low //clear timer 0 interrupts //enable interrupts

Run the program and look at port B, bit 0 with a scope.

Examples:

■ Load 0xF6, prescaler + 128 (10 counts to overflow)

l.usec x 128/count x 10 = 1.2 8 msec

This is the time between each HI/LO or LO/HI transition at the port line. The time to execute the interrupt service routine adds to this slightly.

172

Page 181: c What Happens

Load 0x00, prescaler + 128

0x00 = 256 decimal 128 .̂sec x 256 = 33 msecCounts to overflow

Load 0x00, prescaler bypassed (-^1)

1 j.isec x 256 = 256+ (.isec (no allowance for program overhead)

Load 0x40, prescaler -4- 2 (192 counts to overflow)

1 (.isec x 192 x 2 = 384+ (.isec (no allowance for program overhead)

384+ usee

Page 182: c What Happens

Counting Events (Pulses)

Use the pulser described in Appendix A as a source of negative-going pulses. The negative- going pulses will increment the counter on the falling edge (high to low transition).

174

Page 183: c What Happens

ala pict21.asm////event counting demo /include <16F818.h>/fuses INTRC_IO /byte portb = 0x06 /byte timerO = 0x01 main(){

setup_timer 0(RTCCEXT_H_TO_L|RTCC_DIV_1);

Cs26f

// port B address

output_b (0x00);set_timer0(0);while (input(PINA0)==0);portb = timerO;while (TRUE);

//external pulses, //prescaler 1 = bypass //all port B pins low //clear and start timer 0 //wait for switch to open //port B = timer 0

1). Power-up with switch closed.

• Open switch - all LEDs should he off.

2). Power-up with switch closed.

• Pulse X (few) times.• Open switch. LEDs display pulse count X.

GOING FURTHER

Wouldn't it be nice if our timer/counter could count beyond 255! A 16-bit timer counter known as Timer 1 (TMR1) is available in many of the PIC devices. It can count up to 65,535. TMR1 is usually accompanied by an 8-bit timer/counter called Timer 2 (TMR2) and a capture/compare/pulse width modulation module (CCP) which makes a lot of very useful appli­cations relatively easy to implement. How-to information using assembly language is contained in our book entitled Time'n and Count'n.

175

Page 184: c What Happens

ANALOG TO DIGITAL CONVERSION

Analog to digital (A/D) conversion is essential to using PIC microcontrollers because they can only process digital information. Analog signals, usually voltages from sensors, must be con­verted to binary numbers digestible by the PIC microcontroller. As usual, this may be done by one of several methods with the usual cost/accuracy/resolution/PCB in2/etc. tradeoffs to be made.

V

The PIC16F818 has 5 pins which may (or may not) be used as A/D channels. These are port A, bits 4,3,2,1,0. The five analog inputs are multiplexed into one sample and hold circuit. The out­put of the sample and hold is the input to a successive approximation converter The reference voltage may be the logic supply (5 volt for our example) to the PIC16F818 (range 0-5V) or an external reference via pins RA2 and RA3. If an external reference is used, only 4 A/D channels are available. The intricacies of using an external voltage reference are beyond the scope of this book.

Important electrical specs are:

vain 0 to 5V if Vre£ = 5V logic supply Vref 5V logic supply for this example Maximum source impedance 2.5K

A pre-processor directive and four built-in functions are used to control the A/D conversion process:

• #dcvice with the chip option ADC=x determines whether the A/D conversion resultis 8 or 10 bits. 8-bit mode is the default.

• setup adc {mode) where mode options are device dependent and include things likeA/D off and conversion clock speed (conversion sample time).

• setup adc ports (value) determines which pins having analog capability are actuallyused for A/D in the application.

• set adc channel (chan) determines which A/D channel will be read next.• read_adc ([mode]) controls the conversion proccss.

Details are in the CCS compiler manual.

The options for each built-in function are in the device .h file (PIC16F818) for the example which follows.

176

Page 185: c What Happens

A simple example follows which uses one A/D channel (ANO) to measure the voltage on the wiper of a potentiometer, gives an 8-bit result, and displays the least significant 6 bits of the result via 6 LEDs. The voltage is measured once every 200 milliseconds.

+5VDC

177

Page 186: c What Happens

////A/D test Cs27/include <16F818.h>/device ADC=8 /use delay (internal=4mhz) main(){

byte result;setup adc(ADC_CLOCK_DIV_32); setup_adc_ports (ANO); set_adc_channel (0); delay us(10 ) ;

read do

{result = read_adc(); output_b (result); delay_ms(2 00);

}while (TRUE);

}

For this example, notice:

• A pre-processor directive is used to specify that the result is to be 8 bits.8-bit A/D is the default. It is specified here to make you aware of what is taking place. An 8-bit result provides 256 possibilities.

• A pre-processor directive is used to:- Select the internal clock oscillator- Select the frequency for time delay purposes.

• For A/D conversion, the clock oscillator frequency is divided by 32 as we are notin a hurry and doing so will avoid some timing issues.

• A pin used as an analog channel must be an input pin (taken care of by the compiler).• A time delay is used between selecting the A/D channel to be read and the first readingof the A/D so as to conform to A/D use rules as spelled out in the PIC16F818 Data Sheet.

• Comments in the code provide the rest.

Allow the devicc to come out of reset and observe the LEDs as you turn the potentiometer shaft. The count read from the A/D converter is displayed in binary (least significant 6 bits).

//A/D read returns 8 bits//clock oscillator internal, frequency

//declare variable result (a byte) //A/D - clock divided by 32 //use analog channel 0 //prepare to read analog channel 0 //short delay between select channel &

//start and read A/D //bits to port ^ //delay between reads

178

Page 187: c What Happens

INSERTING ASSEMBLY CODE IN C CODE

For those of you who have experience with PIC assembly language, it may be useful to insert some assembly code in a C program that you are writing. The most common reason for doing this is to optimize a situation where timing is critical. If you have a really neat trick that you have created in assembly language and have not figured out a way to do it in C, or think it can't be done in C, there is a solution.

Two pre-processor directives are used.

• #asm• #endasm

The code between the directives is treated as assembly code by the compiler.

A very simple example follows:

////assembler in C program /include <16F818.h>/fuses INTRC_IO /byte portb = 0x06 main(){

set_tris_b (0x00);/asmmovlw OxOf movwf portb /endasm while (TRUE);

}

ala pictl.asm

//port B address

//port B outputs

//bit pattern to port B

//circle, always

Cs28

Note that the portb identifier/label works in both C and assembly.

179

Page 188: c What Happens

APPENDIX A - PULSER

It is easy to build a simple pulser circuit which provides both positive-going and negative-going pulse outputs. The circuit is built around a 74HC14 hex Schmitt trigger inverter IC and a single pole, single throw momentary contact toggle switch. The switch is spring-loaded to the normal­ly closed position. Pushing the switch lever and releasing it results in one pulse being generated. Either the positive-going or negative-going output is connected to the PIC microcontroller cir­cuit being tested as determined by the application.

The debouncing circuit is followed by a resistor and capacitor which function as differentiator creating a narrow pulse of 10 or more microseconds duration.

The circuit can be constructed on a solderless breadboard or constructed using a more permanent method of your choosing.

PULSER CIRCUIT

ContactSwitch 74HC14

180

Page 189: c What Happens

APPENDIX B - SOURCES

CCS, Inc.P.O. Box 2452Brookfield, WI 53008Sales 262 522 6500 ext. 35Tech Support 262 522 6500 ext. 32FAX 262 522 6504Web www.ccsinfo.com/piccemail [email protected] Brooks Avenue SouthThief River Falls, MN 56701-0677Tel 800 344 4539Web http://www.digikey.comJameco Electronics1355 Shoreway Road Belmont CA 94002-4100 Tel 800 831 4242 Fax 800 237 6948 Web http://www.jameco.com email [email protected] P. Jones & Assoc IncP.O. Box 12685Lake Park FL 33403Tel 800 652 6733Fax 800 432 9937Order Online http://www.mpja.com email [email protected] Technology Inc.2355 W. Chandler Blvd.Chandler AZ 85224Tel 480 792 7200Web http://www.microchip.com

C Compilers Development Systems

Electronic Components PIC Microcontrollers

Electronic Components PIC Microcontrollers

Electronic Components

PIC Microcontrollers PIC Programmer Development Systems In-Circuit Debuggers

181

Page 190: c What Happens

APPENDIX C HEXADECIMAL NUMBERS

Hexadecimal Binary Decimal

0 0000 01 0001 12 0010 23 0011 34 0100 45 0101 56 0110 67 0111 78 1000 89 1001 9A 1010 10B 1011 11C 1100 12D 1101 13E 1110 14F 1111 15

Page 191: c What Happens

APPENDIX D PROGRAM LISTINGS vs. PAGE NUMBER

Program Page Program PageNumber Number Number Number

Csla 34,73 Cs20a 126Cslb 74 Cs20b 126Cs2a 75 Cs2 la 128Cs2b 77 Cs2 lb 129Cs2c 78 Cs2 lc 130Cs2d 79 Cs2 Id 132Cs2e 80 Cs22a 136Cs2f 81 Cs22b 138Cs3 83 Cs22 c 139Cs4 85 Cs23a 142Cs5 87 Cs23b 144Cs6 88 Cs23c 145Cs7a 90 Cs2 3d 145Cs7b 91 Cs2 4a 146Cs8a 92 Cs24b 147Cs8b 93 Cs2 4c 148Cs9a 95 Cs25 157Cs9b 96 Cs26a 163CslOa 97 Cs26b 165CslOb 97 Cs26c 168Csl 1 99 Cs26d 170Csl2 101 Cs26e 172Cs 13 101 Cs26f 175Cs 14 103 Cs2 7 178Csl5 105 Cs2 8 179Cs 16 106Csl7a 108Csl7b 109Csl7c 109Csl7d 110Csl8 112Csl9a 122Csl9b 123Csl9c 123Csl9d 124

183

Page 192: c What Happens

4