<SOLVED> tca9548a i2c multiplexer

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

 

I'm using a Atmega1284 and winavr to control a OLED. Works fine, here is the code:

 

#include <avr/io.h>
#include <avr/pgmspace.h>   
#include <stdlib.h>         
#include <util/delay.h>     

#define FONT_SIZE                         5         
#define OLED_ADRS                         0x3C      

#define SSD1306_CHAR_SIZE                6
#define SSD1306_LASTLINE                0x07
#define SSD1306_COMMAND                 0x00
#define SSD1306_DATA                    0xC0
#define SSD1306_DATA_CONTINUE           0x40
#define SSD1306_SET_CONTRAST_CONTROL    0x81
#define SSD1306_DISPLAY_ALL_ON_RESUME   0xA4
#define SSD1306_DISPLAY_ALL_ON          0xA5
#define SSD1306_NORMAL_DISPLAY          0xA6
#define SSD1306_INVERT_DISPLAY          0xA7
#define SSD1306_DISPLAY_OFF             0xAE
#define SSD1306_DISPLAY_ON              0xAF
#define SSD1306_NOP                     0xE3
#define SSD1306_HORIZONTAL_SCROLL_RIGHT 0x26
#define SSD1306_HORIZONTAL_SCROLL_LEFT  0x27
#define SSD1306_SCROLL_VERT_AND_RIGHT   0x29
#define SSD1306_SCROLL_VERT_AND_LEFT    0x2A
#define SSD1306_DEACTIVATE_SCROLL       0x2E
#define SSD1306_ACTIVATE_SCROLL         0x2F
#define SSD1306_SET_VERT_SCROLL_AREA    0xA3
#define SSD1306_SET_LOWER_COLUMN        0x00
#define SSD1306_SET_HIGHER_COLUMN       0x10
#define SSD1306_MEMORY_ADDR_MODE        0x20
#define SSD1306_SET_COLUMN_ADDR         0x21
#define SSD1306_SET_PAGE_ADDR           0x22
#define SSD1306_SET_START_LINE          0x40
#define SSD1306_SET_SEGMENT_REMAP       0xA0
#define SSD1306_SET_MULTIPLEX_RATIO     0xA8
#define SSD1306_COM_SCAN_DIR_INC        0xC0
#define SSD1306_COM_SCAN_DIR_DEC        0xC8
#define SSD1306_SET_DISPLAY_OFFSET      0xD3
#define SSD1306_SET_COM_PINS            0xDA
#define SSD1306_CHARGE_PUMP             0x8D
#define SSD1306_SET_CLOCK_DIV_RATIO     0xD5
#define SSD1306_SET_PRECHARGE_PERIOD    0xD9
#define SSD1306_SET_VCOM_DESELECT       0xDB

/*##############################################################*/

//  global variables that need to be unchanged between each call to OLED_SetCursor() and OLED_DisplayChar().
uint8_t OLEDlineNumber = 0, OLEDcursorPos = 0;

