Saturday, June 20, 2020

PCF8574A I2C Shift Register/Port Extender--Can't Avoid That Sinking Feeling

It's the weekend, I remain stuck at home, so let's experiment with a new chip (new for me anyway) and see if we can blow it up.

For the recent retro bubble display post (here) I had fun with shift register ICs like the 74HC595--use 595's to increase the number of I/O pins on your MPU/MCU.

OK, I was buying pots at Tayda and found what looked like the same 595-596 shift register/extender idea, but controlled by I2C: the PCF8574A.

Looks powerful--super inexpensive.





(If you've followed this blog or messed with electronics you probably already know I2C, a super easy way to hook together digital devices so they can talk to one another. A good general description of I2C is here. For I2C coding, the Arduino "sketch" language makes communication with this protocol easy using its built in Wire.h library

OK, let's put the 8574A on the bench, get the datasheet (here) and see what's up.  


The short answer: yes.

The long answer: The datasheet is a partial head scratcher for me, what is the d flip flop for really? And what the hell is a "quasi-bidirectional port"?

Turns out, you can ignore all that; instead watch a few you tube videos. Ralph Bacon's 8574 vid, here, is recommended; Ralph always has user friendly and easy-to-follow vids.

Another good video is here--it features an Arduino sketch library to ease 8574 Arduino programming (here). The library however seemed to over-complicate things for the examples below so I stuck with Wire.h.

What I found: this chip for all practical purposes only sinks current and can't source it; also, to set a pin to read, you have to write a "1" bit to it first, then read a signal via when 5V source through a pull-up resistor (seems to need a large value? 100K?) is pulled to GND. When you read GND on this configured "input" pin, it will return to the MPU, via I2C, a value for the pin; read current limited 5v and you'll get a 0. 

This seemed a bit counter-intuitive to me, but beyond that, this is an extremely straightforward chip to use.

So my Saturday morning goal was to come up with the simplest examples of 8574A writes and reads I coulld. There are lots of 8574 examples on the Internet, but the two I have here, in this post, might be the simplest?

Let's do an "output" first--wire up an Uno and a 8574A like this to flash an LED:

don't forget to ground address 8574 pins A0,A1,A2--I bought "A" 8574 chips from Tayda, they have different I2C addresses vs. the non-A chips. Ground em all for an A chip you get 0x38 as your I2C address.  Your address may vary, so use the I2C checker here if you get stuck.

The obligatory bench photo (OBP)--ready to run the code below.....

Code to flash the LED is this:

 #include <Wire.h> //include Wire.h library
//this sketch sets pins to outputs.

void setup() {
  
Wire.begin();
}

void loop() {
  
  Wire.beginTransmission(0x38);
  Wire.write(0x00);
  Wire.endTransmission();
  delay(500);

    Wire.beginTransmission(0x38);
  Wire.write(0xFF);
  Wire.endTransmission();
  delay(500);
 // Wire.write(0x00);

  
}

Pretty easy--as long as you realize the damn thing only sinks current, well, it's pretty much what you'd expect.

Reading data is a tiny bit trickier:



(Note that I didn't use pull up resistors for SDA, SCL, and Interrupt ("N/C" ) as they didn't seem to be needed, although I found posts on line saying they were....not for me. In fact for the 2 examples here "interrupt" isn't used at all.....)

Here's the code for reading the switch's status:

#include <Wire.h> //include Wire.h library
//this sketch sets pins to outputs then reads.
//pull up to 5V at a pin via 100K resistor returns 0
//GND returns a value for the read pin.

byte x = 0;

void setup() {

Wire.begin();
Serial.begin(9600);

//initialize chip--all i/o is "read"
Wire.beginTransmission(0x38);
Wire.write(0b11111111);
Wire.endTransmission();

}

void loop() {


  Wire.requestFrom(0x38,1);
  if (Wire.available())
  x = Wire.read();
  

// you will see x > 0 ("2" in this case) if you ground the pin.
//otherwise you see 0 in stdout. 
// Make sure to use pullups. 

Serial.println(x);
delay(500);

}

Yep, the thing I got stuck on initially was having to send the "read" pins a 1 bit, what you see in setup(), then you can read from them (in the loop) after.  I haven't seen other ICs that require this before.

Also I went down a rabbit hole seeing if I could use address 0x71 (I2C address for the chip with a 1 in the LSB) to simplify the "read" code; the docs seem to indicate you can somehow, but if so, I couldn't figure it out.  See information about I2C read address bit shifting here, but I didn't use this methodology for the work described in this post.

Downshift? OK with all that in hand we have another cool way to generate 1's and 0's which in our world we can turn into things like gates, triggers, note-ons, mux shifts, or whatever. Throw a PNP transistor onto one of these pins and you can source and sink large amounts of current. 

I'll spend hours saying it's hours of fun because it is just that--hours of fun.

Q: We I2C, but do we C I2I? Wow that was corny. No need to ponder that one. Until next time, remember to avoid breathing the fumes, and always sidestep that sinking feeling.

No comments:

Post a Comment

ProMicro HID Keyboard Emulator

Quick one this time. The Arduino ProMicro (examples here and here ) is based on an Atmel 16u4 MCU and has HID keyboard emulation ready to go...