cancel
Showing results for 
Search instead for 
Did you mean: 

Reading 2 bytes from ADT7410 via I2C

munger
Associate II
Posted on May 06, 2014 at 04:06

I've been working for a few days to get a STM32F401 Discovery top talk to an external ADT7410 temperature sensor. I'm just about there. I'm able to read a byte at a time from the device. The last thing I'm having a hard time with is reading 16 bits at a time. The device has this capability, I just need to give it the address of the high byte and I can read the low byute right after the high byte. I don't think I quite understand what calls to use in the Standard Periph Library to do this. Here is the code to do this, I left out some of the beginning part to keep it short.

/* Send ADT7410 register address */

I2C_SendData(I2C1,addr);

 

/* Test on I2C1 EV8 and clear it */

timeout = ADT7410_TIMEOUT_MAX; /* Initialize timeout value */

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))

{

/* If the timeout delay is exeeded, exit with error code */

if ((timeout--) == 0) 

{

return ERROR;

}

}

 

  /* Generate the Start Condition */

  I2C_GenerateSTART(I2C1, ENABLE);

 

  /* Test on I2C1 EV6 and clear it */

  timeout = ADT7410_TIMEOUT_MAX; /* Initialize timeout value */

  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))

  {

    /* If the timeout delay is exeeded, exit with error code */

if ((timeout--) == 0) 

{

return ERROR;

}

  } 

 

  I2C_Send7bitAddress(I2C1, ADT7410_ADDRESS, I2C_Direction_Receiver);

 

  /* Test on I2C1 EV6 and clear it */

  timeout = ADT7410_TIMEOUT_MAX; /* Initialize timeout value */

  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))

  {

    /* If the timeout delay is exeeded, exit with error code */

if ((timeout--) == 0) 

{

return ERROR;

}

  }  

 

If I was only doing one byte I would use I2C_Acknowledge here as is shown commented out and that works.

 

 

Is this the right call to use? I think it is based on info from the Reference Manual and from commenst in stm32f4xx_i2c.c.

I2C_NACKPositionConfig(I2C1, I2C_NACKPosition_Next);

  /* Prepare an NACK for the next data received */

  // I2C_AcknowledgeConfig(I2C1, DISABLE);  

 

  /* Test on I2C1 EV7 and clear it */

  timeout = ADT7410_TIMEOUT_MAX; /* Initialize timeout value */

  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))

  {

    /* If the timeout delay is exeeded, exit with error code */

if ((timeout--) == 0) 

{

return ERROR;

}

  }

 

  /* Receive high byte of Data */

  data_high = I2C_ReceiveData(I2C1);

 

// Read low byte of Data

  /* Prepare an NACK for the next data received */

Do I need to call this again?

  I2C_AcknowledgeConfig(I2C1, DISABLE);  

 

  /* Test on I2C1 EV7 and clear it */

  timeout = ADT7410_TIMEOUT_MAX; /* Initialize timeout value */

  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))

  {

    /* If the timeout delay is exeeded, exit with error code */

if ((timeout--) == 0) 

{

return ERROR;

}

  }

 

  /* Receive the Data */

  data_low = I2C_ReceiveData(I2C1);

// Build 16 bit data word

*read_data = (uint16_t) data_high;

*read_data <<= 8;

*read_data += (uint16_t) data_low;

  I2C_GenerateSTOP(I2C1, ENABLE);

  /* return the read data */

  return SUCCESS;

}

Hopefully the comments I made in red and italicized will show up.

I'd appreciate any comments on this.

Thanks.

Mike

8 REPLIES 8
stm322399
Senior
Posted on May 07, 2014 at 11:05

Mike,

The difference between receiving 1 or 2 bytes from an I2C slave, lies in the generation of the NACK, that must happen right after the first byte or after the second byte. That's why you need to set ''NEXT_Position'' when you want to receive 2 bytes.

But, in both cases, you also need to disable ACK generation. In other words regarding your comment before setting NACKPosition, you must do both calls.

Actually when receiving only 1 bytes or 2, the configuration for ACK and POS can be done very early. As an example, between start and send 7-bit address. At least it must be done before acknowledging the receiver mode. Once you acknowledged the receiver mode, the I2C controller starts the transfer, so having properly configured ACK and POS before is safer.

munger
Associate II
Posted on May 07, 2014 at 21:05

Laurent,

Thank you for your response. I have modifed my read function, I configured POS and ACK as you have suggested (I think). It is working okay but ... seems to be clocking in one extra byte which is all ones. A scope trace is enclosed. It seems like a stop bit is not going out at the right time. I'm continuing to work on it, would appreciate any other thoughts.

Mike

ErrorStatus Read_ADT7410_word(uint8_t addr, uint16_t *read_data)