const uint8_t FontTable[][FONT_SIZE] PROGMEM = {
       { 0x00, 0x00, 0x00, 0x00, 0x00 },   // space 00   20 hexASCII
       { 0x00, 0x00, 0x2f, 0x00, 0x00 },   // !     01   21
       { 0x00, 0x07, 0x00, 0x07, 0x00 },   // "     02   22
       { 0x14, 0x7f, 0x14, 0x7f, 0x14 },   // #     03   23
       { 0x24, 0x2a, 0x7f, 0x2a, 0x12 },   // $     04   24
       { 0x23, 0x13, 0x08, 0x64, 0x62 },   // %     05   25
       { 0x36, 0x49, 0x55, 0x22, 0x50 },   // &     06   26
       { 0x00, 0x05, 0x03, 0x00, 0x00 },   // '     07   27
       { 0x00, 0x1c, 0x22, 0x41, 0x00 },   // (     08   28
       { 0x00, 0x41, 0x22, 0x1c, 0x00 },   // )     09   29
       { 0x14, 0x08, 0x3E, 0x08, 0x14 },   // *     10   2a
       { 0x08, 0x08, 0x3E, 0x08, 0x08 },   // +     11   2b
       { 0x00, 0x00, 0xA0, 0x60, 0x00 },   // ,     12   2c
       { 0x08, 0x08, 0x08, 0x08, 0x08 },   // -     13   2d
       { 0x00, 0x60, 0x60, 0x00, 0x00 },   // .     14   2e
       { 0x20, 0x10, 0x08, 0x04, 0x02 },   // /     15   2f
       { 0x3E, 0x51, 0x49, 0x45, 0x3E },   // 0     16   30
       { 0x00, 0x42, 0x7F, 0x40, 0x00 },   // 1     17   31
       { 0x42, 0x61, 0x51, 0x49, 0x46 },   // 2     18   32
       { 0x21, 0x41, 0x45, 0x4B, 0x31 },   // 3     19   33
       { 0x18, 0x14, 0x12, 0x7F, 0x10 },   // 4     20   34
       { 0x27, 0x45, 0x45, 0x45, 0x39 },   // 5     21   35
       { 0x3C, 0x4A, 0x49, 0x49, 0x30 },   // 6     22   36
       { 0x01, 0x71, 0x09, 0x05, 0x03 },   // 7     23   37
       { 0x36, 0x49, 0x49, 0x49, 0x36 },   // 8     24   38
       { 0x06, 0x49, 0x49, 0x29, 0x1E },   // 9     25   39
       { 0x00, 0x36, 0x36, 0x00, 0x00 },   // :     26   3a
       { 0x00, 0x56, 0x36, 0x00, 0x00 },   // ;     27   3b
       { 0x08, 0x14, 0x22, 0x41, 0x00 },   // <     28   3c
       { 0x14, 0x14, 0x14, 0x14, 0x14 },   // =     29   3d
       { 0x00, 0x41, 0x22, 0x14, 0x08 },   // >     30   3e
       { 0x02, 0x01, 0x51, 0x09, 0x06 },   // ?     31   3f
       { 0x32, 0x49, 0x59, 0x51, 0x3E },   // @     32   40
       { 0x7C, 0x12, 0x11, 0x12, 0x7C },   // A     33   41
       { 0x7F, 0x49, 0x49, 0x49, 0x36 },   // B     34   42
       { 0x3E, 0x41, 0x41, 0x41, 0x22 },   // C     35   43
       { 0x7F, 0x41, 0x41, 0x22, 0x1C },   // D     36   44
       { 0x7F, 0x49, 0x49, 0x49, 0x41 },   // E     37   45
       { 0x7F, 0x09, 0x09, 0x09, 0x01 },   // F     38   46
       { 0x3E, 0x41, 0x49, 0x49, 0x7A },   // G     39   47
       { 0x7F, 0x08, 0x08, 0x08, 0x7F },   // H     40   48
       { 0x00, 0x41, 0x7F, 0x41, 0x00 },   // I     41   49
       { 0x20, 0x40, 0x41, 0x3F, 0x01 },   // J     42   4a
       { 0x7F, 0x08, 0x14, 0x22, 0x41 },   // K     43   4b
       { 0x7F, 0x40, 0x40, 0x40, 0x40 },   // L     44   4c
       { 0x7F, 0x02, 0x0C, 0x02, 0x7F },   // M     45   4d
       { 0x7F, 0x04, 0x08, 0x10, 0x7F },   // N     46   4e
       { 0x3E, 0x41, 0x41, 0x41, 0x3E },   // O     47   4f
       { 0x7F, 0x09, 0x09, 0x09, 0x06 },   // P     48   50
       { 0x3E, 0x41, 0x51, 0x21, 0x5E },   // Q     49   51
       { 0x7F, 0x09, 0x19, 0x29, 0x46 },   // R     50   52
       { 0x46, 0x49, 0x49, 0x49, 0x31 },   // S     51   53
       { 0x01, 0x01, 0x7F, 0x01, 0x01 },   // T     52   54
       { 0x3F, 0x40, 0x40, 0x40, 0x3F },   // U     53   55
       { 0x1F, 0x20, 0x40, 0x20, 0x1F },   // V     54   56
       { 0x3F, 0x40, 0x38, 0x40, 0x3F },   // W     55   57
       { 0x63, 0x14, 0x08, 0x14, 0x63 },   // X     56   58
       { 0x07, 0x08, 0x70, 0x08, 0x07 },   // Y     57   59
       { 0x61, 0x51, 0x49, 0x45, 0x43 },   // Z     58   5a
       { 0x00, 0x7F, 0x41, 0x41, 0x00 },   // [     59   5b
       { 0x55, 0xAA, 0x55, 0xAA, 0x55 },   //       60   5c Backslash (Checker pattern)
       { 0x00, 0x41, 0x41, 0x7F, 0x00 },   // ]     61   5d
       { 0x04, 0x02, 0x01, 0x02, 0x04 },   // ^     62   5e
       { 0x40, 0x40, 0x40, 0x40, 0x40 },   // _     63   5f underscore
       { 0x00, 0x03, 0x05, 0x00, 0x00 },   // `     64   60 apostrophe
       { 0x20, 0x54, 0x54, 0x54, 0x78 },   // a     65   61
       { 0x7F, 0x48, 0x44, 0x44, 0x38 },   // b     66   62
       { 0x38, 0x44, 0x44, 0x44, 0x20 },   // c     67   63
       { 0x38, 0x44, 0x44, 0x48, 0x7F },   // d     68   64
       { 0x38, 0x54, 0x54, 0x54, 0x18 },   // e     69   65
       { 0x08, 0x7E, 0x09, 0x01, 0x02 },   // f     70   66
       { 0x18, 0xA4, 0xA4, 0xA4, 0x7C },   // g     71   67
       { 0x7F, 0x08, 0x04, 0x04, 0x78 },   // h     72   68
       { 0x00, 0x44, 0x7D, 0x40, 0x00 },   // i     73   69
       { 0x40, 0x80, 0x84, 0x7D, 0x00 },   // j     74   6a
       { 0x7F, 0x10, 0x28, 0x44, 0x00 },   // k     75   6b
       { 0x00, 0x41, 0x7F, 0x40, 0x00 },   // l     76   6c
       { 0x7C, 0x04, 0x18, 0x04, 0x78 },   // m     77   6d
       { 0x7C, 0x08, 0x04, 0x04, 0x78 },   // n     78   6e
       { 0x38, 0x44, 0x44, 0x44, 0x38 },   // o     79   6f
       { 0xFC, 0x24, 0x24, 0x24, 0x18 },   // p     80   70
       { 0x18, 0x24, 0x24, 0x18, 0xFC },   // q     81   71
       { 0x7C, 0x08, 0x04, 0x04, 0x08 },   // r     82   72
       { 0x48, 0x54, 0x54, 0x54, 0x20 },   // s     83   73
       { 0x04, 0x3F, 0x44, 0x40, 0x20 },   // t     84   74
       { 0x3C, 0x40, 0x40, 0x20, 0x7C },   // u     85   75
       { 0x1C, 0x20, 0x40, 0x20, 0x1C },   // v     86   76
       { 0x3C, 0x40, 0x30, 0x40, 0x3C },   // w     87   77
       { 0x44, 0x28, 0x10, 0x28, 0x44 },   // x     88   78
       { 0x1C, 0xA0, 0xA0, 0xA0, 0x7C },   // y     89   79
       { 0x44, 0x64, 0x54, 0x4C, 0x44 },   // z     90   7a
       { 0x00, 0x10, 0x7C, 0x82, 0x00 },   // {     91   7b
       { 0x00, 0x00, 0xFF, 0x00, 0x00 },   // |     92   7c
       { 0x00, 0x82, 0x7C, 0x10, 0x00 },   // }     93   7d
       { 0x00, 0x06, 0x09, 0x09, 0x06 }
    };  // ~     94   7e

//  here are the functions for using the SSD1306, followed by documentation.

//~~~~~~~~~~~~~~~~~~~~~~~LOW-level OLED_I2C  @ 180 lines of code ~~~~
void i2c_initialise(void)  {   //  should called at the beginning of main() {Arduino:  by setup() when not using Wire.h}
        TWBR = 12;         //  400KHz   use 0x62 = @80KHz
        TWSR = 0x01;
        TWCR = (1<<TWEN);
}//~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~~~~
void OLED_sendCommand(uint8_t myCmd, uint8_t address)   {
        TWCR=((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // send START
        while(!(TWCR & (1 << TWINT)));  // delay until TWINT set
        TWDR = address << 1;   // send SLA    8-bit SLAw = 0x78
        TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));

        TWDR = SSD1306_COMMAND; // send D/C_sentinel byte for command:0x00
        TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));

        TWDR = myCmd;   // I2C write, wait until completed
        TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));

        TWCR = ( (1<<TWINT)|(1<<TWEN)|(1<<TWSTO) ); // I2C STOP
}//~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~~~~
void OLED_clear(uint8_t address) {
        OLED_sendCommand(SSD1306_SET_COLUMN_ADDR, address);   // opcode:0x21
        OLED_sendCommand(0, address);     // start at left edge of screen
        OLED_sendCommand(127, address);   // end at right edge of screen

        OLED_sendCommand(SSD1306_SET_PAGE_ADDR, address);     // opcode:0x22
        OLED_sendCommand(0, address);     // start at the top row
        OLED_sendCommand(7, address);     // end at the bottom row

        TWCR=((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // send START
        while(!(TWCR & (1 << TWINT)));  // delay until TWINT set
        TWDR = address << 1;   // send SLA  8-bit SLAw = 0x78
        TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));

        TWDR = SSD1306_DATA_CONTINUE;   // send D/C sentinal byte 0x40
        TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));

        // TWI NOT using Wire.h   128 bytes (8 bits vertical) per row * 8 char rows = 1024
        // Send 64 TWI messages { START:SLAw:0x40:16 bytes of 0x00:STOP }  1024 data bytes total
        for (uint16_t scrClearIndex=0; scrClearIndex<1024; scrClearIndex++) {  // (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8)
            TWCR=((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // send START
            while(!(TWCR & (1 << TWINT)));  // delay until TWINT set
            TWDR = address << 1;   // 8-bit SLAw = 0x78
            TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT))); // TX?done delay

            TWDR = 0x40;  // send D/C_sentinal byte for data stream
            TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));

            for (uint8_t x=0; x<16; x++) {
                TWDR = 0x00;  // all eight pixels in the column are off.
                TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));
                scrClearIndex++;  // 16 bytes per I2C transmission__64 transmissions per screen
            }
            scrClearIndex--;
            TWCR = ( (1<<TWINT)|(1<<TWEN)|(1<<TWSTO) ); // I2C STOP
        }
}//~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~
void OLED_Init(uint8_t address) {  // cmd: send START, 8bitSLA, D/C sentinal=0x00, command_value, STOP
        OLED_sendCommand(SSD1306_DISPLAY_OFF, address);          //   0xAE
        OLED_sendCommand(SSD1306_SET_CLOCK_DIV_RATIO, address);  //   0xD5
        OLED_sendCommand(0x80, address);                         //   0x80
        OLED_sendCommand(SSD1306_SET_MULTIPLEX_RATIO, address);  //   0xA8
        OLED_sendCommand(0x3F, address);                         //   0x3F
        OLED_sendCommand(SSD1306_SET_DISPLAY_OFFSET, address);   //   0xD3
        OLED_sendCommand(0x0, address);                          //   0x00
        OLED_sendCommand(SSD1306_SET_START_LINE | 0x00, address); //   0x40
        OLED_sendCommand(SSD1306_CHARGE_PUMP, address);          //   0x8D
        OLED_sendCommand(0x14, address);                         //   0x14
        OLED_sendCommand(SSD1306_MEMORY_ADDR_MODE, address);     //   0x20
        OLED_sendCommand(0x00, address);                         //   0x00
        OLED_sendCommand(SSD1306_SET_SEGMENT_REMAP | 0x01, address);// 0xA1
        OLED_sendCommand(SSD1306_COM_SCAN_DIR_DEC, address);     //   0xC8
        OLED_sendCommand(SSD1306_SET_COM_PINS, address);         //   0xDA
        OLED_sendCommand(0x12, address);                         //   0x12
        OLED_sendCommand(SSD1306_SET_CONTRAST_CONTROL, address); //   0x81
        OLED_sendCommand(0xCF, address);                         //   0xCF
        OLED_sendCommand(SSD1306_SET_PRECHARGE_PERIOD, address); //   0xD9
        OLED_sendCommand(0xF1, address);                         //   0xF1
        OLED_sendCommand(SSD1306_SET_VCOM_DESELECT, address);    //   0xDB
        OLED_sendCommand(0x40, address);                         //   0x40
        OLED_sendCommand(SSD1306_DISPLAY_ALL_ON_RESUME, address);//   0xA4
        OLED_sendCommand(SSD1306_NORMAL_DISPLAY, address);       //   0xA6
        OLED_sendCommand(SSD1306_DISPLAY_ON, address);           //   0xAF
        OLED_clear(address);
}//~~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~
void OLED_SetCursor(uint8_t myLineNumber,uint8_t myColumnPosition, uint8_t address) {
        if( (myLineNumber <= SSD1306_LASTLINE) && (myColumnPosition <= 127) ) {
            OLEDlineNumber = myLineNumber;     // global var: current line number
            OLEDcursorPos =  myColumnPosition; // global var: current cursor position

            OLED_sendCommand(SSD1306_SET_COLUMN_ADDR, address);
            OLED_sendCommand(myColumnPosition, address);
            OLED_sendCommand(127, address);

            OLED_sendCommand(SSD1306_SET_PAGE_ADDR, address);
            OLED_sendCommand(myLineNumber, address);
            OLED_sendCommand(7, address);

            TWCR=((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // send START
            while(!(TWCR & (1 << TWINT)));  // delay until TWINT set
            TWDR = address << 1;   // 8-bit SLAw = 0x78  7-bitSLA=0x3C  SSD1306 controller
            TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));

            TWDR = SSD1306_DATA_CONTINUE;   // 0x40
            TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));  // TWINT?set delay
        }
}//~~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~~~
void OLED_GoToLine(uint8_t myLine, uint8_t address) {
        if( myLine < 8) {
            OLEDlineNumber = myLine;
            OLED_SetCursor(OLEDlineNumber, 0, address);
        }
}//~~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~~~
void OLED_GoToNextLine(uint8_t address) {      // Increment and roll-over the current line number.
        OLEDlineNumber++;       // global var for current line number
        OLEDlineNumber = OLEDlineNumber & 0x07; // roll-over back to top after 8 lines
        OLED_SetCursor(OLEDlineNumber, 0, address);
}//~~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~~~~~~
void OLED_DisplayChar(uint8_t myDisplayChar, uint8_t address) {
        uint8_t charColumnBitMap, charBitMapIndex = 0;

        if(((OLEDcursorPos + FONT_SIZE) >= 128) || (myDisplayChar=='\n')) { // font size=5
//      If the line has no more room for complete chars OR NewLine command,
//      then move the cursor to next line
            OLED_GoToNextLine(address);
        }
        if(myDisplayChar != '\n') {
            myDisplayChar = myDisplayChar - 0x20; // As the lookup table starts from Space(0x20)
            while(1) {
               charColumnBitMap = pgm_read_byte(&FontTable[myDisplayChar][charBitMapIndex]);
               TWDR = charColumnBitMap;
               TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT))); // TX?done delay
               OLEDcursorPos++;
               charBitMapIndex++;
               if( charBitMapIndex == FONT_SIZE) {   // Exit the loop after sending five bytes
                  TWDR = 0x00;  // last column of char is one column of space between chars.
                  TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT))); // TX?done delay
                  OLEDcursorPos++;
                  break;
               }    }    }
}//~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~~~~~~
void OLED_DisplayString(char *ptr, uint8_t address) {
        while(*ptr) OLED_DisplayChar(*ptr++, address);
}//~~~~~~~~~~~~~~~~~~~~~ LOW-level OLED_I2C ~~~~~~~~~~~~~~~~~~~~~~~~~

