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.

TM4C123GH6PM: I2C stuck in infinite loop

Part Number: TM4C123GH6PM
Other Parts Discussed in Thread: ADS7924

I'm using one of the I2C module to connect to the TI ADS 1110 ADC. Currently, the device has not yet been connected, and my application gets stuck in an infinite loop when attempting to fetch new I2C data. My I2C receive function is as follows:

The code gets stuck in the while (I2CMasterBusy(I2C0_BASE)) loop. I tried the suggested solution of configuring the I2C Master Timout using I2CMasterTimeoutSet(I2C0_BASE, 0x7d), but that doesn't seem to work, or i'm not using it correctly. So how exactly do I get out of this loop if the I2C line is not connected? If I should use a timeout, what would be the recommended minimum amount of time after which I should abandon the data transaction - so that the remainder of my application can continue to run unobstructed.

Any help would be appreciated.

Regards,

Ksawery

  • Water-Logged Greetings,

    So we saw a 'Dorsal Fin'  - just off shore this morning - and all we intrepid surfers 'hi-tailed' it to shore.    (Wound up being a Dolphin - yet my heart-rate rose like krazy!)

    Now you've TWO Calls to, "while (I2CMasterBusy(I2C0_BASE))" - and I don't believe it's been noted - just where - your code sticks!    Should you 'pass thru' the first one - such info aids/abets - does it not?   

    Have you (yet) looked into - just how - this function operates?    

    • Is it 'legal' to leave the Slave device 'unconnected?'
    • Are external pull-up Rs in place?
    • etc...

    Arrives now - the MCU's 'I2CMCS' Register - which includes that (dreaded) Busy Bit.

    I've not (yet) been able to determine (just what mechanism) 'Sets the Busy Bit.'    That said - clearly - leaving the Slave disconnected WILL generate an ERROR!

    >

    >

    [edit]  A 'closer read' (i.e. this time w/my eyes open) of the '4C123 manual' reveals that your (troubling) 'I2CMCS Busy Bit' is SET by each/every I2C Start Bit - and  Reset by each/every I2C Stop Bit!

     

    So - when your code executes the 'CMD_BURST_RECEIVE_START' - it is my belief that the 'STOP Bit' is not generated - and thus the Busy Bit remains Set - trapping you (exactly) as you've noted!    From where did you 'Get that specific code sequence?'

       

    Might it be possible that such 'Test for Master Busy' proves 'Improper and/or Illegal' - at that point in your code?

     

    That's my best effort - and now back to (delightful) meetings - and then flight back to Chicago (very) late tonight...

  • Well I'm glad to hear it wasn't an actual shark :) Lucky you that you get to surf!

    What i'm trying to do in my code, is to repeatedly fetch two bytes from the ADS 1110, which is a 16-bit ADC. So every time the above function is called, I expect it to fetch the 16-bit value of the ADC in two bytes and store the bytes in the buffer. The datasheet of the ADC is available here:

    https://www.ti.com/lit/ds/symlink/ads1110.pdf

    I'm basing my code on the suggestions provided in the TivaWare Peripheral Driver Library User's Guide, Chapter 16 (I2C):

    http://www.ti.com/lit/ug/spmu298d/spmu298d.pdf

    At the beginning of the chapter, there is a recommended sequence of function calls when programming an I2C master. There are two methods of receiving data - polling to check if the master is no longer busy, or interrupts. Since my application already uses UART and Timer interrupts, I don't want to add I2C interrupts on top of that, so I chose the polling method (hence my calls to I2CMasterBusy()). I wasn't sure how to complete my code, so I referred to the following forum post I found on google:

    https://e2e.ti.com/support/microcontrollers/other/f/908/t/314084

    Now I understand that obviously the I2C should be connected in order for it to work correctly, but I also would like to account for cases where the connection is non-existent - for example when the connection becomes disrupted or the ADC breaks. In such cases, I would like the remainder of my application to continue working normally (and only record any I2C errors).

    So given all these factors, what would be the best way to implement it, without getting stuck in while loops? :)

    Many thanks and hope you have a good flight,

    Ksawery

     

  • Hello Kwasery,

    I don't think the I2C timeout API would help to break out of that while loop. I think that's just for timeouts of clock stretching.

    You would need to add a check inside for something relevant. If you are getting an error back, such as what cb1 was mentioning with the start bit, you could check for that inside to break. Or you could have a flag variable that is set by a timer - that wouldn't be the same as the I2C timeout, but probably more what you are looking for in spirit of that. 

    Also, you may want to try using the following

    while(!(I2CMasterBusy(I2C0_BASE)));
    
    while(I2CMasterBusy(I2C0_BASE));
    

    instead of 

    while(I2CMasterBusy(I2C0_BASE));

    I recall that adjustment has helped a number of customers with I2C issues before, though I don't think any of those cases were due to being hung on the while function so it may not be applicable for your situation where you have a lack of a slave device. It was more to solve issues of data transmission not happening right. Still, it might be worthwhile to see if that quick adjustment fixes the issue.

  • Hi Ralph,

    Neat to add your (skilled) voice to poster's thread.   Your input re: 'I2C Timeouts' was most instructive.

    Yet - may I add - the first  (historical) occurrence of such 'Back to back' (near) identical calls to:

    • (!(I2CMasterBusy(I2C0_Base)));

    and then

    • ((I2CMasterBusy(I2C0_Base));

    arrived only as a 'Fix' for the added speed introduced by your '4C129' family.    This poster - like my group - employs the '4C123.'

    And it has been discovered that 'Even this mechanism' proves 'not' 100% fool-proof.

    Somewhere my office has the code we wrote to achieve (success) w/in a multi-byte I2C Slave transaction.   As/if time allows - I'll post that here...

    Now Adding:   (appears young Chi. Staff is 'faster than an (attacking) Dolphin!')     Code which follows ran on '4C123' and spoke to (your firm's) ADS7924.    (We had 'run out' of ADC Channels on the MCU!)

    unsigned long
    ads7924_conv(void)
    {

    ROM_I2CMasterSlaveAddrSet(I2C1_MASTER_BASE, 0x48, true);     // Read

    // Start of Channel 0 Conversion:
    ROM_I2CMasterControl(I2C1_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
    while(ROM_I2CMasterBusy(I2C1_MASTER_BASE)) {}

    ulADC0_up = ROM_I2CMasterDataGet(I2C1_MASTER_BASE);   // High 4 bits read from ADS7924

    ROM_I2CMasterControl(I2C1_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);

    while(ROM_I2CMasterBusy(I2C1_MASTER_BASE)) {}

    ulADC0_lo = ROM_I2CMasterDataGet(I2C1_MASTER_BASE);    // Lower 8 bits read from ADS7924

    // Start of Channel 1 Conversion:
    ROM_I2CMasterControl(I2C1_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);     
    while(ROM_I2CMasterBusy(I2C1_MASTER_BASE)) {}

    ulADC1_up = ROM_I2CMasterDataGet(I2C1_MASTER_BASE);     //4 highest bits

    ROM_I2CMasterControl(I2C1_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);   //this 'Receive_Finish' (should) produce a STOP Bit.

    while(ROM_I2CMasterBusy(I2C1_MASTER_BASE)) {}
    ulADC1_lo = ROM_I2CMasterDataGet(I2C1_MASTER_BASE);    //lowest 8 bits

    *** Do Note - this code ran under 'STELLARISWARE Ver 9453!'    Yet - those early 'I2CMasterBusy' checks appear to occur MINUS the arrival of an intervening STOP!   Thus they should 'Stick!'  

    That single one - generated the 'STOP'!   (i.e. STICK-FREE)

    There WERE rather significant changes between StellarisWare & the Re-brand - for this reason - our investors (forbade) us to move to the re-brand for years.

    When I return to Chi I'll have staff  find one of those older boards (w/LX4F231 MCU) and be able to confirm if (indeed) those (now suspect) while loops 'Were Escaped!'   We actually read 4 channels from the ADS7924 - I compressed the code for 'ease of viewing,' here.

    Plot thickens - does it not?

    *** Prior to my return to the office - 'Is not a critical test a, Scope Attachment to 'SDA/SCL' - to 'Detect the Presence/Absence of the 'STOP!'    Should 'STOP' not occur - my belief (seriously - who cares) is that the  'Absent STOP' will 'STICK' the code  ... w/in the (now dreaded)  'I2CMasterBusy' loop.

  • Thank you for your replies. I tried to add error checking to the while loop as follows:

    I assumed that at least the library would generate an error if there was no response (neither an acknowledge or not acknowledge). However the I2CMasterErr ingores all errors if the Master is busy:

      

    So i'm still unsure how to handle this issue. Your suggestion of using

    while(!(I2CMasterBusy(I2C0_BASE)));

    while(I2CMasterBusy(I2C0_BASE));

    didn't help unfortunately.

    I also tried implementing the following, by adding it to the TivaWare I2C.c and I2c.h library files, but I keep getting linker errors:

    https://e2e.ti.com/support/microcontrollers/other/f/908/t/444151

    How can I modify the library and not get linker errors? I'm pretty sure my custom function definition and declarations were correct and in the correct place.

    Many thanks,

    Ksawery  

  • Pardon - may I 'repeat' the (apparently)  'little noted guidance' - offered up earlier?

    cb1_mobile said:
    CRITICAL:  Scope Attachment to 'SDA/SCL' - to 'Detect the Presence/Absence of the 'STOP!'

    The MCU manual specifies:

    • 'START' Sets the 'MCS' Busy Bit
    • 'STOP' Resets it

    Via exhaustive reading (MCU Manual, example code) no other mechanisms reveal.

    It is 'theorized' that:

    • 'I2C_MASTER_CMD_BURST_RECEIVE_START' (parameter)  'blocks the emission of 'STOP'
    • 'I2C_MASTER_CMD_BURST_RECEIVE_CONT' (parameter)    'blocks the emission of 'STOP'
    • 'I2C_MASTER_CMD_BURST_RECEIVE_FINISH' (parameter)  'ENABLES 'STOP'  (thus should 'escape' your 'hung' while loop.)

    There appears 'One other means' to, 'Meet your I2C Interrupt Free' objective.   I suspect that the  insertion of a simple delay - following EACH call to functions which contain those (3 listed, just above) parameters - will enable your code to properly perform!    Do note - the  'delay'  serves as a, Replacement for the offending while loop.   (they are NOT used in concert.)   It is suspected that the 'FINISH' parameter (may) enable the 'escape' from the while loop -and that  loop will (always) prove superior to a fixed delay - assuming the loop can be (properly) exited.

    I'd start w/a 'Delay of a few seconds' - and then systematically reduce  - each time confirming proper results.   (I'd end w/a delay ~30% over your 'minimum' - which allows for: temperature, process variation, & MCU aging.)

  • Thank you for your reply and effort in helping me solve this issue. I ended up implementing a custom timeout timer to indicate a Master timeout and set the stop bit. My application now works correctly when the I2C line is disconnected, but I have yet to test it with the line connected (planning to do so tomorrow). I didn't have access to a scope today, so I couldn't test the presence of the STOP. My I2C receive function now looks as follows:

    I will still need to fine-tune the I2C timeout value to ensure it's not too short (or too long).

    Many thanks,

    Ksawery

  • Thank you - appreciated.   Was hoping for some 'insider knowledge' as to the 'PDL's (apparent) implementation' (as you noted) of that  'While loop.'  

    Especially when those two 'CMD-BURST-RECEIVE  Parameters' (START & CONT) appear to 'BURST' (pardon) the STOP generation.   (thus condemning the PDL code to,  'while loop - permanent residence!')   My group found 'one sentence ONLY' referencing START & STOP generation - w/in 1K+ pages!

    As our group (by multiple client demand) employs ARM Cortex MCUs: (M0, M3, M4, M7) from four vendors - it will prove fruitful to (later) explore just how (others) have resolved (or not) this issue.   (the 'One & only One Technique' - daily resident here - proves limiting - does it not?)

    Staff (the youngest one) asked that you consider the result of, 'One or both of the I2C lines becoming Open.'   Your diagnostic - to be truly effective - must consider 'ALL such eventualities!'    (it is wondered - if & how - Philips (inventor of (2C) 'created such a diagnostic and/or recovery method.')

    Note too - that certain Slaves - depending upon their 'workload' - may 'pull-down' & hold the clock - thus (disrupting) your timer (and our delay) as an effective 'Now READY' implementation.

  • Hello cb1,

    I think you raise a good point about clock stretching from slaves holding the SCL line down. Perhaps the I2CMasterLineStateGet API can be used to get around this though.

    I would imagine the SCL line is high during this case of a device not attached to the bus due to the pull-up resistors. If that is the case, then using that API would return a high for SCL and the timeout can be used to jump out. And then the alternate case of SCL low can be treated as clock stretching and further waiting can commence.

    I think that would be a workable solution to cover both cases.

  • Hi Ralph,

    Forgive my 'Slow Motion' response - had to play Salesman today - w/able, young staff (almost ready to take over) - and we spent 90 minutes - achieving  6.1 miles!  (that 'downhill' - of course)     We tried 2 'additional' (east-west (parallel) crossings - ALL  were 'under construction!'    Note to self/others - Chicago has TWO Seasons:  'Arctic Winter AND Construction!'   (Be still - my heart!)

    I like your comment - yet still staff/I remain bothered, 'How did that code (ever) run?     Especially when those 2 calls to  'CMD_BURST-RECEIVE'  were encountered?   Either of those calls should 'Lock the code in the 'vise-grip' of that 'while loop' - should they not?    (As the 'STOP' is bypassed - and 'ONLY' STOP can clear the I2CMCS 'busy bit' - enabling the 'escape' from the (dreaded) while loop...)

    Staff seeks to 'Go on the Record' here with: "Only Houdini (*) could escape the jaws of that while loop!"      (Maybe)

    *  Houdini - famed Escape Artist

  • Thank you for your reply. In my case the ADC doesn't perform clock stretching so it's not something I have to worry about.

    Regards,

    Ksawery 

  • Good morning - and 'Construction-Free, commute dittos' - to (nearby) office this day.

    One of the young staff (College Junior) notes that 'All three' of us here - have 'Missed the potential value of running (the original PDL code) WITH a valid Slave Device - attached & performing!'     Such will - at minimum - 'confirm or deny' the correctness of the 'PDL code.'    I believe that she's 'spot on' - and once we (reasonably) 'catch-up' today  (Ha!) - she'll 'attack that task.'    (Great to have a staff: 'better looking, much younger & smarter' ... than yourself...)

    Is it not true that we will (all) benefit from 'teasing out further detail' re: the (always suspect) 'I2CMCS Busy Bit?    

  • Hi Ksawery,

    Does your problem happen during CCS debug stepping? If you run pass that code line does it still gets stuck?

    -kel

  • Hi kel,

    The problem occurs when running the application normally. I can't get past that line without a custom timeout timer.

    Regards,

    Ksawery

  • Ksawery said:
    The problem occurs when running the application normally.

    May this reporter - along w/'Sunday Crüe' (4 strong) - note that the, 'Always Absent Slave' - appears to stray outside the definition of 'Normal!'

    As past noted here (few posts up):

    "One of the young staff (College Junior) notes that 'All three' of us here - have 'Missed the potential value of running (the original PDL code) WITH a valid Slave Device - attached & performing!'     Such will - at minimum - 'confirm or deny' the correctness of the 'PDL code.' "

    All of my training/experience 'orders' that one, 'Start from a 'highly standard' - USE CASE!'

    • First Verifying (code & HW) under 'normal & customary' operating conditions.   (i.e. Slave present & functional)
    • and only then - post success - 'Break from that 'norm' - introducing the most 'limited/gentle' of change (ideally just One at a time) - and again test/verify systematically

    Earlier of my posts 'emphasized' the MCU manual's stating, "START Sets the I2CMCS Busy Bit - STOP (apparently alone) Resets it!"  

    As 2 of the 3 'CMD_BURST_RECEIVE' parameters 'BLOCK the STOP'  (preventing the Busy Bit from clearing) - it defies understanding as to  'Just how the 'following wait loop' may be 'escaped' by the original PDL code!'

    To my group's mind - that (AWOL STOP) REMAINS the 'Elephant in the Room' - even though - especially though - that fact has been, 'Cast to the Curb!'

  • 'Unread and/or Unresponded' Greetings,   

    Might this 'dead/dying horse' deserve (yet one more) 'beating?'   Crack staffer 'Cassandra'  (college junior) believes that 'SHE' has 'Solved our poster's  dilemma!'    Let's examine:

    • the post you referenced (    ) had succeeded - yet she/I had noted it employed (only) 'Single I2C Transmits & Receives.'    And in that case - the I2C 'Stop' IS generated!    Thus - under those conditions (I2C STOP occurring) the use of, "while (I2CMaster Busy()" proves valid/legal.
    • that (referenced) Poster sought 'Only the Acceptance' of his code - NO error was reported.
    • And past 'excellent' forum leader 'Amit' - to 'Cassandra's & my mind' shifted the poster from 'Single I2C Receives' to 'Multiple ones!'   Which as I have (repeatedly noted) here - specifically  'DISABLES' the I2C Stop!    Dooming our poster (and all who may follow) to 'Stuck in 'Wait-Loop HELL!'   Such occurs during (either) 'CMD_BURST_RECEIVE_START' or 'CMD_BURST_RECEIVE_CONT' - being chosen as a parameter - w/in the function, "I2CMasterControl()."

    It is believed (now) that Amit simply - due to his desire for code efficiency - 'missed' the (not too well known fact) that those 2 parameters (in highlight, above) cannot be followed w/the, 'Test for the MCS 'Busy Bit' - as that bit will REMAIN -  'ROCK SOLID SET!'

    Find below the (referenced) past poster's code - employing (only) 'Single Receive' - which (nicely) accepts - and is properly responsive to - the (here dreaded) Wait-Loop!    This past poster asked only, "IF his code was 'acceptable' - he did NOT report any error!

    //-------------------------------------- RECEIVE ----------------------------------------------------------

    I2CMasterSlaveAddrSet (I2C6_BASE, 0x48, true); //set slave address to 0x48, initiating read

    I2CMasterControl (I2C6_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE); //Receive

    while (I2CMasterBusy(I2C6_BASE)); //Wait till end of transaction

    Byte1 = I2CMasterDataGet (I2C6_BASE); //Read from FIFO

    ***   ***   ***

    Now Amit (past)  responded:

    Hi Zvika,

    The Transmit section is fine. In the receive section since you want to receive two bytes I would change the sequence as follows

    I2CMasterControl (I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START); //Receive    That's one of two - DEADLY Parameters!

    while (I2CMasterBusy(I2C6_BASE)); //Wait till end of transaction    //  Danger Will Robinson!   Highly Likely  Will HANG - as I2C STOP has NOT been generated!

    Byte1 = I2CMasterDataGet (I2C6_BASE); //Read from FIFO

    I2CMasterControl (I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); //Receive    This parameter is NOT - one of the Deadly DUO - thus I2C STOP IS Generated.

    while (I2CMasterBusy(I2C6_BASE)); //Wait till end of transaction   //  No Danger (to Will or others) as the 'Finish' parameter is 'NOT Within the Deadly 'WAIT-LOCKING DUO!

    Byte2 = I2CMasterDataGet (I2C6_BASE); //Read from FIFO

    ***   So - proper attention to DETAILS - and the (somewhat) 'questioning of (potentially) suspect code' - especially when it 'Defies expectations' - has yielded the (most likely) SOLUTION.   Hats OFF to the lovely (and so talented) Cassandra...

  • Dear cb1_mobile,

    Apologies for my delayed response, I haven't been able to test my application yet, since some essential circuit components haven't arrived yet.

    Many thanks to Cassandra for her efforts in investigating the issue. So it does indeed seem that the CMD_BURST_RECEIVE_START parameter is causing the problem. I assumed (according to Amit's recommendation) that this parameter is essential for multiple-byte transfers. Can the same 2-byte transfer be achieved with the code the poster has initially provided? i.e. :

    //-------------------------------------- RECEIVE ----------------------------------------------------------

    I2CMasterSlaveAddrSet (I2C6_BASE, 0x48, true); //set slave address to 0x48, initiating read

    I2CMasterControl (I2C6_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE); //Receive

    while (I2CMasterBusy(I2C6_BASE)); //Wait till end of transaction

    Byte1 = I2CMasterDataGet (I2C6_BASE); //Read from FIFO

    I2CMasterControl (I2C6_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE); //Receive

    while (I2CMasterBusy(I2C6_BASE)); //Wait till end of transaction

    Byte2 = I2CMasterDataGet (I2C6_BASE); //Read from FIFO

  • Hello - and 'Cassandra, other staff, and cb1' are all glad that you're back.

    That single sentence - w/in the 1K+ page MCU manual - was 'All that was REALLY NEEDED.'   (sentence describing 'I2C START as Setting 'Busy" and (ONLY) 'I2C STOP' as Resetting 'Busy.')    We then noted that there DOES EXIST an 'I2C Error Function - which aims to insure the transmission of 'I2C STOP.'

    We believe - yet have not formally tested - that (past) poster's code - yet believe that it WILL SUCCEED.

    However - so too WILL AMIT'S - provided that initial test for MCS 'Busy" - which was highlighted - is REMOVED from the Code!

    Now (both) Cassandra and I have noted your identification of 'JUST ONE' (i.e. "CMD_BURST_RECEIVE_START") parameter as, 'WAIT-LOOP HANGING.'     In fact - there are TWO!    (as our post above - rather clearly (and in highlight) well notes!   You/others (even Cassandra & myself) must  'Strip Away' that wait loop - following (either) of those (again TWO) EVIL Parameters!

    As the (extremely MISSED) *** LIKE Button *** has (disappeared here - chasing my group from this (poorly recognized/rewarded space) might you reward our 'Super-Star' for her, 'Above/Beyond efforts' in your (and others here) behalf?   A  touch of Green (Verify) seems highly appropriate - and Sends the right message - does it not.

    Best of luck to you - all 'Wait-Escape' work-arounds/bandaids/guesses may be discarded - now that the (real solution - identified far earlier - btw) and now  formally & exactingly verified - has been 'teased out' and completed!

  • Understood. I should be able to test it this week.

    Once again thank you (and your staff) for your invaluable help! I've marked the post as resolved.

    Best regards,

    Ksawery

  • If only 'Sound was accommodated' here.    (Then you'd hear our (combined) very best Elvis ...  "Thank You ... Thank you Very much"   (voiced w/every ounce of our 'true (ok - bit faked - sincerity')    And (that) to remain 'true to the KING'  

    Staff  et moi - do indeed - Thank you.    Rather neat learning experience - hopefully of benefit to many - especially the 'Method of Attack!'   (even the 'persistence demanded...')

    [edit] 09:15 CST ... Crack (mostly) gurl staff request your 'Subject Line EDIT'   "I2C stuck (yet NO MORE) in infinite loop!"    Might that:

    • assure readers of your thread's success - thus encouraging (increased) thread hits!
    • and properly note the 'well resolved status' - of your thread - always of value to others

    Appears (beyond) a 'good idea' to this reporter - who I must remind you - is (vastly) out-numbered...   (not that that's (too) unpleasant...)