« RIGHT! The McLaughlin Group on Internet Gambling | Main | Censor This Post »

Read Modify Write Errors (Why the **** aren't my LED's blinking right?)

This is an issue that I have encountered on virtually every PIC-based project that I have ever worked on. Trying to do something possibly as simple as turn a series of LEDs on at a time becomes a frustrating exercise in futility. Why?

It has to do directly with how the PIC modifies an I/O port when you set a pin individually. (NOTE: this may not apply to all models of PIC hardware, but I have encountered the effect in a wide variety of PIC devices).

When the PIC sees a command such as HIGH PORTB.0 it actually must read the entire PORTB from the pins into an internal register. This happens regardless of whether or not the pin is configured as an input by the TRISB register. Then it modifies bit 0 in the register and finally writes the entire byte from the register out to the I/O port.

Doesn't sound so bad, right? Well usually it's not, unless you do something like:

In this case with an LED connected directly from the PIC pin to ground, when the PIC reads PORTB from the pins, it reads the voltage across the LED, because that is what is connected directly to the pin. Unfortunately, depending on the color and exact makeup of the LED, this voltage will be anywhere from 2-4V, because the current through the LED is limited by the PIC to around 20mA (which is a good thing unless you like the smell of burning silicon).

In the worst case, with a typical red LED connected, the pin voltage will be in the neighborhood of 2V, which is low enough to be read by the PIC as a zero at least part of the time. This leads to a situation where if you were to execute a block of code like
it's quite possible you will end up with only the LED on PORTB.2 turned on, since when the second HIGH statement is executed, pin B0 might be read as low and then get re-set to low, even though that clearly is not the intent of the code.

So what can you do? The simplest (and probably most correct) thing to do in this case is to simply add a resistor to limit the current through the LED external to the PIC. This means that the full 5V from the power supply shows up on the PIC pin, ensuring that the PIC will keep high the pins that are supposed to be high

This effect can also occur if there is a capacitive load on the pin, which can in some cases mean just excessively long wires, or a capacitor of course. The likelihood of this increases as the frequency of the signals gets higher.

This leads to another potential approach that I have used with good results (actually it's how the PIC running at 40MHz is operating with 32 channels of firmware PWM in City Streets, Northern Lights). This alternate approach is to leave all of the port pins set high (PORTB = %11111111), but to use the TRIS registers to turn the outputs on and off. In many cases, making a pin an input is effectively the same as driving it low, and this certainly works in the case of LEDs.

This means the above sequence of turning three LEDs on consecutively would look like this:
LOW TRISB.0 ' note that making TRIS low makes the pin an output,
' driving it high -- don't forget to invert your logic

The situation/solution is the same if you are coding in PICC or even PICASM. My understanding however, is that the AVR that is the core of the Arduino uses an internal shadow register that eliminates this problem, although I haven't checked it firsthand. You can also perform this function manually in the PIC by declaring a variable that acts as a stand-in for the PORT register. You then modify this and write the entire byte to the port manually, like this:
shadowB var byte
shadowB = %00000000

PORTB = shadowB
HIGH shadowB,0
PORTB = shadowB
HIGH shadowB,1
PORTB = shadowB
HIGH shadowB,2
PORTB = shadowB

Being the most complex, this is the solution I use least often. Typically I make sure my outputs are isolated with resistors and then use the TRIS method if I am doing something high speed, like PWM or capacitively loaded.

Hopefully this makes a little sense of some of those random problems that plague your breadboard from time to time.


Warning: fsockopen() [function.fsockopen]: php_network_getaddresses: getaddrinfo failed: Name or service not known in /data/www/jamesnsears.com/site/delicious/del_inc.php on line 31

Warning: fsockopen() [function.fsockopen]: unable to connect to ssl://api.del.icio.us:443 (php_network_getaddresses: getaddrinfo failed: Name or service not known) in /data/www/jamesnsears.com/site/delicious/del_inc.php on line 31
Couldn't connect to api.del.icio.us