Facing pcf8574 and max7219 together to run 7 segment

Hi
Can i use pcf8574 for handling 4digit 7segment with max7219 ?

esp01 <--> pcf8574 <--> max7219 <--> 7segment

I wotk with pcf8574 and max7219 separately but not together ,
I want to use other port of pcf8574 for other matters and since esp01 has limited on io ports i want to use port expander .

What would be the benefit of a pcf8574?

I have some other parts like dht sensor and buzzer to use and with esp01 have limited on io ports, i use it for port expanding .

Spend the extra buck and get an esp8266 module that brings out more I/O. It will be cheaper and easier than a esp01 kludge.

Thanks, but as in experiment , if it's possible, i want to do this .
Is it possible to face pcf8574 with anoher ic like max7219 to write data to it ?

Because i have same issue with nodemcu esp8266 board with running out of io pins , i want to expand my board pins .

As it can be done, it can, but since the communication with the 7219 is serial, compatible with SPI, it will be very difficult for you to manage it along with other signals.
I would think of some other solution.

Sure. The pfc8574 doesn't have a true output mode. For a high level it only has an internal pull up that can source ~100ua (it can sink ~20ma). This is enough to drive the inputs of the max7219. You may want to plan on external pull up resistors in case there is a problem.

The max7219 doesn't have any maximum pulse width, so it will be OK with the slow rate you will have going through the pfc8574. You will need to write your own serial shift code to drive the max7219. It isn't hard - just tedious.

1 Like

@alirezaimi
either get an esp with more pins or if you insist to use I2C to drive your LEDs use a I2C LED Driver like the HT16K33 ... 128LEDs with one IC.

1 Like

Thanks for suggestions , but the problem is "working with some SPI device like max7219 through PCF8574 with I2C port on esp board " not buying some new stuff and clear the problem from board !
esp01 <-I2C-> pcf8574 <-SPI-> max7219 <--> 7segment

@oldcurmudgeon can you explain more on this and tell me some clue to start with ?
@MaximoEsfuerzo can you explain more, what is difficult for managing ?

The signals' timing.

You've said it yourself

Regards

Yes, it is possible.

But there are better, easier ways to achieve the same thing. You would do it this way only because you have no other choice or because you enjoy a difficult challenge and you want to increase your coding skills beyond beginner level. If you do not have any coding skills, it will be very difficult and you may fail and lose confidence. This is not a beginner project.

The reason this it is not a beginner project is that you will not be able to use any of the max7219 Arduino libraries. You will need to learn to drive the max chip without any library, by studying the max7219 data sheet to find out how to control the chip and set the segments in the display directly.

3 pins from the PCF chip will be needed to control the data, clock and load pins of the MAX chip. I do not see any need for external pull-up resistors as suggested by @oldcurmudgeon , unless you plan to use very long wires between the PCF and MAX chips.

1 Like

so if you want a start:
come up with a simple sketch for the max7219 without using a library, without using SPI, without using shiftOut, just with single digitalWrite and the proper timing in your sketch.

If you have this working, replace the digitalWrite with the pcf8574.

As you are refusing other suggestions, just start with the first step and demonstrate that you have the patience to solve the first hurdle. I'm looking forward to support you with the second hurdle.

1 Like

As I recall, the max7219 chip is controlled by sending pairs of bytes. The first byte specifies an internal register within the max chip. The second byte contains the data to be written to that register. There is an internal register in the max chip for each individual digit, and some control registers which need to be written to switch the display on/off, control the brightness, how many digits are used and so on.

So you will need to write a function to send a byte, one bit at a time, by setting the PCF pins that are connected to the data and clock pins of the MAX chip.

Then you can write another function to send a pair of bytes and then load that data into the MAX's registers by controlling the PCF pin connected to the MAX's load pin.

Then you can write more functions to initialise the MAX chip and to update the display as you want.

1 Like

OK, here is a possible plan. The library source files are in your sketchbook/libraries folder.

Add the LedControl and pcf8574 libraries if you don't have them.

