PSoC 5LP I2C Bus Reset Sample

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Originally I posted this to the following thread.

But I'm afraid that leaving there, not many will find this sample.

So let me post this here again.

Enable I2C timeout?

When using I2C device(s), time to time we encounter a situation in which one of the device keeps holding the SDA low and the bus is kept busy.

In some/many articles in the web, people suggest to force toggle SCL 10~16 times to clear this, and I've done this with other manufacture's MCUs.

And I could find similar samples for PSoC 4, too.

But I could not find any for PSoC 5LP, so I tried it by myself with CY8CKIT-059.

I used an LM75B sensor for a test case, as it was one of the most simple I2C sensor I have.

To implement this I needed

(1) change SCL pin to GPIO output and SDA pin to High-Z GPIO input to avoid affecting I2C module

(2) toggle SCL pin 16 times (the number can be changed)

(3) return SCL pin and SDA pin to I2C (open drain driving low)

And changing a pin mode to/from I2C pin (open drain driving low),

I needed to change "driving mode" and "bypass mode".

May be it was because I did not study enough, but it was not easy to come up to "bypass mode",

and I suppose this is the reason why there is not many I2C bus reset sample(s) for PSoC 5LP.

Anyway, here is my sample trial.

Schematic

001-schematic.jpg

Pin assignment

003-pin-list.jpg

Tera Term Log

000-tera-term.jpg

At first reading temperature from LM75B

TEK0000.jpg

Then forced SCL (clock) to toggle 16 times

TEK0001.jpg

Returning to normal I2C mode and keep reading temperature again.

TEK0002.jpg

main.c

===================

#include "project.h"

#include "stdio.h"

#define LM75B_I2C_SLAVE_ADDR (0x48)

char str[128] ; /* print buffer */

void print(char *str)

{

    UART_PutString(str) ;

}

void init_hardware(void)

{

    UART_Start() ;   

    I2C_Start() ; 

    CyGlobalIntEnable; /* Enable global interrupts. */   

}

void splash(void)

{

    sprintf(str, "I2C Reset Test (%s %s)\n", __DATE__, __TIME__) ;

    print(str) ;

}

uint8_t myI2C_ReadByte(uint8_t reg_addr)

{

    uint8_t status ;

    uint8_t value = 0 ;

   

    I2C_MasterClearStatus();

    status = I2C_MasterSendStart(LM75B_I2C_SLAVE_ADDR, I2C_WRITE_XFER_MODE) ;

   

    if (I2C_MSTR_NO_ERROR == status) {

        I2C_MasterWriteByte(reg_addr);

        status = I2C_MasterSendRestart(LM75B_I2C_SLAVE_ADDR, I2C_READ_XFER_MODE); 

    }

   

    if (I2C_MSTR_NO_ERROR == status) {  

        value = I2C_MasterReadByte(I2C_ACK_DATA); // read manufacturers ID from device

    }

    I2C_MasterSendStop(); 

   

    return(value) ;

}

/**

* From 10.1.1 General Call Address The I2C-BUS SPECIFICTIAON Version 2.1

* General call address 0x00 and comand 0x06

* is supposed to reset devices on the bus

* if they are capable of responding to this command.

*/

uint8_t i2c_software_reset(void)

{

    uint8_t status ;

    uint8_t global_address = 0x00 ;

    uint8_t sw_reset = 0x06 ;

   

    I2C_MasterClearStatus();

    status = I2C_MasterSendStart(global_address, I2C_WRITE_XFER_MODE) ;

   

    if (I2C_MSTR_NO_ERROR == status) {

        status = I2C_MasterWriteByte(sw_reset);

    }

   

    I2C_MasterSendStop(); 

   

    return(status) ;

}

#define I2C_CLK_DELAY     5 /* 10us for 100KHz */

void toggle_scl(int num)

{

    int i ;

    for (i = 0 ; i < num ; i++ ) {

        SCL_Write(1) ;

        CyDelayUs(I2C_CLK_DELAY) ;

        SCL_Write(0) ;

        CyDelayUs(I2C_CLK_DELAY) ;

    }

    SCL_Write(1) ;   

}

int i2c_bus_reset(void)

{

    int result = 0 ;

    int sda_bypass, scl_bypass ;

    int try_count = 10 ;

       

    scl_bypass = SCL_BYP ;             /* save scl bypass value */

    sda_bypass = SDA_BYP ;             /* save sda bypass value */

   

    SCL_SetDriveMode(PIN_DM_STRONG) ;  /* Strong CMOS Output */

    SCL_BYP = 0 ;                      /* disconnect SCL pin from I2C and enable DR */ 

    SDA_SetDriveMode(PIN_DM_DIG_HIZ) ; /* Digital HiZ input */

    SDA_BYP = 0 ;                      /* disconnect SDA pin from I2C */

   

    do {

        toggle_scl(16) ;

        result = SDA_Read() && SCL_Read() ;

        try_count-- ;

        if (try_count <= 0) {

            break ;

        }

    } while(result == 0) ;

       

    SCL_SetDriveMode(PIN_DM_OD_LO) ; /* Open drain - drive low */

    SCL_BYP = scl_bypass ;           /* reconnect SCL to I2C   */

    SDA_SetDriveMode(PIN_DM_OD_LO) ; /* Open drain - drive low */

    SDA_BYP = sda_bypass ;           /* reconnect SDA to I2C   */

   

//  i2c_software_reset() ; /* may not be necessary */

       

    return(result) ;

}

int main(void)

{

    uint8_t data ;

    uint8_t addr = 0 ;

    int i ;

   

    init_hardware() ;

    splash() ;

    

    for(;;)

    {      

        for (i = 0 ; i < 5 ; i++ ) {

            data = myI2C_ReadByte(addr) ;       

            sprintf(str, "%d\n", data) ;

            print(str) ;

            CyDelay(1000) ;

        }

       

        print("Resetting I2C Bus ... ") ;

        if (i2c_bus_reset()) {

            print("OK!\n") ;

        } else {

            print("Failed!\n") ;

        }

        CyDelay(1000) ;

    }

}

===================

moto

0 Likes
0 Replies