The microcontroller is simply a computer on a
chip. It is one of the most important developments in electronics since the
invention of the microprocessor itself. It is essential for the operation of
such devices as mobile phones, DVD players, video cameras, and most
self-contained electronic systems. The small LCD screen is good clue to the
presence of an MCU (Microcontroller Unit) – it needs a programmed device to
control it. Working sometimes with other chips, but often on its own, the MCU
provides the key element in the vast range of small, programmed devices which
are now commonplace.
Although small, microcontrollers are complex,
and we have to look carefully at the way the hardware and software (control
program) work together to understand the processes at work. This book will show
how to connect the popular PIC range of microcontrollers to the outside world,
and put them to work. To keep things simple, we will concentrate on just one
device, the PIC 16F877, which has a good range of features, which allow most of
the essential techniques to be explained. It has a set of serial ports built
in, which are used to transfer data to and from other devices, as well as
analogue inputs, which allow measurement of inputs such as temperature. All
standard types of microcontroller work in a similar way, so analysis of one
will make it possible to understand all the others.
The PIC 16F877 is also a good choice for
learning about microcontrollers, because the programming language is relatively
simple, as compared with a microprocessor such as the Intel Pentium ™, which is
used in the PC. This has a powerful, but complex, instruction set to support
advanced multimedia applications. The supporting documentation for the PIC MCU
is well designed, and a development system, for writing and testing programs,
can be downloaded free from the Microchip website (www.microchip.com).
1.1 Processor
System
The microcontroller contains the same main
elements as any computer system:
·
Processor
·
Memory
·
Input/Output
In a PC, these are provided as separate chips,
linked together via bus connections on a printed circuit board, but under the
control of the microprocessor (CPU). A bus is a set of lines which carry data
in parallel form which are shared by the peripheral devices. The system can be
designed to suit a particular application, with the type of CPU, size of memory
and selection of input/output (I/O) devices tailored to the system
requirements.
In the microcontroller, all these elements are
on one chip. This means that the MCU for a particular application must be
chosen from the available range to suit the requirements. In any given circuit,
the microcontroller also tends to have a single dedicated function (in contrast
to the PC); this type of system is described as an embedded application.
Figure
1.1 Block diagram of
a basic microprocessor system

1.1.1 Processor
In a microprocessor system or a microcontroller,
a single processor block is in charge of all input, output, calculations and
control. This cannot operate without a program, which is a list of instructions
that is held in memory. The program consists of a sequence of binary codes that
are fetched from memory by the CPU in sequence, and executed (Figure 1.2).

The instructions are stored in numbered memory
locations, and copied to an instruction register in the CPU, via the data bus.
Here, the instruction controls the selection of the required operation within
the control unit of the processor. The program codes are located in memory by
outputting the address of the instruction on an address bus. The address is
generated in the program counter, a register which starts at zero and is
incremented or modified during each instruction cycle. The busses are parallel
connections which transfer the address or data word in one operation. A set of
control lines from the CPU are also needed to assist with this process; these
control lines are set up according to the requirements of the current
instruction.
Decoding the instruction is a hardware process,
using a block of logic gates to set up the control lines of the processor unit,
and to fetch the instruction operands. The operands are data to be operated on
(or information about where to find it) which follows most instructions.
Typically, a calculation or logical operation is carried out on the operands,
and a result stored back in memory, or an I/O action set up. Each complete
instruction may be one, two or more bytes long, which includes the operation
(instruction) code itself (op-code) and the operand/s (one byte = 8 bits).
Thus, a list of instructions in memory are
executed in turn to carry out the required process. In a word processor, for
example, keystrokes are read in via the keyboard port, stored as character
codes, and sent to a screen output for display. In a game, input from the
switches on the control pad are processed and used to modify the screen. In
this case, speed of the system is a critical factor.
1.1.2 Memory
There are two types memory, volatile and
non-volatile. Volatile memory loses its data when switched off, but can be
written by the CPU to store current data; this is RAM (Read & Write
Memory). ROM (Read Only Memory) is non-volatile, and retains its data when
switched off.
Table
1.1 Memory and data
storage technologies
|
|
ROM |
Flash ROM |
RAM |
CD-ROM |
DVD-RW |
HDD |
|
Description |
Chip |
Chip |
Chip |
Optical disk |
Optical disk |
Magnetic disk |
|
Sample
size* |
128kb |
128Mb |
512Mb |
650Mb |
4.7Gb |
30Gb |
|
Non-volatile |
ü |
ü |
x |
ü |
ü |
ü |
|
Write
(many) |
once |
ü |
ü |
once |
ü |
ü |
|
Large
(bytes) |
x |
? |
x |
ü |
ü |
ü |
|
Cheap
(per bit) |
? |
x |
? |
ü |
ü |
ü |
|
Fast
(access) |
? |
ü |
ü |
x |
x |
x |
*
1 byte = 8 bits
*
1 kb = 1 kilobyte = 1024 bytes
In a PC, a small ROM is used to get the system
started when it is switched on; it contains the BIOS (Basic Input Output
System) program. However, the main
operating system (OS), for example Windows™, and application program (eg Word)
have to be loaded into RAM from hard disk drive (HDD), which takes some time,
as you may have noticed!
So why not put the OS in ROM, where it would be
instantly available? Well, RAM is faster, cheaper and more compact, and the OS
can be changed or upgraded if required. In addition, an OS such as Windows is
very large, and some elements are only loaded into RAM as needed. In addition,
numerous applications can be stored on disk, and loaded only as required.
The ideal memory is non-volatile, read and
write, fast, large and cheap. Unfortunately, it does not exist! Therefore we
have a range of memory technologies as shown in Table 1.1, which provide
different advantages, which may all be used with a standard PC. The main trade
off is cost, size and speed of access. Flash ROM, as used in memory sticks and
MP3 players, is closest to the ideal, having the advantages of being
non-volatile and re-writable. This is why it is used as program memory in
microcontrollers which need to be reprogrammed, such as the PIC 16F877.
1.1.3 Input
and Output
Without some means of getting information and
signals in and out, a data processing or digital control system would not be
very useful. Ports are based on a data register, and set of control registers,
which pass the data in and out in a controlled manner, often according to a
standard protocol (method of communication).
There are two main types of port, parallel and
serial. In a parallel port, the data is usually transferred in and out 8 bits
at a time, while in the serial port it is transmitted one bit at a time on a
single line. Potentially, the parallel port is faster, but needs more pins; on
the other hand, the port hardware and driver software are simpler, because the
serial port must organise the data in groups of bits, usually one byte at a
time, or in packets, as in a network.
(a) Parallel (b) Serial


