0
\$\begingroup\$

I have been chasing the 'read modify write' problem for two days. This is a problem that when you write to one pin a neighboring in is affected. It suddenly started happening - different chips - different computers - different breadboard. The only thing I can think of that is in common is the programmer and the weather. I never had this kind of problem with any other embedded systems, Arduino, raspberry pi, Motorola, etc...

Before I give up completely on (PICs), is this an issue anyone has heard of before? is there a solution?

\$\endgroup\$

closed as unclear what you're asking by Trevor_G, PlasmaHH, Chris Stratton, ThreePhaseEel, Sparky256 Jan 5 '18 at 2:59

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 5
    \$\begingroup\$ You should, like, tell us the problem. "Affected" is as unclear as it can be. And you even tell less about what you do \$\endgroup\$ – PlasmaHH Jan 1 '18 at 14:02
  • \$\begingroup\$ The PIC series is notorious for having multipurpose pins. Likely that an internal function has control of the pins by default. Review the diagrams and users guide carefully. \$\endgroup\$ – Sparky256 Jan 5 '18 at 2:58
12
\$\begingroup\$

These PICs do exactly what the datasheet says they do. If there is a problem, its with your code or your (lack of) reading the datasheet.

The important thing to keep in mind is that bit operations on a port register read the port, do the bit operation, then write the whole port back out. Note that reading the port register reads the instantaneous state of the pins, NOT necessarily what was last written to those pins. That can differ from what was last written in two cases:

  1. The TRIS bit is set so that the pin is in high impedance state. The actual pin value is therefore up to the external circuit. If you permanently intend the pin to be a input in this application, then this case doesn't matter. The value written to the port register bit is irrelevant to a pure input pin.

  2. The external circuit is holding the pin in the opposite state it was last set to. This could be due to a hardware bug, but also just due to slew rate limitations. The pin may not have had enough time to get to its new state before your code reads it again.

It's usually case 2 that people mess up. Here is a classic example of such a screwup:

;   All Port B pins were previously configured as outputs.
;
         banksel portb
         clrf    portb       ;drive all Port B pins low

         ...                 ;many cycles pass

         banksel portb
         bsf     portb, 0    ;RB0 starts going high
         bsf     portb, 1    ;Oops! RB0 not high yet, so gets set low

The simple fix is to leave enough time between the two last BSF instructions so that RB0 is solidly at its new state by the time you try to set RB1. Usually only a single instruction is enough.

Due to the timing of individual actions within each instruction, the example above gives RB0 only 1 Q-cycle, if I remember right, between it getting driven to its new state and PortB being read again. That's only ¼ of a instruction cycle. Even a single other instruction between the two BSF provides 5x more time for RB0 to get to its new state. That's usually enough.

However, don't go by rules of thumb. Do the math. The datasheet tells you the minimum current the PIC will source on attempt to drive RB0 high. You know what your external circuit draws, and the maximum capacitance on the line.

You will see a lot of bad advice out there to always use shadow registers. That makes the code more clunky, adds more chance of error, and is unnecessary in most cases. The real solution is to actually understand what is happening, then make sure the mechanisms that would cause trouble are not triggered. I have on very rare occasions used a shadow register for a port on a old PIC 12 or 16, but probably only once or twice.

Note that all newer PIC families from the original PIC 16 have LAT registers. These completely get around this problem. The LAT register retains the last value written to a port. The port register still exists, and as before, it contains the instantaneous value of the port pins on a read. So instead of doing things like BSF on the port register (BSF PORTB,1) you do it on the LAT register (BSF LATB,1).

All PIC 18, 24, 30, 33 and the new enhanced PIC 16 have LAT registers. You really only run into the read-modify-write problem on the 12 bit and old 14 bit cores.

\$\endgroup\$
  • 2
    \$\begingroup\$ read-modify-write problem ? Isn't the proper engineering term feature when hardware conflicts with intuitive understanding? +1 for including Microchip's newer I/O mechanism & history. \$\endgroup\$ – glen_geek Jan 1 '18 at 15:10
  • 3
    \$\begingroup\$ Good explanation, upvoted. The difference between atomic rmw that reads the I/O pins vs atomic rmw that reads output latches was well-documented in the original Intel MCS-51 manuals, if I remember rightly. That device family used 'quasi-bidirectional' ports rather than a DDR for port bit directions, so this was a pitfall to be aware of. \$\endgroup\$ – TonyM Jan 1 '18 at 15:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.