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.

DK-TM4C129 temperature sensor I2C

Other Parts Discussed in Thread: TMP100

Hi Amit,

I'm writing this new post as my old post is probably lost. I'm back after few days.

I researched lot regarding the I2C_MASTER_CMD_BURST_SEND_START and other similar commands and came to know about many things. 

I understood that we have to issue start and stop condition only once. In between that, you can write various registers. I modified my code accordingly. One more thing is that, TMP100 doesnt have alert pin. Temp high and temp low register are specially used for alert function. Since we have TMP100 on board, do I really need to set the high and low temperature limit? I dont think so. But please confirm it. 

Another thing is that, on what edge I2C captures data? I read the datasheet and it doesnt say anything. I tried looking the waveforms in oscilloscope and it looks like data is captured in falling edge. Please clarify this thing.

It looks like my code is not reliable, in the sense that sometimes it gives me correct reading and sometimes it shows me constant value 1B. I live in toronto and right now its under 10C. Please take a look at the code and any suggestions are welcomed.

void main()
{
    uint8_t data_low, data_high;
    uint16_t data;
    uint32_t sys_clock;

    sys_clock = SysCtlClockFreqSet(SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_25MHZ, 25000000);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C6);
    while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_I2C6)));
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)));

    GPIOPinConfigure(GPIO_PB6_I2C6SCL);
    GPIOPinConfigure(GPIO_PB7_I2C6SDA);

    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_6);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_7);

    I2CMasterInitExpClk(I2C6_BASE, sys_clock, false);
    I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);

    /*  Configuration Register */
    I2CMasterDataPut(I2C6_BASE, 0x01);
    I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    while(I2CMasterBusy(I2C6_BASE));

    I2CMasterDataPut(I2C6_BASE, 0xA1);
    I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    while(I2CMasterBusy(I2C6_BASE));

    /*
    /  T High Register /
    I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
    I2CMasterDataPut(I2C6_BASE, 0x02);
    I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
    while(I2CMasterBusy(I2C6_BASE));

    I2CMasterDataPut(I2C6_BASE, 0x28);
    I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
    while(I2CMasterBusy(I2C6_BASE));

    /  T Low Register /
    I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
    I2CMasterDataPut(I2C6_BASE, 0x03);
    I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
    while(I2CMasterBusy(I2C6_BASE));
    
    I2CMasterDataPut(I2C6_BASE, 0x28);
    I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    while(I2CMasterBusy(I2C6_BASE));    */


    while(1)
    {
        I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
        I2CMasterDataPut(I2C6_BASE, 0x00);  // selects temp register
        I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(I2CMasterBusy(I2C6_BASE));

        I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, true);  // true indicates that master is initiating the receive.
        I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
        while(I2CMasterBusy(I2C6_BASE));
        data_high = I2CMasterDataGet(I2C6_BASE);
        while(I2CMasterBusy(I2C6_BASE));
        I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
        data_low = I2CMasterDataGet(I2C6_BASE);

        data = (data_high << 8) | data_low;

    }

}

  • Hello Spandan89

    Your old post was not lost. Even though the ALERT pin is not there on TMP100, it has the Alert bit in the configuration/status register that can be polled by the uC. So you can still set the high low limits and do periodic polls of the register.

    Secondly the I2C data is captured at the high level of the SCL. Now looking at the code it seems to be 10-bit resolution which would be for 0x1B as 13.5C. so could it be that there is a possible heat source around the device or the ambient temp around the device is 13.5C. Also what reading do you get which would classify it as "correct temp"

    Regards

    Amit

  • Hi Amit,

    Thanks for getting back. How did you get 13.5C? According to my understanding, 0x1B = 27 (DECIMAL). So if you multiply 27*0.25=6.25C. 

    To investigate further in deep, I will now just send the slave address and see if I'm getting the ACK or not. I read somewhere that slave might go out of sync and will not send ACK if operating at higher speed.

    One more thing regarding the slave address. It says its address is 0x4A. Now First 7 bits are 100_1010 = 0x4A. Now if I append R/W bit 0 to 100_1010, it becomes 100_1010_0 = 0x94. Please clarify this thing. According to TMP100 datasheet (timing diagram for write cycle), it should be 0x94.

    Also According to Table 13(pg 10) from TMP100 datasheet, I think slave cant go beyond 0.4MHz. If this is the case then I cant choose 25MHz crystal on board. Am I right? 

  • Hello Spandan89,

    My bad. I read the Resolution as 0.5C instead of 0.25C.

    When sending the Slave Address, the value programmed is left shifted by 1. hence when you program 0x4A it will be shifted in as 0x94 to which the R/W bit will be OR-ed/

    The equation to get the MTPR = (System Clock/(2*10*Baud))-1

    So with System Clock of 25MHz and Baud of 400KHz the value of programmed MTPR works out as 0x3 which is a valid. So it is possible to get the required frequency on I2C Bus Side.

    Regards

    Amit

  • Hi Amit,

    So you mean to say that Tiva ware will automatically shift the slave address for me? Oh yeah, I just checked the Slaveaddressset() function and that function shifts it and does OR with "bReceive". Thanks

    I will check the waveform to see if Slave is sending me the ACK or not. I think its not sending me that ACK at all.

    Thanks

  • Hello Spandan89,

    If you have written 0x94 as the address instead of 0x4A then it will send a NACK.

    Regards

    Amit

  • Hi Amit,

    I finally figured out whats the problem.

    What I did is following:

    - I just send data to config register as follows.

    	while(1){
    
    	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
    
    	/*  Configuration Register */
    	I2CMasterDataPut(I2C6_BASE, 0x01);
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    	while(I2CMasterBusy(I2C6_BASE));
    
    	I2CMasterDataPut(I2C6_BASE, 0xA1);
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    	while(I2CMasterBusy(I2C6_BASE));
    	while(I2CMasterBusy(I2C6_BASE));
    
    	}

    - Then using breakpoints and oscilloscope I came to know that master is too fast for the slave. So I put some delay in between and slave was able to behave properly.

    I remember you saying that TIVA has known side effect operating at higher frequencies.

    Is there any other solution apart from using delay? Is there something using interrupt? I mean whenever slave is ready I get an interrupt or something like that....

    Thanks again for all your help.

    Roank

  • Hello Spandan89,

    You can use the interrupt bit 0 to do the same. In fact the original code I had sent had the two complementary while loops.

    Regards

    Amit

  • Hi Amit,

    I was playing with the code to make sure that whatever i sent i read it back correctly. I noticed that when I put a breakpoint at the 2nd I2CMasterDataput(), it always works. It also updates the data_low value. But If i remove the breakpoint from there it remembers the last value and data_low doesnt show me the new value.

    First I thought I had to put delay, so I did put delay before and after of 2nd I2CMasterDataput() as you can see in the code below.

    Can you try the code and suggest me something to understand this behavior?

    	while(1){
    
    	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
    
    	/*  Configuration Register */
    	I2CMasterDataPut(I2C6_BASE, 0x01);
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    	while(I2CMasterBusy(I2C6_BASE));
    	SysCtlDelay(50);
    
    	I2CMasterDataPut(I2C6_BASE, 0xA0);   // if put breakpoint here, it always works.
    	SysCtlDelay(50);
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    	while(I2CMasterBusy(I2C6_BASE));
    
    	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, true);  // true indicates that master is initiating the receive.
    	I2CMasterDataPut(I2C6_BASE, 0x01);                  // put second breakpoint here
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    
    	while(I2CMasterBusy(I2C6_BASE));
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
    
    	while(I2CMasterBusy(I2C6_BASE));
    	data_low = I2CMasterDataGet(I2C6_BASE);   // put third breakpoint here.
    
    	}
    }
    

  • Hello Spandan89,

    The following code which works on my setup does a little more optimized read out of the temperature and prints it out on a Terminal Window.

    5287.TM4C129_I2C_TMP100.c
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_i2c.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/gpio.h"
    #include "driverlib/i2c.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    
    /* OS/ALERT bit will start the conversion. While setting the config register I set the OS/ALERT bit to be 1. So it will start the conversion.
        OR
        We can set the config register later. */
    #define SLAVE_ADDRESS 0x4A
    
    //*****************************************************************************
    //
    // Configure the UART and its pins.  This must be called before UARTprintf().
    //
    //*****************************************************************************
    void
    ConfigureUART(void)
    {
        //
        // Enable GPIOA
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Enable UART0
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Use the internal 16MHz oscillator as the UART clock source.
        //
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, 16000000);
    }
    
    int
    main(void)
    {
        uint16_t data_low, data_high;
        uint32_t sys_clock;
        sys_clock = SysCtlClockFreqSet(SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_25MHZ, 25000000);
    
        ConfigureUART();
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C6);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        SysCtlDelay(10);
    
        GPIOPinConfigure(GPIO_PB6_I2C6SCL);
        GPIOPinConfigure(GPIO_PB7_I2C6SDA);
        GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_6);
        GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_7);
        I2CMasterInitExpClk(I2C6_BASE, sys_clock, false);
    
        UARTprintf("Initialized I2C-6 peripheral.\n");
    
        /*  Configuration Register */
        I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
        I2CMasterDataPut(I2C6_BASE, 0x01);                  //P1 P0 = 01 = configuration reg. Byte goes to PR
        I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(!(I2CMasterBusy(I2C6_BASE)));
        while(I2CMasterBusy(I2C6_BASE));
        I2CMasterDataPut(I2C6_BASE, 0xA0);      //sets the bits of configuration reg. A1 = 10 bit, shutdown, OS=1 start conv. Byte goes to Config Reg
        I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
        while(!(I2CMasterBusy(I2C6_BASE)));
        while(I2CMasterBusy(I2C6_BASE));
        I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, true);  // true indicates that master is initiating the receive.
        I2CMasterDataPut(I2C6_BASE, 0x01);                  //P1 P0 = 01 = configuration reg. Byte goes to PR
        I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(!(I2CMasterBusy(I2C6_BASE)));
        while(I2CMasterBusy(I2C6_BASE));
        I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
        while(!(I2CMasterBusy(I2C6_BASE)));
        while(I2CMasterBusy(I2C6_BASE));
        data_low = I2CMasterDataGet(I2C6_BASE);
    
        UARTprintf("Data is %x\n",data_low);
    
        while(1)
        {
        	/*  Read Temp Conv Register */
        	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
        	I2CMasterDataPut(I2C6_BASE, 0x00);                  //P1 P0 = 00 = Temp Conversion
        	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        	while(!(I2CMasterBusy(I2C6_BASE)));
        	while(I2CMasterBusy(I2C6_BASE));
        	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, true);  // true indicates that master is initiating the receive.
        	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
        	while(!(I2CMasterBusy(I2C6_BASE)));
        	while(I2CMasterBusy(I2C6_BASE));
        	data_high = I2CMasterDataGet(I2C6_BASE);
        	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
        	while(!(I2CMasterBusy(I2C6_BASE)));
        	while(I2CMasterBusy(I2C6_BASE));
        	data_low = I2CMasterDataGet(I2C6_BASE);
        	data_low = (data_low >> 6);
        	data_high = (data_high << 2);
        	data_high = data_high | data_low;
    
        	UARTprintf("Raw Data: %d Temp: %03d.%03d\n",data_high,(data_high/4),(data_high%4));
        }
    
    }
    

    You can see the manner in which I have implemented the wait for a command completion.

    Regards

    Amit

  • Hi Amit,

    Its very clever way to implement the delay using 2 complemented while loops. With that its works perfectly fine. I also checked the waveforms and they look good so far. I run the program data_high is always 0x1C(28) or 0x1B(27) and data_low changes, which is expected.

    when I calculate the the final temperature , it comes around 28C or 27C which i believe is the room temperature currently. However its not that much accurate.

    Just to confirm the reliability of the code, I will further make some changes and conduct testing. I will also share the code for others like me. 

    Thanks for all the help. Learned the great deal with you.

    Thanks

    Ronak

  • Hello Spandan89,

    The two complementary loops actually besides adding the delay ensure that if after writing the START+STOP+RUN bit the Master Busy is not set, it will wait for it to be set and then wait for it to clear. The only drawback with this if an interrupt from another peripheral occurs, it may get stuck in the loop.

    Regards

    Amit

  • Hi All,

    Here is the code that I would like to share with you. I tried my hands on I2C. TIVA4C129 development kit has TMP100 temperature sensor on it. To read it, one has to use I2C protocol.

    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_i2c.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/gpio.h"
    #include "driverlib/i2c.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    
    #define SLAVE_ADDRESS 0x4A
    //static volatile uint16_t data_low;
    
    void main()
    
    {
    
    	uint32_t sys_clock, error;
    	uint16_t data_low,data_high;
    	sys_clock = SysCtlClockFreqSet(SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_25MHZ, 25000000);
    
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C6);
    	while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_I2C6)));
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    	while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)));
    
    	GPIOPinConfigure(GPIO_PB6_I2C6SCL);
    	GPIOPinConfigure(GPIO_PB7_I2C6SDA);
    
    	GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_6);
    	GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_7);
    
    	I2CMasterInitExpClk(I2C6_BASE, sys_clock, false);	//False = 100KHz
    
    
    	while(1){
    
    	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
    
    	/*  Configuration Register */
    	I2CMasterDataPut(I2C6_BASE, 0x01);
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    	//while(I2CMasterIntStatus(I2C0_MASTER_BASE, false) == 0);
    	while(!(I2CMasterBusy(I2C6_BASE)));
    	while(I2CMasterBusy(I2C6_BASE));
    	error = I2CMasterErr(I2C6_BASE);
    	if (I2CMasterErr(I2C6_BASE) != I2C_MASTER_ERR_NONE)
    	{
    		I2CMasterIntClear(I2C6_BASE);	// clear the interrupts if any occured.
    		return false;
    	}
    	SysCtlDelay(50);
    
    	I2CMasterDataPut(I2C6_BASE, 0xA1);
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    	while(!(I2CMasterBusy(I2C6_BASE)));
    	while(I2CMasterBusy(I2C6_BASE));
    	SysCtlDelay(50);
    
    	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);
    	I2CMasterDataPut(I2C6_BASE, 0x00);                  //P1 P0 = 01 = configuration reg. Byte goes to PR
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    	while(!(I2CMasterBusy(I2C6_BASE)));
    	while(I2CMasterBusy(I2C6_BASE));
    	SysCtlDelay(50);
    
    	I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, true);// true indicates that master is initiating the receive.
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
    	while(!(I2CMasterBusy(I2C6_BASE)));
    	while(I2CMasterBusy(I2C6_BASE));
    
    	data_high = I2CMasterDataGet(I2C6_BASE);
    	I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
    	while(!(I2CMasterBusy(I2C6_BASE)));
    	while(I2CMasterBusy(I2C6_BASE));
    	data_low = I2CMasterDataGet(I2C6_BASE);
    	}
    }
    

    I have used it for 12 bit resolution and so data_high is the byte 1 and data_low is byte 2. As mentioned in the datasheet of TMP100, byte2's lower 4 bits will always be 0. I have kept the two byte separately just to understand and to view the waveforms. Its easy to understand that way.

    I am really grateful to Amit for helping me out in this learning process.

    Thanks

  • Hi Amit,

    I have been having issues communicating with the DS3231 real time clock and I think your code is the answer to my problem. Have not had the chance to test yet but will this code work if my system clock is 120MHz?

    Thanks.

    AJ
  • Yes, for system clock at 120MHz and I2C speed at 100kHz.
  • Thanks Charles for the response. I tested this on my RTC and it worked like a charm. I'm glad I found this post. Thanks guys.

    AJ

  • Hi Spandan,

    Would your code still work without the SysCtlDelay(50)? Just wondering why you had to put it. I'm running my micro at 120MHz and without the delay, it still works.
  • I don't think the sysCtlDelay(5) is needed because the below two lines right before it already make sure to wait while the master is first not busy and then busy before advancing to the next I2C operation.

    while(!(I2CMasterBusy(I2C6_BASE)));
    while(I2CMasterBusy(I2C6_BASE));
  • May I note that I was the "likely perpetrator" - suggesting such delay inclusions - which proved useful- when the (long past) LX4F MCUs first arrived.    There exists here (far back) my post which was first to describe a code method - which enabled I2C operations (under Cortex M4) to succeed.    (vendor staff confirmed the correctness of my posting.)

    The "4C129" devices brought added speed - (which (may) have led to a "race" condition) - and was aided greatly (i.e. required) by that check for, "not busy & later, busy."

    As Amit has noted - should an interrupt occur during that, "busy - /busy" window - this (inventive, yet unusual) method may hang...

  • Hi cb1, thanks, point well taken!
  • Hi cb1,

    The "hanging" worries me a bit so I got rid of the !mastrbusy and replaced it instead with a 10us delay. I have not tested a delay less than this but the program still works. Will eliminating the !masterbusy prevent the "hanging" from happening?

    Thanks for your input.

    Regards,
    AJ
  • Hi AJ,

    Firm/I have avoided the x129 series - preferring (other/faster) ARM MCUs.

    The I2C engine w/in that MCU demands (some) special treatment - I know the "busy - /busy" was developed quickly - and under pressure. The admitted fact that - under some conditions - "hang" may result - troubles me.

    The 123 series (which my post targeted) avoided much of this (added) demand - yet (possibly) due to the "timing demands" of our specific "I2C Slave devices" that delay proved worthwhile.

    Might you "excise (remove)" the possibility of I2C "hang" by "timing" the duration that your code "spins" while w/in, "busy - /busy?" In this manner - you are (then) able to detect dreaded "hang" - and most likely escape from its (unwanted) clutches.

    Perhaps Charles or Bob could arrive - comment...