Writing Multiple Bytes I2C Question

Now that I have my LCD working with PCF8577 I find myself with another dilemma. With "wire.send" If I try to write more then just one byte at a time, it won't compile. How can you write multiple bytes in one wire.send? I am trying to display a number in the LCD, The PCF8577 is supposed to auto increment, so I would like to write to Bank1 which is 00000010 and then write to certain bits on bank 1, and send a third byte with certain bits on Bank2. I thought maybe a bit write would do this but I'm not sure. So in the following array: Address for Bank segment MSB|7|6|5|4|3|2|1| LSB Bank1 00000010 |x|x|x|1|1|1|x "1" are the segments I want turned on, and "x" are the segments I don't want to do anything to. Bank2 00000100 |1|1|1|1|x|x|x

I'm not sure how to pull that off?...

From Wire.h:

    void send(uint8_t);
    void send(uint8_t*, uint8_t);
    void send(int);
    void send(char*);

Either the 2nd or 4th method can be used to send multiple bytes, although the 2nd is the most commonly used.

I try to write more then just one byte at a time, it won't compile.

It would appear, then, that you are not doing it quite right. Post the code, and the error messages, and we'll help you get it right.

Here is my code:

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   Write to the PCF 8577 using I2C
//   Connected to a static LCD re-purposed from a Jaguar X-Type to use in an HVAC system
//   Using 1M resistor and .001uf cap to set LCD display frequency to ~90Hz
//   This is a test script to work out all of the bugs of coding
//                                              Sonny Schade
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <Wire.h>

byte address = 0x3A;   // address of PCF8577 with A1-A2 connected to GND A0 is OSC connected to Gnd by R/C
byte BankA = B00000000; 
byte BankB = B00000010; 
byte BankC = B00000100;
byte BankD = B00000110;

void send(uint8_t*, uint8_t); //Not sure how to use this?
                     
void setup()
{
   Wire.begin();       // Initiate I2C Communication 
   }


void loop(){
  
   byte a = B11110000; //Turn on a,d,e,f of 7 seg1 
   byte b = B00001110; //Turn on b,c,g of 7 seg1
       
      Wire.beginTransmission(address);   // send the address and the write cmnd
      Wire.send(BankB,a,b);                      // send three bytes
      
      Wire.endTransmission();            // send the data
      
       
    }

I get a whole list of errors, but the bottom of the IDE says:

" No matching functions for call to ‘TwoWire::send(byte&,byte&,byte&)’ "

I assume it’s because in the wire library nothing is set up, like the way I have it typed. So how would you word a send like that?

I assume it’s because in the wire library nothing is set up, like the way I have it typed.

It’s because you are using the library wrong.

So how would you word a send like that?

