NXC: PCF8574 read 8 values all at once ?

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

NXC: PCF8574 read 8 values all at once ?

Post by HaWe »

hey,
I currently read the PCF8574 once for each muxport like this:

Code: Select all

int ReadPCF8574Port(char PCF8574Port, byte PCF8574ID, char input);

int ReadPCF8574Port(char PCF8574Port, byte PCF8574ID, char input)
{
    byte cnt = 0x02;
    byte I2CMsg[];
    byte inbuf[];
    byte Address = PCF8574ID;
    byte nByteReady = 0;
    int  result = -1;
    int loop;

    ArrayBuild(I2CMsg, Address);
    Wait(8);
    while (loop == STAT_COMM_PENDING)
    {
          loop = I2CStatus(PCF8574Port, nByteReady);
    }

    if (I2CBytes(PCF8574Port, I2CMsg, cnt, inbuf))
    {
        result = inbuf[1];
    }

    if( result == (result | (1<<input-1)) )
    {
       result = 0;
    }
    else
    {
       result = 1;
    }

    if(result==-1){
     TextOut(0, LCD_LINE1, "Error: Check the");
     TextOut(0, LCD_LINE2, "   connection!");
     Wait(500);
     ClearScreen();
    }
    return result;
}
So currently I need 8 i2c calls to get all the single PCF muxer values.
Now: is it possible to read all the 8 PCF bytes all at once to get an array[8] back by only 1 i2c call ?
That would probably speed up the performance by factor 8x....?!
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: NXC: PCF8574 read 8 values all at once ?

Post by mattallen37 »

You already are, but then you use bitwise math to sort out the one bit you are interested in (actually you use bitwise math to determine if a bit is set, and then you set result to either 0 or 1 based on that).

Take out this:

Code: Select all

    if( result == (result | (1<<input-1)) )
    {
       result = 0;
    }
    else
    {
       result = 1;
    }
Here is my PCF8574 library, which should be more efficient than your function:

Code: Select all

void WritePins(byte port, byte address, byte byteout, bool invert = false, bool wiat = false){
  if(invert){
    byteout = ~byteout;
  }
  
  byte I2CMsg[];
  byte nByteReady = 0;

  ArrayBuild(I2CMsg, address, byteout);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING) {
    Yield();
  }

  I2CWrite(port, 1, I2CMsg);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING && wiat) {
    Yield();
  }
}

int ReadPins(byte port, byte address){
  byte cnt = 2;
  byte I2CMsg[];
  byte inbuf[];
  byte nByteReady = 0;
  int  result = -1;

  ArrayBuild(I2CMsg, address);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING){
    Yield();
  }

  if (I2CBytes(port, I2CMsg, cnt, inbuf)) {
    result = inbuf[1];
  }
  return result;
}
I just made a few minor changes without testing, but it does compile fine.
Last edited by mattallen37 on 29 Apr 2013, 19:17, edited 1 time in total.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: NXC: PCF8574 read 8 values all at once ?

Post by mattallen37 »

BTW in case you didn't figure it out, ReadPins returns -1 if there was an error, otherwise it returns an 8-bit number (each bit represents one IO).

The value you pass to WritePins is also an 8-bit number, where each bit is for an IO. You can also specify if it should invert the bits, so that a 0 bit value represents high, and a 1 represents low.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: PCF8574 read 8 values all at once ?

Post by HaWe »

hey matt,
thanks for your reply and for sharing your code!
The code I was using so far is supposed to be from mindsensors for their experimental board.
To be honest I had no idea what

Code: Select all

if( result == (result | (1<<input-1)) )...
is doing. I have not even wasted a thought for what it might be for. Now I assume it's something like bit shifting the result...

So if I understood correctly, your code returns a byte...
Now if I AND this with a byte mask I will get the representation of a pin?

is the following the correct idea...?

Code: Select all

byte PCF8574array[9]; // just to have slots 1...8 to access, PCF8574array[0] may be dropped or may store the result intermediately
byte result;
PCF8574array[0]=result; // just a gimmick :)

for (i=1; i<=8; ++i) {
  PCF8574array[i]=result & (1<<(i-1)) // correct math ?
}
:?
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: NXC: PCF8574 read 8 values all at once ?

Post by mattallen37 »

Instead of that, any time I wanted to check the state of an IO, instead of using PCF8574array[IO_NUMBER] I would use something like this:

Code: Select all

PCF8574input(input)
Having already defined that as:

Code: Select all

#define PCF8574input(input) (0x01 & (result >> input))
Where "result" is the (non-error) value returned by my ReadPins function. Any time ReadPins is successful, update the variable called "result", and then use PCF8574input(input) to check the state of each IO.

The IOs are indexed 0-7, but if you want them to be 1-8, use this instead:

Code: Select all

#define PCF8574input(input) (0x01 & (result >> (input - 1)))
Since all you're doing is checking the bit state (not at all specific to things related to the PCF8574), I'd use this instead:

Code: Select all

#define BitRead(_Value, _Bit) (0x01 & (_Value >> _Bit))
Where the _Value is the input byte (or int, or whatever else is storing the bit-representation), and _Bit is the bit you want to check the status of (0 through (bits-1))

Here is a complete, self-contained, working example (I just tested it):

Code: Select all

void WritePins(byte port, byte address, byte byteout, bool invert = false, bool wiat = false){
  if(invert){
    byteout = ~byteout;
  }

  byte I2CMsg[];
  byte nByteReady = 0;

  ArrayBuild(I2CMsg, address, byteout);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING) {
    Yield();
  }

  I2CWrite(port, 1, I2CMsg);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING && wiat) {
    Yield();
  }
}

