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.

Problem with I2C communication between Tiva C-series and I/O expander

Other Parts Discussed in Thread: TM4C123GH6PM

Hi all,

I'm doing a school project where I need to be reading data from 8 sensors and writing data to 8 LEDs. To accomplish this I'm using the Tiva C-series launchpad and 2 pcf8547n I/O expanders. I've managed to read values from one pcf correctly, however, I'm having trouble writing to the other to turn the LEDs on.

The code in the if(mode) block is for reading from the sensors which is working correctly. In the else portion of that loop is where I'm trying to write. I have address pins A2 and A1 set to ground and A0 set to Vcc on the LED pcf which should correspond to a slave address of 0x21 (stored in slaveAddLED). For testing, I have an LED connected to the P0 pin and I'm simply trying to write a 1 to that pin (0x01) to turn the LED on. Here is the code.

#define PART_TM4C123GH6PM
#include <stdint.h>
#include <stdbool.h>
#include "stdio.h"
#include "stdlib.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_i2c.h"
#include "inc/hw_nvic.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_gpio.h"
#include "driverlib/systick.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/timer.h"
#include "driverlib/pin_map.h"
#include "driverlib/debug.h"
#include "driverlib/uart.h"
#define slaveAddSensor 0x20
#define slaveAddLED 0x21
char instrument = 0x30;        // harp
char channel = 0;                                      // default channel
char attackVel = 60, releaseVel = 60;   // default attack/release velocity
char note = 60;                                          // input from TIVA
int mode = 0;
uint32_t sensorVal = 0;
uint32_t data = 0xC0;
uint32_t LEDdata;
int intFlag = 0, loop = 0, noPlay = 0;
void waitNote() {
  SysCtlDelay(SysCtlClockGet()/10);
}
void talkMIDI(char action, char data1, char data2) {
   UARTCharPut(UART1_BASE, action);
   UARTCharPut(UART1_BASE, data1);
   if( (action & 0xF0) <= 0xB0) {
     UARTCharPut(UART1_BASE, data2);
   }
}
void noteOn(char channel, char note, char attackVel)
{
  talkMIDI((0x90 | channel), note, attackVel);
}
void playNote(char channel, char note, char attackVel) {
  noteOn(channel, note, attackVel);
  waitNote();
}
void noteOff(char channel, char note, char releaseVel)
{
  talkMIDI((0x80 | channel), note, releaseVel);
}
void stopNote(char channel, char note, char releaseVel) {
  noteOff(channel, note, releaseVel);
  waitNote();
}
void IntGPIOb (void){
  GPIOIntClear(GPIO_PORTB_BASE, GPIO_PIN_0);
  noPlay = 0;
  I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddSensor, true);
  I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
  while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
  {
  }
  I2CSlaveDataPut(I2C0_BASE, data);
  I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
  while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
  {
  }
  sensorVal = I2CMasterDataGet(I2C0_BASE);
  if (sensorVal == 128){
note = 60;
  }
  else if (sensorVal == 64){
note = 70;
  }
  else if (sensorVal == 192){
noPlay = 1;
  }
  intFlag = 1;
}
int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);    // Set system clock to run at 16MHz
// Enable peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);  // Enable I2C0
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);  // Enable UART1
    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    GPIOPinConfigure(GPIO_PB1_U1TX);
    // Enable GPIO Pin Types
GPIOPinTypeGPIOInput(GPIO_PORTB_BASE,GPIO_PIN_0);  // pin0=INT
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);    // pin2=SCL
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);       // pin3=SDA
    GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_1);
// Configure pads
    GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_0, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
    // Enable interrupts from portB pin0
GPIOIntTypeSet(GPIO_PORTB_BASE, GPIO_PIN_0, GPIO_FALLING_EDGE);
GPIOIntEnable(GPIO_PORTB_BASE, GPIO_PIN_0);
IntEnable(INT_GPIOB);
I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
I2CSlaveEnable(I2C0_BASE);
I2CSlaveInit(I2C0_BASE, slaveAddSensor);
I2CSlaveInit(I2C0_BASE, slaveAddLED);
    UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 31250,
                        (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                         UART_CONFIG_PAR_NONE));
    talkMIDI(0xB0, 0, 0x00);     // select melodic bank
    talkMIDI(0xC0, instrument, 0);       // select harp
    talkMIDI(0xB0, 0x07, 120);   // vol of 120 (127 max)
