PIC Microcontrollers

These amazing devices are very easy to program


PIC microcontrollers are made by Microchip Technology, Inc.. They are true computers on a chip, including program and data memory as well as 'peripheral' devices such as timers, comparators, analog to digital converters and serial communications. Even the oscillator and reset circuitry can be on-chip so that all that is required for operation is a power supply. They come in packages as small as six pins, and at surprisingly low prices. Besides the two power supply pins, all the other pins are multipurpose input-output connections that are configured for the particular application.

Very popular for experimentation are the circuit boards with programming software using BASIC or C that runs on a PC, using USB or the Serial port for download to the board. Examples are the BASIC Stamp provided by Parallax, the PICAXE boards, and the Arduino boards. The MCU's that are used are not advertised, but the BASIC Stamp and PICAXE use Microchip PIC's (at least in some cases), and the Arduino uses Atmel MCU's, the ATmega328 or ATmega168 on the Duemilanove board. I think the use of high-level languages on small microcontrollers is very cumbersome and limiting, so I have not investigated these systems. Assembly language gives direct and full access to all the facilities of an MCU, shows exactly what is going on, and is very easy to use.

The PIC MCU's are very well adapted to experimentation because of the excellent information available from Microchip on their website, the existence of programming tools at reasonable prices, the low prices of the chips and the simiplicity of use. I recommend the PICkit 2 with the low-pin-count demonstration board and MPLAB IDE (Integrated Development Environment), which is available for about $75 and comes with a PIC16F690 MCU (the IDE is actually free). To use it, you will need a good PC with an available USB port. With this demonstration board, a reasonable variety of 8, 14 and 20-pin Flash-based MCU's can be programmed. Besides the full-featured 16F690 MCU furnished, I recommend the 14-pin 16F630 or the 8-pin 12F675, a small MCU that includes an analog to digital converter. The IDE includes a simulator that will let you see how the instructions execute on a wide variety of MCU's, not only those that the board can program. You should obtain the PIC Midrange MCU Reference Manual (33023a.pdf), the data sheets for the chips you will study and program, and the assembler and IDE User's Manuals from the Microchip website. The PIC12F6XX/16F6XX Memory Programming Specification explains how the chips are programmed with a serial interface, and may be interesting even if you will not use the information directly. Install the IDE and the PICkit 2 with the CD's furnished, and you will be ready to go.

The very large variety of Flash-based PIC MCU's available is grouped in families identified by the chip ID, from the "Baseline" 10F series with 12-bit instructions through the "Mid-Range" 12F and 16F with 14-bit instructions, to the 18F family, which is a further development of the obsolete 17F family. The 18F family can access external memory. The 16-bit 24F family has an expanded instruction set like that of a general-purpose microprocessor, as well as 16 working registers instead of just one, as well as 16-bit data registers and 24-bit instructions, operating from a 3.3V power supply. There are even larger 32-bit processors as well, which have von Neumann architecture. The experimenter should probably stay with the 8-bit processors for low cost and ease of use. The PICkit 2 LPC board programs 18 different 8, 14 and 20-pin Mid-Range MCU's. I chose this board because it runs from a USB port and is reasonably priced. A programming adapter board for the PICkit 2 is available for 10F2XX MCU's. All the PIC MCU's are programmed by serial data links, and can even be programmed when in a circuit. The identification marking on the chips is very difficult to read; stickers may avoid confusion.

The assembly language programming for all the 8-bit MCU's is identical. Only the amounts of implemented memory and the peripherals included are different in the different chips. The Midrange Reference Manual is a good source of architecture and programming information and should be studied carefully. These processors use a Harvard Architecture where the instructions and data are stored in different instruction and data memories. The instructions are 14 bits wide and each occupies one location. The data are 8 bits wide and occupy the register file memory, organized in four banks of 256 registers each. The upper locations are the special function registers that control the operation of the MCU. The instruction (14 bits wide) and data (8 bits wide) buses are not externally accessible.

