Read-Modify-Write of port bits |
Most microcontrollers implement bit modifications to ports as a byte-wide read-modify-write operation. Where there is a difference is whether the read portion reads the pins or the data register. While it is documented, it isn't always that apparent and is one of those things that can catch you out and drive you up the wall trying to explain what is happening. If you are pin bashing I2C on a couple of port pins and it doesn't seem to work, consider this scenario. A typical method of making a bi-directional port appear as an open collector is to load the data register with a '0' and use the direction register to change the port from an input to an output as needed. When the pin is set as an input, there is no drive onto the bus and an external resistor pulls the bus high ('1'). When you wish to drive a '0' onto the bus, you set the pin to be an output and the '0' in the data register is output, pulling the bus low. When you perform a bit operation on a port pin the micro reads the whole port, modifies the bit you have specified and then writes the whole port back again. A read operation reads the value of the pin and a write, writes to the data register. So what happens if you have defined bits 0 and 1 of your port to be the I2C ports and bit 7 is a flashing LED ? If we assume that there is nothing happening on the I2C bus (for simplicity), then the data register bits for both SCL and SDA are cleared and the port bits are defined as inputs. Every few hundred milliseconds a timer interrupt is generated and we set or clear the LED port bit. If we now consider what is happening internally, a problem becomes apparent. To set the LED port bit, the micro reads the value of the pins and gets a '1' for both SCL and SDA since they are pulled high externally. The LED port bit is modified and the whole byte for the port is written back to the data register. At some time later, we try to perform an I2C operation and nothing happens because when we try to output a '0' by making the pin an output, the data register contains a '1', not the '0' that was written to it. The 8051 family is an exception to this, not only because the port structure differs from say an HC05 or PIC, but also because the bit manipulation operations on ports use the data latch rather than the pin. This is a very simple example but it illustrates how such a problem can arise. I'm not going to propose a solution like "always...." because each application is different. Like most of these things, simply being aware of situation is all the information you need. |
|