int main(void)
{
    i2c_initialise();
    OLED_Init(OLED_ADRS);
    OLED_SetCursor(0, 0, OLED_ADRS);
    OLED_DisplayString("Hello World", OLED_ADRS);
    while(1);
}

I want to use several OLEDS with the help of the TCA9548A (address 0x70), but I don't know how to proceed. 

Any help is appreciated.

Thank you!

 

Patrick.

Last Edited: Thu. Jun 4, 2020 - 05:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

with the help of the TCA9548A

Where do you mention this? Which OLED is this using?

 

 but I don't know how to proceed.

why can't you give details first ?

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sun. May 17, 2020 - 05:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for looking into this,

 

I want to use regular OLEDs like these

https://www.aliexpress.com/item/32957309383.html?spm=a2g0s.9042311.0.0.423a4c4dqIBNL7

 

The code works for one OLED. I want to use several OLEDs (all the same type).  That's where I need the TCA9548A multiplexer.

 

As I understand the multiplexer has an address (0x70) and 8 ports you can select.

I can't figure out, if I connect the OLED to the first port (0) of the multiplexer, what I need to change on the code.

I can give it 0x70 as address, but how can I select the port?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use two SSD1306 e.g. with I2C addresses 0x3C and 0x3D

 

If you want to use more SSD1306 you can add a GPIO line for each SSD1306 to select a single Slave e.g. use DC pin to set a unique Slave address.

But you can use an I2C multiplexer if that floats your boat.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi David,

 

Thank you for your answer. I really want to use the Multiplexer if possible.

 

Any idea how I should change the code?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First off,   I would format your code neatly and store in your AS7.0 project.

 

You don't change anything much.   Except for wiring all the Slaves to the multiplexer chip.

And a single statement for the multiplexer to select the appropriate Slave by index number to be the "current OLED".

 

How many OLEDs do you intend to use?

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks,

 

To begin, I want to start with 4 OLEDs.

 

I have already connected the OLEDs to the multiplexer.

 

What's the single statement for the multiplexer to select the appropriate slave?

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've used the smaller TCA9546A to control 32 LCD displays across four downstream I2C buses.

 

The chip has a single 8-bit register, with each bit representing a downstream bus you want to connect to. Just write a single byte to the chip's address, with zero or more bits set to 1. Then send commands to the display devices in the usual way. These commands will only be seen by devices on the currently selected downstream buses. 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wire the AVR's SDA and SCL lines to the master input SDA/SCL on the Multiplexer (Mux).  Add the 3.3K ohm pull-up resistors.  The Mux gets the AVR's Vcc. (I'll assume that it's +5V).

 

