Looking for an I2C chip to control RGB LEDs

Thread Starter

EasyGoing1

Joined Dec 10, 2016
14
Hello,

I did some googling on this but I'm not even sure if I have my nomenclature correct.

What I would like to do, is control 4 RGB LEDs using I2C from an Arduino Nano because I cannot spare 12 pins just for LED control. I don't need the IC to do any PWM at all, just being able to send it commands over I2C to set pins high or low would be sufficient.

Does anyone know if such a chip even exists? Obviously, it wouldn't be a chip specifically designed for LEDs, it would just be a chip that is capable of having pins set high or low based on whatever I2C command is sent to it. Not sure what you would even call such a chip, but I could really use one.

☺

Thank you,

Mike Sims
 

ericgibbs

Joined Jan 29, 2010
12,901
hi EG1.
I guess you know that 4 pins will be required to drive the external IC for the 12 LED's.?
Do you just want Red or Green or Blue on one LED to be lit at the same time or do you want to mix the colours.?

E

Update:
Would this be suitable.
 

Attachments

Last edited:

BobTPH

Joined Jun 5, 2013
3,308
Just use shift registers. Only two pins needed for as many LEDs as you want.
Don’t have the part # off the top of my head,but someone else might.

Bob
 

Ian0

Joined Aug 7, 2020
2,192
Just use shift registers. Only two pins needed for as many LEDs as you want.
Don’t have the part # off the top of my head,but someone else might.

Bob
74HC594 or 74HC595 (but needs three pins, one to latch the data).
MM5450 with 34 output is another useful one - and it really can manage with 2 pins.
 

Thread Starter

EasyGoing1

Joined Dec 10, 2016
14
hi EG1.
I guess you know that 4 pins will be required to drive the external IC for the 12 LED's.?
Do you just want Red or Green or Blue on one LED to be lit at the same time or do you want to mix the colours.?
Well, the ground pin doesn't need to be managed by the IC, so yes, I know that the LEDs have 4 pins but I only need to control three of them while the 4th will be wired to ground. And YES, I only need them to be one of three colors, red, green or blue. I won't be mixing colors in this application. That LP55231 is a nice chip (even has a 12 bit ADC which almost no Arduino has)... I'll have to see if they have a package that can control 4 as that one only controls 3 and obviously I want to minimize real estate consumption on the circuit board.

Thank you!

Several of those look promising, Thank you for that.


74HC594 or 74HC595 (but needs three pins, one to latch the data).
MM5450 with 34 output is another useful one - and it really can manage with 2 pins.
Those look perfect. And the price is right! ☺
 

MMcLaren

Joined Feb 14, 2010
853
74HC594 or 74HC595 (but needs three pins, one to latch the data).
You could drive a '595 with 2-pins by using an RC Integrator (see below)... Use any RC values that produce Tau = 3.3-uS (10K & 330pF)...

K8LH 2-Pin Backpacks.png

The driver code isn't difficult. It takes about ~100-uS to load the shift register. See the "write" code in the example LCD program below.

