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 DMA usage and programming

Other Parts Discussed in Thread: HALCOGEN

Hi there,

The only paragraph 28.5.2 in the TRM spnu499b explaining DMA for use with I2C doesn't provide enough information to let me understand the coupling between DMA and I2C.

Let say I configure the I2C module in Master Mode.

My understanding is that after enabling DMA in the I2CDMACR register and enable the repeat mode (RM=1), as soon as the START condition STT=1 is set, the DMA will take data synchronously with I2C TXRDY flag from the memory and write to the I2CDXR register. At the end of the TX I can have a DMA BTC interrupt to send the STOP condition STP=1. This seems to be fine with the transmitter mode.

Question: I should have configured the DMA to frame transfer mode with 1 element at a time, and this will be synchronized with the I2CWEVNT. Right?

For receiver mode, I need also to enable the repeat mode (RM=1), then write STT=1 to start. An I2CWEVNT will be generated and at this moment, the first byte containing the slave address PLUS R/nW=1 should be written. Should I setup a separate DMA only for this single byte?

I continue with my understanding ... since RM=1 and the master has requested READ, the data will be clocked in from the slave starting next byte up to the number of DMA programmed number of transfers by I2CREVNT. This sounds good but how about if I need to issue a NACK before reading the last byte? Should I set the DMA to transfer n-1 frames, generate BTC interrupt, then set NACKMOD=1, then read one more byte, then set STP=1 to end?

If so, I don't know how to synchronize all this since even after BTC, since RM is still set, the I2C module will continue to clock in data from the slave.

