Monday, March 18, 2013

A Brief on How PIC Chips Work

In this post, we'll talk a little bit about what a PIC microcontroller is, how they're organized, and how they work.  I'm going to save in depth discussions about features for when we actually start using them.  Remember, this blog is about learning one step at a time, so I don't want to flood you with information you're not going to need for a while.

PIC Chips

There are a number of different microcontroller companies out there, Atmel, Microchip, Intel, etc.  We could spend forever talking about the differences between them, and why use one over the other.  Deciding which MicroController Unit (henceforth MCU) type to use is something of a religious experience for many, and they'll argue to the death.  I chose to learn on the PIC chips because they are abundant, available from many suppliers in small quantities, have all different kinds of chips with all kinds of different features, are available in DIP (dual inline package) which is the only packaging you can really use with a breadboard, they are cheap (about $1-$5 each depending on what you get), and the list goes on, you get the idea.  Why not use Microchip PICs?  Well I don't know, and I don't care, I never gave much thought to it.

PIC originally meant Peripheral Interface Controller.  Today, PIC is more or less just a name, and it applies to pretty much any MCU.  Microchip now uses the PICmicro name for their PIC chips.  We will pretty much just keep referring to them as PICs or PIC chips, etc.

The PIC chips we will use on this blog, the PIC12F629 and the PIC16F628A have an operating voltage range of 2.0V to 5.5V and 3.0V to 5.5V respectively.  In all of our experiments we will use an operating voltage of +5V.

The "F" designation in the name of the PIC chip tells us that these PICs have Flash-type memory, meaning that they are erasable and programmable via electric signal using a programmer (the PICKit).

MCUs are very complicated little devices, and many books have been written about all their inner workings.  Because they are so complex, and since I'm not an expert on microcontrollers, I'm really not qualified to teach you about them with regards to specific details.  So, what I have decided is that rather than plowing over books and just rewriting information that's already out there, I would leave it up to you to get the information yourself.  Sorry, but it's really just better that way since it's not the scope of this blog to teach these things.  Listed below are a couple of texts that I am familiar with and have been used well to write this blog.  The first listed is quite a handy reference to have on your bench, regardless of whether you read it cover to cover or not, and at over 900 pages, it's full of information.

PIC Microcontrollers: Know It All (Newnes Press)
Programming 8-bit PIC Microcontrollers in C: with Interactive Hardware Simulation (Newnes Press)

Languages

Microcontrollers are traditionally programmed in assembly language, which translates directly into machine code.  Machine code, also called binary, is the ones and zeroes language (10010001).  Assembly language is found in a lot of microchip's documentation, including datasheets and reference manuals.  Is it necessary to know assembly language?  No, not really, but it helps quite a bit, especially when trying to look up information in the microchip datasheets or from some other sources.  The first text listed above, PIC Microcontrollers: Know It All is what I used to learn assembly, and it does this quite well.

Each PIC chip has a set of instructions, written in assembly, which can be found in the device datasheet.  These instructions are basically everything that the MCU can do broken down into simple instructions.  The baseline PICs use a 12-bit instruction word, which has 33 instructions.  The mid-range family uses a 14-bit instruction word, which has 35 instructions.  Then, the 16-bit instruction word microcontrollers, at the high end of the PIC MCU family, have 58 instructions.  We'll talk in just a minute about bits, so don't worry if you don't know what I'm talking about right now.  Both of our chips are mid-range MCUs and have 35 instructions.  The table below, taken from the PIC12F629 datasheet shows the 35 instructions.  Assembly uses Mnemonics as instructions.  A mnemonic is a memory aide, like Every Good Boy Deserves Fudge to remember the musical scale E-G-B-D-F.  The first instruction on the list is ADDWF, and it means mathematically add the Working register to F (a literal, such as a number).  I know that probably goes over your head, and that's okay, I'm just making a point that there's a logic behind the instructions.
Figure 1
C language generally eliminates the need to learn assembly and frees the user from managing all the details.  That being said though, I have found that I am better off in understanding what exactly it is that I'm doing when I write a program in C because I know a little bit about Assembly.  Assembly gives you the opportunity to learn the structure of the PIC architecture and memory organization, which can be very useful.

The other thing to note about C vs. Assembly is that, Assembly takes up far less space in the PIC's program memory than a piece of software written in C.  Adversely though, C takes far less time to write, and is infinitely easier to read when you have to go back to a program and make some changes, so it tends to be a trade-off between time and ease of writing vs. data space used.

Memory types found on MCUs

Microcontrollers have different types of memory inside them, program memory, data memory, and data EEPROM memory (some chips).

Program memory is where the program we write gets stored when we "flash" it onto the chip.  Program memory size is dependent on the device being used.  Program memory is non-volatile memory, meaning that the information stored on it remains intact when power to the device is lost.

