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.
So i've got my i2c driver 99% functional but i've stumbled over a problem that is bugging me.
It appears to me that in order to issue repeated starts to a bus, repeat mode has to be on. I've done a fair amount of testing and no matter what I try, if repeat mode isn't on setting the start bit doesn't generate a repeated start even when we've finished everything we were doing previously. That's fine, I can live with that. What I can't live with is erroneous documentation.
Page 2257 of the TRM:
*Ahem...* "Only applicable when the I2C module is a master transmitter". That is 100% incorrect as far as I can tell. Setting the RM bit when in master receive, allows a repeated start to be generated on the bus. If I don't have RM set, I cannot generate a new start while I have control of the bus. Would you disagree with my above assertion?
Another thing.... The TRM discusses what a repeated start is in section 25.3.5.4 but never really tells the user how to do it. If my observations that it is only allowed when RM is set are correct, this should be explicitly spelled out in the TRM. The documentation for the RM bit make it seem like its behavior is only tied to the CNT register and repeated data transmission.
OOOoookkkkk.....now that brings me to the main point of this post. Assuming my observations are accurate and I'm not doing something dumb in my software, the above raises a small but IMO significant issue. How do I clock in an accurate number of bytes? Let's run through an example:
In the above case, because I'm sending the stop in software I don't have deterministic control of the number of bytes that are shifted in. My two choices are to send it after I've received the last requested byte which results in at least one additional unwanted data byte being shifted in. Alternatively, I can try to send the stop at n-1 and hope the timing is right that I get my last byte in before stop is sent.
Ultimately I guess my point is the peripheral should be able to send repeated starts when not in repeat mode. This would allow me to use the count register to shift in an accurate amount of data. So am I crazy? Have you ever been able to send a repeated start when RM isn't set?
Trey,
It is good to hear from you. Hope you're doing great.
Repeated Start can be generated in both repeat mode and non-repeat mode. Please check below example.
//########################################################################### // // FILE: Example_2807xI2C_eeprom.c // // TITLE: I2C EEPROM Example // //! \addtogroup cpu01_example_list //! <h1>I2C EEPROM Example (i2c_eeprom)</h1> //! //! This program will write 1-14 words to EEPROM and read them back. //! The data written and the EEPROM address written to are contained //! in the message structure, I2cMsgOut1. The data read back will be //! contained in the message structure I2cMsgIn1. //! //! \b External \b Connections \n //! - This program requires an external I2C EEPROM connected to //! the I2C bus at address 0x50. //! // //########################################################################### // $TI Release: F2807x Support Library v3.10.00.00 $ // $Release Date: Tue May 26 17:19:45 IST 2020 $ // $Copyright: // Copyright (C) 2014-2020 Texas Instruments Incorporated - http://www.ti.com/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // Neither the name of Texas Instruments Incorporated nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // $ //########################################################################### // // Included Files // #include "F28x_Project.h" // // Defines // #define I2C_SLAVE_ADDR 0x50 #define I2C_NUMBYTES 2 #define I2C_EEPROM_ADDRESS 0x0000 #define I2C_FIFO_LEVEL 16 #define I2C_SUCCESS 0 #define I2C_BUS_BUSY_ERROR 999 #define I2C_STP_NOT_READY_ERROR 1 #define SLAVE_NOT_READY_ERROR 2 #define DEVICE_SYSCLK_FREQ 120000000//SYSCLK = 120 MHz #define EEPROM_MAX_WRITE_CYCLE 5 //5ms #define WRITE_DELAY_TIME (((EEPROM_MAX_WRITE_CYCLE * DEVICE_SYSCLK_FREQ)/1000)-9)/5 #define DEVICE_DELAY_US(x) F28x_usDelay(x) // // Function Prototypes // void i2c_init_old(void); void I2C_GPIO_confi(void); void I2CA_Init(void); Uint16 handleNACK(void); Uint16 checkBusStatus(void); Uint16 I2CA_WriteData(Uint16 SlaveAddress, Uint16 address, Uint16 *txbuffer, Uint16 NumOfBytes); Uint16 I2CA_ReadData(uint16_t SlaveAddress, uint16_t address, uint16_t *txbuffer, uint16_t NumOfBytes); Uint16 PassCount=0; Uint16 FailCount=0; __interrupt void i2c_int1a_isr(void); void pass(void); void fail(void); Uint16 r; Uint16 I2C_status = 9; // // Main // uint16_t TXdata[66]; uint16_t RXdata[256]; void main(void) { // // Step 1. Initialize System Control: // PLL, WatchDog, enable Peripheral Clocks // This example function is found in the F2837xS_SysCtrl.c file. // InitSysCtrl(); // // Step 2. Initialize GPIO: // This example function is found in the F2837xS_Gpio.c file and // illustrates how to set the GPIO to it's default state. // InitGpio(); // // For this example, only init the pins for the SCI-A port. // These functions are found in the F2837xS_Gpio.c file. //XCLKOUT = GPIO73 GPIO_SetupPinMux(73, GPIO_MUX_CPU1, 3); EALLOW; ClkCfgRegs.CLKSRCCTL3.bit.XCLKOUTSEL = 0; ClkCfgRegs.XCLKOUTDIVSEL.bit.XCLKOUTDIV = 3; EDIS; I2C_GPIO_confi(); // i2c_init_old(); // // Step 3. Clear all __interrupts and initialize PIE vector table: // Disable CPU __interrupts // DINT; // // Initialize PIE control registers to their default state. // The default state is all PIE __interrupts disabled and flags // are cleared. // This function is found in the F2837xS_PieCtrl.c file. // InitPieCtrl(); // // Disable CPU __interrupts and clear all CPU __interrupt flags: // IER = 0x0000; IFR = 0x0000; // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // This will populate the entire table, even if the __interrupt // is not used in this example. This is useful for debug purposes. // The shell ISR routines are found in F2837xS_DefaultIsr.c. // This function is found in F2837xS_PieVect.c. // InitPieVectTable(); // // Interrupts that are used in this example are re-mapped to // ISR functions found within this file. // EALLOW; // This is needed to write to EALLOW protected registers PieVectTable.I2CA_INT = &i2c_int1a_isr; EDIS; // This is needed to disable write to EALLOW protected registers // // Step 4. Initialize the Device Peripherals: // I2CA_Init(); // // Step 5. User specific code // // // Clear Counters // PassCount = 0; FailCount = 0; // // Enable __interrupts required for this example // // // Enable I2C __interrupt 1 in the PIE: Group 8 __interrupt 1 // PieCtrlRegs.PIEIER8.bit.INTx1 = 1; //Enabling I2C non-FIFO interrupt // // Enable CPU INT8 which is connected to PIE group 8 // IER |= M_INT8; EINT; ESTOP0; r = 2; while(r<=66) { TXdata[r] = 55; RXdata[r] = 0; r++; } I2C_status = I2CA_WriteData(I2C_SLAVE_ADDR+10, I2C_EEPROM_ADDRESS, TXdata, 4); DEVICE_DELAY_US(2*WRITE_DELAY_TIME); //wait for 5ms for max write time for EEPROM i'm using I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR+30, I2C_EEPROM_ADDRESS, RXdata, 4); ESTOP0; r = 0; while(r<=1) { I2C_status = I2CA_WriteData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS+r*64, TXdata, 64); DEVICE_DELAY_US(WRITE_DELAY_TIME); //wait for 5ms for max write time for EEPROM i'm using r++; } I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS, RXdata, 128); ESTOP0; TXdata[2] = 0; I2C_status = I2CA_WriteData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS, TXdata, 1); DEVICE_DELAY_US(WRITE_DELAY_TIME); //wait for 5ms for max write time for EEPROM i'm using I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS, RXdata, 1); TXdata[2] = 1; TXdata[3] = 2; I2C_status = I2CA_WriteData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS+1, TXdata, 2); DEVICE_DELAY_US(WRITE_DELAY_TIME); //wait for 5ms for max write time for EEPROM i'm using I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS, RXdata, 3); TXdata[2] = 3; TXdata[3] = 4; TXdata[4] = 5; TXdata[5] = 6; I2C_status = I2CA_WriteData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS+3, TXdata, 4); DEVICE_DELAY_US(WRITE_DELAY_TIME); //wait for 5ms for max write time for EEPROM i'm using I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS, RXdata, 7); ESTOP0; r = 2; while(r<=66) { TXdata[r] = 100 + r - 2; RXdata[r] = 0; r++; } I2C_status = I2CA_WriteData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS+64, TXdata, 64); DEVICE_DELAY_US(WRITE_DELAY_TIME); //wait for 5ms for max write time for EEPROM i'm using I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS, RXdata, 128); ESTOP0; I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS+3, RXdata, 10); I2C_status = I2CA_WriteData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS+128, TXdata, 64); DEVICE_DELAY_US(WRITE_DELAY_TIME); //wait for 5ms for max write time for EEPROM i'm using I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS, RXdata, 128); I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR, I2C_EEPROM_ADDRESS+128, RXdata, 128); I2C_status = I2CA_ReadData(I2C_SLAVE_ADDR+10, I2C_EEPROM_ADDRESS+128, RXdata, 17); ESTOP0; } // // I2CA_Init - Initialize I2CA settings // void I2CA_Init(void) { I2caRegs.I2CSAR.all = 0x0050; // Slave address - EEPROM control code I2caRegs.I2CPSC.all = 11; // Prescaler - need 7-12 Mhz on module clk //I2C Module clock = 100 MHz / (9+1) = 10 MHz I2caRegs.I2CCLKL = 45; // NOTE: must be non zero //I2C configured in standard mode I2caRegs.I2CCLKH = 45; // NOTE: must be non zero I2caRegs.I2CIER.all = 0x24; // Enable SCD & ARDY __interrupts I2caRegs.I2CMDR.all = 0x0020; // Take I2C out of reset // Stop I2C when suspended I2caRegs.I2CFFTX.all = 0x6000; // Enable FIFO mode and TXFIFO I2caRegs.I2CFFRX.all = 0x2040; // Enable RXFIFO, clear RXFFINT, return; } // // I2CA_WriteData - Transmit I2CA message // Uint16 I2CA_WriteData(Uint16 SlaveAddress, Uint16 address, Uint16 *txbuffer, Uint16 NumOfBytes) //Uint16 I2CA_WriteData(uint16_t SlaveAddress, Uint16 address) { Uint16 status; Uint16 numofSixteenByte = (2+NumOfBytes) / I2C_FIFO_LEVEL; Uint16 remainingBytes = (2+NumOfBytes) % I2C_FIFO_LEVEL; status = checkBusStatus(); if(status) { return status; } // // Setup slave address // I2caRegs.I2CSAR.all = SlaveAddress; txbuffer[0] = address >> 8; txbuffer[1] = (address) & 0xFF; I2caRegs.I2CCNT = 2+NumOfBytes; I2caRegs.I2CMDR.all = 0x6E20; F28x_usDelay(3000); status = handleNACK(); if(status) { return status; } Uint16 count,i; count = 0; while(count < numofSixteenByte) { for(i=1;i<=I2C_FIFO_LEVEL;i++) { I2caRegs.I2CDXR.all = *(txbuffer); txbuffer++; status = handleNACK(); if(status) { return status; } } count++; while(I2caRegs.I2CFFTX.bit.TXFFST); } //I2caRegs.I2CCNT = remainingBytes; for (i=0; i < remainingBytes; i++) { I2caRegs.I2CDXR.all = *(txbuffer); txbuffer++; status = handleNACK(); if(status) { return status; } } while(I2caRegs.I2CMDR.bit.STP); // // Send start as master transmitter // status = I2C_SUCCESS; return status; } // // I2CA_ReadData - Read I2CA Message // Uint16 I2CA_ReadData(uint16_t SlaveAddress, uint16_t address, uint16_t *rxbuffer, uint16_t NumOfBytes) { Uint16 status; Uint16 numofSixteenByte = NumOfBytes / I2C_FIFO_LEVEL; Uint16 remainingBytes = NumOfBytes % I2C_FIFO_LEVEL; status = checkBusStatus(); if(status) { return status; } // // Setup slave address // I2caRegs.I2CSAR.all = SlaveAddress; // // Setup number of bytes to send // MsgBuffer + Address // I2caRegs.I2CCNT = 2; //Additional 2 bytes for transmitting eeprom address // // Setup data to send // I2caRegs.I2CDXR.all = address >> 8; I2caRegs.I2CDXR.all = (address) & 0xFF; I2caRegs.I2CFFTX.bit.TXFFINTCLR = 1; I2caRegs.I2CMDR.all = 0x6620; F28x_usDelay(5000); status = handleNACK(); if(status) { return status; } remainingBytes = NumOfBytes % I2C_FIFO_LEVEL; while(I2caRegs.I2CFFTX.bit.TXFFST); I2caRegs.I2CCNT = NumOfBytes; // Setup how many bytes to expect I2caRegs.I2CMDR.all = 0x6C20; // Send restart as master receiver Uint16 count,i; count = 0; while(count < numofSixteenByte) { status = handleNACK(); if(status) { return status; } count++; while(!(I2caRegs.I2CFFRX.bit.RXFFST == 16)); for(i=0; i<I2C_FIFO_LEVEL; i++) { *rxbuffer = I2caRegs.I2CDRR.all; rxbuffer++; } } while(!(I2caRegs.I2CFFRX.bit.RXFFST == remainingBytes)); for(i=0; i<remainingBytes; i++) { *rxbuffer = I2caRegs.I2CDRR.all; rxbuffer++; } status = handleNACK(); if(status) { return status; } while(I2caRegs.I2CMDR.bit.STP); return I2C_SUCCESS; } Uint16 checkBusStatus(void) { if(I2caRegs.I2CMDR.bit.STP == 1) { // ESTOP0; return I2C_STP_NOT_READY_ERROR; } if(I2caRegs.I2CSTR.bit.BB == 1) { ESTOP0; return I2C_BUS_BUSY_ERROR; } if(I2caRegs.I2CSTR.bit.NACK) { ESTOP0; return SLAVE_NOT_READY_ERROR; } return 0; } Uint16 handleNACK(void) { if(I2caRegs.I2CSTR.bit.NACK) { I2caRegs.I2CSTR.bit.NACK = 1; //Clear NACK bit to receive future NACKs I2caRegs.I2CFFTX.bit.TXFFRST = 0; //Reset TX FIFO to flush FIFO I2caRegs.I2CMDR.bit.STP = 1; //Generate STOP condition I2caRegs.I2CFFTX.bit.TXFFRST = 1; //Re-enable TX FIFO return SLAVE_NOT_READY_ERROR; } return 0; } // // i2c_int1a_isr - I2CA ISR // __interrupt void i2c_int1a_isr(void) { // Uint16 IntSource; // // Read __interrupt source // // IntSource = I2caRegs.I2CISRC.all; // // Enable future I2C (PIE Group 8) __interrupts // PieCtrlRegs.PIEACK.all = PIEACK_GROUP8; } // // pass - Halt debugger and signify pass // void pass() { __asm(" ESTOP0"); for(;;); } // // fail - Halt debugger and signify fail // void fail() { __asm(" ESTOP0"); for(;;); } // // End of file // /* void i2c_init_old(void) { //Assign GetWordData to the I2C-A version of the //function. GetWordData is a pointer to a function. //GetWordData = I2C_GetWord; I2C_GPIO_confi(); //configure the GPIO for I2C EALLOW; //CpuSysRegs.PCLKCR9.bit.I2C_A = 1; // Turn I2C module clock on // EDIS; // Initialize I2C in master transmitter mode I2caRegs.I2CFFRX.bit.RXFFIL = 3; I2caRegs.I2CSAR.all= 0x68; //50// Slave address - EEPROM control code I2caRegs.I2CPSC.all =0X14; //9 I2C clock should be between 7Mhz-12Mhz 50MHz/6=8.33 I2caRegs.I2CCLKL = 40; //43 // Pre scalers set for 100kHz bit rate I2caRegs.I2CCLKH = 40; //43 // At a 10Mhz I2C clock I2caRegs.I2CMDR.all = 0x0620; // Master transmitter // Take I2C out of reset // Stop when suspended I2caRegs.I2CFFTX.all = 0x6000; // Enable FIFO mode and TXFIFO I2caRegs.I2CFFRX.all = 0x2000; // Enable RXFIFO EDIS; return; } */ void I2C_GPIO_confi(void) { GPIO_SetupPinOptions(104, GPIO_INPUT, (GPIO_ASYNC | GPIO_PULLUP)); GPIO_SetupPinMux(104, GPIO_MUX_CPU1, 1); GPIO_SetupPinOptions(105, GPIO_INPUT, (GPIO_ASYNC | GPIO_PULLUP)); GPIO_SetupPinMux(105, GPIO_MUX_CPU1, 1); } /* * main.c * * Created on: 31-Aug-2020 * Author: shakti */
Regards,
Manoj
Hope you're doing great as well Manoj!
Let me look over this and I'll report back
:)
Trey
Ok I ran the test case you sent and I will concede that SOMETIMES the state machine will allow you to send a repeated start condition. I still think there is some weird behavior that at the very least I need clarification on.
Lets look at my ack polling function:
bool i2cAckPoll(uint16_t slaveAddr, uint16_t maxNack) { uint16_t status = I2C_STS_NO_ACK; uint16_t nackCount = 0; if(i2cState != I2C_IDLE) { return false; } if(I2C_getStopConditionStatus(I2CA_BASE)) { return false; } // // Setup slave address // I2C_setSlaveAddress(I2CA_BASE, slaveAddr); // // Check if bus busy // if(I2C_isBusBusy(I2CA_BASE)) { return false; } i2cState = I2C_ACKPOLL; nackCount = 0; I2C_setDataCount(I2CA_BASE, 0); I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE ); I2C_disableInterrupt(I2CA_BASE, I2C_INT_ADDR_SLAVE | I2C_INT_STOP_CONDITION | I2C_INT_TX_DATA_RDY | I2C_INT_RX_DATA_RDY | I2C_INT_REG_ACCESS_RDY | I2C_INT_NO_ACK | I2C_INT_ARB_LOST | I2C_INT_TXFF | I2C_INT_RXFF); I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_ADDR_SLAVE | I2C_INT_STOP_CONDITION | I2C_INT_TX_DATA_RDY | I2C_INT_RX_DATA_RDY | I2C_INT_REG_ACCESS_RDY | I2C_INT_NO_ACK | I2C_INT_ARB_LOST); while(status & I2C_STS_NO_ACK) { status = 0; // // Setup dummy transfer // I2C_sendStartCondition(I2CA_BASE); //wait for slave address to be sent out //TODO: add timeout mechanism while(!(status & I2C_STS_REG_ACCESS_RDY)) { status = I2C_getStatus(I2CA_BASE) & (~I2C_STS_BYTE_SENT); } if(!(status & I2C_STS_NO_ACK)) { i2cState = I2C_IDLE; return true; } I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_ADDR_SLAVE | I2C_INT_STOP_CONDITION | I2C_INT_TX_DATA_RDY | I2C_INT_RX_DATA_RDY | I2C_INT_REG_ACCESS_RDY | I2C_INT_NO_ACK | I2C_INT_ARB_LOST); DELAY_US(500); nackCount++; if(nackCount > maxNack) { I2C_sendStopCondition(I2CA_BASE); i2cState = I2C_IDLE; return false; } } i2cState = I2C_IDLE; return true; }
I've modified this from my working code that uses repeat mode to show the issue. If you run this with a valid slave attached, the peripheral will address the slave and the slave will ack, but the ARDY bit is never set. If I change the count and add a dummy byte to be transferred, then ARDY gets set correctly.
I use the ARDY bit in order to tell when the slave addressing is complete and the status of the nack bit is valid. To expand on my above observations. When I turn on repeat mode, the ARDY bit is set after slave addressing and using it as a time reference to sample the nack bit works perfectly. I feel like the behavior of this bit should be the same no matter what mode the peripheral is in.
Trey
Trey,
I've modified this from my working code that uses repeat mode to show the issue. If you run this with a valid slave attached, the peripheral will address the slave and the slave will ack, but the ARDY bit is never set. If I change the count and add a dummy byte to be transferred, then ARDY gets set correctly.
I use the ARDY bit in order to tell when the slave addressing is complete and the status of the nack bit is valid. To expand on my above observations. When I turn on repeat mode, the ARDY bit is set after slave addressing and using it as a time reference to sample the nack bit works perfectly. I feel like the behavior of this bit should be the same no matter what mode the peripheral is in.
Your observation is right.
In non repeat mode (RM = 0), ARDY gets set only when I2CCNT not equal to 0.
In repeat mode (RM = 1), ARDY gets set even when I2CCNT = 0. In repeat mode, value on I2CCNT doesn't matter. So, it actually sets ARDY. That is just the way it has been implemented.
Regards.
Manoj
Ok, great! I think we're getting somewhere. First things first, I feel like this difference in behavior should be documented in the TRM. Wouldn't you agree?
With the above observation established, this would lead me to believe there is no good way to do 0 byte transmits for the purpose of ACK polling EXCEPT for repeat mode. Would you agree with that? If so, given how ack polling is commonly used for EEPROM interaction don't you feel this should be documented in the TRM?
I'm going to continue going through my code that is working with repeat mode and change it back to non-repeat mode and report my findings. I still feel like there was some additional undocumented behavior that should be in the TRMs.
Thanks Manoj!
Trey
Trey,
I agree this difference in behavior needs to be documented and I have already filed a JIRA ticket for it.
Regards,
Manoj
Good morning! I think I found another statement in the TRM which is false.
So I've reverted my ack poll code back to using repeat mode since it appears that is the only way to to 0 byte transfers. When this is successful it keeps control of the bus and doesn't issue a stop condition. Immediately after this I start preparing to actually send my data to the slave device.
Section 24.3.4
"When the BB bit is set to 1 and the STT bit is set to 1, a repeated START condition is generated."
When my ack poll code exits, the BB is set to 1 which based on the above statement would indicate I can send a start condition and re-address the slave. When repeat mode is on, I am able to. When repeat mode is off, I am unable to. Here's a quick look at the transmit code:
// // Setup slave address // I2C_setSlaveAddress(I2CA_BASE, 0x51); // // Send start as master transmitter // I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);// | I2C_REPEAT_MODE); I2C_setDataCount(I2CA_BASE, (currentMsg.numBytes*2) + 2); I2C_clearStatus(I2CA_BASE, I2C_INT_ADDR_SLAVE | I2C_INT_STOP_CONDITION | I2C_INT_TX_DATA_RDY | I2C_INT_RX_DATA_RDY | I2C_INT_REG_ACCESS_RDY | I2C_INT_NO_ACK | I2C_INT_ARB_LOST); I2C_sendStartCondition(I2CA_BASE); I2C_sendStopCondition(I2CA_BASE); // // Setup data to send // I2C_putData(I2CA_BASE, currentMsg.memoryHighAddr); I2C_putData(I2CA_BASE, currentMsg.memoryLowAddr); //While there is space in the fifo and we have data available txCount = 0; while((I2C_getTxFIFOStatus(I2CA_BASE) < I2C_FIFO_TX15) && (txCount < currentMsg.numBytes)) { I2C_putData(I2CA_BASE, __byte((int *)&(currentMsg.msgBuffer[txCount]),0)); I2C_putData(I2CA_BASE, __byte((int *)&(currentMsg.msgBuffer[txCount]),1)); txCount++; } I2C_enableInterrupt(I2CA_BASE, I2C_INT_STOP_CONDITION | I2C_INT_TXFF | I2C_INT_NO_ACK); I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_TXFF);
What actually ends up happening is the data is sent out as expected but the slave address isn't. To illustrated this, I changed the slave address to 0x51 (doesn't exist on the bus) and the transfer still occurs because it relies on the address done during the ack poll. This is why I was originally saying repeated starts aren't allowed unless repeat mode is turned on.
What do you think? Feature? Bug? Documentation shortcoming?
Best,
Trey
Trey,
I'm not sure whether I completely understand the problem. But, did you check the status of I2CSTR.XSMT? Is there an underflow condition (XSMT = 0) when you are trying to generate repeated start condition. In my setup, I'm not able to generate repeated start condition when there is a pending underflow condition in non-repeat mode.
In non repeat mode, I2C master number of bytes to be transmitted depends on I2CCNT. It would not allow you generate repeated start condition until you finish transmitting I2CCNT number of bytes. Once you finish transmitted I2CCNT bytes and if the I2CMDR.STT is set to 1, then repeated start condition automatically gets generated.
In repeat mode, number of bytes to be transmitted / received doesn't depend upon I2CCNT and I2C state machine allows you generate repeated start condition even on pending underflow condition (XSMT = 0).
Again, this information is missing in TRM and I will update this information in next release of TRM.
Thanks for bringing these corner cases, really helpful.
Regards,
Manoj
The problem is repeated start isn't sent in some cases. You brought up a good point about underflow, but that still doesn't make a difference.
For this test/observation, I moved the transmit buffer writes for the eeprom memory address (not the slave address) before I set the start bit in MDR. This should prevent the underflow condition. See below:
I2C_setSlaveAddress(I2CA_BASE, 0x51); // // Send start as master transmitter // I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);// | I2C_REPEAT_MODE); I2C_setDataCount(I2CA_BASE, (currentMsg.numBytes*2) + 2); // // Setup data to send // I2C_putData(I2CA_BASE, currentMsg.memoryHighAddr); I2C_putData(I2CA_BASE, currentMsg.memoryLowAddr); //Start condition sent here
All of the above happens immediately after the ack poll code I posted previously. Based on what you've said, a repeated start condition should be generated in this case, but it is not.
The Write 0x50 is the ack poll which is succeeding. Then to illustrate my point, I've changed the desired slave address to 0x51 (doesn't exist on the bus). If your assertions about the repeated start are correct, I should see a repeated start (and subsequent NACK) on the bus, but I do not.
Does that better illustrate the problem?
Trey
Trey,
I think I have figured out the problem.
In non-repeat mode, when I2CCNT = 0: it actually means number of bytes to transmit is 65536 before it generates the STOP condition. I tried this out on silicon and it is true. We have documented this vaguely in register bit-field description. I will make sure to clean this up in future TRMs.
So, I2C state machine doesn't allow you generate another START condition until 65536 bytes are sent.
Regards,
Manoj
Manoj,
Good point. I had not seen that in the CNT register. However, I don't think that explains this behavior. In the above example with the scope capture, I am setting the CNT register to a reasonable value after I have turned off repeat mode... so shouldn't a repeated start be allowed?
Trey
Trey,
What actually ends up happening is the data is sent out as expected but the slave address isn't. To illustrated this, I changed the slave address to 0x51 (doesn't exist on the bus) and the transfer still occurs because it relies on the address done during the ack poll. This is why I was originally saying repeated starts aren't allowed unless repeat mode is turned on.
In your earlier post, you had mentioned the above quote. I believe this condition is happening because of the following reason:
I2CCNT = 0 (Number of bytes to be transferred is 65536)
1st START condition generated.
Now, I2C state machine will expect 65536 bytes to be transmitted. In your case you transmit only SLAVE address and then move on to below code.
I2C_setSlaveAddress(I2CA_BASE, 0x51);
I2C_setDataCount(I2CA_BASE, (currentMsg.numBytes*2) + 2);
I2C_putData(I2CA_BASE, currentMsg.memoryHighAddr);
I2C_putData(I2CA_BASE, currentMsg.memoryLowAddr);
Manoj Santha Mohan said:Now, I2C state machine will expect 65536 bytes to be transmitted. In your case you transmit only SLAVE address and then move on to below code.
I agree with this but I am doing the zero byte transfer in repeat mode. The problem comes when I turn off repeat mode while I still have control of the bus.
Right now I'm doing the zero byte transfer in repeat mode, waiting for it to finish, and then setting count to 0x42 before turning off repeat mode and setting the start bit. I've tried I both by setting count before changing to non-repeat mode and after changing to non-repeat mode.
To me it seems like this is an undocumented limitation of the peripheral. If busy bit is set and non-repeat mode is disabled it is impossible to send another start condition.
I'm happy to share the test code I'm using so you can see this yourself, but would prefer to not put it on the forums. Chris Clearman has my email address or you can DM me on here.
Trey,
Right now I'm doing the zero byte transfer in repeat mode, waiting for it to finish, and then setting count to 0x42 before turning off repeat mode and setting the start bit. I've tried I both by setting count before changing to non-repeat mode and after changing to non-repeat mode.
To me it seems like this is an undocumented limitation of the peripheral. If busy bit is set and non-repeat mode is disabled it is impossible to send another start condition.
I looked into your code and replicated above discussed issue on silicon. I need to confirm the behavior and hypothesis with design. But, here is what I think is happening.
Once a START condition is generated with repeat mode enabled (RM = 1), I2C state machine doesn't allow you to switch to non-repeat mode (RM = 0) and generate repeated START until you generate STOP condition to close out repeat mode transaction completely. This information is clearly missing in TRM and needs update.
I have commented on the I2C code in blue on how I2C machine behaves
I2C_setConfig(base, (I2C_MASTER_SEND_MODE|I2C_REPEAT_MODE));
I2C_setDataCount(I2CA_BASE, 0);
I2C_sendStartCondition(I2CA_BASE); //1st START condition
START condition + Slave address gets generated
After ARDY bit gets set
I2C_setConfig(base, (I2C_MASTER_SEND_MODE));
I2C_setDataCount(I2CA_BASE, non-zero value);
I2C_sendStartCondition(I2CA_BASE); //Repeated START doesn't get generated here
I2C_putData(base, byte1); //Since STOP condition is not generated. I2C statemachine is still in repeat mode (RM = 1)
//I2C transmits byte1. I2CCNT doesn't matter as I2C statemachine is still in repeat mode (RM = 1)
I2C_putData(base, byte2); //I2C transmit byte2
I2C_sendStopCondition(base); //STOP condition doesn't take effect as I2C state machine wants repeat mode bit enabled (RM =1)
I2C_putData(base, byte3); //I2C transmit byte3. I2C state machine still in repeated mode (RM = 1)
I2C_setConfig(base, (I2C_MASTER_SEND_MODE|I2C_REPEAT_MODE)); //STOP condition gets generated here as repeat mode bit is set (RM=1).
Regards,
Manoj
Trey,
I have flagged design team on above issue. I expect them to confirm the above hypothesis and we shall go ahead and document this behavior in TRM.
Regards,
Manoj