Slave address in I2C repeated start is always 0

cancel
Showing results for 
Search instead for 
Did you mean: 

Slave address in I2C repeated start is always 0

1,304 Views
Contributor IV

Hi,

I'm using a KL03 as an I2C master and it is sending the following bytes to a slave with address 0x43:

(0x43 << 1) | 0

0x01

(0x43 << 1) | 1

and then the expectation is to read a byte back from the slave.

However, the problem I'm seeing is that the second time I write the slave address, it shows up as 0 (irrespective of what I actually write) and then the data read is always 0xFF. See attached screenshot.

Here's some snippets of the code I'm using:

// init function

   /* Enable clock for SPI0 module */

   SIM_SCGC4 |= SIM_SCGC4_I2C0_MASK;

   PORT_PCR_REG( PORTA_BASE_PTR, 3 ) = PORT_PCR_MUX(2); // SCL

   PORT_PCR_REG( PORTA_BASE_PTR, 4 ) = PORT_PCR_MUX(2); // SDA

   I2C0_C1 = 0x0;

   I2C0_C2 = 0x0;

   /* No slave address, so this is master */

   I2C0_A1 = 0x0;

   I2C0_C1 |= I2C_C1_IICIE_MASK;

   I2C0_S1 = I2C_S1_IICIF_MASK | I2C_S1_ARBL_MASK;

   /* Initialize and set I2C baudrate to default value */

   i2c_set_baudrate( i2cInfo.baudRate );

   /* Enable I2C0 Interrupts */

   enable_int_vector( INT_I2C0, INT_MAX_PRIORITY_DEFAULT );

   I2C0_C1 |= I2C_C1_IICEN_MASK;

// I trigger the I2C transfer to the slave by writing

   /* Send start signal */

   I2C0_C1 |= I2C_C1_MST_MASK;

   /* Set to TX mode */

   I2C0_C1 |= I2C_C1_TX_MASK;

   /* Write byte */

   I2C0_D = (slaveAddr << 1) | 0;

// And then the rest of the processing is done in the IRQ handler which basically does the following

// first IRQ

   I2C0_S1 |= I2C_S1_IICIF_MASK;

   I2C0_D = 0x1;

// second IRQ
   I2C0_S1 |= I2C_S1_IICIF_MASK;

   I2C0_C1 |= I2C_C1_RSTA_MASK;

   I2C0_D = (slaveAddr << 1) | 1;

// third IRQ

   I2C0_S1 |= I2C_S1_IICIF_MASK;

   I2C0_C1 &= ~I2C_C1_TX_MASK;

   I2C0_C1 |= I2C_C1_TXAK_MASK;

  readData = I2C0_D;

// fourth IRQ

   I2C0_S1 |= I2C_S1_IICIF_MASK;

   I2C0_C1 |= I2C_C1_TXAK_MASK;

   I2C0_C1 |= I2C_C1_TX_MASK;

   I2C0_C1 &= ~I2C_C1_MST_MASK;

Does anyone have any idea of what might be causing this? Appreciate the help.

Screen Shot 2015-01-20 at 1.52.46 PM.png

Labels (1)
10 Replies

72 Views
Specialist V

Hi Will

Please see this post:

https://community.freescale.com/message/460444#460444

I have not been able to work with the KL03 or KL43's I2C in interrupt mode due to the probem. Am awaiting new feedback from Freescale.

Could your problem be the same, or related?

Regards

Mark

µTasker Kinetis support

0 Kudos

72 Views
Contributor IV

Hi Mark,

My problem is not specific to interrupt mode. I tried the following code which does the same as what I was trying to do sequentially but still the exact same output (only IICEN was set in C1, IICIE was cleared):

{

// Set as Master

I2C0_C1  |=  I2C_C1_MST_MASK;

// Set as TX

I2C0_C1 |= I2C_C1_TX_MASK;

// Write slave address byte

I2C0_D = (0x43 << 1) | 0;

// Wait

    // wait flag

    while((I2C0_S1 & I2C_S_IICIF_MASK)==0)

        ;

    // clear flag

    I2C0_S1 |= I2C_S_IICIF_MASK;   

// Write register address byte

I2C0_D = 0x1;

// Wait

    // wait flag

    while((I2C0_S1 & I2C_S_IICIF_MASK)==0)

        ;

    // clear flag

    I2C0_S1 |= I2C_S_IICIF_MASK;

// Repeated Start

I2C0_C1     |= I2C_C1_RSTA_MASK;

// Write slave address byte

I2C0_D = (0x43 << 1) | 1;

// Wait

    // wait flag

    while((I2C0_S1 & I2C_S_IICIF_MASK)==0)

        ;

    // clear flag

    I2C0_S1 |= I2C_S_IICIF_MASK;   

// Set as RX

I2C0_C1 &= ~I2C_C1_TX_MASK;

// Give NACK

I2C0_C1 |= I2C_C1_TXAK_MASK;

// Read byte

tempRead1 = I2C0_D;

// Wait

    // wait flag

    while((I2C0_S1 & I2C_S_IICIF_MASK)==0)

        ;

    // clear flag

    I2C0_S1 |= I2C_S_IICIF_MASK;  

// Stop

// Set Slave

I2C0_C1  &= ~I2C_C1_MST_MASK;

// Set RX

I2C0_C1 &= ~I2C_C1_TX_MASK;

// Read byte

tempRead2 = I2C0_D;

}