Most general-purpose microprocessors use the von Neumann architecture instead, where the same memory holds program and data, which are transferred over a single data bus and accessed by a single address bus. The stack that holds return addresses for subroutine calls is maintained in this memory and is addressed by a stack pointer. The stack has greatly expanded use in most general-purpose processor programs to hold data structures to pass data to subroutines. In the PIC processors, the stack is an 8-level dedicated memory that stores only return addresses, and so the stack is not used as it is in a von Neumann processor. In fact, the stack pointer is not externally accessible since this would be of no use. The general purpose data registers are considered data for all subroutines, so there is no need to pass them to subroutines, and there is no distinction between local and global data. This greatly simplifies programming, but of course lacks the power of the von Neumann stack.

The PIC MCU's are reduced-instruction set (RISC) processors. For example, you will find no compare or branch instructions at all. All branches are made only by instructions that skip the next instruction if a certain bit is set or cleared, or if an increment or decrement results in a zero. The subtract instruction affects the zero and carry bits, and you must interpret the bit states with the skip instructions. There are, in fact, only 35 instructions to learn. The RISC instructions may be combined to perform the same tasks as the more specialized instructions in larger instruction sets. The 18F family includes some of these specialized instructions, such as add with carry and subtract with borrow that make multiple-precision arithmetic easier, as well as conditional branches.

I recommend using assembly programming for using the PIC microcontrollers. Compilers are available for C and Basic, but this is a very cumbersome way to create small programs and can obscure the basic operation of the processor. The PIC IDE editor is very helpful, recognizing instructions and other entries. An assembled program can be single-stepped to verify proper operation with the MPLAB-SIM simulator. If this is done with care, there will be no need for debugging the target system. A successful assembly produces a hex file that can be imported to the PICkit 2 programmer and easily written to the target processor. The program can be edited in the hex file or in the PICkit 2 display if necessary. In fact, I chose the PIC microcontrollers for experimentation because of the ease of assembly programming. It only takes a few minutes to assemble a program and write it into an MCU.

There are two ways to address a data register. One way, called direct, is to put the 8-bit data register address in the instruction. Only the lower seven bits are used (the eighth bit is replaced by the destination bit, as discussed in the next paragraph). The upper two bits, which specify the memory bank, are taken from the status register to give the full 9 bits required. The second way is to put the 8-bit address in the FSR register (address 04). Then, the 9th bit is taken from the IRP bit in the status register when a read or write to location 00 is performed. This is strange, but is the way they do it. In small MCU's the upper two banks are not implemented, so an 8-bit address is sufficient and IRP is ignored. It is always necessary to be aware of which register bank is being addressed and see that the bits in STATUS are properly set. Remember that in direct addressing the eighth bit of the address is ignored.

In most of the register operations, there is a choice of the destination of the result--either to the accumulator W or back into the file register itself. In these cases, the destination must be specified as well as the address. For example, INC 20,0 adds 1 to the contents of register 20 then copies the result in W, leaving register 20 unchanged, while INC 20,1 puts the incremented value back in 20, leaving W unchanged. This is rather different from most processors, and can be useful.

Every bit in the registers can be addressed by following the register address by the bit number. The three bits specifying the bit number replace the 8th address bit and the final two bits of the opcode. For example, BCF 03,5 clears bit 5 of register 03, while BSF 03,5 sets bit 5 of register 03. It is best to set and clear bits by the bit operations, not by writing 8 bits to the register and using logical operations.

Hex numbers are the default in the MPASM IDE and need not be specially identified. They must begin with a valid decimal digit (as in 0A). Decimal numbers are preceded by a "." as in .12, which is interpreted as 0A. An instruction cycle is four clock cycles, so an oscillator frequency of 4 MHz gives a 1μs instruction cycle. All instructions require one cycle period to execute; if there is a change of program counter, as in a GOTO or skip instruction, an extra cycle period is required that is essentially a NOP. The special function registers have the same addresses in all PIC MCU's, which is very convenient.

Programming a PIC Microcontroller

