Connecting an AT91SAM7S,X to a I2C LM75 Temperature Sensor

Discussion about SAM7 Series and ARM7TDMI based products.

Moderator: nferre

User avatar
pfilippi
Contact:
Posts: 327
Joined: Fri Feb 13, 2004 7:53 pm

Connecting an AT91SAM7S,X to a I2C LM75 Temperature Sensor

Fri Sep 22, 2006 2:09 pm

Hello All,

a very simple project with a I2C LM75 Digital Temperature Sensor from National semiconductor connected to the TWI port of a SAM7S or SAM7X.

Project is for IAR with the AT91SAM7S-EK board and SAM-ICE.

Rgds,
Last edited by pfilippi on Fri Nov 24, 2006 12:58 am, edited 1 time in total.
jdupre
Location: Petaluma, California
Posts: 44
Joined: Sat Apr 16, 2005 12:47 am

Tue Oct 31, 2006 1:09 am

Can you please clarify how you are setting the TWI clock?
This code and the results make no sence to me.

Code: Select all

void AT91F_SetTwiClock(void)
{
	int sclock;

	/* Here, CKDIV = 1 and CHDIV=CLDIV  ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6)*/

	sclock = (10*MCK /AT91C_TWI_CLOCK);
	if (sclock % 10 >= 5)
		sclock = (sclock /10) - 5;
	else
		sclock = (sclock /10)- 6;
	sclock = (sclock + (4 - sclock %4)) >> 2;	// div 4

    AT91C_BASE_TWI->TWI_CWGR	= ( 1<<16 ) | (sclock << 8) | sclock  ;
}
With AT91C_TWI_CLOCK = 8000, and MCK= 48054857, this makes sclock = 1501. That is greater then the 8 bits alloted for CHDIV and CLDIV. TWI_CWGR gets set to 101 1101 1101 1101 1101. (CDIV=5, CHDIV=221, CLDIV=221)

What is AT91C_TWI_CLOCK = 8000 supposed to represent? Are you attempting to set the TWI clock to 8000 Hz?

How would I set the TWI clock to 400 KHz?

- Joe
Beanie
Posts: 2
Joined: Sun Dec 03, 2006 5:53 am

Sun Dec 03, 2006 5:57 am

I have the same question. This code is essentially the same as in the FreeRTOS port provided by Priio with there AT91SAM7X256 evaluation board, by the way. This board has a RTC which is supposed to operate with an I2C clock of 400KHz. I need to set up the TWI for 100K (for something else) and have no idea why the above code works... or how to modify it for 100K. Could it be just a quirk that it works with the temperature sensor and is not really correct for 400K?
Beanie
Posts: 2
Joined: Sun Dec 03, 2006 5:53 am

Sun Dec 10, 2006 4:32 am

I had to change the code:

//KAB void AT91F_SetTwiClock(void);
void AT91F_SetTwiClock(int32 freq); //KAB

//KAB #define AT91C_TWI_CLOCK 8000
#define AT91C_TWI_CLOCK 400000 //this is for a 400KHz I2C RTC KAB


AT91F_SetTwiClock(AT91C_TWI_CLOCK);


/****************************************************************************
**
** Sets up the TWI bus for the AT91SAM7X
**
** Parameters: freq desired frequency (e.g., 100000, 400000)
**
** Returns: NONE
**
****************************************************************************/
//KAB void AT91F_SetTwiClock(void)
void AT91F_SetTwiClock(int32 freq) //KAB
{
int32 sclock; //this was int16 KAB
/* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6)*/
//KAB sclock = (10*AT91B_MCK / AT91C_TWI_CLOCK);
sclock = (10*AT91B_MCK / freq); //KAB
if((sclock % 10) >= 5)
sclock = (sclock / 10) - 5;
else
sclock = (sclock / 10) - 6;
sclock = (sclock + (4 - sclock % 4)) >> 2; /* div 4 */
AT91C_BASE_TWI->TWI_CWGR = ( 1 << 16) | (sclock << 8) | sclock;
}


Explanation:

1. The procedure did not take a parameter, which you need if you have I2C devices that work at 100KHz and others that work at 400KHz, for example.

