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.

Why is a delay needed before writing the first byte from I2C Master ?

Hi there,

I am sending two bytes of data (0x10 and 0x00) from the Master in burst mode over I2C0.

The following code works fine as long as I insert two calls to SysCtlDelay prior to placing data onto the I2C master bus and sending it (burst mode).   I am able to observe the data being written using an Agilent DSO that has an I2C decoding option.

Why do I need these delays ?  Without it (or without placing a BP  at the line after it) the first byte (0x10 in this case)  is NOT transmitted only the second and subsequent bytes are.

Jump down to the function named I2C_start_measuring to see where the calls to ROM_SysCtlDelay are.

void configure_I2C(void)
{
	SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);

    // I2C0 is used with PortB[3:2].
    // GPIO port B needs to be enabled so these pins can be used.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    //
    // Configure the pin muxing for I2C0 functions on port B2 and B3.
    // This is labelled PB2 and PB3 on the eval board. (PB=PortB)
    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);


    // set PB3 as I2C SDA pin.  the function name omits the word SDA and one needs to read the documentation to discover this.
    // note an earlier version of my code had both pins being set to SDA (when only one should have) then one of the both being set to SCL
    //This call sets the pin/pad to open-drain operation with weak pull up as per p. 1277 of the datasheet
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);

    //Configure PB2 pin for use as SCL by the I2C peripheral.  this sets the pad for push-pull operation (not open drain (OD) as per as per p. 1277 of the datasheet).
    //OD is is usually how I2C works
    //see here for the confusion surrounding this being set to push pull rather than open drain http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/254099.aspx
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE,GPIO_PIN_2);

     //
     // (En/dis)able loopback mode as required.  Loopback mode is a built in feature that is useful for debugging I2C operations.  It internally connects the I2C
     // master and slave terminals, which effectively let's you send data as a master and receive data as a slave.
     //Refer to p. 1057 of E:\Documentation\Device\Datasheet-LM4F232H5QD.pdf  NOTE: For external I2C operation you will need to use external pullups
     // that are stronger than the internal pullups.  Refer to the datasheet for more information.
     //temporarily disable loopback by explicitly setting bit 0 of I2C_O_MCR to 0 (does this code actually work ?!) and commenting out the next line
     //which sets it to 1
     HWREG(I2C0_BASE + I2C_O_MCR) |= 0x00;
     //HWREG(I2C0_BASE + I2C_O_MCR) |= 0x01;


	//Sensor  might use a clock rate of 400kbps , this isn't documented anywhere I could find, even in their example code.
    // so set the last parameter to true for now as If the parameter bFast is true, then the master block is set up to transfer data at 400 Kbps;
    // otherwise, it is set up to transfer data at 100 Kbps
     I2CMasterInitExpClk(I2C0_BASE, ui32SysClock /*SysCtlClockGet()*/, true);
     I2CMasterSlaveAddrSet(I2C0_BASE, SENS1_AIRPATH_FLOW_SLAVE_ADDRESS, false /*true*/);  //false=>Master reads from slave however the first command will be a write !! (it doesn't work for this parameter to be true)



     //wait until the Master is not busy sending
     while(I2CMasterBusy(I2C0_BASE))
     {
     }

     I2CMasterErrorValue=I2CMasterErr(I2C0_BASE);
     if (I2CMasterErrorValue!=I2C_MASTER_ERR_NONE)
     {
     	UARTprintf("\nI2C Master Error: %d\n", I2CMasterErrorValue);
     }


}

void I2C_start_measuring(void)
{


	ROM_SysCtlDelay(150);//without a delay of at least 80 (at 100MHz) the first byte is not decoded by the CRO.
	I2CMasterDataPut(I2C0_BASE, 0x10);//Tx first 8 bits of 0x1000.  sometimes this isn't properly written or decoded by the CRO. Why ?
	I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);// Initiate send of character from Master to Slave
	 while(I2CMasterBusy(I2C0_BASE))
		{
			//wait until bus is not busy
		}
	 //Check for and report on any error
	 I2CMasterErrorValue=I2CMasterErr(I2C0_BASE);
	 if (I2CMasterErrorValue!=I2C_MASTER_ERR_NONE)
	 {
		UARTprintf("\nI2C Master Error: %d\n", I2CMasterErrorValue);
	 }

	 ROM_SysCtlDelay(150);//without a delay of at least 80 (at 100MHz) the first byte is not decoded by the CRO.
	I2CMasterDataPut(I2C0_BASE, 0x00);//Tx second 8 bits of 0x1000
	I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);// Initiate send of character from Master to Slave
	 while(I2CMasterBusy(I2C0_BASE))
		{
			//wait until bus is not busy
		}
	 //Check for and report on any error
	 I2CMasterErrorValue=I2CMasterErr(I2C0_BASE);
	 if (I2CMasterErrorValue!=I2C_MASTER_ERR_NONE)
	 {
		UARTprintf("\nI2C Master Error: %d\n", I2CMasterErrorValue);
	 }

}

