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.

[SOLVED] Scanning a I2C bus for available slave devices - I2C address scanner source code for use with Stellaris devices

Other Parts Discussed in Thread: TLC59116, TCA4311A

Hi,

I'm trying to implement an I2C address scanner on the Stellaris LM4F120H5QR but the code below deliveres wrong data.

Here is what I would expect to see (probing the I2C bus with my Bus Pirate V3):

but this is what I get from my Stellaris program:

The addresses found by my code were starting from 0xC0 (=192) and were ending with 0XD9 (=217).

What I want the program to do is placing a slaves address to the bus and see if the device acknowledges it. I'm using repeated start and write some dummy data (0x00) on the bus at the moment, but I doubt that this is the way to do it.

So, thank you for your support/advice!

Rgds
aBUGSworstnightmare

//*****************************************************************************
//
//! Probes the selected I2C bus for available slave devices
//!
//! \param ulI2CBase is the base for the I2C module.
//!
//! This function scans the selected I2C bus for available I2C slave device.
//! The ulI2CBase parameter is the I2C modules master base address.
//! \e ulI2CBase parameter can be one of the following values:
//!
//! - \b I2C0_MASTER_BASE
//! - \b I2C1_MASTER_BASE
//! - \b I2C2_MASTER_BASE
//! - \b I2C3_MASTER_BASE
//!
//! \return 0 if there was an error or 1 if there was not.
//
//*****************************************************************************
unsigned long
I2CBusScan(unsigned long ulI2CBase)
{
unsigned char ucProbeAdress;
unsigned long ucerrorstate;

//
// Check the arguments.
//
//ASSERT(I2CMasterBaseValid(ulI2CBase));

//
// Wait until master module is done transferring.
//
while(ROM_I2CMasterBusy(ulI2CBase))
{
};

for (ucProbeAdress = 0; ucProbeAdress < 255; ucProbeAdress++)
{
  //
  // Tell the master module what address it will place on the bus when
  // writing to the slave.
  //
  ROM_I2CMasterSlaveAddrSet(ulI2CBase, (ucProbeAdress>>1), false);
  ROM_SysCtlDelay(50000);

  //
  // Place the command to be sent in the data register.
  //
  ROM_I2CMasterDataPut(ulI2CBase, 0x00);

  //
  // Initiate send of data from the master.
  //
  ROM_I2CMasterControl(ulI2CBase, I2C_MASTER_CMD_BURST_SEND_START);

  ROM_SysCtlDelay(500000);

  ucerrorstate = ROM_I2CMasterErr(ulI2CBase);

  //
  // Check for errors.
  //
  if(ucerrorstate & I2C_MASTER_ERR_ADDR_ACK)
  {
    //
    // device at selected address did not respond
    //UARTprintf("Address not found: %d\n",ucProbeAdress);
    //ROM_SysCtlDelay(1500000);
  }

  else
  {
    UARTprintf("Address found: %d\n",ucProbeAdress);
    ROM_SysCtlDelay(1500000);
  }
}

//
// End transfer of data from the master.
//
ROM_I2CMasterControl(ulI2CBase, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);


UARTprintf("I2C Bus-Scan done...\n");
//
// Return 1 if there is no error.
//
return 1;
}

 

  • Believe this to be a thoughtful and valuable exercise.  Highlights the need for a powerful, inexpensive I2C bus analyzer to learn, "what's what."  However - some questions/issues:

    a) As I2C defines 7 bit address (w/bit 8 (lsb) reserved for read or write) would it not be beneficial to follow this 7 bit convention?

    b) You can't possibly have that many I2C devices on your I2C bus.  Thus - how are you able to generate such a large number of sequential responses?

    c) Can you shift to much lower I2C addresses - say 0x60 - 0x6F - and repeat this test?

    Remain curious as to how many (if any are real) I2C slaves are attached to your bus?  Wouldn't a simple attachment of 2-3 "real I2C" slaves - at "known" different addresses - be worthwhile?  The large and sequential response you've obtained suggests that yours are not "real" I2C slave devices...

     

  • Hi cb1,

    thank's for answering. Find my responses below.

    cb1- said:

    c) Can you shift to much lower I2C addresses - say 0x60 - 0x6F - and repeat this test?

    Remain curious as to how many (if any are real) I2C slaves are attached to your bus?  Wouldn't a simple attachment of 2-3 "real I2C" slaves - at "known" different addresses - be worthwhile?  The large and sequential response you've obtained suggests that yours are not "real" I2C slave devices...

    There are eight (8) TLC59116 connected to my I2C bus. The TLC59116 has a programmable I2C address [1]:[1]:[0]:[A3]:[A2]:[A1]:[A0]:[R/W] which answers your question why there is such a large sequence of addresses in a row. There are even more addresses when dealing with these devices (i.e. 0xD0 is the default ALLCALL-Address, used for addressing every device on the bus at once; refer to the TLC59116 data sheet for more details). 

    And, for those 'who doubt': Here's a video showing the eight units in action! http://www.youtube.com/watch?v=roAdWY0ph84

     

    cb1- said:
    b) You can't possibly have that many I2C devices on your I2C bus.  Thus - how are you able to generate such a large number of sequential responses?

    Bus lenght is not an issue; there are some TCA4311A (hot swappable I2C buffers) used to deal with the bus capacities.

    cb1- said:
    a) As I2C defines 7 bit address (w/bit 8 (lsb) reserved for read or write) would it not be beneficial to follow this 7 bit convention?

    Well, it's a matter of taste! I usually prefer to work with 8-bit addresses.

    EDIT: Changed my addressing scheme to 7-bit:

    //
    // Tell the master module what address it will place on the bus when
    // writing to the slave.
    //
    //ROM_I2CMasterSlaveAddrSet(ulI2CBase, (ucProbeAdress>>1), false);
    ROM_I2CMasterSlaveAddrSet(ulI2CBase, ucProbeAdress, false);
    ROM_SysCtlDelay(50000);

    Which gave me this esults:

    My slave addresses are: 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 and 0x67 (hardwired and working fine!); 0x68 is the ALLCALL-Address of the TLC59116 and 0x6B is the Software-Reset Address.

    But why are there some other slaves found at 0xE?. A slave address of 0xE? is impossible with the TLC59116 devices since the address scheme is [1]:[1]:[0]:[A3]:[A2]:[A1]:[A0]:[R/W] --> there is something wrong with the code.

    Well, the answer is simple: I didn't change my address probing range from 0-255 (8-bit) to 0-127 (7-bit) (see code above!)!

    cb1- said:
    Believe this to be a thoughtful and valuable exercise.  Highlights the need for a powerful, inexpensive I2C bus analyzer to learn, "what's what."

    Any recommendations? I use my Bus Pirate (http://dangerousprototypes.com/docs/Bus_Pirate) for doing the 'dirty stuff', and a 4-channel 200MHz digital storage scope to see what's going on. I have access to a Logic16 (http://www.saleae.com/logic16) to dig into a protocol, but I would like to have a simple I2C bus sniffer to aid in debugging. So again: any idea?

    Rgds
    aBUGSworstnightmare 

  • After changing the address scheme to 7-bit and fixing a BUG in the address range to probe the I2C bus address scan result looks like:

    So, here's the code that does the trick (the ASSERT macro calls a routine which is part of my Stellaris I2C driver which you can find here http://e2e.ti.com/support/microcontrollers/stellaris_arm_cortex-m3_microcontroller/f/473/t/235926.aspx):

    //*****************************************************************************
    //
    //! Probes the selected I2C bus for available slave devices
    //!
    //! \param ulI2CBase is the base for the I2C module.
    //!
    //! This function scans the selected I2C bus for available I2C slave device.
    //! The ulI2CBase parameter is the I2C modules master base address.
    //! \e ulI2CBase parameter can be one of the following values:
    //!
    //! - \b I2C0_MASTER_BASE
    //! - \b I2C1_MASTER_BASE
    //! - \b I2C2_MASTER_BASE
    //! - \b I2C3_MASTER_BASE
    //!
    //! \return 0 if there was an error or 1 if there was not.
    //
    //*****************************************************************************
    unsigned long
    I2CBusScan(unsigned long ulI2CBase)
    {
    unsigned char ucProbeAdress;
    unsigned long ucerrorstate;

    //
    // Check the arguments.
    //
    //ASSERT(I2CMasterBaseValid(ulI2CBase));

    //
    // Wait until master module is done transferring.
    //
    while(ROM_I2CMasterBusy(ulI2CBase))
    {
    };

    //
    // I2C Addresses are 7-bit values
    // probe the address range of 0 to 127 to find I2C slave devices on the bus
    //
    for (ucProbeAdress = 0; ucProbeAdress < 127; ucProbeAdress++)
    {
    //
    // Tell the master module what address it will place on the bus when
    // writing to the slave.
    //
    ROM_I2CMasterSlaveAddrSet(ulI2CBase, ucProbeAdress, false);
    ROM_SysCtlDelay(50000);

    //
    // Place the command to be sent in the data register.
    //
    ROM_I2CMasterDataPut(ulI2CBase, 0x00);

    //
    // Initiate send of data from the master.
    //
    ROM_I2CMasterControl(ulI2CBase, I2C_MASTER_CMD_BURST_SEND_START);

    //
    // Make some delay
    //
    ROM_SysCtlDelay(500000);

    //
    // Read the I2C Master Control/Status (I2CMCS) Register to a local
    // variable
    //
    ucerrorstate = ROM_I2CMasterErr(ulI2CBase);

    //
    // Examining the content I2C Master Control/Status (I2CMCS) Register
    // to see if the ADRACK-Bit (Acknowledge Address) is TRUE (1)
    // ( 1: The transmitted address was not acknowledged by the slave)
    //
    if(ucerrorstate & I2C_MASTER_ERR_ADDR_ACK)
    {
    //
    // device at selected address did not acknowledge --> there's no device
    // with this address present on the I2C bus
    //
    //
    // Print a message to Stdio
    //
    //UARTprintf("Address not found: 0x%2x - %3d\n",ucProbeAdress,ucProbeAdress);
    //
    // Make some delay
    //
    //ROM_SysCtlDelay(1500000);
    }

    //
    // ( 0: The transmitted address was acknowledged by the slave)
    //
    else
    {
    //
    // device at selected address acknowledged --> there is a device
    // with this address present on the I2C bus
    //
    //
    // Print a message to Stdio
    //
    //UARTprintf("Address found: 0x%2x - %3d\n",ucProbeAdress,ucProbeAdress);

    //
    // Make some delay
    //
    //ROM_SysCtlDelay(1500000);
    }
    }

    //
    // End transfer of data from the master.
    //
    ROM_I2CMasterControl(ulI2CBase, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);

    //
    // Print a message to Stdio
    //
    UARTprintf("I2C Bus-Scan done...\n");

    //
    // Return 1 if there is no error.
    //
    return 1;
    }

    EDIT: ADDED SOME MORE COMMENTS AND CHANGED THE STDIO-OUTPUT TO SOMETHINGS THAT'S MORE READABLE!

  • @ Bug/No Nightmare - Very Good Job - Congrats on your persistence & clear thinking - do believe that "focus on 7 bits" was of some value.

    Earlier I failed to anticipate that your "real" I2C slaves brought out 3 address bits - so that you could hang multiple devices on the bus.  (some I2C EEProms also employ this)  So loading the I2C bus may require some shift in value (lowered R) of pull-ups @ both SDA & SCL.  Would also advise that you use your scope to insure that (especially) the SCL "edges" remain clean/sharp.  (this usually is where I2C bus-overload shows up)

    While your method is sound - the use of the serial port does add a foreign (non I2C) code requirement - (and may not always be available) - and itself may be a source of set-up and operational issues.  It would seem that a small, graphic display (able to display 8 to 16 lines of data) which simply "sniffs" the bus - and which may serve as either slave or master - would be a sought & worthwhile addition.  Beyond I2C - this "sniffer" could expand to SPI as well.   It is so helpful to have, "One End - Known GOOD" - when testing/troubleshooting any of these serial-bus ports.

    You set yourself a nice goal - documented your methods well - and succeeded.  Suspect your (and the Bug's) dreams will be more pleasant...

  • Hi cb1,

    thank you for the flowers! I hope the code is also of good use for others!

    cb1- said:
    Earlier I failed to anticipate that your "real" I2C slaves brought out 3 address bits

    They even have 4 address bits, allowing you to connect up to 14 devices on a single I2C bus.

    cb1- said:
    It would seem that a small, graphic display (able to display 8 to 16 lines of data) which simply "sniffs" the bus

    I will have a 128x64 dots I2C-OLED (based on SSD1306 OLED controller) in the system which will be 'miss-used' for some dirty things. The code will see service in my C3ii INS Booster Pack (don't know if you've seen the thread so far http://e2e.ti.com/group/microcontrollerprojects/m/msp430microcontrollerprojects/664688.aspx). 

    I don't have plans to add more 'sniffing' functionality other than I2C address sniffing (will be used to detect the total no. of RGB units connected and 'boundary scan' of sensor array).

    Since the Booster is designed for use with the Stellaris Launchpad (nearly) every piece of code can be used with stand-alone Launchpads too.

    Rgds
    aBUGSworstnightmare 

  • May I also suggest four (4) passes -- one at each available speed. This may also help to identify some chips as some work at some but not all speeds.

  • Hi Dave,

    well, being able to probe for all 4 available communication speeds (Standard, Fast, Fast-Plus and High-Speed Mode) is for sure a desirable functionality.

    Since Fast-Plus and High-Speed Mode requires a different setup routine (i.e.  setting the HS-bit in the I2C Master Control/Status (I2CMCS) to enable High-Speed mode), which is - as far as I know - not available with a simple StellarisWare API-command, I think there should be another function which deals with these modes.

    Before taking care of these modes I would suggest to expand the 'Sniffer'-functionality to 'sniff' for readable and writable addresses by taking the [R/W]-bit into account.

    This could produce an output comparable to the Bus-Pirate ones shown at the beginning of this thread.

    Rgds
    aBUGSworstnightmare