This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/TM4C123GH6PM: I2C not working or SD1306 init wrong?

Part Number: TM4C123GH6PM

Tool/software: Code Composer Studio

Hello, guys. I'm using TIVA TM4C123GXL for a while and tried now to use I2C module. First, tried to make it write and read a 24C04 memory, exactly the same way I did thousand times with PIC. But it didn't worked: all I read is 0xFF even after writing the same adress.
I won a SD1306 display and tried too, but again, it didn't work. So, am I missing something about TI I2C module? The display is connected to board, supplied with 3V3 from board but when I turn on the circuit, nothing happens on display and my green LED lights up (see at the bottom of the code), meaning it has executed all the instructions. Here is my code:

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/i2c.h"


//*****************************************************************************
// The error routine that is called if the driver library encounters an error.
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
    while(1);
}
#endif


//*****************************************************************************
// Definicoes das interfaces da placa Tiva
//*****************************************************************************
#define RED_LED   GPIO_PIN_1    //PF1
#define BLUE_LED  GPIO_PIN_2    //PF2
#define GREEN_LED GPIO_PIN_3    //PF3
#define SW1       GPIO_PIN_4    //PF4
#define SW2       GPIO_PIN_0    //PF0


//*****************************************************************************
// Display definitions
//*****************************************************************************
#define SSD1306_LCDWIDTH      128
#define SSD1306_LCDHEIGHT      64
#define SSD1306_SETCONTRAST   0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF
#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA
#define SSD1306_SETVCOMDETECT 0xDB
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9
#define SSD1306_SETMULTIPLEX 0xA8
#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10
#define SSD1306_SETSTARTLINE 0x40
#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR   0x22
#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8
#define SSD1306_SEGREMAP 0xA0
#define SSD1306_CHARGEPUMP 0x8D
#define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2


void delay_ms (uint16_t atraso)
{
    if (atraso != 0)
        SysCtlDelay(atraso*(SysCtlClockGet()/(3*1000)));    // Atraso de 1ms
}


void I2C0_Init(void)
{
    //
    // Enable Peripheral Clocks
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    //
    // Enable pin PB2 for I2C0 I2C0SCL
    //
    MAP_GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    MAP_GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);

    //
    // Enable pin PB3 for I2C0 I2C0SDA
    //
    MAP_GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    MAP_GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);

    MAP_I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), true);
}

void SSD1306_command(uint8_t c)
{
    uint8_t control = 0x80; // some use 0X00 other examples use 0X80. I tried both
    MAP_I2CMasterSlaveAddrSet(I2C0_BASE, 0x78, false);
    MAP_I2CMasterDataPut(I2C0_BASE, control);
    MAP_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    while(MAP_I2CMasterBusy(I2C0_BASE));
    MAP_I2CMasterDataPut(I2C0_BASE, c);
    MAP_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    while(MAP_I2CMasterBusy(I2C0_BASE));
}

// init according to SSD1306 data sheet and many places on the web
void Initialize_SSD1306()
{
    // Init sequence for 128x64 OLED module
    SSD1306_command(SSD1306_DISPLAYOFF);                    // 0xAE
    SSD1306_command(SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
    SSD1306_command(0x80);                                  // the suggested ratio 0x80
    SSD1306_command(SSD1306_SETMULTIPLEX);                  // 0xA8
    SSD1306_command(0x3F);
    SSD1306_command(SSD1306_SETDISPLAYOFFSET);              // 0xD3
    SSD1306_command(0x0);                                   // no offset
    SSD1306_command(SSD1306_SETSTARTLINE);// | 0x0);        // line #0
    SSD1306_command(SSD1306_CHARGEPUMP);                    // 0x8D
    SSD1306_command(0x14);                                  // using internal VCC
    SSD1306_command(SSD1306_MEMORYMODE);                    // 0x20
    SSD1306_command(0x00);                                  // 0x00 horizontal addressing
    SSD1306_command(SSD1306_SEGREMAP | 0x1);                // rotate screen 180
    SSD1306_command(SSD1306_COMSCANDEC);                    // rotate screen 180
    SSD1306_command(SSD1306_SETCOMPINS);                    // 0xDA
    SSD1306_command(0x12);
    SSD1306_command(SSD1306_SETCONTRAST);                   // 0x81
    SSD1306_command(0xCF);
    SSD1306_command(SSD1306_SETPRECHARGE);                  // 0xd9
    SSD1306_command(0xF1);
    SSD1306_command(SSD1306_SETVCOMDETECT);                 // 0xDB
    SSD1306_command(0x40);
    SSD1306_command(SSD1306_DISPLAYALLON_RESUME);           // 0xA4
    SSD1306_command(SSD1306_NORMALDISPLAY);                 // 0xA6
    SSD1306_command(SSD1306_DISPLAYON);                     //switch on OLED
}

//*****************************************************************************
//  Main
//*****************************************************************************
int main(void)
{
    MAP_SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, RED_LED|BLUE_LED|GREEN_LED);

    I2C0_Init();
    Initialize_SSD1306();

    SSD1306_command(SSD1306_DISPLAYALLON);

    GPIOPinWrite(GPIO_PORTF_BASE, GREEN_LED, GREEN_LED);

    ROM_SysCtlSleep();
}