I assume that you have a PICkit 2 and LPC demonstration board with a PIC16F690 MCU, and have installed the MPLAB IDE v.8.30 and the PICkit 2 v.2.50 from the CD's. Your version numbers may differ, but I show the ones that I used. You also should obtain a PIC16F630 or other MCU supported by the board for your program. Jameco handles the 8 and 14 pin MCU's, but not the 20-pin ones. Digi-Key and Newark sell the 16F690. Microchip will supply any of their products by online order. The Velleman VM134 programmer, which works from a serial port, will program the baseline 10F200, the 16F84, and a number of 18F family devices. I have not used this programmer.

Launch the PICkit 2 program. Select Device: Family Midrange Standard, and Device 16F690 should then be found. VDD should be set at 5.0V. Check the VDD On box to turn on power to the 690, and the LED's should blink. Press Read, and the program loaded in the 690 should appear in the display. This program includes a bug that causes the A/D converter to operate improperly, as you can see if you rotate the potentiometer. The program does not wait for the conversion to be complete. To fix this, change the byte representing the instruction btfss, 1C9F, to that for btfsc, 189F. Just click the location 1A0 to select it, and type in the new value. Now press Write to write the corrected program to the 690. Now the LED's blink more regularly, and you can change the rate with the potentiometer. Note that you cannot change the configuration byte. It must be specified in the hex file and is not available in normal operation. Under the File tab select Export Hex and create a hex file for the new program, giving it a suitable name, like newblink.hex. Study this hex file in Notepad and see how it includes the program data and the configuration word. The configuration word is in the next-to-last record of the hex file, that begins 02400E00 (400E is just twice the program memory address 2007). The first record, of type 04, only gives the upper word of the 32-bit addresses, which is 0000. In between are the data records of the program with byte addresses. The final record is just the standard last record. The hex file is the means of communication between the IDE and the programmer.

There are 12 lessons furnished with PICkit 2, each of which is complete IDE project. On my computer, they showed up after installation in the root directory while the other files were placed in Program Files in the Microchip folder. There is an assembly template file for the 690 and an .INC file for the 690. There are .INC files for any MCU you may use elsewhere. They are not really necessary, but include a full list of symbols that you may want to use. There are separate template .asm files for absolute and relocatable code. When you write a program for the 16F630, or other MCU you may have chosen, put in list p=16f630 and #include , in that order, as the example shows. Study the .asm files to see how to write them. Note that labels must start in column 1.

It is a good idea to read important points in the IDE User's manual (document DS51519C). A tutorial is included early in the manual (Sec. 4.2) that should definitely be studied. The IDE is a complex program, and the Menu choices should be investigated to see what you can do. It can handle complex programs with multiple source files, libraries and relocatable code, although we shall not use these features. The assembler and linker are fully explained in the MPASM User's Guide (document DS33014).

Launch the IDE, and practice creating a new project. The name and the directory must be chosen. Select the MCU that will be used in the Configure tab, and choose the MPLAB SIM in the Debugger/Select Tool. We will generate only absolute, not relocatable code, so we will not need the linker. It is all right to have programmer None. if you select PIC Kit 2, the IDE will recognize the programmer if it is running, and check that a 630 is installed. Under Project, select New and enter the name and directory. Then you can add an .asm source file under Add File. We shall work only with one source file, which simplifies things considerably. Close files you do not need. Note the things you can View, which include the special function registers, file registers and a disassembly listing. Open the .asm file and note that you can edit it here. The last line should be the directive "end" which marks the end of assembly. Everything on a line following a semicolon is a comment and is ignored by the assembler. When you have a created a new project you will have a Make choice under Project, which will perform the assembly and give you a report. After a successful assembly, you will be able to single-step through the program by pressing F7 to execute one instruction. The next instruction to be executed is marked with a green arrow on the .asm file as well as on the listing file, either or both of which may be displayed. You can watch the changing values in the registers. The current contents of the W register are also shown. At any time, the value in a register can be changed by selecting it and typing in the new value. This is valuable experience, and you should watch the execution of each of the instructions. The User's manual will show how to use breakpoints and the timer, which can be useful.

