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.
Hi, I have observed that my I2C bus is hanging on the 7th bit of the second byte
I saw the USCI30 errata so I tried to implement the workaround
case 8u: // Vector 8: STPIFG if (UCB1IFG & UCRXIFG) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len) && (ucb1_i2c.slave_rx != NULL)) { *ucb1_i2c.slave_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer ucb1_i2c.rx_curr_len++; } else { ucb1_i2c.rx_succeed = 0u; uint8_t dummyClear = UCB1RXBUF; } } if (ucb1_i2c.rx_succeed && (NULL != ucb1_i2c.stp_cb)) { (*ucb1_i2c.stp_cb)(); // vTaskNotifyGiveFromISR(decode_i2c_handler, pdFALSE); } ucb1_i2c.rx_succeed = 1u; ucb1_i2c.rx_curr_len = 0; break; case 10u: // Vector 10: RXIFG { if ((UCB1STAT & UCSCLLOW) != 0) { __delay_cycles(200); } else { UCB1IFG |= UCRXIFG; break; } if (ucb1_i2c.rx_mode == SLAVE_RECEIVE) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len) && (ucb1_i2c.slave_rx != NULL)) { *ucb1_i2c.slave_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer ucb1_i2c.rx_curr_len++; } else { ucb1_i2c.rx_succeed = 0u; uint8_t dummyClear = UCB1RXBUF; } }
The above is a snippet of my I2C ISR (the STPIFG and the slave receive of the RXIFG portion)
So, from the errata, the workaround is to check that the UCSCLLOW has been set for at least 3 USCI bit clock cycle.
I am working with 12.8MHz SMCLK and 400kHz I2C clock frequency.
So, in my RX ISR handling, I am checking if UCSCLLOW is set, I will delay for 200 cycles.
I think this should be long enough since 200 cycles of 12.8MHz clock is around 15.625us and I only need 7.5us to cover 3 USCI bit clock cycle
If the UCSCLLOW is not set, I will just break and let it reenter the ISR to check again.
For the last byte received, I am expecting the UCSCLLOW to not be set. So, I will wait for the STPIFG to be triggered and read the last byte in there.
After implementing this, I get this image for my SCL line on the oscilloscope
I observed that I am still facing the same issue with I2C hanging occasionally on the 7th bit of the second byte even after implementing this.
May I ask if I am misunderstanding something regarding this errata/workaround and check if my implementation of the workaround is correct?
Hello,
I am currently working on a solution for you and I have a suggestion for you to try. In the errata, the suggested code flow is as follows:
(1) Enter RX ISR for reading receiving bytes
(2) Check if UCSCLLOW.UCBxSTAT == 1
(3) If no, repeat step 2 until set
(4) If yes, repeat step 2 for a time period > 3 x t (BitClock) where t (BitClock) = 1/ f (BitClock)
(5) If window of 3 x t(BitClock) cycles has elapsed, it is safe to read UCBxRXBUF
In your code, once the UCSCLLOW bit is 0, you skip the delay and break out of the case statement. If the UCSCLLOW bit is 1, you delay and then read the RX buffer. This does not match up with the workaround suggested in the errata.
You may want to try something like this:
while(UCB0STAT & UCSCLLOW == 0); // continually check to see if UCSCLLOW is set
__delay_cycles(200); // delay the time period that you've calculated to be safe
while(UCB0STAT & UCSCLLOW == 0); // final check to ensure that after the delay the UCSCLLOW bit is set
Then perform the read. try implementing something like this in your code and send an update as to whether or not you were able to get things working.
Hello,
I tried using the while loop to continually check if the UCSCLLOW is set. However, this condition is not satisfied for the last byte and my code will be stuck in the while loop for the last byte. I think the errata mentioned that the workaround is not meant to be applied on the last byte.
I also tried a different implementation for my receive interrupt instead
case 10u: // Vector 10: RXIFG { uint8_t clk_low_counter = 0; uint8_t clk_high_counter = 0; while (clk_low_counter < 16 && clk_high_counter < 16) { if ((UCB1STAT & UCSCLLOW) != 0) { clk_low_counter++; clk_high_counter = 0; } else { clk_high_counter++; clk_low_counter = 0; } //400 KHz is 2.5 us //sampling frequency should be more than 3 times of the clock frequency //four times the 400 KHz frequency will give delay, 2.5 us / 4 = 625 ns __delay_cycles(8); // 625 ns } if (ucb1_i2c.rx_mode == SLAVE_RECEIVE) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len) && (ucb1_i2c.slave_rx != NULL)) { *ucb1_i2c.slave_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer ucb1_i2c.rx_curr_len++; } else { ucb1_i2c.rx_succeed = 0u; uint8_t dummyClear = UCB1RXBUF; } }
However, this implementation did not resolve my issue too. The I2C bus still hangs on the 7th bit of the 2nd byte after some time
It seems like this new implementation is also not correctly utilizing the workaround from the Errata. When you say you've attempted using the while loop to check if the UCSCLLOW bit is set, do you mean that you implemented the suggested workaround as described? Or do you mean that you've adjusted your implementation slightly to utilize a while loop?
It may be helpful to post your ISR implementation when attempting to use the workaround so I can see if you've implemented it correctly.
Is there a reason that you are unable to utilize the other workarounds suggested in the errata? The first suggestions appear to be easy to implement and you may find more success with those solutions than using the secondary workaround.
Hello,
I have implemented this in my RX interrupt
case 10u: // Vector 10: RXIFG { while(UCB1STAT & UCSCLLOW == 0); __delay_cycles(400); while(UCB1STAT & UCSCLLOW == 0); if (ucb1_i2c.rx_mode == SLAVE_RECEIVE) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len) && (ucb1_i2c.slave_rx != NULL)) { *ucb1_i2c.slave_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer ucb1_i2c.rx_curr_len++; } else { ucb1_i2c.rx_succeed = 0u; uint8_t dummyClear = UCB1RXBUF; } } else if (ucb1_i2c.rx_mode == MASTER_RECEIVE) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.master_rx->dat_len) && (ucb1_i2c.master_rx != NULL)) { ucb1_i2c.rx_curr_len++; if (ucb1_i2c.rx_curr_len == (ucb1_i2c.master_rx->dat_len - 1)) { UCB1CTL1 |= UCTXSTP; } else if(ucb1_i2c.rx_curr_len == ucb1_i2c.master_rx->dat_len) { ucb1_i2c.rx_mode = SLAVE_RECEIVE; ucb1_i2c.rx_curr_len = 0; } *ucb1_i2c.master_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer } else { UCB1CTL1 |= UCTXSTP; ucb1_i2c.rx_mode = SLAVE_RECEIVE; ucb1_i2c.rx_curr_len = 0; uint8_t dummyClear = UCB1RXBUF; } } break; }
May I verify when you mention primary workaround of errata, are you referring to this?
Unfortunately, my program have other sections which are triggering higher priority isr, so I cannot guarantee that the I2C RX ISR can be serviced in time. So I am looking at the secondary workaround instead.
Yes that is the workaround that I was referring to. I see that you are unable to do this because of your higher priority interrupts.
Can you try implementing a workaround similar to the one in your most recent reply, but incorporate a counter in order to make sure that you don't perform the delays on the last byte as you mentioned? For example, at the beginning of case 10u, use something like
if(numberOfBytes < (totalBytes - 2)) // minus 2 so if you start the byte count at zero, the loop wont execute for the last byte
{
while(UCB0STAT & UCSCLLOW == 0); // continually check to see if UCSCLLOW is set
__delay_cycles(200); // delay the time period that you've calculated to be safe
while(UCB0STAT & UCSCLLOW == 0); // final check to ensure that after the delay the UCSCLLOW bit is set
}
Where numberOfBytes is the number of bytes received so far in that transmission, and totalBytes is the total nuber of bytes to be received. That way you are still implementing the delay as shown in the errata in a simple manner, and skipping it on the last byte as you pointed out.
If that does not work, it may be helpful to provide your entire code by either posting it here or by sending a private message so I can look further into things.
Unfortunately, as a slave receiving, I do not know how many bytes I am gonna be receiving. The number of bytes I am receiving is not fixed. In my recent code, the ucb1_i2c.slave_rx->dat_len is the number of bytes I allocated for receiving. So that is more of a maximum number of bytes I can receive in one transaction.
Currently, I am using the stop bit to know that the transaction has ended.
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector = USCI_B1_VECTOR __interrupt void USCI_B1_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USCI_B1_VECTOR))) USCI_B1_ISR (void) #else #error Compiler not supported! #endif { switch(__even_in_range(UCB1IV,12u)) { case 0u: break; // Vector 0: No interrupts case 2u: UCB1IFG &= ~UCALIFG; ucb1_i2c.arbitration_loss = 1u; UCB1IE &= ~UCTXIE; xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE); break; // Vector 2: ALIFG case 4u: UCB1CTL1 |= UCTXSTP; // I2C stop condition xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE); break; // Vector 4: NACKIFG case 6u: break; // Vector 6: STTIFG case 8u: // Vector 8: STPIFG if (UCB1IFG & UCRXIFG) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len) && (ucb1_i2c.slave_rx != NULL)) { *ucb1_i2c.slave_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer ucb1_i2c.rx_curr_len++; } else { ucb1_i2c.rx_succeed = 0u; uint8_t dummyClear = UCB1RXBUF; } } if (ucb1_i2c.rx_succeed && (NULL != ucb1_i2c.stp_cb)) { (*ucb1_i2c.stp_cb)(); // vTaskNotifyGiveFromISR(decode_i2c_handler, pdFALSE); } ucb1_i2c.rx_succeed = 1u; ucb1_i2c.rx_curr_len = 0; break; case 10u: // Vector 10: RXIFG { while(UCB1STAT & UCSCLLOW == 0); __delay_cycles(400); while(UCB1STAT & UCSCLLOW == 0); if (ucb1_i2c.rx_mode == SLAVE_RECEIVE) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.slave_rx->dat_len) && (ucb1_i2c.slave_rx != NULL)) { *ucb1_i2c.slave_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer ucb1_i2c.rx_curr_len++; } else { ucb1_i2c.rx_succeed = 0u; uint8_t dummyClear = UCB1RXBUF; } } else if (ucb1_i2c.rx_mode == MASTER_RECEIVE) { if ((ucb1_i2c.rx_curr_len < ucb1_i2c.master_rx->dat_len) && (ucb1_i2c.master_rx != NULL)) { ucb1_i2c.rx_curr_len++; if (ucb1_i2c.rx_curr_len == (ucb1_i2c.master_rx->dat_len - 1)) { UCB1CTL1 |= UCTXSTP; } else if(ucb1_i2c.rx_curr_len == ucb1_i2c.master_rx->dat_len) { ucb1_i2c.rx_mode = SLAVE_RECEIVE; ucb1_i2c.rx_curr_len = 0; } *ucb1_i2c.master_rx->data++ = UCB1RXBUF; // Get RX'd byte into buffer } else { UCB1CTL1 |= UCTXSTP; ucb1_i2c.rx_mode = SLAVE_RECEIVE; ucb1_i2c.rx_curr_len = 0; uint8_t dummyClear = UCB1RXBUF; } } break; } case 12u: // Vector 12: TXIFG if (ucb1_i2c.tx->dat_len) // Check TX byte counter { UCB1TXBUF = *ucb1_i2c.tx->data++; // Load TX buffer ucb1_i2c.tx->dat_len--; // Decrement TX byte counter } else { if(1u == ucb1_i2c.with_stop) { // UCB1IFG &= ~UCTXIFG; // Clear USCI_B1 TX int flag UCB1CTL1 |= UCTXSTP; // I2C stop condition UCB1CTL1 &= ~UCTR; UCB1IE &= ~UCTXIE; if(NULL != i2ca_tx_callback) { (*i2ca_tx_callback)(); } xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE); } else { ucb1_i2c.rx_mode = MASTER_RECEIVE; xSemaphoreGiveFromISR(ucb1_i2c.tx_semaphore,pdFALSE); if(NULL != i2ca_tx_callback) { (*i2ca_tx_callback)(); } i2c_set_receive_mode(USCI_B1_BASE); uint8_t UCB1STT_timeout = 0; UCB1CTL1 |= UCTXSTT; if(1u == ucb1_i2c.master_rx->dat_len) { // UCB1STT_timeout usually around 57 // todo: is 1000 timeout enough? what should be done when timeout occurs? while((UCB1CTL1 & UCTXSTT) && (UCB1STT_timeout < 1000)) { UCB1STT_timeout++; } UCB1CTL1 |= UCTXSTP; // Generate I2C stop condition if (UCB1STT_timeout == 1000) { ucb1_i2c.rx_mode = SLAVE_RECEIVE; } } } } default: break; } }
I've taken a look at a proof for the workaround and in that code it is shown that the solution takes the following form:
#pragma vector = USCI_B0_VECTOR __interrupt void USCI_B0_ISR(void) { switch(__even_in_range(UCB0IV,30)) { case -----: // test case for UCRXIFG while(!(UCB0STAT & UCSCLLOW)); // Is the Clock line low? while(!(UCB0STAT & UCSCLLOW)); // Check once again ReadBuf = UCB0RXBUF; // Now it is safe to read break;
It does not appear that it deals with the last byte in a different manner.
Does the code always hang on that 7th bit of the second byte? I saw that you mentioned the data isn't always the same number of bytes, so I was wondering if there is a different scenario where it hangs somewhere else or where it never hangs.
Regards,
Luke
Hello Luke,
After further observation, it might not necessary be the USCI30 errata issue.
I observed that before the i2c bus hangs, the microcontroller that is receiving the data did not reply after receiving.
As shown in the screenshot, address 0x04 will first send 6 bytes to address 0x06. address 0x06 then reply with 38 bytes.
When 0x04 sends another 6 bytes to 0x06, 0x06 did not reply. When 0x04 try to send to 0x06 again, the i2c bus hangs on 7th bit of second byte. I am guessing that for some reason, 0x06 is not servicing the receive interrupt. Currently, I am trying to debug further into this issue.
Regards
Kelvin
**Attention** This is a public forum