ME Labs, Inc.
719-520-5323
 
Home:
  Developer Resources:

Programming Clues
    Sample Programs
   
    PICBASIC PRO™
Compiler Manual
    PICBASIC™ Compiler
Manual
    Serin2/Serout2 Modes
    ASCII Character Set
    Number Conversion
    Floating Point
Routines
    PBP Debug Monitor
    Articles and Tutorials

Hardware Clues
    Parts / Vendor List
    PICPROTO™ Boards
    LAB-X1 Docs
    LAB-X2 Docs
    LAB-X20 Docs
    LAB-X3 Docs
    LAB-X4 Docs
    LAB-XUSB Docs
    LAB-XT Docs
     
 

Micro-Brains

As published in MicroComputer Journal March/April 1997

In the beginning......there was wire.

You could connect a battery, switch and light bulb, and voila - you had something. And then came logic gates. (I know, I know. In between came vacuum tubes and transistors but God has recreated the world digitally.)

With logic gates (and wire) you could connect the battery and light bulb in such a way that the light would only turn on when you flipped this switch and then that one as long as this other one was off.

Next came programmable logic arrays and PALs - basically a collection of logic gates on one chip that could be connected together with programmable wires.

And then came the microcontroller. (There was that whole microprocessor thing in there somewhere, but I don't think they have any legs.) Microcontrollers differ from microprocessors in that pretty much everything they need to operate is right there on one chip. Simply add a power supply, and sometimes a clock oscillator, and you're ready to go. (Oh yeah, there is that programming thing.)

A microcontroller is a generic computing device that can't really do anything right out of the box. But with the right programming, it can do almost anything. These days they are in everything from telephones to toasters and automobiles to zebras (well, zebra robots anyway).

On one chip, the microcontroller contains its microprocessor brain, a place to store its program (ROM, EPROM or flash), some storage for temporary data (RAM) and I/O pins. Some microcontrollers also contain timers and counters, non-volatile data storage areas, analog to digital converters, hardware serial ports and many other goodies on the same chip.

Don't let all the acronyms scare you off. These days, new programming tools make microcontrollers very easy to work with. If you can program your VCR (or ride a robot zebra), you can program a microcontroller. Just like telling your VCR the sequence of programs you would like to record, you tell the microcontroller the sequence of steps you would like it to perform.

Micros, Micros Everywhere

There are quite a variety of microcontrollers manufactured by a multitude of vendors. For the purpose of these articles, I will concentrate on the PIC® series of microcontrollers from Microchip Technology, Inc., although most of the information can be applied to many other companies products. The PIC is mighty popular these days due to its high performance, high drive capability, very low operating current and low cost.

In the old days, five years ago, there were only a few parts in the PIC series. Today there is a wide variety of PICs with Microchip introducing new parts almost daily. There are chips with 8 to 68 pins in packages from DIP to PLCC to surface mount.

The program space on a PIC ranges from 512 words to 8K (that's K not M) words. While this may seem puny compared to your desktop PC, the PIC was never intended to run Windows95 (I am not sure my PC was either, for that matter, but that is another topic). The program space in the PIC is more than adequate for most microcontroller applications.

The program space can be made from several different technologies. Its main attribute must be that it remembers your program under all circumstances, whether the power is on or off. When you power-up your project, it must immediately start running your memorized program.

Most PIC program space is EPROM (erasable programmable read only memory). This program space may be erased by exposure to ultraviolet light for a period of time (usually 5 to 10 minutes) and reprogrammed in a PIC programmer. It can be erased only if the actual part has a quartz window in the top to allow the UV light to shine on the silicon chip inside. If the EPROM part is all plastic and doesn't have a window in the top, it is call an OTP (one time programmable) part and cannot be erased. OTPs are considerably cheaper than windowed parts, but if your program does not operate the way you intended, you must throw away the part and start over. If you use a windowed part, you can simply erase it and reprogram it.

But we live in a better world than that. We live in a world of EEPROM and flash technology. Some PICs have program memory made from EEPROM (electrically erasable read only memory) and flash memory. Each technology is created differently, but for our purposes they are the same. I will refer to them both as flash.

PICs with flash memory for the most part act the same as a PIC with an OTP memory. It can remember your program with the power off and execute it as soon as the power is turned on. Its big benefit is that it can be erased without UV light, therefore it doesn't need to be housed in a package with an expensive skylight. Most PIC programmers have an erase button on the screen to instantaneously erase the flash parts, or you can usually just reprogram over the old program.

So why isn't everything flash? Someday maybe it all will be, but for the moment there are some drawbacks. The flash or EEPROM part costs more than an OTP part. The actual chip is physically larger (one reason for the higher cost) and doesn't fit into the smallest surface mount packages. Since it is electrically erasable, it is not as bulletproof as an OTP or ROM part. There is also a limit to the number of times you can erase and reprogram it. But these considerations aside, its instantaneous erasure and reprogrammability make it the best technology by far to experiment with. We will use the PIC16F84 in conjunction with these articles. It is an 18-pin flash part. If you can't find a PIC16F84, a PIC16C84 may be substituted.

Like the program space, RAM space on a PIC is also fairly small. PICs have from 36 to a couple of hundred bytes of RAM (random access memory). RAM is used for storing your temporary data variables. These go away when the power is turned off. It is possible to battery backup your PIC design to preserve this data. You can also write your data to an external serial EEPROM if the data has to be stored more permanently or if you need more storage space. Some PICs actually have a limited amount on-chip EEPROM data memory that might meet your needs.

EIEIO

Finally we get to I/O. A microcontroller would be pretty worthless if it could not communicate with the outside world - you would never know what it was thinking. Most of the pins on a microcontroller are dedicated to I/O (input/output) functions. The rest are for power supplies and clock connection.

PICs have from 6 to 56 I/O pins depending on the particular part. Each I/O pin can provide simple input/output functions, i.e. it can read in either a high or low, or it can output either a high or low. Some I/O pins can have additional functionality. In addition to the basic I/O functions, some pins can accept analog voltages or send a PWM (pulse width modulated) signal, for example. The part we are going to work with, the PIC16F84, has 13 regular old I/O pins (actually some can trigger interrupts, but we'll save that for another day).

Since each pin can be an input or an output, we need some way to tell the microcontroller what's what. Each I/O port has a corresponding data direction register. On a PIC it is called a TRIS register, for tristate. When a PIC pin is set to an input, its output drivers are tristated.

When a PIC is first powered-up or reset, all of the I/O pins are set to be inputs. This is to keep them from driving other potential outputs that may be connected to them. To set a particular pin to an output, we simply need to set its corresponding bit in its TRIS register to a 0. If we set it to a 1, the I/O bit becomes an input. This really is easy and we'll see some examples later on.

Let's Do It

A PIC requires a scant handful of other parts besides itself to get it working. The schematic in figure 1 shows what needs to be done to get one ready to go. As you can see, most of the pins are unconnected. These are the general purpose I/O pins. We will connect switches and LEDs to some of them later.

Figure 1 - Simple schematic

The pins connected so far are Vdd (+5 volts) and Vss (ground), /MCLR, OSC1 and OSC2. Hopefully the +5 volts and ground lines and the bypass capacitor are fairly self explanatory.

The /MCLR pin is the Master CLeaR (reset) line for the microcontroller. We have tied it to +5 volts through a pull-up resistor to hold it in its non-reset (run) state. If you momentarily hook it to ground (with a push-button for example), you can reset the PIC to start your program all over again from the beginning. Usually the PIC has enough sense to start your program at the beginning on power-up. This is called power-on reset or POR.

The OSC1 and OSC2 pins connect the clock oscillator to the PIC. A microcontroller executes your program one step (instruction) at a time, one after the other. The clock oscillator determines how quickly this happens. At each tick of the clock, the PIC executes one more of your instructions. (Well not exactly. It actually takes 4 ticks of the clock for the PIC to execute each of your instructions.) Using the 4Mhz crystal suggested in the schematic, your instructions will be executed at a whopping rate of 1,000,000 every second. Some PICs can operate 5 times faster than this.

This is all the stuff you need to get a PIC microcontroller up and running. Anything else is just fluff. For some of the fluff, we'll add a couple of switches and an LED for our first application.

And Here We Go

Now that you have a handle on the complex hardware needs of a PIC, let's move on to a programming example. We'll start off easy by simulating an AND gate.

An AND gate has 2 inputs and one output. What happens at the output is a function of what comes in the input. The truth table for an AND gate looks like this:


Input1 Input2 Output
low low low
low high low
high low low
high high high

As you can see, both inputs need to be high in order for the output to be high. A PIC program can be written to implement the AND function in a number of ways.

But first, let's assign some pins. We need a couple of inputs so lets grab PortB.0 (RB0) and PortB.1 (RB1) for those. These are more or less arbitrarily chosen and could be any two PIC I/O pins. For the output, we'll connect an LED to PortB.4 (RB4). A PIC I/O pin is powerful enough to directly drive an LED but we want to add a series resistor to limit the current. The schematic in Figure 2 shows these additions.

Figure 2 - Switch/LED schematic

Programming

The lowest level of programming language available for the PIC is the Microchip assembler. It is a fairly weird language so we'll look at it as well as some other options.

To start the process, you need a text editor of some sort. This can be something as simple as DOS Edit or Windows Notepad, or your favorite word processor. Ultimately you need a standard DOS ASCII text file so if you choose a word processor, make sure to save the file in this format, not the word processor's native format.

Here is our first program in Microchip assembler:

        bsf  status,rp0     ;change to register page 1
        movlw 00001111b     ;make the bottom 4 bits on PortB inputs and the top 4 outputs
        movwf trisb         ;write them to the data direction register for PortB
        bcf  status,rp0     ;change back to register page 0
;
        bcf  portb,4   ;start with output low and LED off
;
loop    rrf  portb,0   ;shift PortB one to the right and put the result in W (PortB is unchanged)
        andwf     portb,0   ;AND in the PortB.0 value
        andlw     #1        ;isolate our bit
        btfss     status,Z  ;if the result is high (not zero) then skip the next line
        goto loop      ;otherwise go check it all again
;
; if we got here then both switches are high
;
        bsf  portb,4   ;set output high to turn on LED

Looks pretty weird, huh? Let's step through it like a microcontroller would and see what's going on.

As soon as the power is turned on, the PIC starts looking for something to do. It starts executing whatever code it finds in its program memory starting at location 0. In our program, that is a line that says 'bsf status,rp0'. In English (sort of) this means set the bit called rp0 in the status register to a 1. Why we need to do this will have to wait. For now, suffice it to say that we need to.

The next line 'movlw 00001111b' tells the PIC to move the 8 bit binary value 00001111 into the W register. The following line 'movwf trisb' says to move the contents of the W register into the data direction register for PortB. This is the line of code that defines the inputs and outputs. Every bit that has a zero written to it will become an output and every bit that has a 1 written to it will become an input. In this case, bits 0 - 3 are set to inputs, and bits 4 - 7 are set to outputs.

The last line in this group is 'bcf status,rp0'. It resets the rp0 bit in the status register to 0.

The next line, 'bcf portb,4', sets the starting value of our output. It says set PortB bit 4 to zero, which will set our AND output initially to low.

Now we are all set up and ready to perform the AND function. You've now seen the "easy" bit manipulation stuff, so let's get a little more sophisticated.

The next line is labeled 'loop' so we can get back to it when we need to. 'rrf portb,0' does a couple of things for us. We need to be able to AND together bit 0 and bit 1 of PortB. To do this, we need them in the same bit location. 'rrf portb' tells the PIC to rotate right PortB. This moves all of the bits one lower so that bit 7 becomes bit 6, the old bit 6 becomes bit 5, and so on until bit 1 moves into bit 0, right where we want it. The old bit 0 falls off the end (it actually falls into Carry). The ',0' at the end doesn't specify bit 0, one of the more confusing things about Microchip assembler. Instead it tells the PIC to store the result of the operation into the W register.

So what we have at this point is bit 0 of the W register containing the setting of the switch connected to PortB.1. Now we can directly AND this value with the current value of PortB to get our result. This is what the next line does, 'andwf portb,0'. It says to AND PortB with W and place the result in W (',0').

Now we have finally done the AND and the result is in bit 0 of W. All we have to do is a little cleanup and we're out of here. Since all we care about is bit 0, we have to get rid of the other 7 bits as we don't know their values. We do that by setting them all to 0 with 'andlw 1'. That instruction, in effect, isolates our 1 bit that we care about and sets the Zero flag accordingly.

The next line 'btfsc status,Z' checks the state of the Zero bit in the status register and then does something odd. If the Zero bit is clear (the result of the AND is not zero), it skips the next instruction and does not execute it. If the Zero bit is set (the result of the AND is zero), things move along normally and the processor goes on to the next instruction, 'goto loop'.

'goto loop' sends the PIC back up the program to the line labeled 'loop'. It does this if the result of the AND is low. The object is to keep looping until the result of the AND is high and only then set the output high.

If the 'goto loop' was skipped over by the Zero test, the instruction 'bsf portb,4' is executed next. This is the big enchilada. It tells the PIC to set bit 4 on PortB high to turn on the LED. The eagle has landed.

In the above example the program can be easily modified to change the type of gate it is simulating. The AND (andwf) could be replaced with OR (iorwf) or XOR (xorwf) or you could easily turn it into a NAND (btfsc status,Z).

Another Way

Since we know from the truth table that the only time the output should go high is when both inputs are high, we could streamline the program like this:

        bsf  status,rp0     ;change to register page 1
        movlw 00001111b     ;make the bottom 4 bits on PortB inputs and the top 4 outputs
        movwf trisb         ;write them to the data direction register for PortB
        bcf  status,rp0     ;change back to register page 0
;
        bcf  portb,4   ;start with output low and LED off
;
loop    btfss     portb,0   ;skip next line if switch on PortB.0 is high
        goto loop      ;otherwise go check again
        btfss     portb,1   ;skip next line if switch on PortB.1 is high
        goto loop      ;otherwise go check again
;
; if we got here then both switches are high
;
        bsf  portb,4   ;set output high to turn on LED

EightyFiftyOne

Both of the previous examples are written in Microchip's assembler dialect. This same program written using an '8051' style assembler is a little more readable and quicker to write.

        setb rp0       ;change to register page 1
        mov  trisb,#00001111b    ;make the bottom 4 bits on PortB inputs and the top 4 outputs
        clrb rp0       ;change back to register page 0
;
        clrb portb.4   ;start with output low and LED off
;
loop    jnb  portb.0,loop   ;jump to loop if switch on PortB.0 is low
        jnb  portb.1,loop   ;jump to loop if switch on PortB.1 is low
;
; if we got here then both switches are high
;
        setb portb.4   ;set output high to turn on LED

I'm sure it all still looks pretty miserable and you might be ready to reach for your soldering iron and an old 7408. Enter high level languages.

Basically, It Could Be Easier

In the old days, 5 years ago, we were stuck working in assembler. Today, higher level languages are available even to these miniature microcontrollers. Programs can now be written in BASIC or C and stuffed into PICs using low cost compilers.

This same example written in PICBASIC could look like this:

        Low 4               'start with output low and LED off

loop:   If Pin0 = 0 Then loop    'jump to loop if the switch on PortB.0 is low
        If Pin1 = 0 Then loop    'jump to loop if the switch on PortB.1 is low

        High 4              'set output high and turn on LED

This program should be much more palatable yet if works exactly the same as the previous examples.

The original AND program written in PICBASIC would look something like this:

        Low 4               'start with output low and LED off
        Pin4 = Pin0 & Pin1  'OK, we're done

I don't think programming microcontrollers can get much simpler.

Once you get the program of your choice typed into the editor of your choice, save it as a pure text file and get ready for the last couple of steps.

Tools Make the Difference

Which of the above programs you can use depends on the tools you have. For the Microchip example above, you simply need their assembler, which may be downloaded from their web site.

To use the '8051' style program, you need either Parallax's assembler or the PIC Macro Assembler from microEngineering Labs, Inc.

To program in PICBASIC, you need the fabulous PICBASIC Compiler from microEngineering Labs, Inc.

These assemblers and compilers convert your text file into the native machine code that the microcontroller needs to operate. While they can be fairly sophisticated programs in their own right, their operation is very simple. All you need to tell them is the name of the file you want to compile/assemble and wait for the inevitable error messages.

For example, to tell the PICBASIC compiler to get your text file ready to program into a PIC, you would type:

     pbc and.bas

at a DOS command prompt (e.g. C:\). This assumes that you saved the program under the name 'and.bas'.

Whether you use the compiler or an assembler (and once you have fixed all the syntax errors), a file is created, in this case 'and.hex'. This is what a PIC programmer requires in order to stuff your program into a PIC. The created file is another text file in a specific format: Intel 8-bit merged hex.

Once you have this file, you are almost home free. All you need now is a PIC programmer. Several companies make these gizmos including Microchip and microEngineering Labs. You also might be able to use a universal device programmer, if you have one of those.

Most programmers plug into either a PC serial or parallel port. Once you have it hooked up, you stick your PIC into its programming socket, load the .hex file and press program. In a few seconds, your program will be magically installed more or less permanently into the PIC.

The final task is to plug the freshly programmed PIC into your circuit and test it. We find that all of our PIC programs work perfectly the first time and never need debugging (time to wake up now).