If you have a program with only one source .asm file, you can use the Quickbuild feature that generates only absolute code. If you open an .asm file in the workspace, then the Quickbuild choice will be available. Just click this to build the program. Be sure that any .inc file is in the same directory as the .asm file. The output files will be placed in this directory.

It may be useful to review how to revise a program in an MCU. The .asm file may be edited in any text editor, such as Notepad. When you launch the IDE and choose Project/Open and click on the project file, the .asm file will be automatically loaded. Of course, it can be edited here, with the help of the color coding. Click on Project/Build All to reassemble. A successful Build will produce a new hex file overwriting the old one. Put the target MCU in the LPC board, at the top of the socket, and launch the PICkit. Choose Device Family/Midrange Standard and the MCU should be recognized. Next, click on File/Import Hex and double click on the filename. When the file loads correctly, click on Write and the MCU will receive the new program. It is not necessary to apply power by clicking the power box, unless you want the program to run in the LPC board. Remove the MCU and restore it to its system. This only takes a couple of minutes and is very straightforward.

Write a program for the 16F630 that controls any of the four LED's that are connected to PC0-PC3 on the demonstration board, or detects a push on the pushbutton connected to PA3 that inputs a 0 when pressed. The 12 lesson programs show how to do this explicitly. I wrote a program that lights two LED's alternately at about a 1/2 Hz rate. Remove the MCU from the programmer board and put it in a solderless breadboard. Bypass the VDD with an 0.1μF capacitor and connect LED's through 470Ω resistors from the output pins to graound. When you apply power, the program should execute.

The 16F630 has two bandgap calibration bits in the configuration word, bits 12 and 13. Determine what they are by installing the 16F630 in the demonstration board, and then Reading it. They will be the two leading bits of the bandgap. The programmer will apparently preserve these bits automatically and will not show them in the configuration word. Your program should calibrate the oscillator by a call to the last program location at 3FF and writing W to OSCCAL. Again the PICkit 2 preserves this instruction. Import the hex file of your program to the PICkit 2. Write the file to the MCU, and the 16F630 should now be programmed with your program, and executing it. There is a statement that Microchip development tools will not change calibration bits, but also a statement that they must be read, saved and restored (apparently when using non-Microchip tools). These bits calibrate the band-gap reference voltage for brown-out detection and power-on reset and possibly the internal voltage reference for the comparator. The PIC16F630 that I used had calibration bits 01, and oscillator calibration byte 34 (in the command RETLW 34).

There is a bit in the PCON register called NOT_POR that is zero after a normal power-on reset. It can be tested to identify this state, and set to 1 so that a later /MCLR reset can learn that it is a "warm" reset. The TRIS registers are set to all 1's, and the CMCON register is set to all 0's after an /MCLR reset, so these registers must be reinitialized after every reset. The CMCON register controls the configuration of the PA0 and PA1 bits. If configured as outputs in the TRISA register, they will be digital outputs as expected, but if configured as inputs they will be analog inputs in the reset 000 comparator configuration. To make them digital inputs, 07 must be written to CMCON.

A good exercise is to write a program that will read from an EEPROM location and display a nibble on LED's. When it is reset with /MCLR, it will read a nibble from logic switches and store this nibble in EEPROM, and display it. When the power is removed, and then again applied, the same nibble should be displayed as when the program last operated. This exercise will show how to use EEPROM to provide nonvolatile memory for a program. I used a PIC16F630, with LED's on PC0-PC3 and digital inputs on PA0, PA1, PC3 and PC4. PA3 is configured as /MCLR. I found the curious difficulty with this program that I could not preset the EEPROM location to be written because the programmer always cleared the location to FF. Locations that were only read were able to be preset to any value.

Another program exercises the interrupt facility. I used the T0 timer with a 1:256 prescaler to blink an LED. At an oscillator frequency of 4 MHz the cycle time is 1 μs, so the timer would time out and cause an interrupt every 0.0655 s. I used the interrupt routine to count out 15 of these timeouts, which amounted to 0.98 s and then to toggle the LED. The T0 interrupt must be enabled by setting T0IE, and the T0IF cleared at each interrupt. The interrupt is active as soon as GIE is set. The interrupt clears GIE, preventing further interrupts, pushes the return address on the hardware stack, and goes to instruction location 0x4. After clearing T0IF, the instruction RETIE pulls the return address into the PC and sets GIE again. In this example it is not necessary to save the context when interrupted, since the main program will be doing nothing but waiting, but in general W and STATUS should be saved and restored, as shown in the assembly template file. There are many sources of interrupts, and they are an important tool in microcontroller programming.

Atmel AVR Microcontrollers

A very popular line of Flash-memory microcontrollers very similar in capabilities tot the PIC microcontrollers is manufactured by Atmel as their AVR devices. "AVR" is not an acronym, and has no particular meaning other than indentifying the MCU core. The AVR programming model is significantly different from the PIC model. Instead of the W register, there are 32 general purpose registers R0-R31 with ALU access. The 64 I/O Registers are analogous to the Special Purpose Registers of the PIC. 64 or more data registers make up the remainder of the data space. Only the general purpose registers and the I/O Registers are bit addressable, not the remainder of the data space. Instructions are maily 16 bits, though some instructions include a second word that usually holds an address. The operation of moving (copyin) data is named differently depending on the location of the data. A move is only among the 32 GP registers. Out and in refer to writing and reading data between the GP registers and the I/O Registers, while load and store refer to data transfer with the general data memory. Data can be moved between two GP Registers in a single instruction, since two GP Register addresses (5 bits) fit in the instruction. A full set of 120 instructions is available, even more in the larger MCU's, which may include multiply and divide. The add and subtract can either use carry or not, as can the rotate instructions.

The two Fuse Bytes (11 fuses) and Lock Byte (2 lock bits) of the AVR are analogous to the configuration bits of the PIC, controlling the clock source, watchdog timer, brownout detector and so forth. Serial programming and an internal clock are configured as the device comes from the factory, so the device can be programmed by the ISP (in-system programming) 3-wire interface. An instruction cycle is one clock cycle, not four, as in the PIC. The default clock is a 9.6 MHz clock, divided by 8, so the effective clock frequency is 1.2 MHz. This can be changed in programming or in software.

The equivalent of the MPLAB is AVR Studio 4, available at no cost from the Atmel website. It includes an assembler and a simulator. As a programming tool, I am trying the ATAVRISP mkII, which connects with the target system through a 6-pin ISP connector, and is available from Digi-Key. It should be easy to make a programming adapter on a solderless breadboard. The ATtiny13 is equivalent to the PIC12F275, supplied in an 8-pin DIP. The ATtiny24 comes in a 14-pin DIP, equivalent to the PIC16F676. The ATtiny26 comes in a 20-pin DIP, with 20 I/O pins, and the ATtiny48 has 28 pins. The ATtiny 88 is the seme except for a larger program memory. This should give some idea of possible devices to experiment with.

There is now a page on AVR MCU's.


The Microchip website is at Microchip. All Microchip products can be purchased online at Microchip Direct with no minimum order size. Microchip, located in Chandler, AZ had its origin in General Instruments, becoming independent in 1989.

Another important manufacturer of recent flash-based microcontrollers is Atmel of San Jose, CA, organized in 1984.

SparkFun Electronics is a Boulder, CO distributor supporting PIC, Arduino, XBee and other interesting products, such as breakout boards for surface-mount chips.

There is a Wikipedia article "PIC microcontroller" that explains the range of PIC MCU's and gives some important history, information and sources.

Intel Hex files are explained in a Wikipedia article. Note that byte addresses are used in hex files, while the PIC program memory uses word addresses. The byte addresses are exactly twice the word addresses, so the configuration word is at byte address 400E, which is prograqm memory location 2007. The format used is INHX32, which includes a type 04 linear address record but is otherwise like the INHX8 format.

Return to Electronics Index

Composed by J. B. Calvert
Created 12 April 2010
Last revised 29 June 2010