0 Kudos

72 Views
Specialist V

Will

In fact the problem is not restricted to interrupt mode, but I believe that some code (which happens to not be interrupt driven) manages to function.

I just never managed to avoid the issue with interrupts and that is why I call it an 'interrupt mode' problem.

Regards

Mark

http://www.utasker.com/kinetis.html

0 Kudos

72 Views
Contributor IV

I see. So do you have any sample code that manages to do a repeated start?

Would be great if I could compare my code with something known to work to understand what could be the problem. I pulled some online code and tried it but it still doesn't work.

0 Kudos

72 Views
Specialist V

Will

I do not have any code that works (I just heard that there was some simple polling code that didn't seem to be [obviously] affected).

In my case I do not use I2C on these devices until there is feedback about the recommended workaround from Freescale.

Regards

Mark

http://www.utasker.com/kinetis.html

0 Kudos

72 Views
Contributor IV

Actually this is the final fix I have come up with for this problem. Need to make sure the TX buffer is ready to accept new data before writing to it.

// Repeated Start

I2C0_C1     |= I2C_C1_RSTA_MASK;

// Wait till TX buffer is ready to be written to

while ( (I2C0_S2 & I2C_S2_EMPTY_MASK) == 0x00 );

// Write slave address byte

I2C0_D = (0x43 << 1) | 1;

0 Kudos

72 Views
Specialist V

Will

The KL03 is different to previous I2C Kinetis implementations due to the fact that it is double-buffered (previously all were single-buffered).

The delay and/or polling for a state may avoid problems which can't be avoided when using only interrupts (without also waiting for flags in the interrupt routine itself).

Since I don't want to use any blocking driver code I am still waiting to hopefully hear details back from Freescale.

Regards

Mark

72 Views
Contributor V

Hey Mark — have you heard back from Freescale (NXP)? I've just stubled into this problem (what a timewaster) here: I2C: delay required between setting RSTA and write to D? and I'm trying to figure out an elegant solution. The newer "double-buffered" I2C peripheral seems like a disaster to me.

I'm wondering in particular whether the repeated start flow you proposed here Double Buffered I2C Difficulties (eg. KL27)  is something that is "officially sanctioned" and expected to work reliably? I mean specifically using the start detection with additional interrupts to detect that a repeated start condition has been transmitted and it is safe to write to the D register?

I read AN4997 and find it disconcerting. This phrase in particular: "apply a small delay (depends on the I2C clock selection) before waiting for the EMPTY flag"  — what is a "small delay"? Do I need to wait before waiting for the EMPTY flag? Sounds like a race condition waiting to happen. Plus, I'd really rather not busy-wait at all.

0 Kudos

72 Views
Specialist V

Jan

Freescale/NXP upgraded the migration guide with new information about the I2C difficulties (originally specified as fully compatible and no SW impact, but subsequently upgraded to essentially "massive" SW impact....). I also had some contact with the Freescale engineer tasked with the rework and I was also asked some questions about whether I knew anything about needing to add SW delays, etc. I referenced to my own work that is also published in the forum where no delays are needed, but the new documents still have them mentioned.

In the meantime the uTasker I2C master/save implementation has been used in various industrial projects, including very high use repeated starts, with no known issues.

I think that some people have to realise that Freescale/NXP is a fantastic chip design/manufacturer but not a necessarily a firmware specialist or embedded system developer. Most code is example code that may have been written by a guru but may also have been written by someone who happened to have a bit of time to experiment (no software developer is reference, no idea if any code management is present, whether things were tested extensively, etc.). Basically Freescale/NXP is interested in giving users something get started with (that may work if the wind blows in the right direction) and so hooked into their chips. It is up the the user to actually get things working, which may involve a rework of the basic examples.

Specifically in the case of the buffered I2C I do think that the design is flawed (not up to the usual standard) but I also think that the example code was something that seemed to work by chance due to adding some delays (the sort of things that one tries when one doesn't "yet" understand behavior) which is adequate for "examples" to show that it "can" work. At the end of the day anyone developing embedded software is responsible for the final functionality so if the examples don't deliver the required quality one can do it properly themselves....

Regards

Mark

P.S: I believe that Microsoft was also caught out when they used Freescale code for the internal clock driver in their (failed?) Zune devices.
https://en.wikipedia.org/wiki/Zune

The first generation devices froze at midnight on December 31st 2008 due to this.....

72 Views
Contributor IV

I was actually able to make it work by adding a 3us delay between sending the repeated start signal and writing the slave address again.

// Repeated Start

I2C0_C1     |= I2C_C1_RSTA_MASK;

delay_us(3);

// Write slave address byte

I2C0_D = (0x43 << 1) | 1;

0 Kudos