Thank you for clarifying all this.

  • Hello Chuck,

      I'm not the expert on I2C but I will try to asnwer your questions the best I can. My asnwers are inline.

    Question: I should have configured the DMA to frame transfer mode with 1 element at a time, and this will be synchronized with the I2CWEVNT. Right?

    Charles>> yes, you should configure with 1 element per frame. Each time you write to I2CDXR register will trigger a DMA request when the data in I2CDXR is copied to I2CXSR shift register. For receiver, the DMA request is generated when the received data in the I2CRSR is copied to the I2CDRR.

    For receiver mode, I need also to enable the repeat mode (RM=1), then write STT=1 to start. An I2CWEVNT will be generated and at this moment, the first byte containing the slave address PLUS R/nW=1 should be written. Should I setup a separate DMA only for this single byte?

    Charles>> Another option is to use the CPU to write the slave address (I2CSAR register) and the direction bit (I2CMDR.TRX bit) first before starting to receive data from the slave.

    I continue with my understanding ... since RM=1 and the master has requested READ, the data will be clocked in from the slave starting next byte up to the number of DMA programmed number of transfers by I2CREVNT. This sounds good but how about if I need to issue a NACK before reading the last byte? Should I set the DMA to transfer n-1 frames, generate BTC interrupt, then set NACKMOD=1, then read one more byte, then set STP=1 to end?

    Charles>> In the TRM it says that in repeat mode the master must generate the stop condition after reading the (message size - 1)th data. You can consider using the last frame started (LFS) interrupt in the DMA. Before the last element in the last frame is received, an interrupt is generated. You can write the STP in the LFS ISR to reply NACK.

     

     

  • Hi Charles,

    I think that I understood your explanations ...

    Another question, under what circumstances I2CSTR_bit.TXRDY always at 0 (TX not ready)?

    In order to advance in my development before installing the I2C slave device, I set I2CEMDR_bit.IGNACK = 1, hoping that the I2C module can TX data to the void, but TXRDY is always 0 thus preventing the code from pass this while () loop.

    Attached is my code: 3250.I2CInit.cpp

    The first byte was written to I2CDXR then sent to I2CDSR, but never actually be shifted out, thus my code stuck at the sending of "0x0D" because TXRDY never get set to 1 again.

    Any clue?

    Thanks!

  • Hi Chuck,

     Wjhat does XSMT bit in the I2CSTR show? Do you see underflow condition?  I I will suggest that you try first with the loopback mode to see if it works. Hopefully this will help to see if the data is shifted out. There is an example project in Halcogen for this.

  • Hello Charles,

    The XSMT bit is 1, so there is no underflow. It is just that the transmitter doesn't want to shift out bits.

    If I turn ON the Digital Loop Back DLB bit, then no more lockup, the while loop ends and data are written to the shift register, and execution continues ... works fine!

    Does this mean that I can't work on a setup without a slave I2C device and with the IGNACK=1, or there is something wrong on my I2C module configuration? Below is the screen capture when it won't shift bits.

    Thanks.

  • Hi Chuck,

      I see that the pull function is disabled. Can you enable the pull and select pullup to see if any effect? Please note that I2C is not the primary signals on the pins. You will need to also setup IOMM module.

  • Charles,

    I've changed the pin function to I/O. I've enabled pull and set to pullup mode, but still no luck to pass the while() loop point. Do I also have to configure the IOMM for this?

    I however don't get your point. Why do I need to change the pin function to I/O mode. I need a slave to be connected to those pins SCL and SDA.

    Thanks.

  • Hi Chuck,

      The SCL and SDA should have an external pullup resistor to the supply on the bus. I assume that your current setup does not have the slave device and the bus is not pullup. If this is the case the master transmitter may find the bus is active low and hence not transmitting as it tries to wait for the bus to become free. I2C is multiplexed with MibSPI3CS signals. It depends how the internal pull is selected for MibSPI3CS CS pins and it will affect how the I2C determines the state on the bus. Not sure if this is the problem but I will suggest to configure I2C as the primary pins using IOMM first and externally you need to have a pullup resistor for SCL and SDA pins.

      To isolate the problem further can you try not using RM=1 and using the 7-bit address mode? See if you can transmit just the slave address + R/W bit.

  • Hello Chuck,

      I try to mimic your setup using RM=1. I also set the I IGNACK=1 as I don't have a slave in my setup to reply acknowledge. I'm sending 4 characters in 7-bit address mode. I can see in the scope the slave address (I use 0x55 as the address) + RW and the 4 characters are properly transmitted. I do have an external pullup and also setup the IOMM to bring the I2C SDA/SCL to the pins. Without the external pullup I see arbitration lost error which is expected. Attached please find my project.

    4807.LS31x_I2C.zip

  • Thank you Charles for your hard work and willingness to help. :)

    I'm however having lot of troubles following code generated by HalCoGen, very difficult to follow and tend to be more complicate than it should have been. Usually 10 to 20 lines of code should allow the understanding of a given problem, but one has to follow and trace on a couple dozen of files. Worst, for lot of constants and macros, you have to search and understanding their meanings. Moreover, I can plug in code generated by HalCoGen and even if it works just as intended, the understanding in the personal knowledge level is unclear when you relay that to the TRM ... what it has done?

    But don't get me wrong, HalCoGen is an excellent tool to prototype something, and does it fast and effective. This is a valuable tool. However its usefulness to provide technical support is rather questionable, because it cannot permit one to fully understand the troubled topic, even though one can just click on those boxes and produce intended results.

    Sorry this is only my two cents.

    In the aerospace industries, software must be certified and a code generator is not really an option unless it was certified or certifiable. I'm writing code manually (with IAR EWARM) because I have to understand fully the use of every single bit in every statement, and they have to be documented.

    I think that I'll wait for my target before continuing testing on the real thing. And surely I'll consult your HalCoGen project if I get into troubles.

    Thank you again.

  • Hello Tsai,

    > Each time you write to I2CDXR register will trigger a DMA request when the data in I2CDXR is copied to I2CXSR shift register.
    This is in line with the TRM, I2C Status Register (I2CSTR) Field Descriptions:
    > TXRDY - This bit is set to 1 to indicate when data in the transmit data register, I2CDXR, has been copied into the
    transmit shift register.
    However, one easily reads "when" as "when and only when", and that would be wrong, as it does not cover the first byte, compare the last paragraph under the heading "DMA Controller Events":
    > A transmit event (I2CWEVNT) is generated after a START condition in master transmitter mode.
    I wonder whether this is the only exception - what about the first byte of data in slave transmitter mode?

    Regarding Chuck's question, whether he needs a separate DMA only for the address byte (plus read bit) in Master receive mode, I think that no I2CWEVNT will be generated for that byte and it must not be written into the I2CDXR but comes from I2CSAR (the TRM is not explicit on that).

    Best regards,
    Rainald