• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

My solution to running out of IO pins

granddad

Well-Known Member
As I kept adding peripherals to a 66 pin PIC project , i realized i was going to end up with loads of connectors, or run out of usable IO pins .. So I scaled down to 44 pic PIC and I2C keeping interrupt provisions for the control and comms inputs. This MCP23017 I2C expander circuit provided the answer. Thought I would share, I still have a few things to code , but basic digits (TM1637) and Encoders work okay (ish)
encoder.jpg
 

Visitor

Well-Known Member
The MCP23017 is my go-to solution when I need a lot of I/O. It can take a little effort to figure out the interface, but after that, the rest is easy.

I'm also an advocate of offloading tasks like LED multiplexing to a dedicated chip like the MAX7219 LED driver. Just send the data and don't worry about the details.

Interesting fact: I have shared the clock and data lines between I2C devices and the SPI MAX7219. The MAX ignores everything when /CS is high, and the I2C devices ignore MAX data not in the right format.
 

granddad

Well-Known Member
I have shared the clock and data lines between I2C devices and the SPI MAX7219.
I didnt consider sharing I2C signals with the TM1637 , as this IC has a non standard I2C bus protocol, it may work .. but may have corrupted the other real I2C devices, MCP23017 is a scratch head device :)
 

Mike - K8LH

Well-Known Member
Funny coincidence... I just hooked up and tested an MCP23017 yesterday on an Arduino Nano. It works well but I was disappointed that the I2C module on the Nano won't go much faster than 400-kHz...
 

granddad

Well-Known Member
As a side note, It does not seem possible (may be wrong) with MCP23017 to switch between , having Banked , or Sequential registers and keep the original register contents, I tried to initialize the device sequentially , then check by reading back as banked, Nope ! took a while to sink in.. But a very useful IC , not tried to run higher than 400khz.


expand.jpg
 
Last edited:

Mike - K8LH

Well-Known Member
Those lighted encoders look very nice. How'd they do that (grin)?

As for sequential mode... I left the chip in "bank 0" with "sequential off" which allows updating pairs of registers. I've only tested the MCP23017 ports as outputs so far in my Arduino test program;

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

 //#include <Wire.h>          // switched to 'direct register'

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

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

   #define mcpAddr  0x20<<1   // MCP23017 I2C address shifted left 1 bit

   #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 hiNibble(x) ((x >> 4) & 0x0F)
   #define loNibble(x) ((x >> 0) & 0x0F)

   const char hex[] = "0123456789ABCDEF";

  /******************************************************************************
   *  low level UART functions                                                  *
   ******************************************************************************/

   #define RX_READY   (UCSR0A & 1<<RXC0)  //
   #define TX_READY   (UCSR0A & 1<<UDRE0) //

   void put232(uint8_t data)              // ************************************
   { while(!TX_READY); UDR0 = data;       // send byte or character             *
   }                                      // ************************************

   void putStr(char *data)                // ************************************
   { while(*data) put232(*data++);        // send string variable               *
   }                                      // ************************************

  /*                                                                            *
   *  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(RX_READY)                         //                                    *
       return 1;                          //                                    *
     else                                 //                                    *
       return 0;                          //                                    *
   }                                      // ************************************

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

   void putHex(byte work)                 // ************************************
   { put232(hex[hiNibble(work)]);         //                                    *
     put232(hex[loNibble(work)]);         //                                    *
   }                                      // ************************************

  /******************************************************************************
   *  low level I2C functions                                                   *
   ******************************************************************************/

   #define TW_START   0xA4                // (1<<TWINT)|(1<<TWSTA)|(1<<TWEN)
   #define TW_READY   (TWCR & 0x80)       // ready when TWINT returns to logic 1
   #define TW_STATUS  (TWSR & 0xF8)       // returns value of status register
   #define TW_SEND    0x84                // (1<<TWINT)|(1<<TWEN)
   #define TW_STOP    0x94                // (1<<TWINT)|(1<<TWSTO)|(1<<TWEN)
   #define TW_ACK     0xC4                //
   #define TW_NAK     0x84                // 

   #define I2C_Stop() TWCR = TW_STOP      // macro
   
   void I2C_Init()                        // ************************************
   { TWSR = 0; TWBR = 72;                 // prescaler 1, 100-kHz clock         *
   }                                      // ************************************

   byte I2C_Start()                       // ************************************
   { TWCR = TW_START; while(!TW_READY);   // send start condition & wait        *
     return(TW_STATUS == 0x08);           // 1 if found, 0 otherwise            *
   }                                      // ************************************

   byte I2C_SendAddr(byte addr)           // ************************************
   { TWDR = addr;                         // load device's I2C bus address      *
     TWCR = TW_SEND; while(!TW_READY);    // send it & wait                     *
     return(TW_STATUS == 0x18);           // 1 if found, 0 otherwise            *
   }                                      // ************************************

   byte I2C_Write(byte data)              // ************************************
   { TWDR = data;                         // send data byte to slave            *
     TWCR = TW_SEND; while(!TW_READY);    // send it & wait                     *
     return(TW_STATUS != 0x28);           //                                    *
   }                                      // ************************************

   byte I2C_ReadNAK()                     // ************************************
   { TWCR = TW_NAK; while(!TW_READY);     //                                    *
     return TWDR;                         //                                    *
   }                                      // ************************************

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

   void writeReg(byte reg, byte value)    // ************************************
   { I2C_Start();                         //                                    *
     I2C_SendAddr(mcpAddr);               //                                    *
     I2C_Write(reg);                      //                                    *
     I2C_Write(value);                    //                                    *
     I2C_Stop();                          //                                    *
   }                                      // ************************************

   void writeReg(byte reg, byte valA, byte valB)  // ****************************
   { I2C_Start();                         //                                    *
     I2C_SendAddr(mcpAddr);               //                                    *
     I2C_Write(reg);                      //                                    *
     I2C_Write(valA);                     //                                    *
     I2C_Write(valB);                     //                                    *
     I2C_Stop();                          //                                    *
   }                                      // ************************************

   byte readReg(byte reg)                 // ************************************
   { byte value = 0;                      //                                    *
     I2C_Start();                         //                                    *
     I2C_SendAddr(mcpAddr);               //                                    *
     I2C_Write(reg);                      //                                    *
     I2C_Start();                         // restart                            *
     I2C_SendAddr(mcpAddr+1);             // read command                       *
     value = I2C_ReadNAK();               //                                    *
     I2C_Stop();                          //                                    *
     return value;                        //                                    *
   }                                      // ************************************

/* 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 

   int main()                             // ************************************
   { DDRD |= 0b00000010;                  //                                    *
     DDRC |= 0b00100000;                  // PB4(SDA) input, PB5(SCL) output    *
     PORTC |= 0b00110000;                 // pull-ups                           *
     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);               // 400-kHz                            *
     I2C_Init();                          //                                    *
     writeReg(IOCON,0b00100000);          // sequential off                     *
     writeReg(IODIRA,0x00);               // port A outputs                     *
     writeReg(IODIRB,0x00);               // port B outputs                     *
     writeReg(IOLATA,0b00000000);         // port A latches                     *
     writeReg(IOLATB,0xAA);               // port B latches                     *

     byte x = 0;

     while(1)                             // **** loop **************************
     { if(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:

Latest threads

EE World Online Articles

Loading
Top