int ReadPins(byte port, byte address){
  byte cnt = 2;
  byte I2CMsg[];
  byte inbuf[];
  byte nByteReady = 0;
  int  result = -1;

  ArrayBuild(I2CMsg, address);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING){
    Yield();
  }

  if (I2CBytes(port, I2CMsg, cnt, inbuf)) {
    result = inbuf[1];
  }
  return result;
}

#define BitRead(_Value, _Bit) (0x01 & (_Value >> _Bit))

byte PCF8574_INPUT;
int result;

task main()
{
  SetSensorLowspeed(S1);
  while (true)
  {
    result = ReadPins(S1, 0x40);
    if(result != -1){
      PCF8574_INPUT = result;
    }

    ClearScreen();
    NumOut(50, LCD_LINE1, BitRead(PCF8574_INPUT, 0));
    NumOut(50, LCD_LINE2, BitRead(PCF8574_INPUT, 1));
    NumOut(50, LCD_LINE3, BitRead(PCF8574_INPUT, 2));
    NumOut(50, LCD_LINE4, BitRead(PCF8574_INPUT, 3));
    NumOut(50, LCD_LINE5, BitRead(PCF8574_INPUT, 4));
    NumOut(50, LCD_LINE6, BitRead(PCF8574_INPUT, 5));
    NumOut(50, LCD_LINE7, BitRead(PCF8574_INPUT, 6));
    NumOut(50, LCD_LINE8, BitRead(PCF8574_INPUT, 7));
    
    Wait(25);
  }
}
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: PCF8574 read 8 values all at once ?

Post by HaWe »

thank you again, that was very revealing, especially

Code: Select all

#define BitRead(_Value, _Bit) (0x01 & (_Value >> _Bit))
resp.

Code: Select all

#define BitRead(_Value, _Bit) (0x01 & (_Value >> (_Bit - 1) ))
As I have a large array of touch sensors (close to 20) and many other sensors to check I will do all the sensor-polling in just 1 quickly running task and "fill" all the sensor arrays.

So if I understood you correctly, for checking the PCF values the code should be like

Code: Select all

#define BitRead(_Value, _Bit) (0x01 & (_Value >> (_Bit - 1) )) // 1...8 instead of 0...7

int ReadPins(byte port, byte address){
  byte cnt = 2;
  byte I2CMsg[];
  byte inbuf[];
  byte nByteReady = 0;
  int  result = -1;

  ArrayBuild(I2CMsg, address);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING){
    Yield();
  }

  if (I2CBytes(port, I2CMsg, cnt, inbuf)) {
    result = inbuf[1];
  }
  return result;
}

byte PCF8574array[9]; // just to have slots 1...8 to access
byte PCF8574port, PCF8574addr, result;

result=ReadPins(PCF8574port, PCF8574addr);

for (i=1; i<=8; ++i) {
  PCF8574array[i]=BitRead(result, i);  // correct math ?
}
is that correct?
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: NXC: PCF8574 read 8 values all at once ?

Post by mattallen37 »

That should work, except you never initialize the values of PCF8574port and PCF8574addr, and you don't check for an error value returned by the ReadPins function (if an I2C communication error took place, the function should return -1).

I don't know why you feel that the IOs need to be numbered 1-8 though, instead of 0-7. It's common practice in anything related to programming, to index starting at 0 rather than 1. Even the NXT sensor and motor ports are numbered starting at 0 (S1 actually is a constant of the value 0, and so is OUT_A). Instead of doing extra math as well as wasting an array element, I suggest you #define and use constants (you could even use the already existing S1-S4, and then just #define S5-S8 as well). This would slightly improve the execution time.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: PCF8574 read 8 values all at once ?

Post by HaWe »

the 1-8 issue is an old compatibility thing.

:oops:
oops - forgeot SetSensorLowspeed
:oops:

But now it shows the values inverted - 0 if pressed and 1 if released :roll:

Code: Select all

#define BitRead(_Value, _Bit) (0x01 & (_Value >> (_Bit - 1 ) )) // 1...8 instead of 0...7

int ReadPins(byte port, byte address){
  byte cnt = 2;
  byte I2CMsg[];
  byte inbuf[];
  byte nByteReady = 0;
  int  result = -1;

  ArrayBuild(I2CMsg, address);

  while (I2CStatus(port, nByteReady) == STAT_COMM_PENDING){
    Yield();
  }

  if (I2CBytes(port, I2CMsg, cnt, inbuf)) {
    result = inbuf[1];
  }
  if(result==-1){
     TextOut(0, LCD_LINE1, "Error: Check the");
     TextOut(0, LCD_LINE2, "   connection!");
     Wait(500);
     ClearScreen();
  }
  return result;
}


task main() {
  byte PCF8574array[9]; // just to have slots 1...8 to access
  byte PCF8574port=S4, PCF8574addr=0x4E, result;

  SetSensorLowspeed(S4);
  
  while(true) {

    result=ReadPins(PCF8574port, PCF8574addr);

    for (char i=1; i<9; ++i) {
      PCF8574array[i]= BitRead(result, i);
      NumOut( 0, 56-(8*i), i);
      NumOut(18, 56-(8*i), PCF8574array[i]);
    }

    Wait(5);
  }


}
Last edited by HaWe on 30 Apr 2013, 18:10, edited 1 time in total.
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: NXC: PCF8574 read 8 values all at once ?

Post by mattallen37 »

You need to set up I2C for the sensor port, by using SetSensorLowspeed(PCF8574port);
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: PCF8574 read 8 values all at once ?

Post by HaWe »

:oops:
yes I just discovered that - :oops:
but now it shoes the values inverted, see my earlier post !
Post Reply

Who is online

Users browsing this forum: Semrush [Bot] and 11 guests