2. sclock was int16, which was not big enough. It might have been OK if parethesis were added so the divide was done before the multiply by 10... did not even try that.
User avatar
pfilippi
Contact:
Posts: 327
Joined: Fri Feb 13, 2004 7:53 pm

Mon Dec 11, 2006 7:10 pm

Hi All,

I did not notice this, but, the SetTWiClock function assumes CKDIV = 1 so if TWI freq is too low, it gives a wrong value. i.e CLDIV and CKDIV more than 256. (8 bit value)

By using the function as is, do not use a twi clock under ~ 100Khz
MechEE
Posts: 53
Joined: Thu Aug 25, 2005 10:16 pm

Tue Dec 12, 2006 5:32 am

So is I2C reasonably functional on these SAM7 parts? With all the errata and complaints on the forums about how broken it was, the thought has never crossed my mind to attempt to use that peripheral.
nleahcim
Posts: 87
Joined: Thu Aug 25, 2005 7:06 pm

Tue Jan 02, 2007 12:43 am

I am currently going through this code and find some bits of it rather confusing.

The thing that is bothering me the most right now is the AT91F_TWI_WriteByte function. I just don't quite understand what is going on with it. One thing that bothers me about it is that it seems to send multiple start conditions! It does a loop where it sends each byte:

Code: Select all

	  for(counter=0;counter<nb;counter++)
      {
          pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN;
          if (counter == (nb - 1)) pTwi->TWI_CR = AT91C_TWI_STOP;
          status = pTwi->TWI_SR;
          if ((status & ERROR) == ERROR) error++;
          while (!(status & AT91C_TWI_TXRDY))
          {
               status = pTwi->TWI_SR;
               if ((status & ERROR) == ERROR) error++;
          }
          pTwi->TWI_THR = *(data2send+counter);
	   }
Unless I'm misinterpreting something - it appears to send a start condition at each byte! My understanding of i2c is that the entire data transfer has a start condition at the very beginning before the slave address and a stop condition after the last byte. Why is it doing this?

Also - I am confused as to the exact functionality of the MSEN bit of the CR register. I thought it just enabled master mode. But it is being set for every byte being transferred - I don't understand why this is. My understanding is that setting the TWI_START bit in TWI_CR would send the start condition and the slave address, and then writing to the TWI_THR would immediately start the transfer of that data. Is that not correct?

Also - are you supposed to set the TWI_STOP bit in TWI_CR right after writing to the TWI_THR register, or after the byte in TWI_THR has finished sending (AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXRDY)? The flowchart on page 286 of the AT91SAM7X256 datasheet says to wait for the last byte to send, but the code in the AT91F_TWI_WriteByte sets the stop condition bit in the control register as soon as the THR register has been written to with the last byte. Looking carefully at the SAM7X256 datasheet I think it's done correctly in the AT91F_TWI_WriteByte function and is written incorrectly in the flowchart on page 286 of the AT91SAM7X256 datasheet. But I could be wrong.

Anyways - I would be very interested to hear any comments on the initialization and writing functions that I have written. They are designed to communicate with the Maxim DS3231 RTC. They are designed to follow the flowcharts given in the SAM7X256 datasheet:

Code: Select all

void enablertc(void)
{
  AT91F_TWI_CfgPIO(); //configure PIOs for TWI
  AT91F_TWI_CfgPMC(); //enable TWI clock
  AT91F_TWI_Configure(AT91C_BASE_TWI); //reset TWI, set to master mode, disable interrupts
  //configure baudrate - set tclocklow = 1.5us, tclockhigh = 1us, CKDIV = 0, CLDIV = 80 (ideally it should be 79.5 - error gives tclocklow of 1.50909us, and thus 398.551KHz clock), CHDIV = 52
  AT91C_BASE_TWI->TWI_CWGR = (AT91C_TWI_CLDIV & (80 << 0)) | (AT91C_TWI_CHDIV & (52 << 8)) | (AT91C_TWI_CKDIV & (0 << 16));
  //DS3231 address = 0b01101000
  //control register at 0x0E = 0b10000000 = 0x80
  //status register at 0x0F = 0b00000000 = 0x00
  char rtcsetup[3] = {0x0E, 0x80, 0x00};
  twimastertransmit(rtcsetup , 3, 0x68);
}

