The PCF8574(A) is a remote I/O pin expander board included in the MAKERbuino inventor’s kit. It’s connected to the ATmega328 via the I2C-Bus.
So if you’re running short on usable I/O-Pins this is the thing you’re looking for!
What is I2C?
The name I2C stands for Inter-Integrated Circuit.
It’s a synchronous serial bus using only two wires. It’s capable of transmitting data bidirectional as one wire is used for the clock signal and the other one is used for the data signal. It was initially developed by Phillips in the early ‘80s. Some other manufacturers call it TWI (two wire interface) due to licensing issues.
There is at least one master and up to 127 slaves on an I2C bus system. You can create a multi-master bus-system too, but that would be too much for this guide. Every slave has its own individual address. Therefore, it’s easy for the master to communicate with the slaves. There’s even a broadcast channel to reach every slave at once.
The PCF8574(A) has three pins to set its address within a defined range. You can set the address using the jumpers on the PCB. They are marked as A0-A2.
There is a difference between the PFC8574 and the PCF8574A as you can see below. Every address provided is written in HEX.
As you can see, the address of the PCF8574 is 0x40 (writing mode) and the address of the PCF8574A is 0x70 (writing mode).
This applies if all jumpers are set to GND, which is the case if you just received your kit and didn’t change anything.
If you want to read the values you need to set Bit 0 to “1”. So the initial read address for the PCF8574 is 0x41 and the initial read address for the PCF8574A is 0x71.
But beware! The Arduino Wire-library handles the addresses a little bit different. You only need to provide the 7 highest Bits of the address. The read/write Bit of the address is determined by which function you use. So if you are calling the Wire.write() function Bit 0 is set to “0” and if you are calling the Wire.requestFrom() function Bit 0 is set to “1”.
Now comes the tricky part: Imagine you want to write some values to the PCF8574.
You’d think “now that’s easy! I left all the jumpers connected to ground, so I just need to use the address 0x40.”, but unfortunately, that’s wrong.
The Wire-library internally shifts the bits one to the left and sets Bit 0 to “0”. So 0x40 would become 0x80 and the PCF8574 wouldn’t respond to your messages.
Therefore, you need to shift the Bits of your address one to the right in order to make it work. You can do this within the code or you can determine the shifted address before writing your code. Using defines is recommended in either way. To determine the right address before coding you can use pen and paper or you can use the programming view of the windows calculator.
If you prefer to adapt the address within the code itself you just need to do the following:
We can now communicate with the PCF8754, but how should we tell it, which pins we want to use as inputs or outputs?
It’s quite easy: to use a pin as an input, you just need to set it to HIGH. Then you can read its value using the Wire.requestFrom() function.
If you’re reading this guide, then you’ve probably bought a MAKERbuino together with the inventor’s kit.
This kit contains a breadboard and a couple of bits and pieces besides the PCF8574(A) GPIO expander.
We’re going to use these included components to build a little circuit and test the whole I2C concept in practice!
Just recreate this circuit using the provided fritzing schematic diagrams:
Your finished circuit should look somewhat like this:
As you can see, there is an LED and its corresponding pre-resistor and one button connected to the PCF8574(A). The LED is powered via VCC and the button is connected to GND. You can easily grab the VCC signal and the GND signal from the PCF8574(A) PCB since I2C is a bus system.
I’ve chosen the blue LED because it needs 2.9V to operate properly so you just need to “eliminate” 0.4V from VCC.
If pin P0 is set to HIGH, the LED will be OFF and if it’s set to LOW the LED will turn ON. How does it work? We’re providing the necessary voltage and current via VCC. This means that we’ve got 3.3V to power the LED and pin P0 has 3.3V at HIGH too. So the difference between the anode (+) and the cathode (-) of the LED is too low to let the current flow. As I mentioned before the LED needs 2.9V to operate. If we set P0 to LOW it has 0V (because it’s connected to GND via an internal transistor) and there’s our needed difference!
Be careful if you are planning to add more LEDs! You are adding more load to the voltage regulator since VCC is provided by this part. The regulator can deliver up to 250mA current max, but it’s not recommended to operate at max load the whole time. Doing so could damage your voltage regulator.
Keep in mind that every part of your MAKERbuino that comes after the voltage regulator needs a certain amount of current. So every part puts a certain amount of load on the regulator.
SOLUTION: You can use the unregulated power provided by the battery directly via VBAT but then you need to measure the voltage of your battery and recalculate the values of the resistors.
One thing to have in mind: you cannot power an LED with its anode (+) connected to a pin on the PCF8574(A) because It only delivers 100µA in its HIGH state (that is about 40 times lower than the current we need to light up the LED).
Therefore, the PCF8674(A) only works in a so-called sink mode. If a pin is set to LOW, the internal transistor of the IC connects the pin directly to GND. One pin of the PCF8574(A) can take up to 25mA current in sink mode but the total current of all 8 pins shouldn’t exceed 100mA as it could damage the PCF8574(A) too.
Now, after our trip to the field of basic electronics, let’s move on to the code.
We want to do the following: If we push the button the LED should light up and if we push the button again, the LED should go off.
The code should do this:
- Set all Pins of the PCF8574 to HIGH within the setup function
- Within the main loop: read the states of the pins
- If the button pin has a LOW signal, increase a counter variable
- If the counter reaches a certain value, toggle a bool variable
- Write the new pin value (HIGH or LOW), determined by a boolean variable, to the PCF8574(A)
- Check if the C-button was pressed, if so – go back to the title screen
Before we can actually start coding we need to find out if we’re trying to communicate with a PCF8574 or with a PCF8574A. You need to look at the IC on the PCB. Mine says PCF8574A so the address is 0x70 since all jumpers are set to GND. All Bits shifted one to the right results in an address value of 0x38.
Alright, let’s go!
Firstly, we need to include all the necessary libraries:
As I mentioned at the beginning of this guide, the Wire-library is the one responsible for the I2C communication. Both, the SPI-library and the Gamebuino-library, should sound familiar to you.
Next, we need to initialize the Gamebuino library by creating a Gamebuino object and declare some basic defines and variables.
The first #define function should be clear because it’s the initial address of the PCF8574A, already shifted 1 Bit to the right.
The second define determines the pin of the PCF8574A we want to use as an input. A value of 0x02 means that we want to use pin P1 of the I/O expander as an input.
The bool variable ledOff is responsible for switching the LED on or off as we write its value into the PCF8574A. You’ll find out why I called it ledOff instead of ledOn once we got to the loop function.
The byte variable values stores PCF8574A’s pin values. The integer counter is going to store the current value of a counter. We’ll need a counter to prevent the LED from flickering as the communication happens pretty fast and you’d need to push the button for about 50 milliseconds as this is the usual screen refresh rate on the MAKERbuino.
With all those variables declared and initialized, we can move on to the setup function.
The first line of code in the setup function shouldn’t need any explanation, but the next four lines are new:
Wire.begin initializes the I2C module so that we can use it. It’s essential to call this first, just like we need to call gb.begin before we’re able to show the title screen on MAKERbuino’s display.
After that, we want to set all pins of the PCF8574A to HIGH.
We can do this by starting a new transmission to the PCF8574A using Wire.beginTransmission and the defined address of the PCF8574A.
Since Wire.beginTransmission is a write-initializing function, PCF8574A is now waiting for some kind of data to arrive.
That expected data can be sent using the Wire.write function and the Hex value 0xFF.
Now every pin is set to HIGH. Hence we’re only using pin P0 and pin P1 we could’ve sent 0x03 too, but let’s stick with 0xFF.
The last step is to tell PCF8574A that we’re done sending.
We’ll make that happen by calling Wire.endTransmission();. This function doesn’t need any parameters.
Now let’s draw a neat title screen and that’s it, we’re done with the setup!
Here comes the main loop section:
This might look a little bit confusing at first, but it’s quite simple in fact.
Of course we need our if(gb.update()) if-statement since we want to check PCF8574A’s state at every frame.
We now need to request the current values from the PCF8574A.
Requesting updated values from the GPIO expander is done using Wire.requestFrom function.
As you can see, there are two parameters required by this function.
The first one is the address of the slave we’re trying to request data from and the second one is a number of bytes that we’re requesting.
One byte is enough since the PCF8574(A) only has 8 pins (P0:P7).
After that, we need to read the data received from the PCF8574A.
Wire.available function returns the number of bytes that are available for reading.
It should return “1” because PCF8574A only sends one byte.
Wire.available is used as a condition of a while loop and will run once every frame.
Current pin values are being updated and stored in the values variable by using Wire.read in the while loop. This function returns the (next) byte that was received.
Now let’s check if the pushbutton was pressed or not. Remember the #define for the button pin? We’ll make use of it now!
We’ve defined the button value with 0x02. In binary, this means 0b00000010.
Okay, but how do we check it now? We need to use the bitwise AND operator.
We’ll combine the current pin values and the inputPin define within the if-statement using the & (AND) operator.
There are only 2 possible results: 0x00 (the button is pressed) and 0x02 (the button isn’t pressed). If the bitwise operation returns 0, the counter is incremented by 1. If the bitwise operation returns 2, it resets the counter to 0.
As I mentioned before, the counter is used to prevent the LED from flickering and now we need to check if the counter has reached a certain value.
This is another simple if-statement and should need no explanation. If the counter reaches the value of 5, the bool variable ledOff is toggled. The counter is then set to 0 again.
The next step is to tell PCF8574A whether the LED should be on or off.
A part of it should already sound familiar to you since we used the exact same functions within the setup function. The only difference is the data we’re sending now.
We need to use another bitwise operator. This time it’s the | (OR) operator. The | (OR) operator sets a Bit to 1 if at least one of the two Bits that are combined already is 1.
It doesn’t matter if the second Bit to be combined is 1 or 0. Now, we can combine the defined inputPin and the ledOff variable using the | (OR) operator. There are only 2 possible results again: 0x03 (LED off) and 0x02 (LED on).
And now you’ll know why I called the variable ledOff instead of ledOn! In order to switch the LED off we need to set pin P0 of the PCF8574A to HIGH. High means 1 in binary or true in bool while Low is represented by 0 or false. If the value of ledOff is true we’re telling the PCF8574A to set pin P0 to HIGH. So it’s true that the LED is off!
Alright, we’re almost done coding! Great job so far! The last three things we need to do:
- print the current LED status on the MAKERbuino screen
- clear the values variable (just in case)
- make it possible to return to the title screen.
This shouldn’t be too hard to understand!
Set both cursorX and cursorY to 0 and print “LED off” or “LED on” to the screen, determined by an if-statement.
Set the value of the values variable to 0 and add an if-statement to check if the C-button was pressed. If it is pressed, show the title screen!
Find the full code here:
Or simply download the Arduino source file here.