Use the LedControl library to talk to the max7219. Look in the the .cpp and you will see that it uses the shiftOut() function to drive the max7219. So you need to replace the shiftOut() with code the talks to the pcf8574 instead. It also uses digitalWrite() to control the CS pin. This will also need to be converted to a pcf8574 operation.

Look at the pcf8574.h file. It has pinMode() and digitalWrite() overloaded functions to set the pins on the pcf8574. Use these functions to implement the shiftOut() function.

Information on the shiftOut() function can be found here

1 Like

Take a look at the I2C LCD code, they talk to the display with a PCF8574 in 4 bit mode.

1 Like

Thank you all, i can'do this :frowning: , there is lots of unknown matters for me and i'm too far from solving it , i just switch to easier way, but this topic stays here if some day someone solved this problem notify everybody else about it .

I have been thinking about this for a while and finally decided to see what it would take. After a couple of days I got it working. I would not recommend it unless you are desperate. Driving the max7219 via the pcf8574 is about 1/60 the speed of using digitalWrite() directly. It takes about 325 ms to write a 4 character display. Here is the code if you want to try it.

Create a folder in your sketchbook (wherever it might be) and add the following code.

===== this set in a file called LedI2C.h ======

/******************************************
*
* LedI2C - A class to implement a class for pcf8574 driving a max7219
* 
* 
************************************************/
#ifndef LED_I2C_h
#define LED_I2C_h

#include <stdint.h>
#include "Arduino.h"
#include <pcf8574.h>

// define MAX7219 registers
const uint8_t REG_NO_OP       =  0x00;
const uint8_t REG_GRP_BASE    =  0x01; // top row of the matrix
const uint8_t REG_CURSOR      =  0x08; // use bottom row for cursor
const uint8_t REG_DECODE_MODE =  0x09;
const uint8_t REG_INTENSITY   =  0x0A;
const uint8_t REG_SCAN_LIMIT  =  0x0B;
const uint8_t REG_SHUTDOWN    =  0x0C;
const uint8_t REG_TEST        =  0x0F;

enum DisplayType {
      MATRIX_T,
      SEGMENT_T
}; 

struct Command {
  uint8_t opCode;
  uint8_t data;
};

class LedI2C {
  public:
    LedI2C(){};   // constructor
    void begin(PCF8574* expand,uint8_t clkPin,uint8_t dataPin,uint8_t loadPin,uint8_t numDevices,DisplayType dt=MATRIX_T);
    void setChar(uint8_t device,uint8_t character );         // Char to display
    void setCursor(uint8_t device,bool state=true );              // Char to set cursor
    void set7Seg(uint8_t digit,uint8_t value,bool dp=false );   // 7 segment digit to disolay
    void clearDisplay();
    void setIntensity(uint8_t intensity);   // 0 - 15 low-high
    void shutDown(bool mode);  // true - shutdown, false - normal operation
    void ledTest(bool mode);   // true - turn on all leds, false - normal operation
    uint8_t getDeviceCount();    // return max device count
    
  private:
    void setDecode();    // set decode mode on
    void setScanAll();
    /* Send out commands to the devices */
    void spiTransfer(uint8_t numCommands);
    /* Send out a single command to a device */
    void spiTransferDevice(uint8_t addr, uint8_t opcode, uint8_t data);
    /* Send out a command to all the devices */
    void spiTransferAll( uint8_t opcode, uint8_t data);
    
    // object data
    uint8_t clk_Pin;
    uint8_t data_Pin;
    uint8_t load_Pin;
    uint8_t num_Devices;
    DisplayType dspType;
    Command spidata[8];   // The array for shifting the data to the devices, max 8 devices
    PCF8574* ex;          //  reference to pcf8574 that we created at the specified address
   
    /*******************************************
    *
    * Minimize chances for user to screw up - if you think
    * you need these think again. If you are sure, write the constructors
    * rather than relying on the default. Also write the destructor.
    * 
    * If left to its own devices the compiler will generate default 
    * copy and assignment constructors which do a bit wise copy of 
    * the object. The default destructor simply releases the storage.
    * This may not be correct depending on the complexity of the object.
    * 
    * In the case of limited resource processors like 8 bit Arduinos
    * this is usually a waste of time and storage. I discourage the easily accidental
    * use of these constructors by causing an error.
    * 
    * Search on "C++ big three rule" for more information
    * 
    * **********************************************/
    LedI2C (const  LedI2C& a);              // disallow copy constructor
    LedI2C & operator=(const  LedI2C& a);   // disallow assignment operator
 
};  // end LedI2C class definition

#endif

============ end LedI2c.h ======

=========== this set in a file called LedI2C.cpp ====


#include <Streaming.h>  // for debug

/******************************************
*
* LedI2C - A class to implement the MAX7219 using a pcf8574 interface
* 
* The data sheet is here - you may want to follow along as you look at the code
* https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
* 
* 
* Note:
*   In the comments in begin() and spiTransfer() you can see the 
*   changes if you want to use digitalWrite() directly to
*   the pins.
**********************************************/ 

// using program memeory will conserve ram - important on
// 8 bit AVR processors, on esp processors not a big deal
#define USE_PGM_MEM

#include "LedI2C.h"

#ifdef USE_PGM_MEM
const static uint8_t charTable [96*7] PROGMEM  = {
#else  
const static uint8_t charTable [96*7] = {  
#endif
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // 20  32
	0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00,  // 21  33 !
	0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00,  // 22  34 "
	0x28, 0x7C, 0x28, 0x28, 0x7C, 0x28, 0x00,  // 23  35 #
	0x38, 0x54, 0x30, 0x18, 0x54, 0x38, 0x00,  // 24  36 $
	0x44, 0x4C, 0x18, 0x30, 0x64, 0x44, 0x00,  // 25  37 %
	0x20, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00,  // 26  38 &
	0x08, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00,  // 27  39 '
	0x08, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00,  // 28  40 (
	0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00,  // 29  41 )
	0x44, 0x28, 0x7C, 0x28, 0x44, 0x00, 0x00,  // 2A  42 *
	0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00,  // 2B  43 +
	0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20,  // 2C  44 ,
	0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00,  // 2D  45 -
	0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,  // 2E  46 .
	0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00,  // 2F  47 /
	0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00,  // 30  48 0
	0x10, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00,  // 31  49 1
	0x38, 0x44, 0x08, 0x10, 0x20, 0x7C, 0x00,  // 32  50 2
	0x38, 0x44, 0x18, 0x04, 0x44, 0x38, 0x00,  // 33  51 3
	0x08, 0x18, 0x28, 0x48, 0x7C, 0x08, 0x00,  // 34  52 4
	0x78, 0x40, 0x78, 0x04, 0x44, 0x38, 0x00,  // 35  53 5
	0x38, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00,  // 36  54 6
	0x7C, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00,  // 37  55 7
	0x38, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00,  // 38  56 8
	0x38, 0x44, 0x44, 0x3C, 0x04, 0x78, 0x00,  // 39  57 9
	0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00,  // 3A  58 :
	0x00, 0x30, 0x30, 0x00, 0x30, 0x10, 0x20,  // 3B  59 ;
	0x00, 0x10, 0x20, 0x40, 0x20, 0x10, 0x00,  // 3C  60 <
	0x00, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x00,  // 3D  61 =
	0x00, 0x10, 0x08, 0x04, 0x08, 0x10, 0x00,  // 3E  62 >
	0x38, 0x44, 0x08, 0x10, 0x00, 0x10, 0x00,  // 3F  63 ?
	0x38, 0x44, 0x54, 0x58, 0x40, 0x3C, 0x00,  // 40  64 @
	0x38, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x00,  // 41  65 A
	0x78, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00,  // 42  66 B
	0x38, 0x44, 0x40, 0x40, 0x44, 0x38, 0x00,  // 43  67 C
	0x78, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00,  // 44  68 D
	0x7C, 0x40, 0x78, 0x40, 0x40, 0x7C, 0x00,  // 45  69 E
	0x7C, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00,  // 46  70 F
	0x38, 0x44, 0x40, 0x4C, 0x44, 0x38, 0x00,  // 47  71 G
	0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0x00,  // 48  72 H
	0x38, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00,  // 49  73 I
	0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00,  // 4A  74 J
	0x44, 0x48, 0x50, 0x70, 0x48, 0x44, 0x00,  // 4B  75 K
	0x40, 0x40, 0x40, 0x40, 0x40, 0x7C, 0x00,  // 4C  76 L
	0x44, 0x6C, 0x54, 0x44, 0x44, 0x44, 0x00,  // 4D  77 M
	0x44, 0x64, 0x54, 0x54, 0x4C, 0x44, 0x00,  // 4E  78 N
	0x7C, 0x44, 0x44, 0x44, 0x44, 0x7C, 0x00,  // 4F  79 O
	0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x00,  // 50  80 P
	0x38, 0x44, 0x44, 0x54, 0x4C, 0x3C, 0x00,  // 51  81 Q
	0x78, 0x44, 0x44, 0x78, 0x48, 0x44, 0x00,  // 52  82 R
	0x38, 0x44, 0x30, 0x08, 0x44, 0x38, 0x00,  // 53  83 S
	0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00,  // 54  84 T
	0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00,  // 55  85 U
	0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00,  // 56  86 V
	0x44, 0x44, 0x44, 0x54, 0x54, 0x28, 0x00,  // 57  87 W
	0x44, 0x28, 0x10, 0x10, 0x28, 0x44, 0x00,  // 58  88 X
	0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00,  // 59  89 Y
	0x7C, 0x08, 0x10, 0x20, 0x40, 0x7C, 0x00,  // 5A  90 Z
	0x38, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00,  // 5B  91 [
	0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00,  // 5C  92 '\'
	0x38, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,  // 5D  93 ]
	0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00,  // 5E  94 ^
	0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00,  // 5F  95 _
	0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00,  // 60  96 `
	0x00, 0x00, 0x38, 0x48, 0x48, 0x3C, 0x00,  // 61  97 a
	0x20, 0x20, 0x38, 0x24, 0x24, 0x38, 0x00,  // 62  98 b
	0x00, 0x00, 0x1C, 0x20, 0x20, 0x1C, 0x00,  // 63  99 c
	0x04, 0x04, 0x1C, 0x24, 0x24, 0x1C, 0x00,  // 64 100 d
	0x00, 0x00, 0x1C, 0x28, 0x30, 0x1C, 0x00,  // 65 101 e
	0x0C, 0x10, 0x38, 0x10, 0x10, 0x10, 0x00,  // 66 102 f
	0x00, 0x00, 0x1C, 0x24, 0x1C, 0x04, 0x38,  // 67 103 g
	0x20, 0x20, 0x38, 0x24, 0x24, 0x24, 0x00,  // 68 104 h
	0x10, 0x00, 0x30, 0x10, 0x10, 0x38, 0x00,  // 69 105 i
	0x08, 0x00, 0x08, 0x08, 0x08, 0x48, 0x30,  // 6A 106 j
	0x20, 0x20, 0x24, 0x38, 0x28, 0x24, 0x00,  // 6B 107 k
	0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00,  // 6C 108 l
	0x00, 0x00, 0x78, 0x54, 0x54, 0x54, 0x00,  // 6D 109 m
	0x00, 0x00, 0x38, 0x24, 0x24, 0x24, 0x00,  // 6E 110 n
	0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x00,  // 6F 111 o
	0x00, 0x00, 0x38, 0x24, 0x38, 0x20, 0x20,  // 70 112 p
	0x00, 0x00, 0x1C, 0x24, 0x1C, 0x04, 0x04,  // 71 113 q
	0x00, 0x00, 0x28, 0x34, 0x20, 0x20, 0x00,  // 72 114 r
	0x00, 0x00, 0x1C, 0x30, 0x0C, 0x38, 0x00,  // 73 115 s
	0x10, 0x10, 0x38, 0x10, 0x10, 0x0C, 0x00,  // 74 116 t
	0x00, 0x00, 0x24, 0x24, 0x24, 0x1C, 0x00,  // 75 117 u
	0x00, 0x00, 0x44, 0x28, 0x28, 0x10, 0x00,  // 76 118 v
	0x00, 0x00, 0x44, 0x54, 0x54, 0x28, 0x00,  // 77 119 w
	0x00, 0x00, 0x24, 0x18, 0x18, 0x24, 0x00,  // 78 120 x
	0x00, 0x00, 0x24, 0x24, 0x1C, 0x04, 0x38,  // 79 121 y
	0x00, 0x00, 0x3C, 0x08, 0x10, 0x3C, 0x00,  // 7A 122 z
	0x0C, 0x10, 0x10, 0x20, 0x10, 0x10, 0x0C,  // 7B 123 {
	0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x10,  // 7C 124 |
	0x60, 0x10, 0x10, 0x08, 0x10, 0x10, 0x60,  // 7D 125 }
	0x00, 0x20, 0x54, 0x08, 0x00, 0x00, 0x00,  // 7E 126 ~
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00   // 7F 127
};


// implementation
void LedI2C::begin(PCF8574* expand,uint8_t clkPin,uint8_t dataPin,uint8_t loadPin,
                   uint8_t numDevices,DisplayType dt){
  clk_Pin = clkPin;
  data_Pin = dataPin;
  load_Pin = loadPin;
  dspType = dt;
  // pcf8574 doesn't have pin modes
 /* 
  pinMode(clk_Pin,OUTPUT);
  pinMode(data_Pin,OUTPUT);
  pinMode(load_Pin,OUTPUT);
*/  
  num_Devices = numDevices;
  ex = expand;
  if (dspType == SEGMENT_T) {
      setDecode();     // set digit decode for 7 seg device
  }     
  setScanAll();        // use all groups
  setIntensity(7);     // set to half intensity
  clearDisplay();      // what is says :-)
  shutDown(false);     // start display
// Serial << "end begin " << " num_Devices " << num_Devices <<endl;  
}

void LedI2C::clearDisplay(){
  // set all groups/segment off
  uint8_t i;
  for(i = 0; i<8;i++) {
     // The blank code for for matrix display is 0
    if (dspType==MATRIX_T)  { // clear all rows on all devices
       spiTransferAll(i+REG_GRP_BASE,(0));
    } else {
       // The blank code for 7 seg decode is 0x0F 
       // we only need to clear the individual digits
       spidata[0].opCode = i+REG_GRP_BASE;
       spidata[0].data = 0x0f;
       spiTransfer(1);      // transfer 1 command
    }  
  }
}  
      
  
void LedI2C::setIntensity(uint8_t intensity) {
  spiTransferAll(REG_INTENSITY,intensity);
}

void LedI2C::shutDown(bool mode){
  spiTransferAll(REG_SHUTDOWN, (mode?0:1));  
}    

void LedI2C::ledTest(bool mode){
    spiTransferAll(REG_TEST, (mode?0:1)); 
}    

uint8_t  LedI2C::getDeviceCount(){
  return num_Devices;
}

void LedI2C::setChar(uint8_t device,uint8_t character ){         // Char to display
  uint8_t rowVal,i;
  uint16_t charStart;

  if (dspType == SEGMENT_T) {return;};     // invalid call for 7 segment type device
  // the character table starts at blank, so we subtact the blank value
  // to get the table entry for the character. Each character has a 7 byte 
  //   entry for the 7 rows in the display               
  charStart = (character - ' ')*7; // offset to start of character data in table               
  for (i=0;i<7;i++){   
#ifdef USE_PGM_MEM                
     rowVal=pgm_read_byte_near(charTable+charStart + i);
#else     
     rowVal= charTable[charStart + i];
#endif     
     spiTransferDevice(device,REG_GRP_BASE+i,rowVal);
  }
       
}  

void LedI2C::setCursor(uint8_t device,bool state ){   // where to set curcor
  if (state){
     spiTransferDevice(device,REG_CURSOR,0xff);
  }else {   
     spiTransferDevice(device,REG_CURSOR,0);
  }
}   

void LedI2C::set7Seg(uint8_t digit,uint8_t value, bool dp   ){   // number to display
     if (dspType == MATRIX_T) return;     // invalid call for matrix device
     spidata[0].opCode = REG_GRP_BASE+digit;
     if (dp) {
        spidata[0].data = value|0x80;   // turn on decimal point
     }else {
        spidata[0].data = value; 
     }
     spiTransfer(1);      // transfer 1 command
}     

void LedI2C::setDecode(){
  spidata[0].opCode = REG_DECODE_MODE;
  spidata[0].data = 0xff;   // decode all digits
  spiTransfer(1);      // transfer 1 command
}

void LedI2C::setScanAll(){
  spiTransferAll(REG_SCAN_LIMIT, 0xff); 
}  


void LedI2C::spiTransferAll(uint8_t opcode, uint8_t data) {
    // Send same command to all devices
    for(int i=0;i<num_Devices;i++){
      //put our device data into the array
      spidata[i].opCode=opcode;
      spidata[i].data=data;
    }
    spiTransfer(num_Devices);
    
}

void LedI2C::spiTransferDevice(uint8_t addr, uint8_t opcode, uint8_t data) {
    // send command to specified device
    //Create an array with the data to shift out
    // Set no-ops for all devices
    for(int i=0;i<num_Devices;i++){
        spidata[i].opCode=REG_NO_OP;
        spidata[i].data=0;
    }    
    //put our device data into the array
    spidata[addr].opCode=opcode;
    spidata[addr].data=data;
    spiTransfer(num_Devices);

}

void LedI2C::spiTransfer(uint8_t numCommands) {
    // transfer data to device(s)
    //enable the load line 
    digitalWrite(*ex,load_Pin,LOW);  
 /*   digitalWrite(load_Pin,LOW);  */
    //Now shift out the data 
    for(int i= 0 ;i<numCommands;i++) {
       uint8_t val;  
       uint8_t j;    
       uint8_t mask = 0x80;  
       val = spidata[i].opCode;   
       //  send op code msb first
       for (j = 8; j > 0; j--) {             
     
          // the ? operator is shorthand for if .. then ..else
          digitalWrite(*ex, data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
          digitalWrite(*ex,clk_Pin, HIGH);            //  toggle
          digitalWrite(*ex,clk_Pin, LOW);             //       clock
 /*
          digitalWrite(data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
          digitalWrite(clk_Pin, HIGH);            //  toggle
          digitalWrite(clk_Pin, LOW);             //       clock
*/          
          mask >>= 1;                                 //   next msb
       } 
       mask = 0x80; 
       val = spidata[i].data;   
       // send data msb first 
       for (j = 8; j > 0; j--) {             
 
          // the ? operator is shorthand for if .. then ..else
          digitalWrite(*ex, data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
          digitalWrite(*ex,clk_Pin, HIGH);            //  toggle
          digitalWrite(*ex,clk_Pin, LOW);             //       clock
/*         

          digitalWrite(data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
          digitalWrite(clk_Pin, HIGH);            //  toggle
          digitalWrite(clk_Pin, LOW);             //       clock
 */         
          mask >>= 1;                                 //   next msb
      } 
        
   } 
    //latch the data onto the display 
   digitalWrite(*ex,load_Pin,HIGH); 
/*   digitalWrite(load_Pin,HIGH); */
 
}    

// end implementation

========== end LedI2C.cpp ===========

======== finally this test case as [folder name].ino =======



/*
 *  Demo for useing a pcf8574 as the interface to a max7219
 * 
 *  oldcurmudgeon Nov 11,2023
 * 
 */ 

#include <Streaming.h>
#include <pcf8574.h>
PCF8574 ex(0x3F);

#include "LedI2C.h"  
#include <Wire.h>

const uint8_t clkPin = 4;
const uint8_t dataPin = 5;
const uint8_t matrixLoad = 6;
const uint8_t segmentLoad = 7;

LedI2C lcMatrix; 
LedI2C lcSegment; 

void setup() {
   uint8_t i;
   uint32_t startMs,endMs,totalTime;
   Serial.begin(9600);
   Serial.println(" in setup");
  // Uncomment the following line for ESP-01
  Wire.begin(0,2);     // Set I2C for esp01 gpio0=data,gpio2=clock
   
// the begin sets up the dosplay, on return the state is
// scan all,intensity = 7,display clear,display on
// format of the begin is
// begin(PCF8574* expander,uint8_t clkPin,uint8_t dataPin,uint8_t loadPin,uint8_t numDevices,DisplayType dt=MATRIX_T)

    // set up for 7 segment display
    lcSegment.begin(&ex,clkPin,dataPin,segmentLoad,1,SEGMENT_T);

    // set up for matrix display
    lcMatrix.begin(&ex,clkPin,dataPin,matrixLoad,4);   // matrix type is the default

    // Run 7 seg display
    for (i = 0;i<8;i++){
      if (i !=4){ 
        lcSegment.set7Seg(i,i);
       } else {
        lcSegment.set7Seg(i,i,true);  // set decimal point on digit 4
       }
    }   
    delay(2000);
    for (i = 8;i<16;i++){
       lcSegment.set7Seg(i-8,i);
    }
    delay(2000);
    
     // run matrix display
     
     lcMatrix.setChar(0,'C'); 
     lcMatrix.setChar(1,'s'); 
     lcMatrix.setChar(2,'r');
     lcMatrix.setCursor(3);    // on is the default
     delay(2000);
     lcMatrix.setCursor(3,false);  // set cursor off
     delay(2000); 

     startMs = millis();
     for (i=0;i<96;i+=4) { // display characters starting at blank, 4 at a time
      lcMatrix.setChar(0,i+' ');    // 
      lcMatrix.setChar(1,i+' '+1);    // 
      lcMatrix.setChar(2,i+' '+2);    // 
      lcMatrix.setChar(3,i+' '+3);    // 
      yield();     //  for esp8266 processors - pet the dog
     }
    endMs = millis();
    totalTime = endMs -startMs;
    Serial << " total ms = "<< totalTime << " ms/char= "<< totalTime/96 <<endl;
    
 }
 
 void loop() { }

========== end test case =========

Open the sketch in the Arduino IDE and set up the hardware to give it a try.
The test case is set up for an esp-01 driving a pcf8574 via a level shifter. Connected to the pcf8574 are a max7219 driving 8 7 segment displays and a set of 4 max7219 8x8 dot matrix displays.

1 Like

I did think of an optimisation that might speed up the process by 33%. The max chip reads the data pin on the rising edge of the clock pin, if I remember. It ignores the falling edge of the clock pin. So by setting the clock pin low in the same i2c update as setting the data pin, the number of PCF updates could be reduced from 3 to 2 for each bit of data. This would involve setting 2 pins in the same update, but I'm not sure the library you used supports that?

The original library I used only supports updating individual pins. I picked it because it was easy to map port digitalWrite() commands. For your suggestion I switched to the PCF8574 library. Same name but upper case PCF - just to confuse things. It has more function but different interfaces. See the diff at the end of this post.

I was a little worried because the Dout for daisy chaining is clocked on the falling edge. But it seems to work OK. A more significant improvement is to run the pcf8574 in fast mode - 400KHz vs the default 100KHz. Here are some timings for the max7219 clock rate.

port digitalWrite ~80 Khz (no I2C interface)
original code >1.5KHz
your suggestion ~1.89KHz
your suggestion and fast clock ~8.7KHz (not sure why 4x clock has more than 4x improvement)

Some pictures - first 7 seg decode

Second 7 seg decode and matrix char

ESP-01, level shifter, and LCD backpack for pcf8574

Here are the changes from the original code

<  lines removed from original source
>  lines added to original source
original source numbers X new source numbers X = c changes
                                                 d deleted
                                                 a added
examples
12,15d11   lines 12-15 deleted fron original, now line 11 in new code

270c261    line 270 in original changed, now line 261 in new code
<        uint8_t val;  
---
>        uint8_t val,sendVal; 

273a265   at line 273 in original line added, now line 265 in new code
>        sendVal = ex->valueOut();  // get last value written

read the diff command for more info

======================= diff LedI2C.cpp ============
12,15d11
< * Note:
< *   In the comments in begin() and spiTransfer() you can see the 
< *   changes if you want to use digitalWrite() directly to
< *   the pins.
135,140d130
<   // pcf8574 doesn't have pin modes
<  /* 
<   pinMode(clk_Pin,OUTPUT);
<   pinMode(data_Pin,OUTPUT);
<   pinMode(load_Pin,OUTPUT);
< */  
266,267c256,258
<     digitalWrite(*ex,load_Pin,LOW);  
<  /*   digitalWrite(load_Pin,LOW);  */
---
>     ex->write(load_Pin,LOW);  
>  
>     
270c261
<        uint8_t val;  
---
>        uint8_t val,sendVal;  
273a265
>        sendVal = ex->valueOut();  // get last value written
276,285c268,275
<      
<           // the ? operator is shorthand for if .. then ..else
<           digitalWrite(*ex, data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
<           digitalWrite(*ex,clk_Pin, HIGH);            //  toggle
<           digitalWrite(*ex,clk_Pin, LOW);             //       clock
<  /*
<           digitalWrite(data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
<           digitalWrite(clk_Pin, HIGH);            //  toggle
<           digitalWrite(clk_Pin, LOW);             //       clock
< */          
---
>           sendVal  &= ~(1 << clk_Pin);  // set clock low
>           if ( val & mask){
>             sendVal  |= (1 << data_Pin); // set data high
>           } else {
>             sendVal  &= ~(1 << data_Pin); // set data low
>           }
>           ex->write8(sendVal);  // send data and clock low
>           ex->write(clk_Pin,HIGH);  
289a280
>        sendVal = ex->valueOut();  // get last value written
292,302c283,290
<  
<           // the ? operator is shorthand for if .. then ..else
<           digitalWrite(*ex, data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
<           digitalWrite(*ex,clk_Pin, HIGH);            //  toggle
<           digitalWrite(*ex,clk_Pin, LOW);             //       clock
< /*         
< 
<           digitalWrite(data_Pin, ( val & mask) ? HIGH:LOW);   //  get data bit
<           digitalWrite(clk_Pin, HIGH);            //  toggle
<           digitalWrite(clk_Pin, LOW);             //       clock
<  */         
---
>           sendVal  &= ~(1 << clk_Pin);  // set clock low
>           if ( val & mask){
>             sendVal  |= (1 << data_Pin); // set data high
>           } else {
>             sendVal  &= ~(1 << data_Pin); // set data low
>           }
>           ex->write8(sendVal);  // send data and clock low
>           ex->write(clk_Pin,HIGH);  
308,309c296,297
<    digitalWrite(*ex,load_Pin,HIGH); 
< /*   digitalWrite(load_Pin,HIGH); */
---
>    ex->write(load_Pin,HIGH); 
> 

===========================================


============== diff LedI2C.h
12c12
< #include <pcf8574.h>
---
> #include <PCF8574.h>

==============================

================ diff demo.ino =========

9c9
< 
---
> #include "LedI2C.h"  
11c11,12
< #include <pcf8574.h>
---
> 
> 
29a31
>    Wire.setClock(400000);   // set fast mode
====================================

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.