Embedded Design by Interactive Simulation








Chapter 1





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 (



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).



Figure 1.2                      Processor program execution























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


















Magnetic disk

Sample size*














Write (many)







Large (bytes)







Cheap (per bit)







Fast (access)








* 1 byte = 8 bits

* 1 kb = 1 kilobyte = 1024 bytes

* 1 Mb = 1 megabyte = 1024 kb

* 1 Gb = 1 gigabyte = 1204 Mb



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.



Figure 1.3                      Parallel and serial data ports



(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.


Figure 1.4                      PIC 16F877 pin out



Reset = 0, Run = 1





Port B, Bit 7 (Prog. Data, Interrupt)

Port A, Bit 0 (Analogue AN0)





Port B, Bit 6 (Prog. Clock, Interrupt))

Port A, Bit 1 (Analogue AN1)





Port B, Bit 5 (Interrupt)

Port A, Bit 2 (Analogue AN2)





Port B, Bit 4 (Interrupt)

Port A, Bit 3 (Analogue AN3)





Port B, Bit 3 (LV Program)

Port A, Bit 4 (Timer 0)





Port B, Bit 2

Port A, Bit 5 (Analogue AN4)





Port B, Bit 1

Port E, Bit 0 (AN5, Slave control)





Port B, Bit 0 (Interrupt)

Port E, Bit 1 (AN6, Slave control)





+5V Power Supply

Port E, Bit 2 (AN7, Slave control)





0V   Power Supply

+5V Power Supply





Port D, Bit 7 (Slave Port)

0V Power Supply





Port D, Bit 6 (Slave Port)

(CR clock) XTAL circuit





Port D, Bit 5 (Slave Port)

XTAL circuit





Port D, Bit 4 (Slave Port)

Port C, Bit 0 (Timer 1)





Port C, Bit 7 (Serial Ports)

Port C, Bit 1 (Timer 1)





Port C, Bit 6 (Serial Ports)

Port C, Bit 2 (Timer 1)





Port C, Bit 5 (Serial Ports)

Port C, Bit 3 (Serial Clocks)





Port C, Bit 4 (Serial Ports)

Port D, Bit 0 (Slave Port)





Port D, Bit 3 (Slave Port)

Port D, Bit 1 (Slave Port)





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 A somewhat simplified version is given in Figure 1.5, which emphasises the program execution mechanism.



Figure 1.5                      16F877 program execution block diagram











































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.



Figure 1.6                      BINX Hardware Outline


(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 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.



Program 1.1                   BIN1 List File


Memory          Hex                                            Line                                           Address                         Operation       Operand

Address         Code                                         Number                                    Label                         Mnemonic



                             00001                        PROCESSOR 16F877


0000      3000               00003                        MOVLW     00

0001      0066               00004                        TRIS      06


0002      0186               00006                        CLRF      06

0003      0A86               00007              again             INCF      06

0004      2803               00008                        GOTO      again


                             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.



Program 1.2                   BIN4 List File



MPASM 03.00 Released             BIN4.ASM   8-28-2005  19:54:36         PAGE  1



  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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                      00027         PROCESSOR 16F877        ; Define MCU type

2007   3733           00028         __CONFIG 0x3733         ; Set config fuses


                      00030 ; Register Label Equates....................................


  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


                      00037 ; Input Bit Label Equates ..................................


  00000000            00039 Inres   EQU     0       ; 'Reset' input button = RD0

  00000001            00040 Inrun   EQU     1       ; 'Run' input button = RD1


                      00042 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                      00044 ; Initialise Port B (Port A defaults to inputs).............


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



                      00053 ; 'delay' subroutine ........................................


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



                      00061 ; Start main loop ...........................................


000B   0186           00063 reset   CLRF    PORTB           ; Clear Port B Data


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


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


                      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.



Table 1.2                        Configuration bits
























Code protection

(4 levels)









In-circuit debugging (ICD)












Program memory write enable






EEPROM data memory write protect






Low voltage programming enable






Brown-out reset (BoR) enable








Code protection (CP)










Power up timer (PuT) enable






Watchdog timer (WdT) enable








Oscillator type select

RC = 11, HS = 10, XT = 01, LP = 00








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)





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 (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.




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.




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 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.




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.




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.




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.




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.



Table 1.3                        PIC instruction set by functional groups




 F   =   Any file register (specified by address or label), example is labelled GPR1                      

  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 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      


       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


       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


       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


       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


       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.




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 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.




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.




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.




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.




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 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.



Figure 1.6                      P16F877 program memory and stack



14 bit program



13 bit

hex address

Instruction 1

0000h (RESET)

Instruction 2


Instruction 3


Instruction 4


Instruction 5


Instruction 6




Page 0 (2k)




Instruction 2048

07FFh (END PAGE 0)

Instruction 2049

0800h (START PAGE 1)



Page 1 (2k)




Instruction 4096


Instruction 4097

1000h (START PAGE 2)



Page 2 (2k)




Instruction 6144

17FFh (END PAGE 2)

Instruction 6145

1800h (START PAGE 3)



Page 3 (2k)




Instruction 8192




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







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.




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.




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.




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.



Table 1.4                        Arithmetic results


Flag after



( Z )


( C )






A+B < 256 

8-bit sum, no carry




A+B = 256 (100h)

Exactly, carry out




A+B > 256

9-bit sum, carry out




A-B < 256

8-bit difference, no borrow




A-B = 0

Numbers equal, no borrow




A-B < 0

Borrow taken, result negative


* set carry flag before subtracting





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.




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.



Table 1.5                        Register bank select











00 – 20

20 – 7F



Special Function Registers

General Purpose Registers




80 – 9F

A0 – EF

F0 – FF




SFRs, some repeat


Repeat 70 - 7F




100 – 10F

110 – 16F

170 – 17F




SFRs, some repeat


Repeat 70 - 7F




180 – 18F

190 – 1EF

1F0 – 1FF




SFRs, some repeat


Repeat 70 - 7F




000 – 1FF







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.




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.



Table 1.6                        Port Alternate Functions





Alternate function/s





RA0 – RA5

Analogue inputs

Timer0 clock input

Serial port slave select input








RB0 – RB7

External interrupt

Low voltage programming input

Serial programming

In-circuit debugging









RC0 – RC7

Timer1 clock input/output


SPI,I2C synchronous clock/data

USART asynchronous clock/data









RD0 – RD7

Parallel slave port data I/O


Digital I/O



RE0 – RE2

Analogue inputs

Parallel slave port control bits







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



Table 1.7                        Typical configurations for Timer 0







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



Internal clock (fOSC/4)

Prescale = 16

Timer mode using instruction clock with prescale


Extend the count period x16 for applications 1 & 2.


External clock

T0CKI pin


Counter mode

Prescale = 256

Count one pulse in 256 at RA4


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.



Table 1.8                        Interrupt sources and control bits




Bit Set


Bit Set

Interrupt Trigger Event




Timer0 count overflowed




RB0 input changed (also uses INTEDG)




Port B high nibble input changed








Timer1 count overflowed




Timer2 count matched period register PR2




Timer1 count captured in or matched CCPR1




Data transmitted or received in Synchronous Serial Port




Transmit buffer empty in Asynchronous Serial Port




Receive buffer full in Asynchronous Serial Port




Analogue to Digital Conversion completed




A read or write has occurred in the Parallel Slave Port




Timer2 count captured in or matched CCPR2




Bus collision detected in SSP (I2C mode)




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.