There are two ways to do what you want to do. The send functions send data to the addressed device in order, just like Serial.print() does. It does not matter whether you use:

      Wire.beginTransmission(address);   // send the address and the write cmnd
      Wire.send(BankB);
      Wire.send(a;
      Wire.send(b);                      // send three bytes
      Wire.endTransmission();            // send the data

or:

      byte stuffToSend[3];
      stuffToSend[0] = BankB;
      stuffToSend[a] = a;
      stuffToSend[b] = b;
      Wire.beginTransmission(address);   // send the address and the write cmnd
      Wire.send(stuffToSend, 3);                      // send three bytes
      Wire.endTransmission();            // send the data

(You might need to cast stuffToSend…)

Since it doesn’t matter, I’d use the first approach.

Yes I tried your example and it works properly. I just thought I could save code lines by putting three bytes, like an array in one send , but I guess it doesn't work that way.

In the Documentation it says " send(data,quantity); but I haven't seen any examples of that working, so I'm not sure how to use it, or if it's even meant for what I'm trying to do.

Lastly, How would I use a bitwrite to send only 1's or 0's to the segments I want to effect?

By the way thanks PaulS for your input thus far, it is much appreciated.

Sorry, Just so I’m clear, in case I didn’t explain myself properly.

In the example of:

void loop(){
  
   byte a = B11110000; //Turn on a,d,e,f of 7 seg1 
   byte b = B00001110; //Turn on b,c,g of 7 seg1
     
   
      Wire.beginTransmission(address);   // send the address and the write cmnd
       Wire.send(BankC);
       Wire.send(a);
       Wire.send(b);                     // my three bytes
     
       Wire.endTransmission();            // send the data
      
       
    }

In “Byte b” I only want to effect bits 1,2,and3 ( B0000(111)0 ) I actually don’t want to set the others to “0” because they might be segments turned on, from some other command.

How would I use a bitwrite to send only 1's or 0's to the segments I want to effect?

byte val = 0; // All bits 0
byte index; // Which bit to set

index = 3;
bitWrite(val, index, 1); // Set bit 3 to 1 in val
segment = 2;
       Wire.beginTransmission(address);   // send the address and the write cmnd
       Wire.send(BankA);
       Wire.send(bitWrite(val,segment,1));            
       Wire.endTransmission();            // send the data

Works great thanks PaulS.

In your opinion, now considering that I am driving a seven segment looking LCD display. Would it be better to Wire.send all of my bitWrite's to create my numbers in the seven segment? or would it be more optimized to somehow create that wire.send to send a character? or string? If a string would be better would I make an array of my bank and segment values?

That code will send just two bytes. Your previous questions involved sending 3 bytes.

That code will send a value that lights up one segment. It doesn't appear too useful, since most numbers are composed by lighting several segments.

I'm not understanding what the bitWrite part is about.

I integrated your bitwrite example into my wire.send, to turn on a single segment. Which it does, It turns on a single segment in my Bank or register A,B whatever...

In this Table:

Address for Bank segment MSB|7|6|5|4|3|2|1| LSB Bank1 00000010 |x|x|x|1|1|1|x "1" are the segments I want turned on, and "x" are the segments I don't want to do anything to. Bank2 00000100 |1|1|1|1|x|x|x

I want to select just the bits that are corresponding to the 'a,b,c,d,e,f,g ( 7 seg parts)" of the LED display, so I can turn certain ones on or off to make the number display corresponding to whatever I am commanding it to display at the time. In my set up, because of the way I made the circuit board,( and wasn't thinking about programming, was thinking about how the easiest way to run the wires was) I ended up with the seven segments being split between two registers. I can display all seven segments by sending three complete bytes: ie:

       Wire.beginTransmission(address);   // send the address and the write cmnd
       Wire.send(BankA);
       Wire.send(B00001110);
       Wire.send(B11110000);     // my three bytes
       Wire.endTransmission();

Because the PCF8577 automatically increments to the next register if you send multiple bytes. I only have to send the first register and my two bytes to make a number "8" appear in the 7 seg display.

However If I do it that way. it sets other bits to "0" which are attached to other parts of the display that I might not want to turn off, or set to "0". So I thought the "bitwrite" was the way I needed to set just the certain bits in that byte to display the number "8" in the display. If this is not correct and my logic is flawed, please correct me.

Maybe I'm trying to do this the wrong way... Below is a quote from the PCF8577 Data Sheet:

6.5 Direct drive mode The PCF8577C is set to the direct drive mode by loading the MODE control bit with logic 0. In this mode only four bytes are required to store the data for the 32 segment drivers. Setting the BANK bit to logic 0 selects even bytes (BANK A), setting the BANK bit to logic 1 selects odd bytes (BANK B). In the direct drive mode the SBV is auto-incremented by two after the loading of each segment byte register. This means that auto-incremented loading of BANK A or BANK B is possible. Either bank may be completely or partially loaded irrespective of which bank is being displayed.

So according to that I can set all 32 of my output pins with 4 bytes. Instead of trying to bit write directly to the display, does it make more sense, to make an "update display" function

Do all of my if/else, what button is pushed stuff in a loop, and bit write to a variable, ie:

pseudo code:

int BankA = B00000000;

 if (buttonState == HIGH) { 
    bitWrite(BankA,3,1));
   displayupdate();
   //changing BankA from B00000000 to B00001000
   
 };
 else (bitWrite(BankA,3,0); 

   //changing nothing BankA stays B00000000
   
   //So multiple items could just change my bank variable byte
   // and my displayupdate() would just send four complete bytes
   // whenever something changes?

Does that make more sense?

OK I tried my brain storm above and got some nice results, well the results I expected, with the code below:

#include <Wire.h>
 
byte address = 0x3A;  //Address of PCF8577 with A1,A2 to grnd, and A0 as OSC
byte Register = B00000000; // Control byte for first register
byte BankA = B00000000;    //
byte BankB = B00000000;    // four segment bytes
byte BankC = B00000000;    //
byte BankD = B00000000;    //

const int buttonPin = 11; 
int buttonState = 0;  

void setup() {
   Wire.begin();
   pinMode(buttonPin, INPUT); //Pin 11 hooked to switch for testing
}

void loop() {
  buttonState = digitalRead(buttonPin);
  
  bitSet(BankB,0); //
  bitSet(BankB,1); // Default display (a Fan symbol)
  bitSet(BankB,2); //
  // If I push the Switch
  if (buttonState == HIGH) { 
  
      bitSet(BankD,3);
      bitSet(BankD,4);
   }
  else {
  
       bitClear(BankD,3);
       bitClear(BankD,4);
  
  }
 
  DisplayUpdate();
}

void DisplayUpdate() {

Wire.beginTransmission(address);   // send the address and the write cmnd
       Wire.send(Register);        // choose start register
       Wire.send(BankA);           //
       Wire.send(BankB);           // send the four bytes
       Wire.send(BankC);           //
       Wire.send(BankD);           //
       Wire.endTransmission();  
       }

So that will work well for me for displaying a bar graph to show my fan speed, depending on switch position. I guess it could also be adapted to drive my 7 seg displays.
A few questions:

  1. Is there a more streamlined way to write the if/else statement for the switch? So I don’t have to do a bit clear and take away everything I added with bit set by retyping in the “else” function ?
  2. I will be using 16 switches on a PCF8575 I/O Expander to set the different HVAC modes, will checking and setting the display in this fashion use up too much memory and detract from my ability to have the Arduino doing other things in the mean time?

OK, using a 7 segment LED tutorial, I adapted it for my LCD below:

//Test script to display numbers on a Static LCD via I2C

#include <Wire.h> //Include Wire Libarary
 
byte address = 0x3A;  //Address of PCF8577 with A1,A2 to grnd, and A0 as OSC
byte Register = B00000000; // Control byte for first register
byte BankA = B00000000;    //
byte BankB = B00000000;    // four segment bytes
byte BankC = B00000000;    //
byte BankD = B00000000;    //
#define BUTTON 11          //  Attach Switch to Digital Pin 11 of Arduino

int count = 0; // current display count
int val = 0;   // digital input from button

void setup() {
  Wire.begin();
  pinMode(BUTTON, INPUT); 
  zero();                 // set "0" for default display
}

void loop() {
  val = digitalRead(BUTTON);
  if (val == HIGH) {       // if Switch is pressed
    count++;
    delay(200);
    switch (count) {
      case 0:
        zero();
        break;
      case 1:
        one();
        break;
      case 2:
        two();
        break;
      case 3:
        three();
        break;
      case 4:
        four();
        break;
      case 5:
        five();
        break;
      case 6:
        six();
        break;
      case 7:
        seven();
        break;
      case 8:
        eight();
        break;
      case 9: {
        nine();
        count = -1;
        break;
      }
    }
  }
  DisplayUpdate();     //Update the LCD with the current info
}

// 0 => ABCDEF
void zero() {
  bitSet(BankB,7);
  bitSet(BankB,6);
  bitSet(BankB,5);
  bitSet(BankB,4);
  bitSet(BankC,1);
  bitSet(BankC,3);
  bitClear(BankC,2);
}

// 1 => BC
void one() {
  bitClear(BankB,7);
  bitClear(BankB,6);
  bitClear(BankB,5);
  bitClear(BankB,4);
  bitSet(BankC,1);
  bitSet(BankC,3);
  bitClear(BankC,2);
}

// 2 => ABDEG
void two() {
  bitSet(BankB,7);
  bitClear(BankB,6);
  bitSet(BankB,5);
  bitSet(BankB,4);
  bitClear(BankC,1);
  bitSet(BankC,3);
  bitSet(BankC,2);
}

// 3 => ABCDG
void three() {
  bitSet(BankB,7);
  bitClear(BankB,6);
  bitClear(BankB,5);
  bitSet(BankB,4);
  bitSet(BankC,1);
  bitSet(BankC,3);
  bitSet(BankC,2);
}

// 4 => BCFG
void four() {
  bitClear(BankB,7);
  bitSet(BankB,6);
  bitClear(BankB,5);
  bitClear(BankB,4);
  bitSet(BankC,1);
  bitSet(BankC,3);
  bitSet(BankC,2);
}

// 5 => ACDFG
void five() {
  bitSet(BankB,7);
  bitSet(BankB,6);
  bitClear(BankB,5);
  bitSet(BankB,4);
  bitSet(BankC,1);
  bitClear(BankC,3);
  bitSet(BankC,2);
}

// 6 => ACDEFG
void six() {
  bitSet(BankB,7);
  bitSet(BankB,6);
  bitSet(BankB,5);
  bitSet(BankB,4);
  bitSet(BankC,1);
  bitClear(BankC,3);
  bitSet(BankC,2);
}

// 7 => ABC
void seven() {
  bitSet(BankB,7);
  bitClear(BankB,6);
  bitClear(BankB,5);
  bitClear(BankB,4);
  bitSet(BankC,1);
  bitSet(BankC,3);
  bitClear(BankC,2);
}

// 8 => ABCDEFG
void eight() {
  bitSet(BankB,7);
  bitSet(BankB,6);
  bitSet(BankB,5);
  bitSet(BankB,4);
  bitSet(BankC,1);
  bitSet(BankC,3);
  bitSet(BankC,2);
}

// 9 => ABCDFG
void nine() {
  bitSet(BankB,7);
  bitSet(BankB,6);
  bitClear(BankB,5);
  bitSet(BankB,4);
  bitSet(BankC,1);
  bitSet(BankC,3);
  bitSet(BankC,2);
}

void DisplayUpdate() {

Wire.beginTransmission(address);   // send the address and the write cmnd
       Wire.send(Register);        // choose start register
       Wire.send(BankA);           //
       Wire.send(BankB);           // send the four bytes
       Wire.send(BankC);           //
       Wire.send(BankD);           //
       Wire.endTransmission();  
       }

As I progress I will post my additions. At any time, If someone sees something I could have coded differently to save space and memory Please offer your opinions/expertise. I am just a novice, so basically I’m just mimicking everyone else’s genius.
Thanks PaulS for spoon feeding me earlier to get me started.

Seems to me like this would be a good place to use a lookup table and a translation function.

Something like this:

#define ADDRESS 0x3A

// numbers, ignoring the LSB because only 7 segments
uint8_t numbers[10] = {
  B11111100, // zero   BCDEFG
  B00001100, // one    BC    
  B10110110, // two   AB DE G
  B10011110, // three ABCD  G
  B01001110, // four  ABC  F 
  B11011010, // five  A CD FG
  B11111010, // six   A CDEFG
  B10001100, // seven  BC   G
  B11111110, // eight ABCDEFG
  B11011110, // nine  ABCD FG
}

void writeDigit(uint8_t digit) {
  // digit is out of range
  if ( digit > 9 )
    return;
    
  uint8_t buf[5];
  
  buf[2] |= ( numbers[digit] & ( 1 << 7 ) ) << 7; // G
  buf[2] |= ( numbers[digit] & ( 1 << 6 ) ) << 6; // F
  buf[2] |= ( numbers[digit] & ( 1 << 5 ) ) << 5; // E
  buf[2] |= ( numbers[digit] & ( 1 << 4 ) ) << 4; // D
  buf[3] |= ( numbers[digit] & ( 1 << 3 ) ) << 1; // C
  buf[3] |= ( numbers[digit] & ( 1 << 2 ) ) << 3; // B
  buf[3] |= ( numbers[digit] & ( 1 << 1 ) ) << 2; // A
  
  Wire.beginTransmission( ADDRESS );
  
  for ( uint8_t i = 0; i < 5; i++ ) {
    Wire.send( buf[i] );
  }
  
  Wire.endTransmission();
}

That's actually really nice code, it is short and precise. However unfortunately my 7 seg section of the PCF8577 drive pins span three registers. So some of the bits in your example might actually be the pins that are used on other display items, if I am reading it correctly.

It has a fan display and bar graph for fan speed that goes from resister 1 segment A0 - A7 & B0 -B3 than a numeral "1" is C0, and a negative sign is B4 Than the seven segment starts with B5 and goes to C4 than another seven segment goes from C5 - D3 and the remaining pins are for a Fahrenheit symbol a Celsius symbol, and external air temp symbol and an auto hvac symbol.

If each segment was within one register your code would be perfect, but I don't think it would work with the way my pin out ended up. Thanks for posting the code, I'm sure it will help someone, or help me when I make my next project.

Your code only has it span two (B and C, which in my code are buf[2] and buf[3])… and all you need to do is to change this part:

  buf[2] |= ( numbers[digit] & ( 1 << 7 ) ) << 7; // G
  buf[2] |= ( numbers[digit] & ( 1 << 6 ) ) << 6; // F
  buf[2] |= ( numbers[digit] & ( 1 << 5 ) ) << 5; // E
  buf[2] |= ( numbers[digit] & ( 1 << 4 ) ) << 4; // D
  buf[3] |= ( numbers[digit] & ( 1 << 3 ) ) << 1; // C
  buf[3] |= ( numbers[digit] & ( 1 << 2 ) ) << 3; // B
  buf[3] |= ( numbers[digit] & ( 1 << 1 ) ) << 2; // A

to set/clear the appropriate bits before writing the registers to the device.

Your code only has it span two (B and C

Yes that's true, because I was only working with one 7 seg display at a time, as I'm just starting to grasp what is happening. the LCD I'm using is a 2 1/2 display with some other symbols and characters.

like this :


_ | || || | || ||

Anyway I'm going to try your code, as I obviously didn't grasp what you were doing, with the different buffers and shifting. Can you explain how that works?

Aeturnalus,

#define ADDRESS 0x3A
#include <Wire.h>
// numbers, ignoring the LSB because only 7 segments
uint8_t numbers[10] = {
  B11111100, // zero   BCDEFG
  B00001100, // one    BC    
  B10110110, // two   AB DE G
  B10011110, // three ABCD  G
  B01001110, // four  ABC  F 
  B11011010, // five  A CD FG
  B11111010, // six   A CDEFG
  B10001100, // seven  BC   G
  B11111110, // eight ABCDEFG
  B11011110, // nine  ABCD FG
};

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

void writeDigit(uint8_t digit) {
  // digit is out of range
  if ( digit > 9 )
    return;
    
  uint8_t buf[5];
  
  buf[2] |= ( numbers[digit] & ( 1 << 7 ) ) << 7; // G
  buf[2] |= ( numbers[digit] & ( 1 << 6 ) ) << 6; // F
  buf[2] |= ( numbers[digit] & ( 1 << 5 ) ) << 5; // E
  buf[2] |= ( numbers[digit] & ( 1 << 4 ) ) << 4; // D
  buf[3] |= ( numbers[digit] & ( 1 << 3 ) ) << 1; // C
  buf[3] |= ( numbers[digit] & ( 1 << 2 ) ) << 3; // B
  buf[3] |= ( numbers[digit] & ( 1 << 1 ) ) << 2; // A
  
  Wire.beginTransmission( ADDRESS );
  
  for ( uint8_t i = 0; i < 5; i++ ) {
    Wire.send( buf[i] );
  }
  
  Wire.endTransmission();
}

I tried your code, and added the #include wire, and Wire.begin, and I get an error when trying to compile it.
The error says undefined reference to loop.
Where would I add the void loop to this script?

I only defined a function to print data to the LCD - you will need to add in your own setup/loop functions, similar to the ones you already have, and then call the method. For testing, you can just add an empty loop in:

void loop() {}

How the code works: Under the assumption that you are only using one 7-segment display, there are at most 7 different LEDs (ABCDEFG) to turn on or off. Thus, I take a single byte (uint8_t) to store the information, such that: bit 0 is unused A is bit 1 B is bit 2 C is bit 3 D is bit 4 E is bit 5 F is bit 6 G is bit 7

This allows you to use an array of predefined bytes to store the representations for the numbers 0 through 9, which can be reused throughout the code. When processing the values, I go through each of the bits (by comparing with 1 left-shifted n bits, where n is the bit number) and set the appropriate register bit that corresponds to it. Since bytes in the buffer are initialized to 0x00 by default, I just OR the requisite bits in - ORing with a 1 sets the bit, ORing with a 0 doesn't do anything.

#define ADDRESS 0x3A
#include <Wire.h>
// numbers, ignoring the LSB because only 7 segments
uint8_t numbers[10] = {
  B11111100, // zero   BCDEFG
  B00001100, // one    BC    
  B10110110, // two   AB DE G
  B10011110, // three ABCD  G
  B01001110, // four  ABC  F 
  B11011010, // five  A CD FG
  B11111010, // six   A CDEFG
  B10001100, // seven  BC   G
  B11111110, // eight ABCDEFG
  B11011110, // nine  ABCD FG
};

int digit = 0;
void setup() {
  Wire.begin();
 
}

void writeDigit(uint8_t digit) {
  // digit is out of range
  if ( digit > 9 )
    return;
    
  uint8_t buf[5];
  
  buf[2] |= ( numbers[digit] & ( 1 << 7 ) ) << 7; // G
  buf[2] |= ( numbers[digit] & ( 1 << 6 ) ) << 6; // F
  buf[2] |= ( numbers[digit] & ( 1 << 5 ) ) << 5; // E
  buf[2] |= ( numbers[digit] & ( 1 << 4 ) ) << 4; // D
  buf[3] |= ( numbers[digit] & ( 1 << 3 ) ) << 1; // C
  buf[3] |= ( numbers[digit] & ( 1 << 2 ) ) << 3; // B
  buf[3] |= ( numbers[digit] & ( 1 << 1 ) ) << 2; // A
}

void lopp() {
  
  Wire.beginTransmission( ADDRESS );
  
  for ( uint8_t i = 0; i < 5; i++ ) {
    Wire.send( buf[i] );
  }
  
  Wire.endTransmission();
}

I’m not sure what I’m doing wrong, the compiler doesn’t like the “buf” stuff, it keeps saying it can’t be used in this scope. Can someone show me what I’m doing wrong?