Taking printers as an example, the old standard
is a parallel port (Centronics), which provides data to the printer one byte (8
bits) at a time via a multi-pin connector. The new standard, USB (Universal
Serial Bus) is a serial data system, sending only one bit at a time.
Potentially, the parallel connection is 8 times faster, but USB operates at up
to 480 megabits (Mb) per second, and the printer is slow anyway, so there is no
problem. One advantage of using USB is that it provides a simple, robust
connector and this outweighs the fact that the interface protocol (driver
software) is relatively complex, because this is hidden from the user. USB also
provides power to the peripheral, if required, and the printer can be
daisy-chained with other devices. USB also automatically configures itself for
different peripherals, such as scanners and cameras.
In the parallel port operating in output mode,
the data byte is loaded from the internal data bus under the control of a
read/write signal from the CPU. The data can then be seen on the output pins by
the peripheral; for testing, a logic probe, logic analyser, or just a simple
LED indicator can be used. In input mode, data presented at the input pins from
a set of switches or other data source is latched into the register when the
port is read, and is then available on the data bus for collection by the CPU.
One of the functions of the port is to separate the internal data bus from the
external hardware, another is to temporarily store the data. The data can then
be transferred to memory, or otherwise processed, as determined by the CPU
program.
The serial port register also loads data from
the internal bus in parallel, but then sends it out one bit at a time,
operating as a shift register. If an asynchronous serial format is used, such
as RS232 (COM ports on old PCs), start and stop bits are added so that bytes
can be separated at the receiving end. An error check bit is also available, to
allow the receiver to detect corrupt data. In receive mode, the register waits
for a start bit, and then shifts in the data at the same speed as it is sent.
This means the clock rate for the send and receive port must be the same. The
USART (Universal Synchronous/Asynchronous Receive/Transmit) protocol will be
described in more detail later.
A USB or network port is more sophisticated, and
arranges the data bytes in packets of, say, 1k bytes, which are sent in a form
which is self clocking, that is, there is a transition within each bit (1 or
0), so each can be picked up individually. An error correction code follows the
data which allows mistakes to be corrected, rather just detected. This reduces
the need for retransmission of incorrectly received data, as required by simple
error detection. Addressing information preceding the data allows multiple
receivers to be used.
The PIC 16F877, in common with most current
MCUs, does not have USB or network interfaces built in, so we can avoid
detailed consideration of these complex protocols. It does nevertheless have a
good selection of other interfaces, which will be discussed in detail and
sample programs provided.
1.2 PIC
16F877 Architecture
Microcontrollers contain all the components
required for a processor system in one chip: a CPU, memory and I/O. A complete
system can therefore be built using one MCU chip and a few I/O devices such as
a keypad, display and other interfacing circuits. We will now see how this is
done in practice in our typical microcontroller.
1.2.1 PIC
16F877 Pin Out
Let us first consider the pins that are seen on
the IC package, and we can then discover how they relate the internal
architecture. The chip can be obtained in different packages, such as
conventional 40-pin DIP (Dual In-Line Package), square surface mount or socket
format. The DIP version is recommended for prototyping, and is shown in Figure
1.01.
|
Reset = 0, Run = 1 |
MCLR
|
1 |
40 |
RB7
|
Port
B, Bit 7 (Prog. Data, Interrupt) |
|
Port A, Bit 0 (Analogue
AN0) |
RA0 |
2 |
39 |
RB6 |
Port
B, Bit 6 (Prog. Clock, Interrupt)) |
|
Port A, Bit 1 (Analogue
AN1) |
RA1 |
3 |
38 |
RB5 |
Port
B, Bit 5 (Interrupt) |
|
Port A, Bit 2 (Analogue
AN2) |
RA2 |
4 |
37 |
RB4 |
Port
B, Bit 4 (Interrupt) |
|
Port A, Bit 3 (Analogue
AN3) |
RA3 |
5 |
36 |
RB3 |
Port
B, Bit 3 (LV Program) |
|
Port A, Bit 4 (Timer 0) |
RA4 |
6 |
35 |
RB2 |
Port
B, Bit 2 |
|
Port A, Bit 5 (Analogue
AN4) |
RA5 |
7 |
34 |
RB1 |
Port
B, Bit 1 |
|
Port E, Bit 0 (AN5, Slave
control) |
RE0 |
8 |
33 |
RB0 |
Port
B, Bit 0 (Interrupt) |
|
Port E, Bit 1 (AN6, Slave
control) |
RE1 |
9 |
32 |
VDD |
+5V
Power Supply |
|
Port E, Bit 2 (AN7, Slave
control) |
RE2 |
10 |
31 |
Vss |
0V Power Supply |
|
+5V Power Supply |
VDD |
11 |
30 |
RD7 |
Port
D, Bit 7 (Slave Port) |
|
0V Power Supply |
Vss |
12 |
29 |
RD6 |
Port
D, Bit 6 (Slave Port) |
|
(CR clock) XTAL circuit |
CLKIN |
13 |
28 |
RD5 |
Port
D, Bit 5 (Slave Port) |
|
XTAL circuit |
CLKOUT |
14 |
27 |
RD4 |
Port
D, Bit 4 (Slave Port) |
|
Port C, Bit 0 (Timer 1) |
RC0 |
15 |
26 |
RC7 |
Port
C, Bit 7 (Serial Ports) |
|
Port C, Bit 1 (Timer 1) |
RC1 |
16 |
25 |
RC6 |
Port
C, Bit 6 (Serial Ports) |
|
Port C, Bit 2 (Timer 1) |
RC2 |
17 |
24 |
RC5 |
Port
C, Bit 5 (Serial Ports) |
|
Port C, Bit 3 (Serial
Clocks) |
RC3 |
18 |
23 |
RC4 |
Port
C, Bit 4 (Serial Ports) |
|
Port D, Bit 0 (Slave Port) |
RD0 |
19 |
22 |
RD3 |
Port
D, Bit 3 (Slave Port) |
|
Port D, Bit 1 (Slave Port) |
RD1 |
20 |
21 |
RD2 |
Port
D, Bit 2 (Slave Port) |
Most of the pins are for input and output
arranged as 5 ports: A(5), B(8), C(8), D(8) and E(3), giving a total of 32 I/O
pins. These can all operate as simple digital I/O pins, but most have more than
one function, and the mode of operation of each is selected by initialising
various control registers within the chip. Note, in particular, that Port A and
E become ANALOGUE INPUTS by default (on power up or reset), so they have to set
up for digital I/O if required.
Port B is used for downloading the program to
the chip flash ROM (RB6 and RB7), and RB0 and RB4-RB7 can generate an
interrupt. Port C gives access to timers and serial ports, while Port D can be used as a slave port, with
Port E providing the control pins for this function. All these options will be
explained in detail later.
The chip has two pairs of power pins (VDD
= +5V nominal and Vss = 0V), and either
pair can be used. The chip can actually work down to about 2V supply, for
battery and power saving operation. A low frequency clock circuit using only a
capacitor and resistor to set the frequency can be connected to CLKIN, or a
crystal oscillator circuit can be connected across CLKIN and CLKOUT. MCLR is
the reset input; when cleared to 0, the MCU stops, and restarts when MCLR = 1.
This input must be tied high allow the chip to run if an external reset circuit
is not connected, but it is usually a good idea to incorporate a manual reset
button in all but the most trivial applications.
1.2.2 PIC
16F877 Block Diagram
A block diagram of the 16F877 architecture is
given in the data sheet, Figure 1-2 (downloadable from www.microchip.com).
A somewhat simplified version is given in Figure 1.5, which emphasises the
program execution mechanism.

The main program memory is flash ROM, which
stores a list of 14-bits instructions. These are fed to the execution unit, and
used to modify the RAM file registers. These include special control registers,
the port registers and a set of general purpose registers which can be used to
store data temporarily. A separate working register (W) is used with the
Arithmetic Logic Unit (ALU) to process data. Various special peripheral modules
provide a range of I/O options.
There are 512 RAM File Register addresses (0 to
1FFh), which are organised in 4 banks (0-3), each bank containing 128 addresses. The default (selected on
power up) Bank 0 is numbered from 0 to 7Fh, Bank 1 from 80h to FFh and so on.
These contain both Special Function Registers (SFRs), which have a dedicated
purpose, and the General Purpose Registers (GPRs). The file registers are
mapped in Figure 2-3 of the data sheet. The SFRs may be shown in the block
diagram as separate from the GPRs, but they are in fact in the same logical
block, and addressed in the same way. Deducting the SFRs from the total number
of RAM locations, and allowing for some registers which are repeated in more
than one bank, leaves 368 bytes of GPR (data) registers.
1.2.3 Test
Hardware
We need to define the hardware in which we will
demonstrate PIC program operation. Initially, a block diagram is used to
outline the hardware design (Figure 1.6). The schematic symbol for the MCU is
also shown, with the pins to be used indicated. For this test program, we simply need inputs which switch between
0V and +5V, and a logic indication at the outputs. For simulation purposes, we
will see that the clock circuit does not have to be included in the schematic;
instead, the clock frequency must be input to the MCU properties dialogue. The
power supply pins are implicit – the simulated MCU operates at +5V by default.
Unused pins can be left open circuit, as long as they are programmed as inputs.
The full schematic is shown in Chapter 3, Figure
3.1.
(a)
Block Diagram (b) PIC 16F877 MCU Pinout


The first test program, BIN1, will simply light
a set of LEDs connected to Port B in a binary count sequence, by incrementing
Port B data register. The second program, BIN4, will use two input push buttons
attached to Port D to control the output (start, stop and reset). The program
will also include a delay so that the output is slower, and visible to the
user. Detailed design of the interfacing will be covered later. A simple CR
clock will be used, which set to 40kHz ( C = 4.7nF, R »
5kW
(preset), CR = 25ms ). This will give an instruction execution
time of 100ms.
1.2.4 The
PIC Program
The program is written as a source code (a
simple text file) on a PC host computer. Any text editor such as Notepad ™ can
be used, but an editor is provided with the standard PIC development system
software MPLAB (downloadable from www.microchip.com). The instructions
are selected from the pre-defined PIC instruction set (Table 13-2 in the data
sheet) according to the operational sequence required. The source code file is
saved as PROGNAME.ASM. More details of the assembler program syntax are given
later.
The source code is assembled (converted to
machine code) by the assembler program MPASM, which creates the list of binary
instruction codes. As this is normally displayed as hexadecimal numbers, it is
saved as PROGNAME.HEX. This is then downloaded to the PIC chip from the PC by
placing the MCU in a programming unit which is attached to the PC serial port,
or by connecting the chip to a programmer after fitting it in the application
board (in-circuit programming). The hex code is transferred in serial form via
Port B into the PIC flash program memory. A list file is created by the
assembler, which shows the source code and machine code in one text file. The
list file for a simple program which outputs a binary count at Port B is shown
in Program 1.1.
Memory Hex Line Address Operation Operand
Address Code Number Label Mnemonic
-------------------------------------------------------------------------------
00001 PROCESSOR 16F877
00002
0000 3000 00003 MOVLW 00
0001 0066 00004 TRIS 06
00005
0002 0186 00006 CLRF 06
0003 0A86 00007
again INCF 06
0004 2803 00008 GOTO again
00009
00010 END
Note: Lines 00001 and 00010 are assembler directives
The program listing includes the source code at
the right, with source line numbers, the hex machine code and the memory
location where each instruction is stored (0000 – 0004). Notice that some
statements are assembler directives, not instructions: PROCESSOR to specify the
MCU type and END to terminate the source code. These are not converted to machine
code.
The ‘877 has 8k of program memory, that is, it
can store a maximum of 1024 x 8 = 8192 14-bit instructions. By default, it is
loaded, and starts executing, from address zero. In real-time (control)
applications, the program runs continuously, and therefore loops back at the
end. If it does not, be careful – it will run through the blank locations and
start again at the beginning!
Let us look at a typical instruction to see how
the program instructions are executed.
Source code: MOVLW 05A
Hex code: 305A
(4 hex digits)
Binary code: 0011
0000 0101 1010 (16 bits)
Instruction: 11
00xx kkkk kkkk (14 bits)
The instruction means: Move a Literal (given
number, 5Ah) into the Working register.
The source code consists of a mnemonic MOVLW and
operand 05A. This assembles into the hex code 305A, and is stored in binary in
program memory as 11 0000 0101 1010. Since each hex digit represents four
binary bits, the leading two bits are zero, and the leading digit will only
range from 0-3 for a 14-bit number.
In the instruction set (data sheet, table 13-2),
it can seen that the first 4 bits (11 00) are the instruction code, the next
two are unused (xx, appearing as 00 in the binary code) and the last 8 are the
literal value (5A). The literal is represented as ‘kkkk kkkk’ since it can have
any value from 00000000 to 11111111 (00 – FF).
The format of other instructions depends mainly
on the number of bits required for the operand (data to be processed). The
number of op-code bits can vary from three to all fourteen, depending on the
number of bits needed for the operand. This is different from a conventional
processor, such as the Pentium, where the op-code and operand are each created
as a whole number of bytes. The PIC instruction is more compact, as is the
instruction set itself, for greater speed of operation. This defines it as a
RISC (Reduced Instruction Set Computer) chip.
1.2.5 Program
BIN4
The program BIN4 contains many of the basic
program elements, and the list file (Program 2) shows the source code, machine
code, memory address and list file line number as before. There are additional
comments to aid program analysis and debugging.
MPASM
03.00 Released BIN4.ASM 8-28-2005
19:54:36 PAGE 1
LOC OBJECT CODE SOURCE TEXT
VALUE LINE
00001
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
00002 ;
00003 ; Source File: BIN4.ASM
00004 ; Author: MPB
00005 ; Date: 28-5-05
00006 ;
00007
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
00008 ;
00009 ; Slow output binary count is stopped,
started
00010 ; and reset with push buttons.
00011 ;
00012 ; Processor: PIC 16F877
00013 ;
00014 ; Hardware: PIC Demo System
00015 ; Clock: RC = 40kHz
00016 ; Inputs: Port D: Push Buttons
00017 ; RD0, RD1
(active low)
00018 ; Outputs: Port B: LEDs (active high)
00019 ;
00020 ; WDTimer: Disabled
00021 ; PUTimer: Enabled
00022 ; Interrupts: Disabled
00023 ; Code Protect: Disabled
00024 ;
00025 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
00026
00027 PROCESSOR 16F877 ; Define MCU type
2007 3733 00028
__CONFIG 0x3733 ; Set
config fuses
00029
00030 ; Register Label
Equates....................................
00031
00000006 00032 PORTB
EQU 06 ; Port B Data Register
00000086 00033 TRISB EQU 86
; Port B Direction Register
00000008 00034 PORTD
EQU 08 ; Port D Data Register
00000020 00035 Timer
EQU 20 ; GPR used as delay counter
00036
00037 ; Input Bit Label
Equates ..................................
00038
00000000 00039 Inres
EQU 0 ; 'Reset' input button = RD0
00000001 00040 Inrun
EQU 1 ; 'Run' input button = RD1
00041
00042
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
00043
00044 ; Initialise Port
B (Port A defaults to inputs).............
00045
0000 1683 1303 00046 BANKSEL
TRISB ; Select bank 1
0002 3000 00047
MOVLW b'00000000' ; Port B Direction Code
0003 0086 00048
MOVWF TRISB ; Load the DDR code into F86
0004 1283 1303 00049 BANKSEL
PORTB ; Select bank 0
0006 280B 00050
GOTO reset ; Jump to main loop
00051
00052
00053 ; 'delay'
subroutine ........................................
00054
0007 00A0 00055 delay
MOVWF Timer ; Copy W to timer register
0008 0BA0 00056 down
DECFSZ Timer ; Decrement timer register
0009 2808 00057
GOTO down ; and repeat until zero
000A 0008 00058
RETURN ; Jump
back to main program
00059
00060
00061 ; Start main loop
...........................................
00062
000B 0186 00063 reset
CLRF PORTB ; Clear Port B Data
00064
000C 1C08 00065 start
BTFSS PORTD,Inres ; Test reset button
000D 280B 00066
GOTO reset ; and reset Port B if pressed
000E 1888 00067
BTFSC PORTD,Inrun ; Test run button
000F 280C 00068
GOTO start ; and repeat if not pressed
00069
0010 0A86 00070
INCF PORTB ; Increment output at Port B
0011 30FF 00071
MOVLW 0FF ; Delay count literal
0012 2007
00072 CALL delay ; Jump
to subroutine 'delay'
0013 280C 00073
GOTO start ; Repeat main loop always
00074
00075 END ; Terminate source code
Note that two types of labels are used in
program to represent numbers. Label equates are used at the top of the program
to declare labels for the file registers which will be used in the program.
Address labels are placed in the first column to mark the destination for GOTO
and CALL instructions.
1.2.6 Chip
Configuration Word
In Program BIN4, the assembler directive
__CONFIG is included at the top of the
program, which sets up aspects of the chip operation which cannot subsequently
be changed without reprogramming. A special area of program memory outside the
normal range (address 2007h) stores a chip configuration word; the clock type,
and other MCU options detailed below, are set by loading the configuration bits
with a suitable binary code. The function of each bit is shown in Table 1.2.,
along with some typical configuration settings. Details can be found in the
data sheet, section 12.
|
Bit |
Label |
Function |
Default |
Enabled |
Typical |
|
15 14 |
- - |
none none |
0 0 |
x x |
0 0 |
|
13 12 |
CP1 CP0 |
Code
protection (4
levels) |
1 1 |
0 0 |
1 1 |
|
11 |
DEBUG |
In-circuit
debugging (ICD) |
1 |
0 |
0 |
|
10 |
- |
None |
1 |
x |
1 |
|
9 |
WRT |
Program
memory write enable |
1 |
1 |
1 |
|
8 |
CPD |
EEPROM
data memory write protect |
1 |
0 |
1 |
|
7 |
LVP |
Low
voltage programming enable |
1 |
1 |
0 |
|
6 |
BODEN |
Brown-out
reset (BoR) enable |
1 |
1 |
0 |
|
5 4 |
CP1 CP0 |
Code
protection (CP) (repeats) |
1 1 |
0 0 |
1 1 |
|
3 |
PWRTE |
Power
up timer (PuT) enable |
1 |
0 |
0 |
|
2 |
WDTE |
Watchdog
timer (WdT) enable |
1 |
1 |
0 |
|
1 0 |
FOSC1 FOSC0 |
Oscillator
type select RC
= 11, HS = 10, XT = 01, LP = 00 |
1 1 |
x x |
0 1 |
Default
= 3FFF (RC clock, PuT disabled, WdT enabled)
Typical
RC clock = 3FF3 (RC clock, ICD disabled, PuT enabled, WdT disabled)
Typical
XT clock = 3731 (XT clock, ICD enabled, PuT enabled, WdT disabled)
CODE PROTECTION
Normally, the program machine code can be read
back to the programming host computer, be disassembled and the original source
program recovered. This can be prevented if commercial or security
considerations require it. The code protection bits (CP1:CP0) disable reads from
selected program areas. Program memory may also be written from within the
program itself, so that data tables or error checking data can be modified.
Obviously, this needs some care, and this option can be disabled via the WRT
bit. Data EEPROM may also be protected from external reads in the same way via
the CPD bit, while internal read and write operations are still allowed,
regardless of the state of the code protection bits.
IN-CIRCUIT DEBUGGING
In-circuit debugging (ICD) allows the program to
be downloaded after the chip has been fitted in the application circuit, and
allows it to be tested with the real hardware. This is more useful than the
previous method which requires the chip to be programmed in a separate
programmer unit before insertion in its socket on the board. With ICD, the chip
can be programmed, and reprogrammed during debugging, while avoiding possible
electrical and mechanical damage caused by removal from the circuit. The normal
debugging techniques of single stepping, breakpoints and tracing are can be
applied in ICD mode. This allows a final stage of debugging in the prototype
hardware, where problems with the interaction of the MCU with the real hardware
can be resolved.
LOW VOLTAGE PROGRAMMING
Normally, when the chip is programmed, a high
voltage (12-14V) is applied to the PGM pin (RB3). To avoid the need to supply
this voltage during in-circuit programming (for example, during remote
reprogramming), a low voltage programming mode is available; however, using
this option means that RB3 is not then available for general I/O functions
during normal operation.
POWER-UP TIMER
When the supply power is applied to the
programmed MCU, the start of program execution should be delayed until the
power supply and clock are stable, otherwise the program may not run correctly.
The Power-up Timer may therefore be enabled (PWRTE = 0) as a matter of routine.
It avoids the need to reset the MCU manually at start up, or connect an
external reset circuit, as is necessary with some microprocessors. An internal oscillator provides a delay
between the power coming on and an internal MCU reset of about 72ms. This is
followed by an oscillator start up delay of 1024 cycles of the clock before
program execution starts. At a clock frequency of 4MHz, this works out to 256 ms.
BROWN-OUT RESET
Brown out refers to a short dip in the power
supply voltage, caused by mains supply fluctuation, or some other supply fault,
which might disrupt the program execution. If the Brown-Out Detect Enable bit
(BODEN) is set, a PSU glitch of longer than about 100ms
will cause the device to be held in reset until the supply recovers, and then
wait for the power-up timer to time out, before restarting. The program must be
designed to recover automatically.
WATCHDOG TIMER
The watchdog timer is designed to automatically
reset the MCU if the program malfunctions, by stopping or getting stuck in
loop. This could be caused by an undetected bug in the program, an unplanned
sequence of inputs or supply fault. A separate internal oscillator and counter
automatically generates a reset about every 18ms, unless this is disabled in
the configuration word. If the watchdog
timer is enabled, it should be regularly reset by an instruction in the program
loop (CLRWDT) to prevent the reset. If the program hangs, and the watchdog
timer reset instruction not executed, the MCU will restart, and (possibly)
continue correctly, depending on the nature of the fault.
RC OSCILLATOR
The MCU clock drives the program along,
providing the timing signals for program execution. The RC (Resistor-Capacitor)
clock is cheap and cheerful, requiring only these two inexpensive external
components, operating with the internal clock driver circuit, to generate the
clock. The time constant (product R x C) determines clock period. A variable
resistor can be used to give a manually adjustable frequency, although it is
not very stable or accurate.
CRYSTAL OSCILLATOR
If greater precision is required, especially if
the program uses the hardware timers to make accurate measurements or generate
precise output signals, a crystal (XTAL) oscillator is needed. Normally, it is
connected across the clock pins with a pair of small capacitors (15pF) to
stabilise the frequency. The crystal acts as a self-contained resonant circuit,
where the quartz or ceramic crystal vibrates at a precise frequency when
subject to electrical stimulation. The oscillator runs at a set frequency with
a typical accuracy of better than 50 parts per million (PPM), which is
equivalent to +/- 0.005%. A convenient value (used in our examples later) is
4MHz; this gives an instruction cycle time of 1ms, making
timing calculations a little easier (each instruction takes four clock cycles).
This is also the maximum frequency allowed for the XT configuration setting.
The PIC 16FXXX series MCUs generally run at a maximum clock rate of 20MHz,
using a high speed (HS) crystal which requires the selection of the HS
configuration option.
CONFIGURATION SETTINGS
The default setting for the configuration bits
is 3FFF, which means that the code protection is off, in-circuit debugging
disabled, program write enabled, low voltage programming enabled, brown out
reset enabled, power up timer disabled, watchdog timer enabled, and RC
oscillator selected. A typical setting for basic development work would enable
in-circuit debugging, enable the power up timer for reliable starting, disable
the watchdog timer, and use the XT oscillator type.
By default, the watchdog timer is enabled. This
produces an automatic reset at regular intervals, which will disrupt normal
program operation. Therefore, this option will usually be disabled (bit 2 = 0).
Conversely, it is generally desirable to enable the power up timer, to minimise
the possibility of a faulty start-up.
1.3 PIC
Instruction Set
Each microcontroller family has its own set of
instructions, which carry out essentially the same set of operations, but using
different syntax. The PIC uses a minimal set of instructions, which makes it a
good choice for learning.
L
= Literal value (follows
instruction), example is labelled num1
W
= Working register, W (default
label)
Labels Register labels must be declared
in include file or by register label equate (eg GPR1 EQU 0C)
Bit labels must be declared in include
file or by bit label equate (eg bit1 EQU 3)
Address labels must be placed at the left
margin of the source code file (eg start, delay)
Operation Example
Move
Move
data from F to W MOVF GPR1,W
Move
data from W to F MOVWF GPR1
Move
literal into W MOVLW num1
Test the
register data MOVF GPR1,F
Register
Clear
W (reset all bits and value to 0) CLRW
Clear F
(reset all bits and value to 0) CLRF GPR1
Decrement
F (reduce by 1) DECF GPR1
Increment
F (increase by 1) INCF GPR1
Swap the
upper and lower four bits in F SWAPF GPR1
Complement
F value (invert all bits) COMF GPR1
Rotate
bits Left through carry flag RLF GPR1
Rotate
bits Right through carry flag RRF GPR1
Clear (
= 0 ) the bit specified BCF GPR1,but1
Set ( =
1 ) the bit specified BSF GPR1,but1
Arithmetic
Add W to
F, with carry out ADDWF GPR1
Add F to
W, with carry out ADDWF GPR1,W
Add L to
W, with carry out ADDLW num1
Subtract
W from F, using borrow SUBWF GPR1
Subtract
W from F, placing result in W SUBWF GPR1,W
Subtract
W from L, placing result in W SUBLW num1
Logic
AND the
bits of W and F, result in F ANDWF GPR1
AND the
bits of W and F, result in W ANDWF GPR1,W
AND the
bits of L and W, result in W ANDLW num1
OR the
bits of W and F, result in F IORWF GPR1
OR the
bits of W and F, result in W IORWF GPR1,W
OR the
bits of L and W, result in W IORLW num1
Exclusive
OR the bits of W and F, result in F XORWF GPR1
Exclusive
OR the bits of W and F, result in W XORWF GPR1,W
Exclusive
OR the bits of L and W XORLW num1
Test &
Skip
Test a
bit in F and Skip next instruction if it is Clear ( = 0 ) BTFSC GPR1,but1
Test a
bit in F and Skip next instruction if it is Set ( = 1) BTFSS GPR1,but1
Decrement
F and Skip next instruction if F = 0 DECFSZ GPR1
Increment
F and Skip next instruction if F = 0 INCFSZ GPR1
Jump
Go to a
labelled line in the program GOTO start
Jump to
the label at the start of a subroutine CALL delay
Return
at the end of a subroutine to the next instruction RETURN
Return
at the end of a subroutine with L in W RETLW num1
Return
From Interrupt service routine RETFIE
Control
No
Operation - delay for 1 cycle NOP
Go into
standby mode to save power SLEEP
Clear
watchdog timer to prevent automatic reset CLRWDT
Note 1 For MOVE instructions data is
copied to the destination but retained in the source register.
Note 2 General Purpose Register
1, labelled ’ GPR1’, represents all file registers (00 - 4F).
Literal value ‘num1’ represents all 8-bit values 00 - FF. File register bits 0
– 7 are represented by the label ‘but1’.
Note 3 The result of arithmetic
and logic operations can generally be stored in W instead of the file register
by adding ‘,W’ to the instruction. The full syntax for register operations with
the result remaining in the file register F is ADDWF GPR1,F etc. F is the
default destination, and W is alternative, so the instructions above are
shortened to ADDWF GPR1 etc. This will generate a message from the assembler
that the default destination will be used.
A version of the PIC instruction set organised
by functional groups is listed in Table 1.3. It consists of 35 separate
instructions, some with alternate result destinations. The default destination
for the result of an operation is the file register, but the working register W
is sometimes an option. Each instruction is described in detail in the MCU data
sheet, section 13.
1.3.1 Instruction
Types
The functional groups of instructions, and some
points about how they work, are described below. The use of most of these
instructions will be illustrated in due course within the demonstration
programs for each type of interface.
MOVE
The contents of a register are copied to
another. Notice that we cannot move a byte directly from one file register to
another, it has to go via the working register. To put data into the system
from the program (a literal) we must use MOVLW to place the literal into W
initially. It can then be moved to another register as required.
The syntax is not symmetrical; to move a byte
from W to a file register, MOVWF is used. To move it the other way, MOVF F,W is
used, where F is any file register address. This means that MOVF F,F is also
available. This may seem pointless, but in fact can be used to test a register
without changing it.
REGISTER
Register operations affect only a single
register, and all except CLRW (clear W) operate on file registers. Clear sets
all bits to zero (00h), decrement decreases the value by 1 and increment
increases it by 1. Swap exchanges the upper and lower four bits (nibbles).
Complement inverts all the bits, which in effect negates the number. Rotate
moves all bits left or right, including the carry flag in this process (see
below for flags). Clear and set a bit operate on a selected bit, where the
register and bit need to be specified in the instruction.
ARITHMETIC & LOGIC
Add and subtract in binary gives the same result
as in decimal or hex. If the result generates an extra bit (eg FF + FF = 1FE),
or requires a borrow (eg 1FE – FF = FF), the carry flag is used. Logic
operations are carried out on bit pairs in two numbers to give the result which
would be obtained if they were fed to the corresponding logic gate (eg 00001111
AND 01010101 = 00000101). If necessary, reference should be made to an
introductory text for further details of arithmetic and logical operations, and
conversion between number systems. Some examples will be seen later.
TEST, SKIP & JUMP
A mechanism is needed to make decisions
(conditional program branches) which depend on some input condition or the
result of a calculation. Programmed jumps are initiated using a bit test and
conditional skip, followed by a GOTO or CALL. The bit test can be made on any
file register bit. This could be a port bit, to check if an input has changed,
or a status bit in a control register.
BTFSC (Bit Test and Skip if Clear) and BTFSS
(Bit Test and Skip if Set) are used to test the bit and skip the next
instruction, or not, according to the state of the bit tested. DECFSZ and INCFSZ
embody a commonly used test – decrement or increment a register and jump
depending on the effect of the result on the zero flag (Z is set if result =
0). Decrement is probably used more often (see BIN4 delay routine), but
increment also works because when a register is incremented from the maximum
vale (FFh) it goes to zero (00h).
The bit test and skip may be followed by a
single instruction to be carried out conditionally, but GOTO and CALL allow a
block of conditional code. Using GOTO label simply transfers the program
execution point to some other point in the program indicated by a label in the
first column of the source code line, but CALL label means that the
program returns to the instruction following the CALL when RETURN is
encountered at the end of the subroutine.
Another option which is useful for making
program data tables is RETLW (Return with Literal in W). See the KEYPAD program
later for an example of this. RETFIE (Return From Interrupt) is explained
below.
CONTROL
NOP simply does nothing for one instruction
cycle (four clock cycles). This may seem pointless, but is in fact very useful
for putting short delays in the program so that, for example, external hardware
can be synchronised or a delay loop adjusted for an exact time interval. In the
LCD driver program (Chapter 4), NOP is used to allow in-circuit debugging to be
incorporated later when the program is downloaded, and to pad a timing loop so
that it is exactly 1ms.
SLEEP stops the program, such that it can be
restarted with a external interrupt. It should also used at the end of any
program does not loop back continuously, to prevent the program execution
continuing into unused locations. The unused locations contain the code 3FFF
(all 1s), which is a valid instruction (ADDLW FF). If the program is not
stopped, it will run through, repeating this instruction, and start again when
the program counter rolls over to 0000.
CLRWDT means clear the Watchdog Timer. If the
program gets stuck in a loop or stops for any other reason, it will be
restarted automatically by the watchdog timer. To stop this happening when the
program is operating normally, the watchdog timer must be reset at regular
intervals of less than, say, 10ms, within the program loop, using CLRWDT.
OPTIONAL INSTRUCTIONS
TRIS was an instruction originally provided to
make port initialisation simpler (see program BIN1). It selects register bank 1
so that the TRIS data direction registers (TRISA, TRISB etc) can be loaded with
a data direction code (0 = output). The manufacturer no longer recommends use
of this instruction, although it is still supported by the current assembler
versions to maintain backward compatibility, and is useful when learning with
very simple programs. The assembler directive BANKSEL can be used in more
advanced programs, because it gives more flexible access to the registers in
banks 1,2,3. It will be used here from Program BIN4 onwards. The other option
is to change the bank select bits in the STATUS register direct, using BSF and
BCF.
OPTION, providing special access to the OPTION
register, is the other instruction which is no longer recommended. It can be
replaced by BANKSEL to select bank 1 which contains the OPTION register, which
can then be accessed directly.
1.3.2 Program
Execution
The PIC instruction contains both the op-code
and operand. When the program executes, the instructions are copied to the
instruction register in sequence, and the upper bits, containing the op-code,
are decoded and used to set up the operation within the MCU. Figure 1.5, which
illustrates the key hardware elements in this process, is derived from the
system block diagram given in the data sheet.
The program counter keeps track of program
execution; it clears to zero on power up or reset. With 8k of program memory, a
count from 0000 to 1FFF (8191) is required (13 bits). The PCL (Program Counter
Low) register (SFR 02) contains the low byte, and this can be read or written
like any other file register. The high byte is only indirectly accessible via
PCLATH (Program Counter Latch High, SFR 0Ah).
SUBROUTINES
Subroutines are used to create functional blocks
of code, and provide good program structure. This makes it easier for the
program to be understood, allows blocks of code to be re-used, and ultimately
allows ready-made library routines to be created for future use. This saves on
programming time and allows us to avoid ‘re-inventing the wheel’ when writing
new applications.
A label is used at the start of the subroutine,
which the assembler then replaces with the actual program memory address. When
a subroutine is called, this destination address is copied into the program
counter, and the program continues from the new address. At the same time, the
return address (the one following the CALL) is pushed onto the stack, which is
a block of memory dedicated to this purpose. In the PIC, there are 8 stack
address storage levels, which are used in turn. The return addresses may thus
be viewed as a stack of items which must be added and removed in the same
sequence.
The subroutine is terminated with a RETURN
instruction, which causes the program go back the original position and
continue. This is achieved by pulling the address from the top of the stack and
replacing it in the program counter. It should be clear that CALL and RETURN
must always be used in sequence in order to avoid a stack error, and a possible
program crash. Conventional microprocessor systems often use general RAM as the
stack, in which case it is possible to manipulate it directly. In the PIC, the
stack is not directly accessible.
A delay subroutine is included in the program
BIN4. The stack mechanism and program memory arrangement is shown in Figure 2-1
in the data sheet, and a somewhat simplified version is shown in Figure 1.6.
|
14 bit program instructions |
13 bit
hex address
|
|
Instruction 1 |
0000h
(RESET) |
|
Instruction 2 |
0001h |
|
Instruction 3 |
0002h |
|
Instruction 4 |
0003h |
|
Instruction 5 |
0004h
(INTERRUPT) |
|
Instruction 6 |
0005h |
|
l |
|
|
Page 0 (2k) |
|
|
l |
|
|
Instruction 2048 |
07FFh
(END PAGE 0) |
|
Instruction 2049 |
0800h
(START PAGE 1) |
|
l |
|
|
Page 1 (2k) |
|
|
l |
|
|
Instruction 4096 |
0FFFh
(END PAGE 1) |
|
Instruction 4097 |
1000h
(START PAGE 2) |
|
l |
|
|
Page 2 (2k) |
|
|
l |
|
|
Instruction 6144 |
17FFh
(END PAGE 2) |
|
Instruction 6145 |
1800h
(START PAGE 3) |
|
l |
|
|
Page 3 (2k) |
|
|
l |
|
|
Instruction 8192 |
1FFFh
(END PAGE 3) |

