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.

I2C transfer with Re-Start using interrupts

Other Parts Discussed in Thread: TMS570LS3137

Hello,

I'm trying to read an I2C sensor using DMA and interrupts to minimize CPU load on a TMS570LS3137.

The sensor is read much like an EEPROM where the register address is sent first, then after a re-start all the data is clocked out, and finally the stop condition is sent.

I began by modifying the Re-Start example on the I2C Wiki page to read the sensor data. Here's the simplified version:

/* Transmit the register address */
i2cREG1->CNT = 1u;
i2cREG1->MDR = I2C_RESET_OUT | I2C_MASTER | I2C_TRANSMITTER | I2C_START_COND;
i2cREG1->DXR = regAddress;

while((i2cREG1->STR & (I2C_TX | I2C_ARDY)) == 0u); /* Wait for TX */

/* Re-start in receive mode*/
i2cREG1->CNT = count;
i2cREG1->MDR = I2C_RESET_OUT | I2C_MASTER | I2C_RECEIVER | I2C_START_COND |
               I2C_STOP_COND;

/* Receive the data */
for (i = 0; i < count; i++) {
  while ((i2cREG1->STR & I2C_RX) == 0u); /* Wait for Rx */
  data[i] = i2cREG1->DRR;
}

It works fine but is not practical with all the waits, so next I setup DMA to receive all the data from I2CDRR. The DMA works too, so the "Receive the data" part in the above code goes away.

The final part of my task is to use an interrupt to remove the "Wait for TX". I thought it would be relatively simple to get a TXRDY interrupt after the register address is sent, and perform the "Re-start in receive mode" in the ISR. But I enable TXRDY_INT and I never get the interrupt. After some experimenting I found that I can get the TXRDY interrupt if I set my initial count to so that it doesn't reach zero after sending the address, but then the read operation fails so that can't be right. 

Can anyone suggest how I would use an interrupt to perform the re-start after the address byte is transmitted? Perhaps TXRDY is not the right interrupt source.

Thanks, Bryan

  • After struggling with this all day, I think I solved it minutes after posting my question.

    Instead using only the TXRDY interrupt, I now trigger my ISR from both TXRDY and ARDY. I had avoided the ARDY interrupt because I don't fully understand the ARDY source, but since the non-interrupt example I was referencing waits on (TXRDY | ARDY), it makes sense to do the same with my interrupts.

    I'm still interested to hear if anyone suggests a better way to do it.
  • Bryan,

    It is not clear to me why you need this while loop on trasmission conditions. In your case, the I2C on the MCU is a master. To write/read data from a slave, you can follow the following unctions to start.

    void I2C_MasterTransmitStart(I2C_ST *ptr, unsigned int SAR_UL)
    {
     ptr->MDR_UN.MDR_ST.stp_B1 = 1;
     ptr->MDR_UN.MDR_ST.mst_B1 = 1;
     ptr->MDR_UN.MDR_ST.trx_B1 = 1;
     ptr->SAR_UN.SAR_UL = SAR_UL;
     ptr->MDR_UN.MDR_ST.stt_B1 = 1;
    }
           
    void I2C_MasterReceiveStart(I2C_ST *ptr, unsigned int SAR_UL)
    {
     ptr->MDR_UN.MDR_ST.stp_B1 = 1;
     ptr->MDR_UN.MDR_ST.mst_B1 = 1;
     ptr->MDR_UN.MDR_ST.trx_B1 = 0;
     ptr->SAR_UN.SAR_UL = SAR_UL;
     ptr->MDR_UN.MDR_ST.stt_B1 = 1;
    }

    If you configure the I2C message size register, the stop condition will be automatically generated after the entire message is snet or read. If you do not set this register, you will need to count the data being received or sent and generate the stop condition manually. If you use DMA, you can set up a DMA block transfer complete interrupt.  Then you can restart..

    Thanks and regards,

    Zhaohong

  • Thank you for the response, but I think you didn't understand the sensor read requirements:

    1. Start in Master-Transmit mode to send a 1-byte sensor read address (distinct for the sensor I2C address)

    2. Then "Re-start"  (i.e. start condition without a preceding stop) in Master-Receive mode.

    3. Finally perform n-reads to get n-bytes of data.

    As far as I understand, any solution has to wait for the transmit to complete before switching to receive. The solution with blocking waits was taken from the TI I2C Wiki page. I modified that solution to use interrupts rather than blocking, and it seems like it's now working. Both these solutions use the CNT register to automatically generate the stop condition.

    If there is a way to do this without blocking or interrupts, I'm all ears!

    Thanks,

    Bryan

  • Bryan,
    If you can share your sensor inteface spec, we may be able to come up with some ideas.
    Thanks.Zhaohong
  • Zaohong,
    that kind of sensor interface is quite common, just two examples:
    High-side current and voltage monitor LTC4151, www.linear.com/.../26089 (PDF).
    MEMS accelerometer IIS328DQ, www.st.com/.../iis328dq.pdf
    A third example is the TI audio chip mentioned on the wiki page linked by Byan.
    I appreciate Bryan's solution and would like to see an acknowledgement (or improvement) from TI.
    Thanks. Rainald

  • Thank you, Bryan, for experimenting and sharing the results.
    I think the TXRDY is only emitted once, since CNT=1, or not at all. I am not sure about the timing, may be the nominal point for that signal is between the start condition and the first bit of the slave address (compare the NOTE: "Unexpected DMA transmit and receive event") or of the data (compare Figure "Typical Timing Diagram of Repeat Mode"). May be that the write to DXR is faster so that TXRDY is not emitted at all. Anyways, that point would be too early to switch to reveive mode. It is ARDY to wait for, and it is not neccessary to enable TXRDY interrupts if CNT=1 (all imho -- my HW isn't ready for testing).
    Regards, Rainald