Data memory is a RAM (Random Access Memory) memory type which contains "Registers".  A register is a place inside the microcontroller that can be written to, read from, or both.  Think of a register as a piece of paper in the MCU.  There are two types of registers that we will be concerned with, Special Function Registers (SFRs) and General Purpose Registers (GPRs).
Special Function Registers are the registers that control special functions of the MCU, like timers, Input/Output peripheral controls, etc.
General Purpose Registers are registers where we store temporary data that we want to save for later.
Data memory is Volatile memory, meaning that any data we put there will be lost when the device loses power.

Data EEPROM memory is like a general purpose register in that we get to use it like paper for writing down variables and reading them later when we need them.  The difference is that EEPROM memory is non-volatile, meaning that the information is saved even when the device loses power.  This is useful for things like storing the access code of an electronic lock.  Not all devices have EEPROM, but both of the devices we'll use in this blog do.  EEPROM stands for Electronically Erasable-Programmable Read-Only Memory.

Bits, Bytes, and Words

I mentioned before that assembly translates down to machine code.  Well, so does C in a manner of speaking.  All MCUs operate solely on machine code.  The program you write gets "compiled" (the purpose of a compiler, in our case the Microchip XC8 compiler) down to ones and zeroes which are executed through the chip.

Each 1 or 0 is a bit, and 8 bits is a byte.  A byte is the 8 bits from 0 through 7.  It's important to remember that a byte is not 1 through 8, but rather 0-7 with 0 being the "least significant bit" and 7 being the "most significant bit".  A bit confused?  We'll clear it up for you in just a second.

You're going to think a lot in bits and bytes when you start programming.  There are a lot of registers inside a PIC, and each of these registers has a control to set up the feature associated with the register.  These controls are a string of ones and zeroes, typically a byte in length which are user-set to either a 1 or a 0 depending on the options desired.  Let's take a quick look at an example to help make this more clear.

In one of our first example projects, we're going to turn a LED on and off using the PIC12F629 MCU.  In order for the MCU to do this, we utilize one of the Input/Output (I/O) pins to complete the circuit to the LED.  We need to access two registers inside the MCU in order to do this, the TRISIO register, and the GPIO register.  These two registers work together to tell the MCU what to do with I/O pins.  The TRISIO register controls whether a specific pin is an input or an output, and the GPIO register controls whether the pin is turned on or turned off.  Let's open up the PIC12F629 Datasheet.  For other PIC datasheets when you need them, they can be searched for on Microchip's website at www.microchip.com.

Section 3.0 in the datasheet covers the GPIO (General Purpose Input Output) Port.  Figure 2.1 below shows the bits associated with the TRISIO and GPIO registers from the device datasheet.
Figure 2.1
Notice that underneath each register is "bit 7" on the far left, and "bit 0" on the far right.  The PIC12F629 has 6 GPIO pins (Figure 2.2), so the two most significant bits (7 and 6) of the registers' bytes are blanked out.  These blanked out bits are called "unimplemented" bits and are read as "0".  The pinout (arrangement of the device's pins) are shown in Figure 2.2, which is taken from the datasheet. Figure 2.3 shows a photograph of a PIC12F629 MCU. Notice that the device has a small indent on the upper left corner. This indent on all microchip MCUs indicates the location of pin number 1.

Figure 2.2
Figure 2.3

Let's say that we are going to use pin number 4 of the device (indicated on the pinout as GP3) to hook our LED up to.  Since a LED is an output device (it outputs light), we need to setup pin 4 as an output in the TRISIO register, and for the sake of this example we'll set all other GPIO pins as inputs.  Remember that the TRISIO register controls the port direction (sets input or output).  Setting a bit to 1 sets it as an input, and setting a bit to 0 sets it as an output, therefore our TRISIO bits would be 00110111.  Read in order from left to right, these would be bits 76543210, and is a byte.  Remember that bits 7 and 6 are unimplemented and read as 0. For the sake of clarity, PIN 4 of the device is associated with the GP3 bit, as determined by referencing figure 2.2.

The GPIO register sets whether the GPIO pins are turned on or off.  A bit set to 1 is on, and a bit set to 0 is off.  Therefore, when the LED is off, each bit would be set as 0, and the byte would look like 00000000.  When we want to turn the LED on via its connection to pin 4, we set the GP3 bit to 1, and our byte would look like 00001000.  Again, a byte is read from the most significant bit (7) on the left to the least significant bit (0) on the right.