void twimastertransmit(char * datatosend, unsigned int datalength, unsigned int address)
{//transmit (datalength) bytes starting at (datatosend) to slave at (address) 
  AT91C_BASE_TWI->TWI_MMR = (AT91C_TWI_DADR & (address << 16)); //set slave address and set data direction for transmit
  AT91C_BASE_TWI->TWI_THR = *datatosend++; //load in first byte and increment data pointer
  datalength--;
  AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START; //send start condition
  while (datalength)
  {
    datalength--;
    while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXRDY)) ; //wait for last tx to finish
    AT91C_BASE_TWI->TWI_THR = *datatosend++; //load in new byte
  }
  while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXRDY)) ; //wait for last tx to finish
  AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP; //send stop condition
  while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXCOMP)) ; //wait for TWI tx to finish
}

Thanks!
nleahcim
Posts: 87
Joined: Thu Aug 25, 2005 7:06 pm

Sat Jan 20, 2007 6:34 pm

Hello again! I finally had a chance to test out all my TWI code last night. All of my suspicions regarding mistakes in the code on this page and mistakes in the datasheet were correct. The flowchart for TWI communication in the SAM7X datasheet is very much incorrect about the stop bit. The way start bits are sent in the code in this thread is also wrong (unless some chips needs this - but I don't know of any that do).

I am including my initialization, reading, and writing routines. I hope somebody finds them helpful. I am using them to initialize, read from, and write to a Maxim DS3231 clock, so a bit of the initialization is specific for just that chip.

-Mike

Code: Select all

void enablertc(void)
{
  AT91F_TWI_CfgPIO(); //configure PIOs for TWI
  AT91F_TWI_CfgPMC(); //enable TWI clock
  AT91F_TWI_Configure(AT91C_BASE_TWI); //reset TWI, set to master mode, disable interrupts
  AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA11_TWCK|AT91C_PA10_TWD; //disable pull ups
  
  //configure baudrate - set tclocklow = 1.5us, tclockhigh = 1us, CKDIV = 0, CLDIV = 80 (should be 79.5 - gives tclocklow of 1.50909us, and thus 398.551KHz clock), CHDIV = 52
  AT91C_BASE_TWI->TWI_CWGR = (AT91C_TWI_CLDIV & (80 << 0)) | (AT91C_TWI_CHDIV & (52 << 8)) | (AT91C_TWI_CKDIV & (0 << 16));
  //DS3231 address = 0b01101000
  //control register at 0x0E = 0b10000000 = 0x80
  //status register at 0x0F = 0b00000000 = 0x00
  char rtcsetup[3] = {0x0E, 0x80, 0x00};
  twimastertransmit(rtcsetup , 3, 0x68);
}

void twimastertransmit(char * datatosend, unsigned int datalength, unsigned int address)
{//transmit (datalength) bytes from slave at (address) writing to (datatoreceive)
  AT91C_BASE_TWI->TWI_MMR = (AT91C_TWI_DADR & (address << 16)); //set slave address and set data direction for transmit
  AT91C_BASE_TWI->TWI_THR = *datatosend++; //load in first byte and increment data pointer
  datalength--;
  AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START; //send start condition
  while (datalength)
  {
    datalength--;
    while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXRDY)) ; //wait for last tx to finish
    AT91C_BASE_TWI->TWI_THR = *datatosend++; //load in new byte
  }
  while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXRDY)) ; //wait for last tx to finish
  AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP; //send stop condition
  while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXCOMP)) ; //wait for TWI tx to finish
}

void twimasterreceive(char * datatoreceive, unsigned int datalength, unsigned int address)
{//receive (datalength) bytes from slave at (address) starting at (datatoreceive)
  AT91C_BASE_TWI->TWI_MMR = (AT91C_TWI_DADR & (address << 16)) | AT91C_TWI_MREAD; //set slave address and set data direction for receive
  AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START; //send start condition
  while (datalength--)
  {
    if (!(datalength)) AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP; //send stop condition
    while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_RXRDY)) ; //wait for last rx to finish
    *datatoreceive++ = AT91C_BASE_TWI->TWI_RHR; //read in last byte received
  }
  while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXCOMP)) ; //wait for TWI rx to finish
}
User avatar
pfilippi
Contact:
Posts: 327
Joined: Fri Feb 13, 2004 7:53 pm

