ME Labs, Inc.
PIC® and a Better Mousetrap
As published in MicroComputer Journal May/June 1997
One of the hottest pieces of silicon around these days is a microcontroller from Microchip Technology called the PIC®. The PIC, which keeps getting renamed to PIC16/17 and now I believe to PICmicro®, is not just one chip but a whole line of microcontrollers. We'll review the line-up and maybe invent a better mousetrap along the way.
The PIC is actually a very simple device. But the way this simplicity is implemented makes it very powerful. It only has a few parts: its microprocessor brain, a place to store its program and a bunch of special purpose and general purpose registers - that's it. We covered program storage last time. This time we'll take a look at the brain.
In keeping with the PIC's simple nature, it is a RISC device. RISC stands for Reduced Instruction Set Computer, which means that the chip designers didn't put in a lot of instructions for the microprocessor to have to deal with. At the opposite end of the spectrum is CISC, Complex Instruction..., things like Intel '86 series parts. These things have a large instruction set that can do much more in a single instruction. Of course a single instruction may take many words of storage space and a long time (relatively) to execute.
RISC keeps the instruction size small so each instruction takes up only 1 word of storage space (ROM, EPROM, EEPROM or flash). Usually, but not always, it also means that each instruction takes only 1 cycle to execute. But let's come back to that.
Notice that program storage is referred to in words rather than bytes. A byte is an 8-bit wide entity. A word is usually thought of as 2 bytes or 16 bits and at the programming level this can be true. But at the microcontroller core level, in the case of the PIC, a word may be 12, 14 or 16 bits long.
What does this mean to you? Well, the more bits in an instruction word, the more complex the reduced instruction set can be. This is sort of paradoxical but it is why RISC and CISC is converging in the middle.
PICs started off life with a 12-bit core. The PIC16C54 was the first of its kind. This chip is used in all kinds of things from computer mice to satellite TV access cards. Other PICs with the 12-bit core include the PIC16C52, 55, 56, 57 and 58, generically known as the PIC16C5x series. These chips have between 18 and 28 pins with 13 or 21 of those as general purpose I/O pins.
Microchip recently introduced 8-pin PICs based on the 12-bit core. Up to 6 of these pins can be used for I/O!
Pardon the Interruption
The majority of PICs now available have a 14-bit core. The extra bits have allowed additional features to be added, one of which is interrupts. Interrupts provide a mechanism that allows a microprocessor to drop whatever it is doing and immediately go take care of a much more important event.
An example of interrupts in real life would be the telephone. Just as you are concentrating on writing a magazine article it rings interrupting my, er, your train of thought. You have 2 choices: you can drop whatever you are doing and immediately answer it, or you can ignore it. The PIC does this by either having the interrupts enabled or not. When it is first powered up, the interrupts are disabled. I suppose there would be a third choice for the phone interruption, but the PIC doesn't have an answering machine. (You could use a PIC to build a pretty nifty answering machine though.)
Interrupt sources on the PIC range from counter/timers overflowing, serial characters being received, analog to digital conversion completions or I/O pins changing states, among others.
When an interrupt occurs, the microprocessor completes the instruction it is currently working on and jumps to a specific location. On the PIC it is address 4. At this location you would write a routine called an interrupt handler, to act upon whatever event tripped the interrupt. If it was a complete serial character being received, for example, you might want to get the character from the hardware register and store it in some kind of RAM buffer to be interpreted later.
Once the interrupt handler has done its job, it would be nice to go back to where we were in our program before we were so rudely interrupted. That means we need the address of where we left off so we can get back there. Fortunately that has already been taken care of for us or we would be in real trouble. Just before the PIC made the jump to the interrupt handler, it stored the address of the current program counter on the stack.
The stack is a special set of memory locations that are used to store the program counter when a subroutine is called or an interrupt occurs. When a subroutine is called, the current value of the program counter is pushed onto the stack and the program counter is loaded with the address of the subroutine. Execution then continues at this new address. When a return is encountered, the program counter is reloaded with the value popped from the stack. This should be the address of where it came from at the time of the call.
On a PIC with a 12-bit core the stack is 2 levels deep. This means that you can nest subroutine calls only 2 deep (keep in mind there are no interrupts on a PIC with a 12-bit core). If you make a third nested call, the program counter value from the first call is pushed off the stack and lost forever. You can never go home again. Usually this results in a program crash which can be difficult to find.
The 2-level stack is pretty limiting, so much so that interrupts would really not be practical. So along with interrupts, the 14-bit core includes an 8-level stack. This allows many more levels of call nesting and room for return from interrupt program counter storage.
On some microprocessors the stack is in main RAM and both extendable and accessible for data storage. On the PIC it is inaccessible and may not be used for any purpose other than storing the program counter. By the way, the stack is 13 bits long - just enough to store a return address to anywhere in up to 8K of program memory.
PICs with a 14-bit core also include built-in weak pull-up resistors for PortB I/O pins. This feature can help avoid the use of extra discrete components like external pull-up resistors on switches.
And Now the Players
The list of PICs with the 14-bit core is too long to bore you with here and Microchip is adding more entries almost daily. Generically the series are the PIC16C55x, 16C6x, 16C7x, 16C8x, 16C92x, PIC14000 and PIC12C67x. Each series has its notable features.
The 18-pin PIC16C55x series includes the 16C554 which is the lowest-cost member of the 14-bit core PICs. It is essentially a souped up PIC16C54 with an 8-level stack and interrupts.
The PIC16C7x series include 18- to 40-pin devices that have on-chip 8-bit analog to digital convertors, along with many other goodies. The 28-pin PIC14000 also excels at analog manipulations.
The PIC16C(F)8x series main benefit is reprogrammability through the use of EEPROM or flash program storage.
The 17Cxx PICs have a 16-bit core. This adds yet another set of additional capabilities including the ability to run programs from memory that is not built on to the chip. All program storage for 12- and 14-bit core PICs must be on the PIC itself.
Picking the Right PIC
With all of the different PICs available, how do you choose the right one for any given application? First look at your objective. If you plan to build a million of your gizmo and every penny counts, you might want to consider using one of the parts with a 12-bit core. They are without a doubt the lowest cost members. Try to fit your program in as little memory as possible as the parts with less memory and less pins also cost less money.
But very few applications actually sell that many. Or maybe you are just building a one-off project. In these cases, choosing a PIC with a 14-bit core will make the development cycle quicker and easier. It doesn't take long to use up a 2-level stack and some of the newer PIC's other hardware features can be a real boon.
Next figure out how much I/O you need. If you can get by with only 13 I/O pins, and you might be surprised how often you can, then the 18-pin PIC16F84 is an excellent place to start. Its flash nature allows even quicker program development since you don't have to wait for a UV erase cycle to reprogram and test again. It is also inexpensive for an erasable part. You can always substitute a cheaper OTP (one time programmable) at production time.
Of course if you need analog inputs, the '84 is out. You'll need to choose something in the 16C7x series or perhaps a PIC14000. However, you might still wish to debug the major part of the program on an '84 and then switch to a '7x part or a part with more I/O pins to finish up the program. This technique can get you off to a quick start without the need for an emulator.
Sometimes you already have a project in mind and sometimes you have to go looking. I had an idea to build a PIC sprinkler controller with an LCD to activate off-the-shelf sprinkler solonoid valves. My old mechanical one wasn't tripping my trigger. While I was contemplating the design, I looked through the building supply superstores for information. Of course what I found was a prepackaged electronic controller with power supply for $35.00. It did much more than I was thinking about doing and had a nice molded case that I could never duplicate. I decided to spend my energies elsewhere and bought it.
Let's get on to that mousetrap application I eluded to earlier. While we might not actually end up with a better mousetrap, it will be exquisitely more electronic and costly than the original.
All the more fun.
First let's look at I/O. We really only need a mouse detector and something to smash the mouse with once it has been detected.
For the mouse detector we could use a passive infrared (PIR) detector like the kind used in burglar alarm system. Or we could use a focused microwave or sonar ranging system to give us range and direction. Or we could use a load cell under the cheese to detect the change in weight. Or we could use a simple IR transmitter and detector to form a beam across the mouse pad (where the mouse will be standing) or across the cheese. Let's choose the latter.
For mouse deletion we can skewer the poor critter using a miniature spring-loaded spear gun rotated by a small motor and targeted with the above sonar or microwave ranger. A simple solonoid could provide the firing mechanism.
Or we could use that solonoid to release the standard spring-loaded mouse-squisher bar. Or if you are more humane, the solonoid could release a door to seal a cage. We will leave the spear gun behind and choose this approach.
So, for I/O lines, we need an input for the IR detector and an output for the solonoid. For special effects we'll throw in some LEDs to count the kills and a speaker for full THX sound (well sound anyway).
We could easily do this project with any PIC. If we were looking to commercialize it I would choose the 8-pin 12C508 for cost and size reasons. But since we are only amusing ourselves we will use the PIC16F84 (or 'C84, whatever you have) for some of the reasons described above.
The schematic in Figure 1 shows how to connect things up.
We'll try the program 2 ways, the first in assembler. I am using the "8051" variant.
clr portb ;write 0 to portb, turning off solonoid among other things clr mouse_cnt ;zero the mouse counter setb rp0 ;change to register page 1 mov trisb,#1000000b ;portb.7 input, rest outputs clrb rbpu ;enable internal portb pull-up resistors clrb rp0 ;change back to register page 0 chkbeam jnb portb.7,chkbeam ;Wait for IR beam to be broken setb portb.4 ;fire the solonoid clr wait_cnt ;set up a timer clr wait_cnt+1 waitloop djnz wait_cnt ;count down the timer djnz wait_cnt+1 inc mouse_cnt ;add one to the mouse death toll and mouse_cnt,#00001111b ;remove top four bits from count mov portb,mouse_cnt ;display new count and turn off solonoid endloop jmp endloop ;wait here until reset
The program flow is pretty straightforward. It does some initialization, setting the starting output port and direction values and zeroing the mouse counter. It then waits in a short loop for a mouse to wander up and break the IR beam. When the IR beam is broken, the internal pull-up resistor will make input bit 7 on PortB high and the program will break out of the loop.
Bit 4 on PortB will then be set high turning on the transistor and solonoid. A delay follows solonoid turnon. The PIC moves along so quickly that if the solonoid is turned back off right away, it wouldn't even have time to move and release the squisher bar, I mean door.
Finally the mouse counter is incremented by one, the count is sent to the display and the solonoid is turned off. Since there is no automatic way for the program to reset the springs, it waits for user intervention.
The program flow in PICBASIC is really not much different.
Pins = 0 'Start all pins off at 0 Dirs = %01111111 'Set Pin7 to input, rest to output Poke $81,%01111111 'Enable internal pull-up resistors chkbeam: If Pin7 = 0 Then chkbeam 'Wait for IR beam to be broken High 4 'Fire the solonoid to release the hounds Pause 250 'Wait for spring to sproing B0 = B0 + 1 'Add one to mouse death toll B0 = B0 & %00001111 'remove top four bits from count Pins = B0 'Display count and turn off solonoid For B1 = 100 to 1 Step - 1 'Set up a sound effect Sound 5, (B1, 5) 'Play sound Next B1 End 'Wait here until reset
The main difference between the assembler and PICBASIC program is that we have added some special sound effects at the end of the PICBASIC code. This could also be done in assembler but would take a fair bit more code. You could easily change the sound code to have it play taps if you built the more homicidal trap.
ME Labs, Inc.
2845 Ore Mill Road, STE 4
Colorado Springs CO 80904
(719) 520-1867 fax