{

uint32_t timeout = ADT7410_TIMEOUT_MAX;

uint8_t data_high;

uint8_t data_low;

/* Generate the Start Condition */

I2C_GenerateSTART(I2C1, ENABLE);

/* Test on I2C1 EV5 and clear it */

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

I2C_Send7bitAddress(I2C1, ADT7410_ADDRESS, I2C_Direction_Transmitter);

/* Test on I2C1 EV6 and clear it */

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

/* Send ADT7410 register address */

I2C_SendData(I2C1,addr);

/* Test on I2C1 EV8 and clear it */

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

/* Generate the Start Condition */

I2C_GenerateSTART(I2C1, ENABLE);

/* Test on I2C1 EV6 and clear it */

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

I2C_Send7bitAddress(I2C1, ADT7410_ADDRESS, I2C_Direction_Receiver);

I2C_NACKPositionConfig(I2C1, I2C_NACKPosition_Next);

/* Prepare an NACK for the next data received */

I2C_AcknowledgeConfig(I2C1, DISABLE);

/* Test on I2C1 EV6 and clear it */

timeout = ADT7410_TIMEOUT_MAX; /* Initialize timeout value */

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

// I2C_NACKPositionConfig(I2C1, I2C_NACKPosition_Next);

/* Prepare an NACK for the next data received */

// I2C_AcknowledgeConfig(I2C1, DISABLE);

/* Test on I2C1 EV7 and clear it */

/* Receive high byte of Data */

data_high = I2C_ReceiveData(I2C1);

// Read low byte of Data

/* Prepare an NACK for the next data received */

//I2C_AcknowledgeConfig(I2C1, DISABLE);

/* Test on I2C1 EV7 and clear it */

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))

/* Receive the Data */

data_low = I2C_ReceiveData(I2C1);

// Build 16 bit data word

*read_data = (uint16_t) data_high;

*read_data <<= 8;

*read_data += (uint16_t) data_low;

I2C_GenerateSTOP(I2C1, ENABLE);

/* return the read data */

return SUCCESS;

}

________________

Attachments :

ADC_Timing_2_byte.BMP : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I11v&d=%2Fa%2F0X0000000bhA%2F6PZu0FdVScKblK6iwWRQG96OYme7MEwUOuH2lemR8MU&asPdf=false
stm322399
Senior
Posted on May 07, 2014 at 21:23

On the trace the second byte is correctly Nacked by the I2C controller, that's what we wanted.

About the extra (3rd) byte being clocked, I guess it is because you did not generate the stop at time. You must request the STOP during the time the second byte is clocked. To make this happen, ask STOP generation immediately after receiving the first byte.

munger
Associate II
Posted on May 08, 2014 at 21:53

Laurent,

Thanks a lot! That did the trick. I still don't completely understand how it all works but it's working. There is a lot of good info in the comments in stm32f4xx_i2c.c, I think if I read it 18 more times maybe it will start to make sense to me.

Thanks again.

Mike

stm322399
Senior
Posted on May 09, 2014 at 11:02

Glad to help you.

To understand how it works, consider the following: you must always tell the controller what it must do in advance. Think about the STOP generation, if you wait the second byte to be received, it's too late, the controller already started to clock a third one, the software is not fast enough to stop the controller on time. Of course, as you nack'ed the second byte, it would make sense to stop the transfer ... but that's not the way it has been designed.

Have fun.

munger
Associate II
Posted on May 09, 2014 at 15:53

That makes sense. I think what's hard sometimes is that the purpose of the function calls are not always intuitive (to me). For example, I2C_NACKPositionConfig(I2C1, I2C_NACKPosition_Next), that doesn't immediately make sense to me.

Anyway, the only other weirdness I'm having is setting the clock speed. I am setting I2C_InitStruct.I2C_ClockSpeed to 100000 but the clock runs at about 33 kHz. To get 100 kHz I have to set I2C_ClockSpeed to 300000, then the duty cycle ends up being about 33%. It all works but I can't explain the discrepancy. I've examined and stepped through I2C_Init many times and maybe I have my clocks set up in some kind of weird way but ... I thought that's the whole idea of using I2C_Init, it figures out what your clock is set to and adjusts accordingly. The Duty Cycle is set to I2C_DutyCycle_2. Weird, right?

Thanks.

Mike

stm322399
Senior
Posted on May 09, 2014 at 16:21

Let's start with low hanging fruits: if you I2C CLK is 1/3 the speed you expect, it's maybe because the clock of MCU is 1/3 of what the software expect. There is a common mistake with ST peripheral lib, not to take care of HSE_VALUE, that is sometimes 8MHz (your discovery board) or 24-25MHZ (other boards). Oh, there is a 1/3 ratio ! Make sure HSE_VALUe is 8000000 (the better is to set it as compiler flag: -DHSEVALUE=8000000 ).

Not sure about the duty cycle problem. 33% is close to what you get with 16/9 duty mode, so if i were you I'll double check what's behind

I2C_DutyCycle_2.

munger
Associate II
Posted on May 12, 2014 at 13:44

Laurent,

Again, thanks a million, that was it. I put -DHSE_VALUE=8000000 in Misc Controls in the C/C++ options (Keil), you had a small typo below, at least I figured that out :). The frequency came in at 100 kHz right on the money. 

I don't know why the heck that wouldn't have been set right, my project started out life as a project for the Discovery Board ... or why can't I2C_Init figure that out? That's the whole idea of having those functions.

Thanks again.

Mike