while(1)
{
 if (mode){
   I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddSensor, true);
   I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
   while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
   {
   }
   I2CSlaveDataPut(I2C0_BASE, data);
   if (intFlag) {
     intFlag = 0;
     if (!noPlay) {
       playNote(channel, note, attackVel);
       stopNote(channel, note, releaseVel);
     }
   }
   noPlay = 0;
   mode = 0;
 }
 else {
   I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddLED, false);
   I2CSlaveDataPut(I2C0_BASE, 0x01);
   I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
        while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_RREQ))
        {
        }
    //    mode = 1;
 }
}
}

In addition, from what I've been reading it seems you can connect both pcf's on the same I2C bus, but I'm not sure how this works with the I2CSlaveInit() command. Do I simply initialize both slave addresses (0x20 and 0x21) onto the I2C0_BASE? When I tried this it didn't seem to work.

Thank you for any help possible.

  • Hello Nick,

    First of all since the I2C-0 is a master you do need the following code

    I2CSlaveInit(I2C0_BASE, slaveAddSensor);
    I2CSlaveInit(I2C0_BASE, slaveAddLED);
    In fact remove all I2CSlave code sections.
    In the while loop where you wait for the polling to complete replace the I2CSlaveStatus with the following
    while(!I2CMasterBusy(I2C0_BASE));
    while(I2CMasterBusy(I2C0_BASE));
    Regards
    Amit
  • Hi Amit,

    Thank you for the quick reply. I tried what you suggested and replaced I2CSlaveStatus with the two I2CMasterBusy statements, and the program gets stuck in the second one. Also I'm a little confused on what you mean by replace all I2CSlave code sections. Do you mean just the I2CSlaveStatus commands or any command starting with I2CSlave? Right now the I2CSlave commands I have are I2CSlaveEnable, I2CSlaveInit for both pcf's, and the I2CSlaveDataPut for writing. Thanks again.

    -Nick

  • Hello Nick,

    The I2C Slave configuration that has been done, is for the on-chip I2C Slave. You are not using the on-chip Slave but two slaves outside of the device. If you can post the updated code, it would be useful to remove any further parts of the code.

    Secondly, do you have external Pull up on the SCL and SDA lines.

    Regards

    Amit

  • Yes I am using external pull-ups on the SCL and SDA lines. My code is the same except I have both:

    I2CSlaveInit(I2C0_BASE, slaveAddSensor);
    I2CSlaveInit(I2C0_BASE, slaveAddLED);

    for the 2 slaves.

    In the write loop I now have:

    I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddLED, false);
    I2CSlaveDataPut(I2C0_BASE, 0x01);
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);

    while(!I2CMasterBusy(I2C0_BASE));

    while(I2CMasterBusy(I2C0_BASE));

    Is it possible to read and write on the same SDA/SCL line (I2C0) at the same time with 2 different slaves? Thanks again.

  • Hello Nick,

    Yes, it is possible to have multiple Masters and Slave on a single set of I2C line.

    Secondly if you are writting to an external slave then I2CSlaveDataPut has to be replaced with I2CMasterDataPut. The Slave command is for writing via the TIVA Slave to an external Master. The Master command is for writing to an external Slave via the TIVA Master

    Regards

    Amit

  • Oh okay, it seems I misunderstood how those commands work. I made those changes and the code is still getting stuck in the    while(I2CMasterBusy(I2C0_BASE)){}    loop. My initialization and write block look like this now:

    #define slaveAddSensor 0x20

    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);                                SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);

    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2); // pin2=SCL
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3); // pin3=SDA

    I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
    I2CMasterEnable(I2C0_BASE);
    I2CSlaveInit(I2C0_BASE, slaveAddLED);

    I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddLED, false);
    I2CMasterDataPut(I2C0_BASE, 0x01);
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
    while(I2CMasterBusy(I2C0_BASE)) {}

    Somehow it's still getting stuck in that last line. I will be driving up to my school to grab a logic analyzer this afternoon to see what is going on with the I2C. Thanks again for any help possible.

  • I2CSlaveInit(I2C0_BASE, slaveAddLED);

    I believe that you are still confusing the meaning of, "Master & Slave."

    Your MCU is the Master - your multiple I/O Extenders are Slaves.

    Use of the function above likely places your MCU Master in an incorrect state.  Your MCU must - at all times - remain the Master - check carefully what the function I've highlighted (above) does...

  • As cb1_mobile aptly set the I2CSlaveInit is the problem.

    If you put a :A you will see that the SCL line is driven low during the write operation. This is because the internal slave has the same address as the external slave. Since it has accepted the data due to address match, the CPU has to read the data from the Internal Slave Buffer, otherwise the internal slave will not release the SCL line.

    Regards

    Amit