Code:
  /******************************************************************
   *  Mike McLaren's 2-pin 8-bit 74HC595/74HC164 LCD Backpack Demo  *
   *                                                                *
   *  16-Sep-2018  Arduino 1.8.6 / Arduino Nano                     *
   ******************************************************************/

   #define line1 (0x80+0x00)    // set DDRAM command + line 1 address
   #define line2 (0x80+0x40)    // set DDRAM command + line 2 address
   #define line3 (0x80+0x14)    // set DDRAM command + line 3 address
   #define line4 (0x80+0x54)    // set DDRAM command + line 4 address

   char Scramble [] = "AAAAAAAAA";

   #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~(1<<bit))  // clear bit
   #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= 1<<bit)     // set bit

  /******************************************************************
   *                                                                *
   ******************************************************************/

   #define clk PORTC2           // 'clk' on PC2 (A2) pin
   #define lat PORTC3           // 'lat' on PC3 (A3) pin

   class LCD : public Print
   { public:                    //
       void init();             //
       void cmd(uint8_t);       //
       size_t write(uint8_t);   //
     private:                   //
       bool rs = 0;             // lcd 'rs' flag
   };

   void LCD::init ()
   { DDRC = 1<<clk|1<<lat;      // make 'clk' and 'lat' outputs
     cbi(PORTC,lat);            // lat = 0
     sbi(PORTC,clk);            // clk = 1
     _delay_ms(100);            // LCD power-on delay
     cmd(0x38);                 // 8-bit, 2-lines, 5x7 font
     cmd(0x0C);                 // display on, currsor & blink off
     cmd(0x06);                 // cursor inc, shift off
     cmd(0x01);                 // clear display
     _delay_ms(2);              // required delay
   }                            //

   size_t LCD::write (uint8_t work)
   { uint8_t bitctr = 8;        //
     uint8_t SaveSREG = SREG;   //
     do                         // start of bit clock cycle
     { if(!(work & 128))        // if b7 is a '0' bit
         cbi(PORTC,clk);        // start clock cycle for a '0'
       _delay_us(9);            // charge or drain cap to 3t
       cli();                   // interrupts off
       cbi(PORTC,clk);          // start clock cycle for a '1'
       sbi(PORTC,clk);          // clock out the '0' or '1'
       SREG = SaveSREG;         // restore SREG & irq enable bit
       work <<= 1;              // prep for next bit
     } while(--bitctr);         // until all 8 bits clocked out
     if(!rs) cbi(PORTC,clk);    // make clk pin = rs flag
     sbi(PORTC,lat);            // latch 595, pulse LCD 'E' pin
     cbi(PORTC,lat);            //  "
     sbi(PORTC,clk);            // always leave clk pin high
     return 1;                  //
   }                            //

   void LCD::cmd (uint8_t c)
   { rs = 0; write(c); rs = 1;  //
   }                            //

  /******************************************************************
   *  main                                                          *
   ******************************************************************/

   LCD lcd;                     // create instance 'lcd' of LCD class

   int main()                   //
   {
     lcd.init();                // 8-bit mode, cursor inc & off
  /*                                                                *
   *  send a couple 'flash string helper' strings                   *
   *                                                                */
     lcd.print(F("K8LH Backpack")); // (44) 1st instance
     lcd.print(F(" v2"));           // (16) 2nd instance

     lcd.cmd(line2+2);          // line 2, tab 2 (6 bytes)
     lcd.print(Scramble);       // (40 bytes) 1st instance
     lcd.cmd(line2+1);          // line 2, tab 1 (6 bytes)
     lcd.print(Scramble);       // (8 bytes) 2nd instance

     while(1)                   // loop forever
     {                          //
     }                          //
   }                            //
 

MMcLaren

Joined Feb 14, 2010
853
If your application can stand periodic interrupts, you might consider multiplexing the four RGB LEDs which would require seven pins (three for the R, G, and B connections and four to drive the common anodes or cathodes). A single 74HC595 should do it.

Cheerful regards, Mike, K8LH
 
Last edited:

Ian0

Joined Aug 7, 2020
2,192
You could drive a '595 with 2-pins by using an RC Integrator (see below)... Use any RC values that produce Tau = 3.3-uS (10K & 330pF)...

View attachment 226526
That's rather neat. The only snag is that you have to "bit-bang" the pins, and you can't use the SPI.
I bet that you could make it automatically generate the latch signal at the end of the clock signal using a similar method.
Then you could still use the SPI
 

MMcLaren

Joined Feb 14, 2010
853
That's rather neat. The only snag is that you have to "bit-bang" the pins, and you can't use the SPI.
I bet that you could make it automatically generate the latch signal at the end of the clock signal using a similar method.
Then you could still use the SPI
Not familiar with Arduino 'hardware' SPI... Would that tie up three or four pins (MOSI, MISO, SCK and /SS)?
 