Tue Feb 06, 2007 11:10 pm

Hello,

your remarks are relevant. I'm aware of that , this is a beta version code that has been adapted from an old code from an old chip for a quick test for this device. This does not prevent the TWI to work.

I have developped a full test code in polling mode which match the flow chart of the datasheet. I have a TWI network using a DS1337 RTC, a LM75 temp sensor, a Graphic LCD with the PCF8558 I2C LCD controller and an AT24C1024 ATMEL EEPROM.
Everything is working , so far. The IRQ driven version is under development .

Rgds.
nleahcim
Posts: 87
Joined: Thu Aug 25, 2005 7:06 pm

Wed Feb 07, 2007 8:56 pm

pfilippi wrote:Hello,

your remarks are relevant. I'm aware of that , this is a beta version code that has been adapted from an old code from an old chip for a quick test for this device. This does not prevent the TWI to work.

I have developped a full test code in polling mode which match the flow chart of the datasheet. I have a TWI network using a DS1337 RTC, a LM75 temp sensor, a Graphic LCD with the PCF8558 I2C LCD controller and an AT24C1024 ATMEL EEPROM.
Everything is working , so far. The IRQ driven version is under development .

Rgds.
Hi - please note that as I mentioned - there is also a mistake in the datasheet that should be corrected as well. I think it's more important to fix that then the example code.
alperuzi
Contact:
Posts: 7
Joined: Thu Apr 12, 2007 10:19 am

Mon Apr 16, 2007 3:51 pm

I've been working for two days trying to get the AT24C256 EEPROM working without any luck. The code Mike gave gets stuck at the loop here:

Code: Select all

while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_RXRDY)) ; //wait for last rx to finish 
The example code from Atmel doesn't seem to work either. I was only able to write a few random bytes here and there somehow, and always read zeros.

And I also noticed that the 3rd bit in the TWI status register is always set, including at hardware reset. Is this normal? I'm using a AT91SAM7S256-EK board from IAR.
nleahcim
Posts: 87
Joined: Thu Aug 25, 2005 7:06 pm

Mon Apr 16, 2007 4:56 pm

alperuzi wrote:I've been working for two days trying to get the AT24C256 EEPROM working without any luck. The code Mike gave gets stuck at the loop here:

Code: Select all

while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_RXRDY)) ; //wait for last rx to finish 
The example code from Atmel doesn't seem to work either. I was only able to write a few random bytes here and there somehow, and always read zeros.

And I also noticed that the 3rd bit in the TWI status register is always set, including at hardware reset. Is this normal? I'm using a AT91SAM7S256-EK board from IAR.
Hi - have you looked at the communication with a logic analyzer? That should clear up what is wrong. I suspect that the eeprom is never sending a stop condition.
alperuzi
Contact:
Posts: 7
Joined: Thu Apr 12, 2007 10:19 am

Tue Apr 17, 2007 12:33 pm

Thanks for the reply. I was able to get a good waveform yesterday with our good old oscilloscope, wish i had access to a proper logic analyzer :) Would have made this much easier.

I got both codes working today after my friend actually got the whole thing soldered rather then breadboarded (?). The address wasn't set set correctly at first, but then I noticed you shifted the address in the function (which is more correct IMHO), so thats done too. Thanks again!
nleahcim
Posts: 87
Joined: Thu Aug 25, 2005 7:06 pm

Tue Apr 17, 2007 9:21 pm

alperuzi wrote:Thanks for the reply. I was able to get a good waveform yesterday with our good old oscilloscope, wish i had access to a proper logic analyzer :) Would have made this much easier.

I got both codes working today after my friend actually got the whole thing soldered rather then breadboarded (?). The address wasn't set set correctly at first, but then I noticed you shifted the address in the function (which is more correct IMHO), so thats done too. Thanks again!
Glad to hear it's working properly! You had me worried there for a bit that there might be a bug in my code!

Return to “SAM7 ARM7TDMI MCU”

Who is online

Users browsing this forum: No registered users and 2 guests