Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
barjaktar
PostPosted: Apr 18, 2012 - 03:12 PM
Rookie


Joined: Apr 20, 2011
Posts: 40


Hello.

I obtained Inertial Two platform and I've been trying to use it through ATmega1281. The 1281 is within the ATZB A2 24 module. TWI communication pins (SCL and SDA) of 1281 are available in this module (pins no 11 and 12, respectively). These are connected to SCL and SDA pins of the Inertial Two platform, which further connects them to IMU 3000 gyroscope. After powering up, I try to read the signature register of IMU 3000, but I get a NACK on SDA after sending slave address of the gyroscope.

For now, I'd ask if you could check my TWI code, just to make sure that everything is all right there. For programming I use bootloader and serial connection.

Code:

int main(void)
{
   //disable WATCHDOG timer
   MCUSR = 0x00;   
   WDTCSR = (1 << WDCE) | (1 << WDE);
   WDTCSR = 0x00;
   
   //F_CPU = 8MHz
   CLKPR = 0x80;
   CLKPR = 0x00;
   
   //enable UART and TWI
   USART_I(23);// for 38400 at 8MHz
   TWI_I();
   
   //test UART
   USART_W(0xFA);//WORKS fine
   
   //test TWI   
   char d[1] = {0x00};
   
   TWI(0x68, 'W', &d, 0x01);
   
   USART_W(0xFB);//doesn't get here
   
   return 0;
}


void TWI_I(void)
{
   DDRD = 0x00;
   PORTD = 0x00;
   
   TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWINT);
   TWBR = 0x01;
   TWSR = 0x00;
}

void TWI_START(void)
{
   TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN) | (1 << TWEA);
   
   while(!(TWCR & (1 << TWINT)));

   //za greske
   //if((TWSR & 0xF8) != START) ERROR();
}

void TWI_STOP(void)
{
   TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN) | (1 << TWEA);
}

void TWI_SlaveAddress_Set(char SlaveAddress)
{
   TWAR = (SlaveAddress & 0xFE) | (1 << TWGCE);
}

void RESET_TWINT(void)
{
   TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
   
   while(!(TWCR & (1 << TWINT)));
}

void TWI(char address, char RW, char *data, char length)
{
   char i = 0x00;
   
   TWI_START();
   
   //USART_W(TWSR);
   
   switch(RW)
   {
      case 'R':
         address |= 0x01;
   
         TWDR = address;
         RESET_TWINT();
         
         while(TWSR != 0x40)
         {
            TWI_START();
            RESET_TWINT();
         }
         
         while(i < length)
         {
            while(!(TWCR & (1 << TWINT)));
            
            *(data + i) = TWDR;
            
            i++;

            TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
         }
      break;
      
      case 'W':
         address &= 0xFE;
         
         TWDR = address;
         RESET_TWINT();
         
         USART_W(TWDR);
         USART_W(TWSR);
         
         for(char i = 0x00; i < length; i++)
         {
            TWDR = *(data + i);
            
            RESET_TWINT();
            
            USART_W(TWDR);
            USART_W(TWSR);
         }
      break;
   }
   
   TWI_STOP();
}


If anything else is needed to get the full picture of the situation, please let me know.

Thank you for your help.
 
 View user's profile Send private message  
Reply with quote Back to top
david.prentice
PostPosted: Apr 18, 2012 - 03:28 PM
10k+ Postman


Joined: Feb 12, 2005
Posts: 16297
Location: Wormshill, England

First off. You must have external pull-up resistors.

I suggest that you start off by using the Fleury TWI library.

Once you have your project working, you can develop your own TWI functions. There are several suspicious points in your code. You can compare your functions with proven versions from a respected library.

David.
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
barjaktar
PostPosted: Apr 18, 2012 - 04:35 PM
Rookie


Joined: Apr 20, 2011
Posts: 40


Thank you, David.

I've tested these functions and I was able to communcicate between two ATmega16. As I see, from datasheets of 1281 and 16, their TWI modules are the same (1281 has an additional register, but it does not affect basic functions, it's only for masking slave address bits). Anyway, I will check out suggested library and get back to you here. Could you, please, tell me, which points are suspicious in the listed code?

As far as pull-ups are concerned, please check out the schematic provided in this post. It shows the connections of the Inertial Two platform (taken from atmel.com). There you can see that two pull-ups exist (2.2 kohm). I bridged the voltage regulator, so that the supply voltage is actually 3.3V. Have you taken these into account when you said that I need pull-ups? Are these enough?

Communication speed is set up as 307 200 Hz by:
Code:
TWBR = 0x01;
TWSR = 0x00;

This means that:
Code:
F_SCL = F_CPU / 24,