I need some help. I can't have such microcontroller and not be able to understand how to use it's I2C module.

  • I'm not familiar with SD1306. Several things for you to check.

    1. It seems like the SD1306 can select from different MCU interfaces with I2C being one of them with other such as SPI. Please check if you are selecting the I2C interface.
    2. Are you sure the SD1306 can run at 400kps rate? Why don't you start with the 100kps standard I2C baudrate first? You should set the I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false) instead of true.
    3. Use a scope or logic analyzer and monitor the bus transaction. Is the master driving the bus and is the slave replying with the ACK?
    4. Do you have proper pull up resistors on the SCL and SCA bus?
  • 1) I can't select, once the board I have has only access to I2C interface;

    2) Not even at 100 kbps it worked;

    3) I don't have :(

    4) It has pull-up resistors already soldered to the board.

    With my PIC18F2550 it worked well. I just moved the code above to XC8 and it worked, so, not a problem of code nor connections. And as the 24C04 memory didn't worked with TivaWare + Tiva Launchpad, I really guess it is it's problem.

    Should I try direct register access instead TivaWare library?

  • Try to add the below lines and see it it makes a difference.

    From:

        MAP_I2CMasterSlaveAddrSet(I2C0_BASE, 0x78, false);
        MAP_I2CMasterDataPut(I2C0_BASE, control);
        MAP_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(MAP_I2CMasterBusy(I2C0_BASE));
        MAP_I2CMasterDataPut(I2C0_BASE, c);
        MAP_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
        while(MAP_I2CMasterBusy(I2C0_BASE));
    To:
        MAP_I2CMasterSlaveAddrSet(I2C0_BASE, 0x78, false);
        MAP_I2CMasterDataPut(I2C0_BASE, control);
        MAP_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(!MAP_I2CMasterBusy(I2C0_BASE)); // Add this line
        while(MAP_I2CMasterBusy(I2C0_BASE));
        MAP_I2CMasterDataPut(I2C0_BASE, c);
        MAP_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
        while(!MAP_I2CMasterBusy(I2C0_BASE)); // Add this line
        while(MAP_I2CMasterBusy(I2C0_BASE));
  • Nothing happens (and I also set the bus speed to 100 kbps)

  • I can't really help you if you don't even have a scope. It is an essential tool for any engineering type of work. See if you could borrow one. Are you sure the slave address is 0x80 and not 0x3C? The slave address is the address without the R/W bit.
  • I can't afford one. It is very expensive for a student at Brazil. I'm not sure if I can use my University's labs during vacations. It is the chance!
    And yes, I'm sure that is the address, since it works with PIC using this address.

  • Looking at the API below. The slave address is a 7bit address. It is being left shifted by one bit. The slave address is 0x3C per SD1306 datasheet. Different MCU works differently. You can't just port your PIC code directly to Tiva device. 

    void

    I2CMasterSlaveAddrSet(uint32_t ui32Base, uint8_t ui8SlaveAddr,

                         bool bReceive)

    {

       //

       // Check the arguments.

       //

       ASSERT(_I2CBaseValid(ui32Base));

       ASSERT(!(ui8SlaveAddr & 0x80));

       //

       // Set the address of the slave with which the master will communicate.

       //

       HWREG(ui32Base + I2C_O_MSA) = (ui8SlaveAddr << 1) | bReceive

  • Oh, man! It solved my problems. I assumed TivaWare would do (Address | R/W bit), instead it takes ((Address << 1 ) | R/W bit), so, my fault, guys.
    Just adding >> 1 to all address (my display and my EEPROM memories) solved and everything worked! Thank you all.