dendad

Joined Feb 20, 2016
3,835
Have you seen the PCA9685 chip?
It has 16 channels of 12 bit PWM and is an I2C chip. 2 pins!
It is used to run servos or LEDS.
https://github.com/codmpm/pca9685-net-udp
As each pin PWM is adjustable, the colours can be set and forget. The Arduino does not need to generate the PWM to keep it running.
I've got a board but have not used it so far, so cannot say how well it works.
 

djsfantasi

Joined Apr 11, 2010
7,689
Not familiar with Arduino 'hardware' SPI... Would that tie up three or four pins (MOSI, MISO, SCK and /SS)?
Those pins would be used, but can be shared with any other SPI devices. In other words, additional SPI devices would not need additional pins. And some functions could be implemented with four pins instead of a greater quantity.

Each SPI device would be assigned a unique hardware address, usually by jumpers on the device. The Arduino code would initialize each device on its assigned address.
 

MMcLaren

Joined Feb 14, 2010
853
I just breadboarded and tested a Microchip MCP23017 "I2C I/O Expander" chip with 16 I/O pins which seems to work fine but the chip I'm testing is in a rather large DIP-28 package...

Code:
  /******************************************************************************
   *  MCP23017_Test                                                             *
   *                                                                            *
   *  Mike McLaren, K8LH                                                        *
   *  Micro Application Consultants                                             *
   *                                                                            *
   *  MCP23017 I2C/WIRE driver experiment                                       *
   *                                                                            *
   *                                                                            *
   *                                                                            *
   *  02-Jan-2021    Arduino 1.8.13 / Arduino Uno                               *
   ******************************************************************************/

   #include <Wire.h>

  /******************************************************************************
   *  function prototypes                                                       *
   ******************************************************************************/

  /******************************************************************************
   *  hardware constants, helper macros, variables                              *
   ******************************************************************************/

   #define mcpAddr  0x20  // MCP23017 I2C address, 0x20..0x27

   #define IODIRA   0x00  // MCP23017 register locations (BANK = 0)
   #define IODIRB   0x01
   #define POLA     0x02
   #define POLB     0x03
   #define INTENA   0x04
   #define INTENB   0x05
   #define DEFVALA  0x06
   #define DEFVALB  0x07
   #define INTCONA  0x08
   #define INTCONB  0x09
   #define IOCON    0x0A
   #define IOCON2   0x0B
   #define GPPUA    0x0C
   #define GPPUB    0x0D
   #define INTFA    0x0E
   #define INTFB    0x0F
   #define INTCAPA  0x10
   #define INTCAPB  0x11
   #define GPIOA    0x12
   #define GPIOB    0x13
   #define IOLATA   0x14
   #define IOLATB   0x15

   #define hi(x) ((x >> 8) & 0xFF)
   #define lo(x) ((x >> 0) & 0xFF)


  /******************************************************************************
   *                                                                            *
   ******************************************************************************/

   void put232(uint8_t data)              // send byte or char
   { while(!(UCSR0A & _BV(UDRE0)));       //
     UDR0 = data;                         //
   }                                      //

   void putStr(char *data)                // send string variable
   { while(*data) put232(*data++);        //
   }                                      //
  /*                                                                            *
   *  overload function for FlashStringHelper string constants                  *
   *                                                                            */
   void putStr(const __FlashStringHelper *ifsh)
   { PGM_P p = reinterpret_cast<PGM_P>(ifsh);
     while(char c = pgm_read_byte(p++))
       put232(c);
   }                                      //

   uint8_t rxAvail(void)                  //
   { if(UCSR0A & _BV(RXC0))               //
       return 1;                          //
     else                                 //
       return 0;                          //
   }                                      //

   uint8_t get232(void)                   //
   { return(uint8_t) UDR0;                //
   }                                      //

  /******************************************************************************
   *                                                                            *
   ******************************************************************************/

   void writeReg(uint8_t reg, uint8_t value)
   { Wire.beginTransmission(mcpAddr);     //
     Wire.write(reg);                     //
     Wire.write(value);                   //
     Wire.endTransmission();              //
   }                                      //

   void writeReg(uint8_t reg, uint8_t portA, uint8_t portB)
   { Wire.beginTransmission(mcpAddr);     //
     Wire.write(reg);                     //
     Wire.write(portA);                   //
     Wire.write(portB);                   //
     Wire.endTransmission();              //
   }                                      //

  /******************************************************************************
   *                                                                            *
   ******************************************************************************/

   #define _BAUD (115200/2)               // 57600 or 115200 (double speed)
   #define _UBRR (F_CPU/16)/_BAUD-1       // Used for UBRRL and UBRRH

   void setup()                           // ************************************
   { DDRD |= 0b00111010;                  // clk/dat/vcc Outputs                *
     DDRB |= 0x01;                        // mclr Output                        *
                                          //                                    *
     UBRR0 = _UBRR;                       // setup USART                        *
     UCSR0A |= _BV(U2X0);                 //  " (57600 x 2 for 115200 baud)     *
     UCSR0B |= _BV(TXEN0);                //  "                                 *
     UCSR0B |= _BV(RXEN0);                //  "                                 *
     UCSR0C  = 3<<UCSZ00;                 // async' 8/N/1                       *
     put232('\n');                        //                                    *
     putStr(F(" PIC MCP23017 Test\n"));   //                                    *
     putStr(F(" Mike McLaren, K8LH \n")); //                                    *
                                          //                                    *
     Wire.begin();                        //                                    *
   //Wire.setClock(400000);               // 400K                               *
     writeReg(IOCON,0b00100000);          // sequential off                     *
     writeReg(IOLATA,0b00000000);         // port A latches                     *
     writeReg(IOLATB,0b00000000);         // port B latches                     *
     writeReg(IODIRA,0x00);               // port A outputs                     *
     writeReg(IODIRB,0x00);               // port B outputs                     *
   }                                      // ************************************

  /******************************************************************************
   *                                                                            *
   ******************************************************************************/

   byte x = 0;

   void loop()                            // ************************************
   {                                      //                                    *
  // while(!rxAvail());                   // get serial working first           *
  // put232(get232());                    // ok, got it...                      *
     writeReg(IOLATA,x,x);                // test MCP23017 outputs
     _delay_ms(50); x++;                  // ok, it works
   }                                      // ************************************
 