|
|
|
|
Program Counter (13) |
|
|
|
On subroutine
call or interrupt store return address in next available stack level register
|
|
Return address 1 |
Stack
level 0 |
|
Return address 2 |
Stack
level 1 |
|
Return address 3 |
Stack
level 2 |
|
Return address 4 |
Stack
level 3 |
|
Return address 5 |
Stack
level 4 |
|
Return address 6 |
Stack
level 5 |
|
Return address 7 |
Stack
level 6 |
|
Return address 8 |
Stack
level 7 |
INTERRUPTS
The stack is also used when an interrupt is
processed. This is effectively a call and return which is initiated by an
external hardware signal which forces the processor to jump to a dedicated
instruction sequence, an Interrupt Service Routine (ISR). For example, the MCU
can be set up so that when a hardware timer times out (finishes its count), the
process required at that time is called via a timer interrupt.
When an interrupt signal is received, the
current instruction is completed and the address of the next instruction (the
return address) is pushed into the first available stack location. The ISR is
terminated with the instruction RETFIE (return from interrupt), which causes
the return address to be pulled from the stack. Program execution then restarts
at the original location. However, remember to take into account any changes in
the registers which may have happened in the ISR. If necessary, the registers
must be saved at the beginning of the ISR, and restored at the end, in spare
set of file registers. A simple example using a timer interrupt is seen later
in a test program which generates a pulse output.
PAGE BOUNDARIES
In normal program execution, the operation of
the program counter is automatic, but there are potential problems when a
program branch occurs. Jump instructions (CALL or GOTO) provide only an 11-bit
destination addresses, so the program memory is effectively divided into four
2k blocks, or pages. A jump across the program memory page boundary requires
the page selection bits (PCLATH 4:3) to be modified by the user program. In
addition, if the 8-bit PCL is modified directly, as in table read, care must be
taken if a jump is made from one 256 byte block to another; PCLATH again may
need to be modified explicitly. Sections 2.3 and 2.4 in the 16F877 data sheet
details how to handle these problems.
1.4 Special
Function Registers
As we have seen, the file register set is
divided into special function registers and general purpose registers. The SFRs
have predetermined functions, as specified in the 16F877 data sheet (Figure
2-3), and occupy locations 00-1F in bank 0, 80-9F in bank 1, 100-10F in bank 2
and 180-18F in bank 3 Many are repeated in more than one bank. Their functions
will be explained below in order of significance.
1.4.1 Program
Counter (PCL)
The function the program counter has been
described above, under program execution. PCL contains the low 8 bits of the
program counter, while the upper bits (PC<8-12>) are accessed via PCLATH.
It is incremented during each instruction, and the contents replaced during a
GOTO, CALL (program address) or RETURN (stack).
1.4.2 Status
Register
The status register records the result of
certain operations, MCU power status and includes the bank selection bits. The
bit functions are detailed in the table Register 2-1 in the data sheet.
ZERO FLAG (Z)
This is set when the result of a register
operation is zero, and cleared when it is not zero. The full instruction set
must be consulted to confirm which operations affect the Z flag. Bit test and
skip instructions use this flag for conditional branching, but remember that
there are dedicated instructions for decrement or increment and skip if zero.
Curiously, these do not affect the zero flag itself. A typical use of the zero
flag is to check if two numbers are the same by subtracting and applying bit
test and skip to the Z bit.
CARRY FLAG (C)
This
flag is only affected by add, subtract and rotate instructions. If the
result of an add operation generates a carry out, this flag is set; that is,
when two 8-bit numbers give a nine bit sum. The carry bit must then be included
in subsequent calculations to give the right result. When subtracting, the
carry flag must be set initially, because it provides the borrow digit (if
required) in the most significant bit of the result. If the carry flag is
cleared after a subtract, it means the result was negative, because the number
being subtracted was the larger. An example of this is seen later in the
calculator program.
Taken together, the zero and carry flags allow
the result of an arithmetic operation to be detected as positive, negative or
zero, as shown in Table 1.4. Again, remember that the carry flag must be set
before a subtract operation, so that a borrow can be detected as C = 0.
|
Flag
after operation |
ZERO ( Z ) |
CARRY ( C ) |
RESULT |
COMMENT |
|
ADD |
0 |
0 |
A+B
< 256 |
8-bit
sum, no carry |
|
A+B |
1 |
1 |
A+B
= 256 (100h) |
Exactly,
carry out |
|
|
0 |
1 |
A+B
> 256 |
9-bit
sum, carry out |
|
SUB |
0 |
1* |
A-B
< 256 |
8-bit
difference, no borrow |
|
A-B |
1 |
1* |
A-B
= 0 |
Numbers
equal, no borrow |
|
|
0 |
0* |
A-B
< 0 |
Borrow
taken, result negative |
*
set carry flag before subtracting
DIGIT CARRY (DC)
A file register can be seen as containing 8
individual bits, or 1 byte. It can also be used as 2 x 4-bit nibbles (a small
byte!). Each nibble can be represented as 1 hex digit (0-F). The digit carry
records a carry from the most significant bit of the low nibble (bit 3). Hence
the digit carry allows 4-bit hexadecimal arithmetic to be carried out in same
way as 8-bit binary arithmetic uses the carry flag C.
REGISTER BANK SELECT (RP1:RP0)
The PIC 16F877 file register RAM is divided into
four banks of 128 locations, banks 0-3 (Figure 2-3 in data sheet). At power on
reset, bank 0 is selected by default. To access the others, these register bank
select bits must be changed, as shown in Table 1.5.
|
RP1 |
RP0 |
Bank |
Address |
Total |
Function |
|
0 |
0 |
0 |
00 – 20 20 – 7F |
32 96 |
Special
Function Registers General
Purpose Registers |
|
0 |
1 |
1 |
80 – 9F A0 – EF F0 – FF |
32 80 16 |
SFRs,
some repeat GPRs Repeat
70 - 7F |
|
1 |
0 |
2 |
100 – 10F 110 – 16F 170 – 17F |
16 96 16 |
SFRs,
some repeat GPRs Repeat
70 - 7F |
|
1 |
1 |
3 |
180 – 18F 190 – 1EF 1F0 – 1FF |
16 96 16 |
SFRs,
some repeat GPRs Repeat
70 - 7F |
|
|
|
|
000 – 1FF |
96 368 |
SFRs GPRs |
It can be seen that some registers repeat in
more than one bank, making it easier and quicker to access them when switched
to that bank. For example, the status register repeats in all banks. In
addition, a block of GPRs at the end of each bank repeat, so that their data
contents are available without changing banks.
The register banks are selected by setting and
clearing the bits RP0 and RP1 in the status register. More conveniently, the
pseudo-operation BANKSEL can be used instead. The operand for BANKSEL is any
register in that bank, or its label. In effect, BANKSEL detects the bank bits
in the register address and copies them to the status register bank select
bits.
POWER STATUS BITS
There are two read only bits in the status
register which indicate the overall MCU status. The Power Down (PD) bit is
clear to zero when SLEEP mode is entered. The Time Out (TO) bit is cleared when
a watchdog time out has occurred.
1.4.3 Ports
There are five parallel ports in the PIC 16F877,
labelled A-E. All pins can be used as bit or byte oriented digital input or
output. Their alternate functions are summarised in Table 1.6.
|
|
Bits |
Pins |
Alternate
function/s |
Bit |
Default |
PORTA
|
6 |
RA0
– RA5 |
Analogue
inputs Timer0
clock input Serial
port slave select input |
0,1,2,3,5 4 5 |
Analogue Input |
|
PORTB |
8 |
RB0
– RB7 |
External
interrupt Low
voltage programming input Serial
programming In-circuit
debugging |
0 3 6,7 6,7 |
Digital
I/O |
|
PORTC |
8 |
RC0
– RC7 |
Timer1
clock input/output Capture/Compare/PWM
SPI,I2C
synchronous clock/data USART
asynchronous clock/data |
0,1 1,2 3,4,5 6,7 |
Digital
I/O |
|
PORTD |
8 |
RD0
– RD7 |
Parallel
slave port data I/O |
0-7 |
Digital
I/O |
|
PORTE |
3 |
RE0
– RE2 |
Analogue
inputs Parallel
slave port control bits |
0,1,2 0,1,2 |
Analogue Input |
It can be seen that many of the port pins have
two or more functions, depending on the initialisation of the relevant control
registers. On power up or reset, the port control register bits adopt a default
condition (see Table 2-1 in the data sheet, right hand columns). The TRIS (data
direction) register bits in bank 1 default to 1, setting the ports B, C and D
as inputs. If this is as required, no further initialisation is needed, since
other relevant control registers are generally reset to provide plain digital
I/O by default.
However, there is an IMPORTANT exception. Port A
and Port E are set to ANALOGUE INPUT by default, because the analogue control
register ADCON1 in bank 1 defaults to 0--- 0000. To set up these ports for
digital I/O, this register must be loaded with the code x--- 011x (x = don’t
care), say 06h. If analogue input is required only on selected pins, ADCON1 can
be initialised with bit codes that give a mixture of analogue and digital I/O
on Port A and E. Note that ADCON1 is in bank 1 so BANKSEL is needed to access
it. Initialisation for analogue I/O will be explained in more detail later.
1.4.4 Timers
The PIC 16F877 has three hardware timers (data
sheet, section 5,6 and 7). These are used to carry out timing operations
simultaneously with the program, to make the program faster and more efficient.
A example would be generating a pulse every second at an output.
Timer0 uses an 8-bit register, TMR0, file
register address 01. Its output is an overflow flag, T0IF, bit 2 in the
Interrupt Control Register INTCON, address 0B. The timer register is
incremented via a clock input which is derived either from the MCU oscillator
(fOSC) or an external pulse train at RA4. The register counts from 0
to 255d in binary, then rolls over to 00 again. When the register goes from FF
to 00, T0IF is set.
If the internal clock is used, the register acts
as a timer. Each instruction in the MCU takes four clock cycles to execute, so
the instruction clock is fOSC /4. The timers are driven from the
instruction clock, which can be monitored externally at CLKOUT, if the chip is
operating with an RC clock. If preloaded with a value of say, 155d, TMR0 will
count 100 clock pulses until T0IF is set. If the chip is driven from a crystal
of 4MHz, the instruction clock will be 1MHz, and the timer will overflow after
100ms.
If this were used to toggle an output, a signal with a period of exactly 2x100
= 200ms
(frequency = 5kHz) would be obtained.
Alternatively, a count of external pulses can be
made, and read from the register when finished, or the read triggered by
external signal. Thus, the timers can also be used as counters. Figure 5-1 in
the data sheet shows the full block diagram of Timer0, which shows a pre-scale
register and the watchdog timer. The pre-scaler is a divide by N register,
where N = 2, 4, 8, 16, 32, 64, 128 or 256, meaning that the output count rate
is reduced by this factor. This extends the count period or total count by the
same ratio, giving a greater range to the measurement. The watchdog timer
interval can also be extended, if this is selected as the clock source. The
pre-scale select bits, and other control bits for Timer0 are found in
OPTION_REG. Some typical Timer0 configurations are detailed in Table 1.7
|
OPTION_REG |
Configuration |
Effect |
Applications |
|
11010000 Active
bits in bold |
Internal
clock (fOSC/4) No
prescale |
Timer
mode using instruction clock |
1.
Preload Timer0 with initial value, and count up to 256 2.
Clear Timer0 initially and read count later to measure time elapsed |
|
11010011 |
Internal
clock (fOSC/4) Prescale
= 16 |
Timer
mode using instruction clock with prescale |
Extend
the count period x16 for applications 1 & 2. |
|
11110111 |
External
clock T0CKI
pin |
Counter
mode Prescale
= 256 |
Count
one pulse in 256 at RA4 |
|
11111110 |
Watchdog
timer selected prescale = 64 |
Extend
watchdog reset period to 18x64 = 1152ms |
Watchdog
timer checks program every second |
Timer1 is a 16-bit counter, consisting of TMR1H
and TMR1L (0E AND 0F). When the low byte rolls over from FF to 00, the high
byte is incremented. The maximum count is therefore 65535d, which allows a
higher count without sacrificing accuracy.
Timer2 is an 8-bit counter (TMR2) with a 4-bit
pre-scaler, 4-bit post-scaler and a comparator. It can be used to generate
Pulse Width Modulated (PWM) output which is useful for driving dc motors and
servos, amongst other things (see the data sheet, section 7, for more details).
These timers also can be used in capture and compare modes, which allow
external signals to be more easily measured. There will be further detail
provided with demonstration programs on timed I/O.
1.4.5 Indirect
File Register Addressing
File register 00 (INDF) is used for indirect
file register addressing. The address of the register is placed in the file
select register (FSR). When data is written to or read from INDF, it is
actually written to or read from the file register pointed to by FSR. This is
most useful for carrying out a read or write on a continuous block of GPRs, for
example, when saving data being read in from a port over a period of time.
Since nine bits are needed to address all file registers (000 – 1FF), the IRP
bit in the status register is used as the extra bit. Direct and indirect
addressing of the file registers are compared in the data sheet, Figure 2-6.
1.4.6 Interrupt
Control Registers
The registers involved in interrupt handling are
INTCON, PIR1, PIR2, PIE1, PIE2 and PCON. Interrupts are external hardware
signals which force the MCU to suspend its current process, and carry out an
Interrupt Service Routine (ISR). A interrupt can be generated in various ways,
but, in the PIC, the result is always to jump to program address 004. If more
than one interrupt source is operational, then the source of the interrupt must
be detected and the corresponding ISR selected.
By default, interrupts are disabled, so programs
can be loaded with their origin (first instruction) at address 0000, and the
significance of address 0004 can be ignored. If interrupts are to be used, the
main program start address needs to be 0005, or higher, and a ‘GOTO start’ (or
similar label) placed at address 0000. A ‘GOTO ISR’ instruction can then be
placed at 004, using the ORG directive, which sets the address at which the
instruction will be placed by the assembler.
The Global Interrupt Enable bit (INTCON,7) must
be set to enable the interrupt system. The individual interrupt source is then
enabled. For example, the bit INTCON,T0IE is set to enable the Timer0 overflow
to trigger the interrupt sequence. When the timer overflows, INTCON, T0IF
(Timer0 Interrupt Flag) is set to indicate the interrupt source, and the ISR
called. The flags can be checked by the ISR to establish the source of the
interrupt, if more than one is enabled. A list of interrupt sources and their
control bits is given in Table 1.8.
|
Source |
Enable
Bit
Set |
Flag
Bit
Set |
Interrupt
Trigger Event |
|
TMR0 |
INTCON,5 |
INTCON,2 |
Timer0
count overflowed |
|
RB0 |
INTCON,4 |
INTCON,1 |
RB0
input changed (also uses INTEDG) |
|
RB4-7 |
INTCON,3 |
INTCON,0 |
Port
B high nibble input changed |
Peripherals
|
INTCON,6 |
see |
below |
|
TMR1 |
PIE1,0 |
PIR1,0 |
Timer1
count overflowed |
|
TMR2 |
PIE1,1 |
PIR1,1 |
Timer2
count matched period register PR2 |
|
CCP1 |
PIE1,2 |
PIR1,2 |
Timer1
count captured in or matched CCPR1 |
|
SSP |
PIE1,3 |
PIR1,3 |
Data
transmitted or received in Synchronous Serial Port |
|
TX |
PIE1,4 |
PIR1,4 |
Transmit
buffer empty in Asynchronous Serial Port |
|
RC |
PIE1,5 |
PIR1,5 |
Receive
buffer full in Asynchronous Serial Port |
|
AD |
PIE1,6 |
PIR1,6 |
Analogue
to Digital Conversion completed |
|
PSP |
PIE1,7 |
PIR1,7 |
A read
or write has occurred in the Parallel Slave Port |
|
CCP2 |
PIE2,0 |
PIR2,0 |
Timer2
count captured in or matched CCPR2 |
|
BCL |
PIE2,3 |
PIR2,3 |
Bus
collision detected in SSP (I2C mode) |
|
EE |
PIE2,4 |
PIR2,4 |
Write
to EEPROM memory completed |
The
primary interrupt sources are Timer0 and PortB. Input RB0 is used for single
interrupts, and pins RB4-RB7 can be set up so that any change on these inputs
initiates the interrupt. This could be used to detect when a button on a keypad
connected to PortB has been pressed, and the ISR would then process the input
accordingly.
The remaining interrupt sources are
enabled by the Peripheral Interrupt Enable bit (INTCON, PEIE). These are then
individually enabled and flagged in PIE1, PIE2, PIR1 and PIR2. Many of these
peripherals will be examined in more detail later, but the demonstration
programs do not generally use interrupts, to keep them as simple as possible.
However, if these peripherals are used in more complex programs where multiple
processes are required, interrupts are useful. The program designer then has to
decide on interrupt priority. This means selectively disabling lower priority
interrupts, using the enable bits, when a more important process is in progress.
For example, when reading a serial port, the data has to be picked up from the
port before being overwritten by the next data to arrive.
In more complex processors, a more
sophisticated interrupt priority system may be available, so that interrupts
have to be placed in order of priority, and those of a lower priority
automatically disabled during a high priority process. The limited stack depth
(8 return addresses) in the PIC must also be taken into account, especially if
several levels of subroutine are implemented as well as multiple interrupts.
1.4.7 Peripheral Control Registers
The function of most of the
peripheral blocks and their set up will be explained as each is examined in
turn, with a sample program.
The only peripheral which does not
require external connections is the Electrically Erasable Programmable Read
Only Memory. This is a block of non-volatile read and write memory which stores
data during power down; for example, a security code or combination for an
electronic lock. A set of registers in banks 2 and 3 are used to access this
memory, as well as a special EEPROM write sequence designed to prevent
accidental overwriting of the secure data. See Section 4 of the data sheet for
details.