Add the OLEDs to mux pins SDA0/SCL0  thru SDA3/SCL3; one OLED to each pair.  You need to add 3.3K (more or less) pull-up resistors to each OLED channel, but these pull-up resistors can go to the OLED's Vcc. This is the best way to convert I2C voltage levels, for instance if the AVR has +5 and the OLEDs are all on +3.3V.  The Mux allows each OLED to have the same SLA7 (7-bit Slave Address).   Address the Mux using SLA7 address 0x70 (do confirm this) with bit 0 being low for a TWI [I2C] write operation.  Next write a single byte that indicates which of the mux channels will output the I2C levels on the Mux input SDA/SCL master pins  (connected to the AVR).  

 

The 8 bits of this selection byte each determine whether the Mux channel will output the input I2C data.  For example, if you write b00000001 then only SDA0/SCL0 will present the I2C data from the AVR.  You can have multiple channels output the input data by setting the channel's bit in the write byte.  For instance if you have several +5V I2C devices and a few +3.3V Vcc devices, then you can put all the +5 devices on one channel and the +3.3V on a second mux channel.  Each channel will have a set of pull-up resistors to the correct Vcc.  Use a write byte with two bits set: b00000011.  All the I2C data from the AVR will go to each device, but only one device will give an ACK.  The Mux is bidirectional, so data sent from the 3.3V slave device gets level-shifted to +5 for the AVR.

 

For multiple devices with the same SLA7,  write the Mux's SLA7 (0x70) and the write byte with one bit set.  This will program only the I2C device on that mux channel.  For the next device, write the Mux SLA7 (0x70) again with a different write byte that has the new channel as the only bit sent.  Resend the OLED data for the second screen.  Repeat until you have programmed all screens.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you all. It took me some time but I got it working.

Here's the working code:

 

 

#include <avr/io.h>
#include <avr/pgmspace.h>   
#include <stdlib.h>         
#include <util/delay.h> 
#include <util/twi.h>    

#define NPORTS							7
#define FONT_SIZE                       5         
#define OLED_ADRS                       0x3C   

#define SSD1306_WIDTH           		128
#define SSD1306_HEIGHT          		64
   
#define SSD1306_CHAR_SIZE               6
#define SSD1306_LASTLINE                0x07
#define SSD1306_COMMAND                 0x00
#define SSD1306_DATA                    0xC0
#define SSD1306_DATA_CONTINUE           0x40
#define SSD1306_SET_CONTRAST_CONTROL    0x81
#define SSD1306_DISPLAY_ALL_ON_RESUME   0xA4
#define SSD1306_DISPLAY_ALL_ON          0xA5
#define SSD1306_NORMAL_DISPLAY          0xA6
#define SSD1306_INVERT_DISPLAY          0xA7
#define SSD1306_DISPLAY_OFF             0xAE
#define SSD1306_DISPLAY_ON              0xAF
#define SSD1306_NOP                     0xE3
#define SSD1306_HORIZONTAL_SCROLL_RIGHT 0x26
#define SSD1306_HORIZONTAL_SCROLL_LEFT  0x27
#define SSD1306_SCROLL_VERT_AND_RIGHT   0x29
#define SSD1306_SCROLL_VERT_AND_LEFT    0x2A
#define SSD1306_DEACTIVATE_SCROLL       0x2E
#define SSD1306_ACTIVATE_SCROLL         0x2F
#define SSD1306_SET_VERT_SCROLL_AREA    0xA3
#define SSD1306_SET_LOWER_COLUMN        0x00
#define SSD1306_SET_HIGHER_COLUMN       0x10
#define SSD1306_MEMORY_ADDR_MODE        0x20
#define SSD1306_SET_COLUMN_ADDR         0x21
#define SSD1306_SET_PAGE_ADDR           0x22
#define SSD1306_SET_START_LINE          0x40
#define SSD1306_SET_SEGMENT_REMAP       0xA0
#define SSD1306_SET_MULTIPLEX_RATIO     0xA8
#define SSD1306_COM_SCAN_DIR_INC        0xC0
#define SSD1306_COM_SCAN_DIR_DEC        0xC8
#define SSD1306_SET_DISPLAY_OFFSET      0xD3
#define SSD1306_SET_COM_PINS            0xDA
#define SSD1306_CHARGE_PUMP             0x8D
#define SSD1306_SET_CLOCK_DIV_RATIO     0xD5
#define SSD1306_SET_PRECHARGE_PERIOD    0xD9
#define SSD1306_SET_VCOM_DESELECT       0xDB
//__________________________________________________________
//i2c
static void i2c_initialise(void);
static uint8_t i2c_start(uint8_t address, uint8_t port);
static uint8_t i2c_write(uint8_t data);
static void i2c_stop(void);
static uint8_t i2c_switch_port(uint8_t port);
//--------------
//oled
static uint8_t oled_init(void);
static uint8_t i2c_start_oled(void);
static uint8_t oled_clear_screen(void);
static uint8_t oled_go_to_next_line(void);
static uint8_t string_to_oled(char *s, uint8_t address, uint8_t port);
static uint8_t oled_set_cursor(uint8_t myLineNumber,uint8_t myColumnPosition);
static uint8_t oled_display_char(uint8_t myDisplayChar);

/*##############################################################*/

//  global variables that need to be unchanged between each call to OLED_SetCursor() and OLED_DisplayChar().
uint8_t OLEDlineNumber = 0, OLEDcursorPos = 0;