Now let's talk about Words real quick.  Words, instructions, bits, bytes... it's all kind of a confusing mess, but can be summarized like this:  Different MCUs come in different word sizes.  There are 12-bit instruction words, 14-bit instruction words, 16-bit instruction words, etc.  Both the PIC12F629 and PIC16F628A have 14-bit instruction words.  The internal architecture of the PIC chips (called Harvard Architecture) has three sections, RAM, the Central Processing Unit (CPU), and the Program Memory (figure 3).  There are 8 data buss lines between the RAM and CPU.  Each line is a bit, so the 8 lines corresponds to 1 byte worth of data transfer lines.  The connection between the CPU and the program memory however, is 12, 14, 16, etc lines depending on the size of the instruction word.  In our chips there are 14 data transfer lines (14-bit) between these two components.
Figure 3
Since all RAM memory registers are 8-bits wide, all data being exchanged is of the same width.  All the programs written for these MCUs will be stored in the microcontroller internal ROM (program memory) after being compiled to machine code.  ROM locations however, are not 8-bits wide, but 12, 14, 16, etc. bits wide.  The CPU needs to know what to do with the 8-bit wide RAM data, and the extra bits of the program data are the instructions for doing just that.  Figure 4 shows the four different types of 14-bit instruction words.  Remember that bits start at 0, so 14 bits is bit 0 through 13.
Figure 4
The OPCODE (Operation Code) is the portion of the instruction that specifies the operation to be performed.  The other bits specify the program memory address and data on which the operation should be performed.

I realize that this is all confusing and can be difficult to grasp, but I wanted to make sure and cover it so that when you're browsing around at other information on-line and in books, and you start seeing things about bits and bytes and words, that you had something of a basis to start on.  To make matters worse, despite the fact that the instruction words on mid-range PIC chips is 14-bits wide, they are still considered 8-bit microcontrollers because that's the width of the RAM memory.

How the PIC Processes Instructions

Figure 5
We know from earlier that a microcontroller operates by processing instructions.  The program we write into the MCU is made of up individual instructions that are executed one at a time and reside in the program memory.  Think of the program memory as a sheet of lined paper.  In the case of the 12F629, our lined paper has just over 1,000 lines and each line can hold one instruction.  When we power on the MCU, instruction execution begins at line 0.  The instruction on line 0 will be executed, and then the instruction on line 1 will be executed, then line 2, line 3, and so forth.  At the top of figure 5, we see the top box is labeled PC <12:0>.  This is the Program Counter, and it is 13 bits wide (bits 12 through 0).  The job of the program counter is to direct the MCU on which instruction to execute next, and generally speaking it will start at the first line and just run down the list of instructions until there aren't anymore to execute.

Certain instructions however, will tell the MCU to go to a specific location in the program memory and execute that instruction next rather than proceed in order down the list.  As a matter of fact, the very first instruction in a program typically does just that, because as shown in figure 5, the first line that gets executed on reset is line 0, but program memory doesn't start until line 5.  Therefore, the first program instruction will typically be an instruction that tells the program counter to skip down to line 5.  To summarize the program counter, it is a device which holds the memory location of the next instruction to execute.  The bit-width of the program counter corresponds to the number of bits required to address all of the available space in the program memory.

Lastly, looking at figure 5 again, we see the stack, labeled as Stack Level 1 through 8.  What is the stack?  Certain instructions will interrupt normal program flow and instead execute a subroutine.  In order for the program to return to the location it left off from when the subroutine is completed, the program address needs to be saved so that it can be called later.  The purpose of the stack is to store this program address.  The stack tends to be several layers deep so that multiple subroutines and interrupts can be called without losing the normal program's place.  Going to multiple subroutines or interrupts in succession, in other words a program flow which would take MCU operation from Regular program flow, into a subroutine, then from that subroutine into another subroutine and so forth without first returning to the normal program flow is called "nesting".  Care must be taken by the programmer that the program doesn't go deeper into the stack than the number of stacks you have available, otherwise the address needed to return to normal operation will be lost.  We don't need to worry about this at all right now, so we won't cover it any further.

Conclusion

Wow, okay what a post huh?  I know there's a lot of information in there, but it will make more sense the more we learn about programming and using PIC chips.  The nice thing is that when using C language to program PICs, the compiler pretty much takes care of everything so we don't need to watch the finer details too much.  I presented the information here because I feel that these are basic things you should know in order to be a better programmer, and to help understand certain things when designing a program.

Thanks for reading, and I'll see you on the next post!

November 25, 2014: Post edited for clarity.

3 comments:

  1. Great tutorial!

    Just one point I don't get. When you say the output to the LED is on pin 4, do you mean port4? As pin 4 is port3 on a PIC12F629.

    FANTASTIC BLOG

    ReplyDelete
  2. When I reference a PIN number, such as pin 4, I am referring to the actual pin number as used on the device's data sheet. If you look at page 4 of the PIC12F629's datasheet from microchip, http://ww1.microchip.com/downloads/en/DeviceDoc/41190G.pdf you can see that the pins are numbered starting from the top left (pin 1) and working down the left side to 4. So PIN 4 is labeled as "4" and also labeled as GP3/MCLR/VPP. What this tells us is that PIN 4 is capable of functioning as a General Purpose Input/Output number 3, the Master Clear/Reset, and it is the pin to which programming level voltage is applied when we are programming the device with the programs we write for it.

    ReplyDelete
  3. Anonymous pointed out an issue with the clarity of my description regarding the connection to PIN 4/Port 3. This post has been edited to simplify this description.

    ReplyDelete