where
Code:
F_CPU = 8 * 921 600 = 7 372 800 Hz

So, it's actually not 8MHz, but close to it. This is because I use internal RC oscillator.

Do this resistors satisfy for this speed?

Thank you.
 
 View user's profile Send private message  
Reply with quote Back to top
david.prentice
PostPosted: Apr 18, 2012 - 05:37 PM
10k+ Postman


Joined: Feb 12, 2005
Posts: 16297
Location: Wormshill, England

Your SCL speed is miles off.
What I2C bus speed does your gyro support?
2k2 pull-ups should be fine.

A standard 100kHz bus needs TWBR = 32, TWSR = 0.
A non-standard 200kHz bus needs TWBR = 12, TWSR = 0.
The formula says 400kHz bus needs TWBR = 2, TWSR = 0.

In practice, AVRs do not achieve 400kHz.

I still recommend using proven code. But if you are confident your code is better, I will not argue.

David.
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
atomicdog
PostPosted: Apr 18, 2012 - 06:39 PM
Posting Freak


Joined: Jan 14, 2008
Posts: 1147
Location: San Diego

What are you trying to do here?
You've already sent the start before this.

Code:
         while(TWSR != 0x40)
         {
            TWI_START();
            RESET_TWINT();
         }

_________________
~~John
TWI C source code
 
 View user's profile Send private message  
Reply with quote Back to top
barjaktar
PostPosted: Apr 18, 2012 - 11:38 PM
Rookie


Joined: Apr 20, 2011
Posts: 40


@David

IMU 3000 datasheet says it works at up to 400kHz (it can be found here, pages 9 and 20 about digital output and primary I2C interface). Why is my frequency miles off? What am I doing wrong? Here is how I calculated:

Since I use serial bootloader for programming 1281, I am not able to control fuse bits. I use internal RC oscillator. The bootloader application sets up CPU frequancy to be 921600Hz. Then I multiply it by 8, thus achieving 7372800Hz. The equation to get the I2C frequency is given as:

Code:
F_SCL = F_CPU / (16 + [2 * TWBR * (4 ^ TWPS)])


where for TWPS I can choose only beween 1, 4, 16 and 64.

If I choose lowest values (TWPS = 1 (this you get if TWSR = 0x00) and TWBR = 0x00) I get:

Code:
F_SCL = F_CPU / 16 = 460800Hz,


which is greater then 400kHz.

Next possible value is obtained if TWBR = 0x01:

Code:
F_SCL = F_CPU / 24 = 307200Hz,


which should be fine, since IMU 3000 works at frequencies up to 400kHz.

So, what did I do wrong? Did I misunderstand something about the way TWI works? Something about its frequency?

@atomicdog

This code was written and tested on two ATmega16. I used these functiones to successfully exchange informations between these two controllers. Sometimes it happens that slave is busy, working on something and it disables itself from TWI. During that time, if master tries to send something it gets NACK from slave. Thus, I put this piece of code in order to force master to wait until it gets ACK (that is 0x40 in its status register, TWSR).

Ofcourse, in this case, when slave is only a sensor, these few lines don't make sense.[/url]
 
 View user's profile Send private message  
Reply with quote Back to top
atomicdog
PostPosted: Apr 19, 2012 - 12:16 AM
Posting Freak


Joined: Jan 14, 2008
Posts: 1147
Location: San Diego

You send a start but no address, it just goes straight to reading TWDR.

_________________
~~John
TWI C source code


Last edited by atomicdog on Apr 19, 2012 - 04:03 AM; edited 1 time in total
 
 View user's profile Send private message  
Reply with quote Back to top
barjaktar
PostPosted: Apr 19, 2012 - 12:49 AM
Rookie


Joined: Apr 20, 2011
Posts: 40


@atomicdog

It actually works fine. As I mentioned, the code has been tested on a system containing two ATmega16.

If you look at the code again:

Code:
         address |= 0x01;
   
         TWDR = address;          (1)
         RESET_TWINT();           (2)
         
         while(TWSR != 0x40)      (3)
         {
            TWI_START();
            RESET_TWINT();        (4)
         }
         
         while(i < length)
         {
            while(!(TWCR & (1 << TWINT)));
           
            *(data + i) = TWDR;
           
            i++;

            TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
         }


you will see that I put the address into TWDR register (1). Then I send it (that's what RESET_TWINT() does at (2)). Now, I check wheter I received an ACK from slave (3). If not, then send START signal again (repeated start, actually, but doesn't make any difference) and resend what is already in TWDR - that is what RESET_TWINT() does at (4). Since the address is what is in TWDR at this point of time, then address is resent, and only after that I change the content of TWDR (within the loop) and start sending data (*data). You might also want to look at the code of RESET_TWINT() function.