const uint8_t FontTable[][FONT_SIZE] PROGMEM = {
	   { 0x00, 0x00, 0x00, 0x00, 0x00 },   // space 00   20 hexASCII
	   { 0x00, 0x00, 0x2f, 0x00, 0x00 },   // !     01   21
	   { 0x00, 0x07, 0x00, 0x07, 0x00 },   // "     02   22
	   { 0x14, 0x7f, 0x14, 0x7f, 0x14 },   // #     03   23
	   { 0x24, 0x2a, 0x7f, 0x2a, 0x12 },   // $     04   24
	   { 0x23, 0x13, 0x08, 0x64, 0x62 },   // %     05   25
	   { 0x36, 0x49, 0x55, 0x22, 0x50 },   // &     06   26
	   { 0x00, 0x05, 0x03, 0x00, 0x00 },   // '     07   27
	   { 0x00, 0x1c, 0x22, 0x41, 0x00 },   // (     08   28
	   { 0x00, 0x41, 0x22, 0x1c, 0x00 },   // )     09   29
	   { 0x14, 0x08, 0x3E, 0x08, 0x14 },   // *     10   2a
	   { 0x08, 0x08, 0x3E, 0x08, 0x08 },   // +     11   2b
	   { 0x00, 0x00, 0xA0, 0x60, 0x00 },   // ,     12   2c
	   { 0x08, 0x08, 0x08, 0x08, 0x08 },   // -     13   2d
	   { 0x00, 0x60, 0x60, 0x00, 0x00 },   // .     14   2e
	   { 0x20, 0x10, 0x08, 0x04, 0x02 },   // /     15   2f
	   { 0x3E, 0x51, 0x49, 0x45, 0x3E },   // 0     16   30
	   { 0x00, 0x42, 0x7F, 0x40, 0x00 },   // 1     17   31
	   { 0x42, 0x61, 0x51, 0x49, 0x46 },   // 2     18   32
	   { 0x21, 0x41, 0x45, 0x4B, 0x31 },   // 3     19   33
	   { 0x18, 0x14, 0x12, 0x7F, 0x10 },   // 4     20   34
	   { 0x27, 0x45, 0x45, 0x45, 0x39 },   // 5     21   35
	   { 0x3C, 0x4A, 0x49, 0x49, 0x30 },   // 6     22   36
	   { 0x01, 0x71, 0x09, 0x05, 0x03 },   // 7     23   37
	   { 0x36, 0x49, 0x49, 0x49, 0x36 },   // 8     24   38
	   { 0x06, 0x49, 0x49, 0x29, 0x1E },   // 9     25   39
	   { 0x00, 0x36, 0x36, 0x00, 0x00 },   // :     26   3a
	   { 0x00, 0x56, 0x36, 0x00, 0x00 },   // ;     27   3b
	   { 0x08, 0x14, 0x22, 0x41, 0x00 },   // <     28   3c
	   { 0x14, 0x14, 0x14, 0x14, 0x14 },   // =     29   3d
	   { 0x00, 0x41, 0x22, 0x14, 0x08 },   // >     30   3e
	   { 0x02, 0x01, 0x51, 0x09, 0x06 },   // ?     31   3f
	   { 0x32, 0x49, 0x59, 0x51, 0x3E },   // @     32   40
	   { 0x7C, 0x12, 0x11, 0x12, 0x7C },   // A     33   41
	   { 0x7F, 0x49, 0x49, 0x49, 0x36 },   // B     34   42
	   { 0x3E, 0x41, 0x41, 0x41, 0x22 },   // C     35   43
	   { 0x7F, 0x41, 0x41, 0x22, 0x1C },   // D     36   44
	   { 0x7F, 0x49, 0x49, 0x49, 0x41 },   // E     37   45
	   { 0x7F, 0x09, 0x09, 0x09, 0x01 },   // F     38   46
	   { 0x3E, 0x41, 0x49, 0x49, 0x7A },   // G     39   47
	   { 0x7F, 0x08, 0x08, 0x08, 0x7F },   // H     40   48
	   { 0x00, 0x41, 0x7F, 0x41, 0x00 },   // I     41   49
	   { 0x20, 0x40, 0x41, 0x3F, 0x01 },   // J     42   4a
	   { 0x7F, 0x08, 0x14, 0x22, 0x41 },   // K     43   4b
	   { 0x7F, 0x40, 0x40, 0x40, 0x40 },   // L     44   4c
	   { 0x7F, 0x02, 0x0C, 0x02, 0x7F },   // M     45   4d
	   { 0x7F, 0x04, 0x08, 0x10, 0x7F },   // N     46   4e
	   { 0x3E, 0x41, 0x41, 0x41, 0x3E },   // O     47   4f
	   { 0x7F, 0x09, 0x09, 0x09, 0x06 },   // P     48   50
	   { 0x3E, 0x41, 0x51, 0x21, 0x5E },   // Q     49   51
	   { 0x7F, 0x09, 0x19, 0x29, 0x46 },   // R     50   52
	   { 0x46, 0x49, 0x49, 0x49, 0x31 },   // S     51   53
	   { 0x01, 0x01, 0x7F, 0x01, 0x01 },   // T     52   54
	   { 0x3F, 0x40, 0x40, 0x40, 0x3F },   // U     53   55
	   { 0x1F, 0x20, 0x40, 0x20, 0x1F },   // V     54   56
	   { 0x3F, 0x40, 0x38, 0x40, 0x3F },   // W     55   57
	   { 0x63, 0x14, 0x08, 0x14, 0x63 },   // X     56   58
	   { 0x07, 0x08, 0x70, 0x08, 0x07 },   // Y     57   59
	   { 0x61, 0x51, 0x49, 0x45, 0x43 },   // Z     58   5a
	   { 0x00, 0x7F, 0x41, 0x41, 0x00 },   // [     59   5b
	   { 0x55, 0xAA, 0x55, 0xAA, 0x55 },   //       60   5c Backslash (Checker pattern)
	   { 0x00, 0x41, 0x41, 0x7F, 0x00 },   // ]     61   5d
	   { 0x04, 0x02, 0x01, 0x02, 0x04 },   // ^     62   5e
	   { 0x40, 0x40, 0x40, 0x40, 0x40 },   // _     63   5f underscore
	   { 0x00, 0x03, 0x05, 0x00, 0x00 },   // `     64   60 apostrophe
	   { 0x20, 0x54, 0x54, 0x54, 0x78 },   // a     65   61
	   { 0x7F, 0x48, 0x44, 0x44, 0x38 },   // b     66   62
	   { 0x38, 0x44, 0x44, 0x44, 0x20 },   // c     67   63
	   { 0x38, 0x44, 0x44, 0x48, 0x7F },   // d     68   64
	   { 0x38, 0x54, 0x54, 0x54, 0x18 },   // e     69   65
	   { 0x08, 0x7E, 0x09, 0x01, 0x02 },   // f     70   66
	   { 0x18, 0xA4, 0xA4, 0xA4, 0x7C },   // g     71   67
	   { 0x7F, 0x08, 0x04, 0x04, 0x78 },   // h     72   68
	   { 0x00, 0x44, 0x7D, 0x40, 0x00 },   // i     73   69
	   { 0x40, 0x80, 0x84, 0x7D, 0x00 },   // j     74   6a
	   { 0x7F, 0x10, 0x28, 0x44, 0x00 },   // k     75   6b
	   { 0x00, 0x41, 0x7F, 0x40, 0x00 },   // l     76   6c
	   { 0x7C, 0x04, 0x18, 0x04, 0x78 },   // m     77   6d
	   { 0x7C, 0x08, 0x04, 0x04, 0x78 },   // n     78   6e
	   { 0x38, 0x44, 0x44, 0x44, 0x38 },   // o     79   6f
	   { 0xFC, 0x24, 0x24, 0x24, 0x18 },   // p     80   70
	   { 0x18, 0x24, 0x24, 0x18, 0xFC },   // q     81   71
	   { 0x7C, 0x08, 0x04, 0x04, 0x08 },   // r     82   72
	   { 0x48, 0x54, 0x54, 0x54, 0x20 },   // s     83   73
	   { 0x04, 0x3F, 0x44, 0x40, 0x20 },   // t     84   74
	   { 0x3C, 0x40, 0x40, 0x20, 0x7C },   // u     85   75
	   { 0x1C, 0x20, 0x40, 0x20, 0x1C },   // v     86   76
	   { 0x3C, 0x40, 0x30, 0x40, 0x3C },   // w     87   77
	   { 0x44, 0x28, 0x10, 0x28, 0x44 },   // x     88   78
	   { 0x1C, 0xA0, 0xA0, 0xA0, 0x7C },   // y     89   79
	   { 0x44, 0x64, 0x54, 0x4C, 0x44 },   // z     90   7a
	   { 0x00, 0x10, 0x7C, 0x82, 0x00 },   // {     91   7b
	   { 0x00, 0x00, 0xFF, 0x00, 0x00 },   // |     92   7c
	   { 0x00, 0x82, 0x7C, 0x10, 0x00 },   // }     93   7d
	   { 0x00, 0x06, 0x09, 0x09, 0x06 }
	};  // ~     94   7e


//~~~~~~~~~~~~~~~~~~~~~~~LOW-level OLED_I2C  @ 180 lines of code ~~~~
static void i2c_initialise(void)  
{   //  should called at the beginning of main() {Arduino:  by setup() when not using Wire.h}
	TWBR = 12;         //  400KHz   use 0x62 = @80KHz
	TWSR = 0x01;
	TWCR = (1<<TWEN);
}

static uint8_t i2c_switch_port(uint8_t port)
{
	if(port > NPORTS) return 1;
	
	//i2c_write(0x00);
	return i2c_write(1 << port);
	
	return 0;
}

static uint8_t i2c_start(uint8_t address, uint8_t port)
{
	if(port > NPORTS) return 5;  // error report
	
	uint8_t tw_status;
	TWCR = 0; //reset TWI control register
	
	TWCR=((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // send START
	while(!(TWCR & (1 << TWINT)));  // delay until TWINT set (= startcondition is transmitted)
	
	//check if start condition is transmitted succesfully
	tw_status = TW_STATUS & 0xF8;
	if ( (tw_status != TW_START) && (tw_status != TW_REP_START)) return 1; 
	
	//transfer address and shift the port bit 
	i2c_write(address << 1);// << 1 << port;   

	
	//check whether the write/read is acked
	tw_status = TW_STATUS & 0xF8;
	if((tw_status != TW_MT_SLA_ACK) && (tw_status != TW_MR_SLA_ACK)) return 2;
	
	//set port
	return i2c_switch_port(port);
	
	return 0;
}

static uint8_t i2c_start_oled(void)
{
	uint8_t tw_status;
	TWCR = 0; //reset TWI control register
	
	TWCR=((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // send START
	while(!(TWCR & (1 << TWINT)));  // delay until TWINT set (= startcondition is transmitted)
	
	//check if start condition is transmitted succesfully
	tw_status = TW_STATUS & 0xF8;
	if ( (tw_status != TW_START) && (tw_status != TW_REP_START)) return 1; 
	
	//transfer address and shift the port bit 
	i2c_write(OLED_ADRS << 1);// << 1 << port;   

	
	//check whether the write/read is acked
	tw_status = TW_STATUS & 0xF8;
	if((tw_status != TW_MT_SLA_ACK) && (tw_status != TW_MR_SLA_ACK)) return 2;
	
	return 0;
}

//end the transmission 
static void i2c_stop(void)
{
	TWCR = ( (1<<TWINT)|(1<<TWEN)|(1<<TWSTO) ); // I2C STOP
}

//put data on the data line and check if acked
//error if !=0
static uint8_t i2c_write(uint8_t data)
{
	TWDR = data;   // I2C write, wait until completed
	TWCR = ((1<<TWINT)|(1<<TWEN)); while(!(TWCR & (1<<TWINT)));
	if( (TWSR & 0xF8) != TW_MT_DATA_ACK ) return 1;
	
	return 0;
}

//initialise the oled (like the SSD1306 library)
static uint8_t oled_init(void)
{
	uint8_t errors = 0;
	errors += i2c_start_oled();	
	
	errors += i2c_write(SSD1306_COMMAND);	
	
	errors += i2c_write(SSD1306_DISPLAY_OFF);
	errors += i2c_write(SSD1306_SET_CLOCK_DIV_RATIO);
	errors += i2c_write(0x80);
	errors += i2c_write(SSD1306_SET_MULTIPLEX_RATIO);
	errors += i2c_write(0x3F);
	errors += i2c_write(SSD1306_SET_DISPLAY_OFFSET);
	
	errors += i2c_write(0x0);
	errors += i2c_write(SSD1306_SET_START_LINE | 0x00);
	errors += i2c_write(SSD1306_CHARGE_PUMP);
	errors += i2c_write(0x14);
	errors += i2c_write(SSD1306_MEMORY_ADDR_MODE);
	errors += i2c_write(0x00);
	errors += i2c_write(SSD1306_SET_SEGMENT_REMAP | 0x01);
	errors += i2c_write(SSD1306_COM_SCAN_DIR_DEC);
	
	errors += i2c_write(SSD1306_SET_COM_PINS);
	errors += i2c_write(0x12);
	errors += i2c_write(SSD1306_SET_CONTRAST_CONTROL);
	errors += i2c_write(0xCF);
	errors += i2c_write(SSD1306_SET_PRECHARGE_PERIOD);
	errors += i2c_write(0xF1);
	errors += i2c_write(SSD1306_SET_VCOM_DESELECT);
	errors += i2c_write(0x40);
	
	errors += i2c_write(SSD1306_DISPLAY_ALL_ON_RESUME);
	errors += i2c_write(SSD1306_NORMAL_DISPLAY);
	errors += i2c_write(SSD1306_DISPLAY_ON);
	
	i2c_stop();
	
	return errors;
}

static uint8_t oled_clear_screen(void)
{
	uint8_t errors = 0;
	
	errors += oled_set_cursor(0,0);
	
	errors += i2c_start_oled();
    i2c_write(0x40);
	for (uint16_t scrClearIndex=0; scrClearIndex<1024; scrClearIndex++) 
	{  // (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8)
		errors += i2c_start_oled();
		errors += i2c_write(0x40);

		for (uint8_t x=0; x<16; x++) 
		{
			errors += i2c_write(0x00);
			scrClearIndex++;  // 16 bytes per I2C transmission__64 transmissions per screen
		}
		scrClearIndex--;
		i2c_stop();// I2C STOP
	}
		
	i2c_stop();
	
	errors += oled_set_cursor(0,0);
		
	return errors;
}

static uint8_t oled_go_to_next_line(void) 
{      // Increment and roll-over the current line number.
	OLEDlineNumber++;       // global var for current line number
	OLEDlineNumber = OLEDlineNumber & 0x07; // roll-over back to top after 8 lines
	return oled_set_cursor(OLEDlineNumber, 0);
}

static uint8_t oled_set_cursor(uint8_t myLineNumber,uint8_t myColumnPosition) 
{
	uint8_t errors = 0;
	if( (myLineNumber <= SSD1306_LASTLINE) && (myColumnPosition <= 127) ) 
	{
		OLEDlineNumber = myLineNumber;     // global var: current line number
		OLEDcursorPos =  myColumnPosition; // global var: current cursor position

		errors += i2c_start_oled();
		//set cursor to start of first line
		errors += i2c_write(SSD1306_COMMAND);
		errors += i2c_write(SSD1306_SET_COLUMN_ADDR);
		errors += i2c_write(myColumnPosition);
		errors += i2c_write(127);

		errors += i2c_write(SSD1306_SET_PAGE_ADDR);
		errors += i2c_write(myLineNumber);
		errors += i2c_write(7);
	
		i2c_stop();
	}
	return errors;
}

static uint8_t oled_display_char(uint8_t myDisplayChar) 
{
	uint8_t charColumnBitMap, charBitMapIndex = 0;
	uint8_t errors = 0;

	if(((OLEDcursorPos + FONT_SIZE) >= 128) || (myDisplayChar=='\n')) 
	{ // font size=5
//      If the line has no more room for complete chars OR NewLine command,
//      then move the cursor to next line
		errors += oled_go_to_next_line();
	}
	
	if(myDisplayChar != '\n') 
	{
		myDisplayChar = myDisplayChar - 0x20; // As the lookup table starts from Space(0x20)
		while(1) 
		{
			//start oled connection
			errors += i2c_start_oled();
			//send display bit
			errors += i2c_write(0x40);
			charColumnBitMap = pgm_read_byte(&FontTable[myDisplayChar][charBitMapIndex]);
			errors += i2c_write(charColumnBitMap);
			OLEDcursorPos++;
			charBitMapIndex++;
			if( charBitMapIndex == FONT_SIZE) 
			{   // Exit the loop after sending five bytes
				errors += i2c_write(0x00); // turn bits off to get a space between the chars
				OLEDcursorPos++;
				break;
			}    
		}
    }
	return errors;
}

//send/display a string on the oled
static uint8_t string_to_oled(char *s, uint8_t address, uint8_t port)
{
	
	uint8_t errors = 0;
	i2c_initialise();
	//start a connection on address[port] (=multiplexer level)
	//init the oled
	errors += i2c_start(address, port);
	
	//start at oled level	
	errors += oled_init();
			
	//clear the screen
	errors += oled_clear_screen();
	
	//set the cursor
	errors += oled_set_cursor(0,0);
		
	//send the text 
	while(*s)
	{ 
		errors += oled_display_char(*s++);
	}
		
	//end the connection at multiplexer level
	i2c_stop();
	return errors;
}

int main(void){
	uint8_t address = 0x70;
	uint8_t port = 0;
		
	port = 0;
	string_to_oled("test OLED 1", address, port);

	_delay_ms(500); 
	port = 1;
	string_to_oled("test OLED 2", address, port);
	
	while(1);
}