Last edited:

Ian0

Joined Aug 7, 2020
2,192
Have you seen the PCA9685 chip?
It has 16 channels of 12 bit PWM and is an I2C chip. 2 pins!
It is used to run servos or LEDS.
https://github.com/codmpm/pca9685-net-udp
As each pin PWM is adjustable, the colours can be set and forget. The Arduino does not need to generate the PWM to keep it running.
I've got a board but have not used it so far, so cannot say how well it works.
I've used the PCA9685 and it "does what it says on the tin".
 

BobaMosfet

Joined Jul 1, 2009
1,768
Hello,

I did some googling on this but I'm not even sure if I have my nomenclature correct.

What I would like to do, is control 4 RGB LEDs using I2C from an Arduino Nano because I cannot spare 12 pins just for LED control. I don't need the IC to do any PWM at all, just being able to send it commands over I2C to set pins high or low would be sufficient.

Does anyone know if such a chip even exists? Obviously, it wouldn't be a chip specifically designed for LEDs, it would just be a chip that is capable of having pins set high or low based on whatever I2C command is sent to it. Not sure what you would even call such a chip, but I could really use one.

☺

Thank you,

Mike Sims
Use a Serial to Parallel Shift register. That's what I did for my IV-22 clock:

1609692856757.png

The above image was a test of the RGB LEDs, and VFD segments using '595s before writing the driver code, several years ago.
 
Last edited:
Top