int main(void)
{
	   uint32_t TxData[NUMBER_OF_SAMPLES_PER_CHANNEL]; //32 bits of data in each array element however SPI only uses 16 bits
	    uint32_t RxData[NUMBER_OF_SAMPLES_PER_CHANNEL]; //32 bits of data in each array element however SPI only uses 16 bits
	    uint32_t index,mainLoopIndex,SysTickPeriod;//SysTickPeriod is the period to set the SysTick timer.
	    uint32_t CPU_Load,SysTickAfterAllTasksRun=0;//CPU_Load is the CPU load as an integer value (units=%).
	    												//SysTickAfterAllTasksRun it the value the Systick timer has counted down
	    												//to after all tasks in the main loop have run.
	//    uint32_t ui32SysClock;  // system clock setting schieved by SysCtlClockFreqSet  now extern
	    unsigned short TxDataRaw;//SPI bus is only 16 bits wide
	    long ReadDataCount=0;// number of Data bytes read from the Rx read FIFO buffer
	    long WriteDataCount=0;// number of Data bytes able to be written to the Tx write FIFO buffer

	    uint32_t ulDataRx[NUM_SSI_DATA]; //DataRx is an array to receive the data from the AD7606.  Each element contains 32 bits of data.


	    //
	    // Set the clocking to run directly from the external crystal/oscillator.
	    // TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
	    // crystal on your board.
	    // use the following line to run the system clock at 16MHz directly from the external crystal
	    // SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

	    // set the Master clock to be say 50MHz (max is 80MHz) by
	    // running from the external crystal via the PLL.  The PLL runs at 200MHz
	    // Divide the 200MHz signal by 4.0 (SYSCTL_SYSDIV_4).
	    // The main crystal on the TI LM4F eval board is 16MHz
	    // set the Master clock to be 50MHz which is a period of 20ns
	    // SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);  //deprecated

	    // On TM4C129 the function pair of SysCtlClockSet and SysCtlClockGet are now replaced by
	    // SysCtlClockFreqSet which returns the System Clock
	    // set the Master clock to be 100MHz which is a period of 10ns (Max speed is 120MHz and is avoided for now to leave a buffer for future use if needed)
	    ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
	                                           SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
	                                           SYSCTL_CFG_VCO_480), 100000000);

	    // to run at 80MHz use SYSCTL_SYSDIV_2_5
	    //SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);



	    //setup the SysTick timer used to delay until the next time multiple
	    //this method is only being used until a real ADC value is being returned and we can sync off that
	    //If the master clock is set to run at 16MHz  (max 80MHz)
	    //then loading 16,000,000 into the systick period will cause it to wrap around once per second
	    //it is a 24 bit counter capable of counting down from 2^24=16,777,216

	    //SysTickPeriod=(SysCtlClockGet()*INTER_PROCESSOR_SAMPLE_PERIOD_IN_MILLISECONDS)/1000;//be careful of the order of evaluation interim fractional results cause truncation.incorrect values
	    SysTickPeriod=(ui32SysClock*INTER_PROCESSOR_SAMPLE_PERIOD_IN_MILLISECONDS)/1000;//be careful of the order of evaluation interim fractional results cause truncation.incorrect values

	    //i.e. SysTickPeriod=50,000,000 x 50 / 1000 = 2,500,000  for 20Hz bulk sample frequency
	    // or  SysTickPeriod=50,000,000 x 20 / 1000 = 1,000,000  for 50Hz bulk sample frequency
	    SysTickPeriodSet(SysTickPeriod);
	    //this sets the SysTickPeriod to be the number of system clock periods during the BULK_SAMPLE_PERIOD
	    //setting the SysTickPeriod to this means the counter will wrap around that often.
	    //If not using the ADC for timing then this could be used as the interrupt period.
	    //when faster than 16MHz the SysTick counter will wrap around in less than 1 second as it only has 16,777,216 values
	    //at 50MHz the Systick counter will reach


	    // Enable SysTick to generate interrupts.
	    SysTickIntEnable();

	    //the systick counter will wrap around in 1 second at 16MHz, and proportionately shorter at the faster clock speeds.
	    //for this reason when timing, ensure you don't exceed the period of the systick timer

	    //reset the timer to a known value by writing to the NVIC_ST__CURRENT register.
	    //refer to the Stellaris Peripheral Driver Library UG and search for NVIC_ST_CURRENT
	    //this might not be necessary as I think the act of loading a value in sets it to zero.
	    HWREG(NVIC_ST_CURRENT)=0x000000;
	    //the SysTick timer is enabled later on.



	    //
	    // Set up the serial console to use for displaying messages.  This is
	    // just for debugging and is not needed for SPI operation.
	    //
	    InitConsole();
		//init the I2C
		configure_I2C();
		//send start measurement command
		I2C_start_measuring();
		//read flow measurement data
		read_I2C_data();

}

  • Hello Peter,

    On the TM4C129 we discovered a design behavior that due to higher system clock frequency and lower I2C baud rate, the CPU when it starts a I2C Transaction or writes a new I2C Command, for the I2C to respond to it takes time. So in effect the I2CMasterBusy statement gets skipped which causes The I2C controller to lose data.

    This has been documented in the Forum quite a few times. The other workaround if not using interrupt is to use

    while(!(I2CMasterBusy(I2C0_BASE)));

    while(I2CMasterBusy(I2C0_BASE));

    So that the CPU waits for the Command processing to begin before it goes to check if the command is processed.

    Regards

    Amit

  • Thank you Amit.   This solution causes the potential for hangs if an interrupt takes longer than the time taken to write to the I2C bus and I will forget to check for that !  This worked for me thank you.   Yes I could have read this on the forums but given my relative inexperience with I2C it probably would have taken me longer to search for this answer.

    BTW if I can squeeze one more I2C question in (I'm happy to begin a new topic if you like). Is the following command supposed to illicit an ACK or a NACK from the Master ?  I thought it should complete with an ACK but it only gives me a NACK.

    I2CMasterControl(I2C0_BASE,I2C_MASTER_CMD_BURST_RECEIVE_FINISH);

    By way of comparison, 

    I2CMasterControl(I2C0_BASE,I2C_MASTER_CMD_BURST_RECEIVE_CONT)

    finishes with an ACK.

  • Hello Peter,

    In receive mode the Master is receiving data and needs to inform the slave that the transaction is completed. That is why the last transfer is NACK-ed by the Master when the FINISH is used. This would be a part of the I2C Specification.

    Regards

    Amit

  • Thanks Amit.

    BTW I had to smile at the following statement in your reply.  In relation to the need to wait for the bus to first be not busy then busy and not busy again:

    >On the TM4C129 we discovered a design behavior 

    You make it sound like this behaviour was intended !  I reckon most people would instead classify it as a bug :)

    On a less critical note, THANK YOU again for your fast and accurate help. I really appreciate how responsive  you and other TI employees are.  If I wasn't able to get answers so quickly it would make me think twice about using TI's products again.  Support "on tap" is crucial in situations where somebody is first experiencing a new IDE and MCU as I am, so as to not make the experience any more daunting than it has to be.

  • Peter John said:
    discovered a design behavior 

    Peter - you, "Stole my thunder" w/your catch & protest of that MCU's (alleged) "behavior!"  Seems new commandment dawns here, "Thou shall not admit a mistake!"  (we note that murder, other mayhem is also, bit unwelcome, "behavior!")

    Amit's truly outstanding in his dedication & responsiveness - yet we find the local & regional FAEs & Sales offices to be a great, added resource.  (of course it helps if you/firm has a reasonable order book...)

  • :)    never a dull read when cb1 writes.

    If there was a gold star for the most entertaining and colorful contributor I think it would belong to you cb1.  Thank you too for your contributions.

  • Peter John said:
    If there was a gold star

    Be still my (bit aged) heart!  (heartbeat too - registers as a "behavior"...)

    Defending the "indefensible" never fun - never easy.  (we were past taught to "parade" our weaknesses - not hide/bury them - proves a better means to correct...)

    Merci, mon ami.

  • Hello Peter, cb1,

    I appreciate you acknowledgement and not the hammering sometimes that comes on the forum :-)

    As cb1 may agree, that the products may behave strange"r" at times, and we can only offset the same by finding it first and having a reasonable response ready. Bug? could be, Side Effect, yes.

    Regards

    Amit

  • Amit Ashara said:
    This has been documented in the Forum quite a few times.

    If I may gently suggest that since this is the case it is an indication that perhaps it should be documented elsewhere as well?

    The problem with forum searches (and it's not specific to this forum) is that if you do not happen to use the right set of magic keywords you can end up either being overwhelmed by a tidal wave of results or left bereft in a desert dry of results. Consider all the posts where the first half of the conversation is simply coming to a common understanding of what the topic actually is.

    Robert

  • Oh my god, why do i only know of this now. Could TM4C129 I2C stop being stuff in my nightmares?

    This should be in the datasheet without a doubt! and maybe TivaWare peripheral guide. Is it and i'm simply blind? if it isn't please do add this. It's major to get the I2C working since it doesn't really work without it

  • Robert Adsett said:
    If I may gently suggest...it is an indication that ...should be documented elsewhere as well?

    Several here - perhaps most loudly/longest (this reporter) have long requested such, "critical updates" be promoted to formal MCU manual status.  These suggestions/requests always, "Duly noted."  (such notice rarely (i.e. almost never) arrives - a more findable, appropriate, "promised land."  (i.e. where it belongs - MCU manual and errata!)

    As for code listing (Luis) IAR other "more serious" IDEs seem not to accept your "code entry" suggestion...

  • Jeff McDevitt said:

    Does anyone besides me think that this should be documented in the Errata?

    Jeff

    I think so too Jeff.

    cb1- said:

    If I may gently suggest...it is an indication that ...should be documented elsewhere as well?

    Several here - perhaps most loudly/longest (this reporter) have long requested such, "critical updates" be promoted to formal MCU manual status.  These suggestions/requests always, "Duly noted."  (such notice rarely (i.e. almost never) arrives - a more findable, appropriate, "promised land."  (i.e. where it belongs - MCU manual and errata!)

    As for code listing (Luis) IAR other "more serious" IDEs seem not to accept your "code entry" suggestion...

    [/quote]

    This is to post just the simple code that they want to highlight. So many users i like ask something, "can you show me your interrupt handler" and then they simply paste it making it very hard to read. The insert code just does highlights 

  • Really good documentation should be

    • Complete
    • Timely
    • Readily available
    • Easy to understand

     TI in general has done a good job of doing this.  This is a case in which it has failed on the first count.

    It is probably impossible to meet all four criteria with reasonable resource expenditure (and maybe even with unreasonable expenditure) and I would prefer the last be where the sacrifice is made since I can work my way through leaden prose and interpret odd phrasing but I cannot fill in information that simply is not revealed.

    I have noticed a distressing tendency amoung some suppliers to severely scrimp on the first item (to be clear+ not TI that I have noticed), neglecting many basic data items. 

    Those of you have been in the business for a while will no doubt remember the documentation for the original 8255 which never went beyond being flagged preliminary (despite many years of production) and had a basic flaw where it promised to support handshaking but never showed how it was supported*. Imagine how much more difficult it must be to to fully document a much more complex micro.

    TI can certainly improve (making the errata mor visible would be a help, some manufacturers used to append them to updated versions of the data sheets and user manuals) but its documentation is quite good generally.

    Robert

    +Now I feel like Tomson and Thompson

    *It actually wasn't supported, it just didn't prevent you from supporting it. This is where I started to learn how to 'interpret' data sheets. What the front page giveth the remaining pages take away.

  • Don't misunderstand me! I really like TI documentation. Of course it lacks in some things here and there. Some from rushing a bit it seems, but in general it is great, i really like it. The datasheets i feel like they are very complete and i love the TivaWare peripheral guide