Does it make sense to you now?

I appriciate all the comments, so please if it is still unclear tell me, I will try to explain or, if it turns out I am wrong, to correct.

Thank you.
 
 View user's profile Send private message  
Reply with quote Back to top
atomicdog
PostPosted: Apr 19, 2012 - 04:01 AM
Posting Freak


Joined: Jan 14, 2008
Posts: 1147
Location: San Diego

Quote:
Does it make sense to you now?
Oh, OK I didn't know the data would stay valid.

_________________
~~John
TWI C source code
 
 View user's profile Send private message  
Reply with quote Back to top
atomicdog
PostPosted: Apr 19, 2012 - 04:06 AM
Posting Freak


Joined: Jan 14, 2008
Posts: 1147
Location: San Diego

Since your confident your code works how sure are you that you're sending the correct address?

_________________
~~John
TWI C source code
 
 View user's profile Send private message  
Reply with quote Back to top
barjaktar
PostPosted: Apr 19, 2012 - 08:52 AM
Rookie


Joined: Apr 20, 2011
Posts: 40


Well, I am confident that it works on ATmega16. I put it up here to check if somebody did something alike with 1281, just to see if it is the same thing, as I thought it should be. Today I will test the library that David suggested.

About the address. Checkout the datasheet on page 30. It says that the ID is 110 100. These are bits 6-1. Bits 7 and 0 are both 0s, thus the address is 0b01101000 = 0x68 (in order to write, for reading it would be 0b01101001 = 0x69). Also, the same thing is said in this document (AVR4018: Inertial Two (ATAVRSBIN2) Hardware User Guide) on page 5.
 
 View user's profile Send private message  
Reply with quote Back to top
atomicdog
PostPosted: Apr 19, 2012 - 09:12 AM
Posting Freak


Joined: Jan 14, 2008
Posts: 1147
Location: San Diego

Quote:
The slave address of the IMU-3000 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is
determined by the logic level on pin ADO. This allows two IMU-3000s to be connected to the same I2C bus.
When used in this configuration, the address of the one of the devices should be b1101000 (pin ADO is logic
low) and the address of the other should be b1101001 (pin AD0 is logic high). The I2C address is stored in
the WHO_AM_I register.


If your passing in 0x68/0x69 to void TWI(char address... that's not correct.
The 7-bit address is either 0x68 or 0x69 depending on AD0. You need to shift the address so it takes up bit 7 to bit 1. bit 0 is the R/W bit.
So you would pass in 0xD0 or 0xD2(depending on AD0) to the function

_________________
~~John
TWI C source code
 
 View user's profile Send private message  
Reply with quote Back to top
barjaktar
PostPosted: Apr 19, 2012 - 09:27 AM
Rookie


Joined: Apr 20, 2011
Posts: 40


Yes... Just a f****** minute ago I figured it out... I read the whole thing again when I was referencing the document to you.

Once again I realize that my stupidity is inifinte.

I got confused while reading about 'Who am I' register, because it shows a 0 for bit 7, so I just plug 110 1000 in there, for bits 6-0. Of course, I was supposed to shift everything to left.

Thank you very much and I am sorry for being so stupid.

p.s. On the other hand, we know that my functions definetly work now!
 
 View user's profile Send private message  
Reply with quote Back to top
david.prentice
PostPosted: Apr 19, 2012 - 09:56 AM
10k+ Postman


Joined: Feb 12, 2005
Posts: 16297
Location: Wormshill, England

I am sorry to repeat myself. Proven libraries give return values to their functions. Hence you will see whether a device has ACK or NAK to a Master.

If you like driving blindfolded, by all means use void functions. As you have shown, a car can easily get from A to B with no windows and a blind driver.

Note that many people use libraries and completely ignore the return values. However they only need to remove the blindfold to see where they are going.

David.
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
barjaktar
PostPosted: Apr 19, 2012 - 11:24 AM
Rookie


Joined: Apr 20, 2011
Posts: 40


Thank you, David, for your time and a valuable suggestion.

The blindfold is removed by looking into TWSR register (just call USART_W(TWSR); ), even though the function is void. I was doing this, but with a wrong slave address, whence came my problem.
 
 View user's profile Send private message  
Reply with quote Back to top
david.prentice
PostPosted: Apr 19, 2012 - 02:29 PM
10k+ Postman


Joined: Feb 12, 2005
Posts: 16297
Location: Wormshill, England

TWSR tells you exactly where and what the problem might be. There are special values for each type of SLAVE_NAK.

I am pleased that you have it working.

David.
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits