When the PIC doesn't "co-operate" ...
Using a PIC is easy: write some code, burn it into the Flash-ROM, put the PIC in your application circuit, supply voltage and it works — well, mostly ...
For all supporters of Murphy's law, here are some ideas what may go wrong.
The PIC doesn't work at all
- The PIC is not inserted correctly.
Sounds stupid, but it is not impossible to put a PIC the wrong way around, bend a pin or get a socket with bad contacts.
- Missing supply voltage.
- Oscillator failure.
If speed and accuracy doesn't matter I would recommend the use of the internal oscillator. It simplifies the board design and you get two extra I/O pins.
- A typical beginner's mistake is to forget setting the configuration word. In this way the PIC has the RC oscillator enabled and obviously will not work when the external circuit is missing. To avoid this you should always set the configuration word within your source file.
- An RC oscillator may become unstable when R and C are outside of the recommended ranges (3kΩ < R < 100kΩ, C > 20pF).
- If you are using quartz crystal or ceramic resonators, try HS mode for frequencies above 4 MHz.
- Most 12Fxxx/16Fxxx devices have a limited oscillator frequency range for supply voltages Vdd below 4.5 volts.
- The PIC stays in RESET state.
Besides of the simple cause that the /MCLR pin is "accidentally" held "low", there are other not so visible situations which may prevent the PIC from running:
- If "Brown-out Reset" is enabled, then the PIC will stay in RESET while Vdd is below certain level. This "Brown-out" voltage level is device specific. You may use the "PIC Info" function of 'PicProm' to find it out.
- The build-in "Power-on Reset" logic of some PIC requires that Vdd starts raising from near Vss. Otherwise, it can lock.
This may be crucial when turning off and on low-power circuits. A 100µF buffer capacitor, e.g., may take minutes to discharge completely, since the PIC will not drive any significant current at very low voltage levels.
- Voltage spikes below Vss at the /MCLR pin may cause latch-up. If this pin is driven by a low-impedance circuit, e.g. a key, a series resistor of 100Ω should be used.
- With voltages significantly above Vdd at the /MCLR pin the PIC will enter programming mode.
- The debugger logic is enabled.
When debugger logic is enabled, the PIC executes the first instruction and then halts. Therefore, after debugging your software you must re-program the PIC in normal mode before using it within the application circuit.
- The PIC seems to have passed away.
In my experience, PIC are "hard to die". Even after reversing the supply voltage, there is hope.
Before transforming it into a nice sculpture you may try to recover the PIC by erasing it several times.
The PIC doesn't work as intended
- Wrong register bank selected.
The op-code of the mid-range PIC 12Fxxx/16Fxxx can only hold the lower 7 bits of the register address. To permit more than 128 registers they are organized in two or four banks. When writing assembler code, before accessing a register, you must ensure that the corresponding bank is selected within the STATUS register.
If MASM generates a "List file", the editor of 'PicProm' can show both, the HEX file and the corresponding source lines, which permits a visual verification of the generated code.
(With MASM you indeed can use the pseudo instruction "banksel" before every register access, which will generate the required instructions to set the bank bits properly, but I would not recommend this method for two reasons: first, apparently you can't put a label in the same line, and second, this pseudo instruction always generates code — even if the bank bits are already correctly set — thus blowing up code size and increasing execution time.)
- Wrong code page selected.
The op-code of the mid-range PIC 12Fxxx/16Fxxx can only hold the lower 11 bits of a call or jump target address, which gives a direct address range of 2 KWords. When your code exceeds these 2 KWords, you must ensure that the corresponding target page is selected within the PCLATH register before executing a "goto" or "call".
- The Watchdog Timer periodically resets the device.
If the Watchdog Timer is enabled — which is the default state —, make shure to distribute "clrwdt" instructions within the code in such a way that on normal operation at least every 10 ms one of them is executed.
Before deactivating the Watchdog Timer in the Configuration Word you should take into consideration, that this is the only way for a PIC to "recover itself" when it is trapped in an endless loop for some software malfunction.
- The I/O ports are not initialized properly.
On "Power-on Reset" some port pins may be configured as analog input and always read '0'. This is device dependent. See the data sheet for correct port initialization.
- Instruction with I/O port as target.
When modifying port data, you should be aware that the instruction will read the current – external – state of all port pins and write the result to all output buffers of the port. This may give unexpected results. Some examples:
- It is a good practice to pre-load the output buffer before switching port pins to output — but take care when switching multiple pins!
Let's assume you want to put RB0 and RB1 to output "low" and the pins are actually driven "high" either from internal or external pull-up resistors:
The second instruction, "bcf PORTB,1", will read the entire PORTB, clear bit 1 and write the result to the output buffers. Since RB0 is still input, it will reflect the "high" state (= '1').
|bcf ||PORTB,0 ||; output buffer RB0='0'|
|bcf ||PORTB,1 ||; output buffer RB1='0', but RB0='1' |
|bsf ||STATUS,RP0 ||; bank 1|
|bcf ||TRISB,0 ||; configure RB0 as output|
|bcf ||TRISB,1 ||; configure RB1 as output|
To avoid this you must set the output buffers of RB0 and RB1 in the same instruction:
By the way, if your interrupt service routine affects PORTB you should disable interrupts during the above sequence.
|movlw ||0FCh |
|andwf ||PORTB ||; output buffer RB0='0' and RB1='0' |
|bsf ||STATUS,RP0 ||; bank 1|
|andwf ||TRISB ||; configure RB0 and RB1 as output|
- If you have a capacitive load on an output pin – e.g., a long wire –, switching the pin may take a significant amount of time.
Let's assume RB2 and RB3 are configured as output, both driven "low" and you want to put them to "high":
The first instruction writes the output buffers of PORTB at the end of the Q4 clock cycle and the second instruction will read the real state of the port pins at the beginning of the next Q2 clock cycle. If RB2 can't recharge the wire within less than one oscillator period the second instruction still may read "low" and will return it to the output buffer of RB2, overwriting the desired state.
|bsf ||PORTB,2 ||; output buffer RB2='1'|
|bsf ||PORTB,3 ||; output buffer RB3='1', but eventually RB2='0' |
Thus, one should set the output buffers of RB2 and RB3 in the same instruction:
|movlw ||00Ch |
|iorwf ||PORTB ||; output buffer RB2='1' and RB3='1' |
- On older devices the RA4 pin is an "open drain" output. If the output buffer of RA4 is set to '1', but the pin is externally driven "low", any instruction modifying another pin of PORTA will have the side effect to switch the output buffer of RA4 to '0'. Thus, the line will now be driven "low" from the PIC.
- When a pin is configured as analog input, any instruction modifying another pin of the affected port will set the output buffer of this pin to '0', since the input always reads '0'.
The 'trap' of analog pins is, that clearing the TRIS bit will (only) activate the output stage while the input stays in analog mode, i.e., you get a combination of digital output and analog input. To make a output work reliable you should configure the pin as digital I/O or use a shadow register for the port (see next point).
- The impedance of the output drivers varies with temperature, Vdd and external load. Due to the saturation of the output stage, in the worst case a current injection of a few milliamperes may flip a pin state from "high" to "low".
(Unfortunately, newer data sheets don't show the entire operating range of the output drivers. Just compare the graphs "VOH vs. IOH" for 16F88x, Figure 18-28, with those for 16F87xA, Figure 18-17.)
If your circuit works in a noisy environment, transients may find a way to the port pins.
And a spike hitting a pin just during an output operation gives a nice example for Murphy's law...
To prevent "strange" behaviour of "real-world" applications, the use of shadow registers is recommended. All manipulation of the port bits is done with the shadow register first and the result then transferred to the port register, e.g.:
In this way, the external state of the port pins doesn't matter.
|bsf ||Port_B,4 ||; shadow register RB4='1'|
|bcf ||Port_B,7 ||; shadow register RB7='0' |
|movf ||Port_B,w ||; read shadow register and ...|
|movwf ||PORTB ||; ... set all output buffers |
- Missing main loop.
Simply spoken, once powered up, the PIC will execute instructions from program memory until it is powered down. There is no "halt" instruction and the "END" statement of an assembler code block has no meaning with respect of code execution.
Thus, the program must contain a (never ending) main loop or — if the program should only run once after power-up — at least terminate with a "goto $" instruction to avoid the execution of empty program memory.
- Stack overflow.
The 12Fxxx/16Fxxx PIC have only 8 stack levels for "call" instructions and the interrupt routine. If your program makes a too excessive use of nested subroutines a stack overflow may occur — and there is no special handling provided. The program will simply crash.
Therefore, when loading the HEX file, 'PicProm' will analyze it and gives a warning if stack overflow might occur during program execution.
- Missing bypass capacitor.
A decoupling capacitor of 100nF should be provided as close as possible to the Vdd and Vss pins of the PIC. My preferred way for PDIP-devices is soldering a small SMD capacitor directly between the pins.
back to the main page