Receiving NACK when attempting to write in Master Transmitter Mode

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have the atmega2560 on the APM 2.6 board so the MPU-6050 is wired so I'm unable to mess with resistors and what not.     

 

I'm trying to talk to the MPU on startup but am unable. My understanding is that I need to be in master transmitter mode to send the SLA+W and then in the data sequence I write a byte which is the address in the MPU that I'm requesting. From there I go into master receiving mode. I'm unable to get a good response after the first SLA+W. Instead of getting 0x18 (ACK) I get 0x20 (NACK). One possible issue I'm question now is in the MPU datasheet I see normal mode is 100kHz and fast mode is 400kHz. I'm running at 400kHz but would using something besides those two values cause a problem? This isn't my issue but it is something I'm curious about.

 

Here is my current sequence. 

 

    F_CPU = 8000000 // defined by the compiler
    
    const uint32_t I2C_BAUD = 400000; // fast mode (400 kHz)
    
    // TWI Bit Rate Register
    TWBR = ((F_CPU / I2C_BAUD) - 16)/2;
    comms.putChar(TWBR);

    // Set prescalar to 1 (0x00)
    TWSR |= (0 << TWPS1) | (0 << TWPS0);

Looking through this now I noticed I don't have the *4^TWPS in my TWBR formula and I'm confused if the equation is taking TWPS to be the prescalar value or the value of the bits? My equation is assuming the latter where the bits show 0 so 4^0 = 1. Is this correct or should the formula be 4^1 = 4. 

 

Anyways, I'll continue because I think the above is correct the way it is.

 

bool twi::TwiMgr::idle()
{
    while ((TWCR & (1 << TWINT)) == 0)
        comms.putChar(1);
        ;
    return true;
}

void twi::TwiMgr::sendStart()
{
    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
}

void twi::TwiMgr::request_read(uint8_t addr, uint8_t reg)
{
    print_status();

    sendStart();
    idle();

    print_status();

    TWDR = (addr << 1); // SLA + W
    //TWDR = addr; // SLA + W
    TWCR = (1 << TWINT) | (1 << TWEN);
    idle();

    print_status();

    //TWDR = 0x6B; // pwr register
    TWDR = 0x75; // who am i register
    TWCR = (1 << TWINT) | (1 << TWEN);
    idle();

    print_status();
}

void twi::TwiMgr::print_status()
{
    comms.putChar(TWCR);
    comms.putChar(TWSR & 0xF8);
    comms.putChar(TWDR);
    comms.putChar('-');
}

Before calling request_read I delay for 2 seconds to make sure everything has settled and is stable. As you can see I print the current registers after each step in the sequence. First print_status() doesn't really provide much. I send the start condition and wait for the hardware to set the TWINT flag at which point I check the registers. Checking the registers I see that 0x08 is reported (START condition). After that I load the slave address (0x68) into the register. I have the line below it commented out because it seems like in the atmel TWI driver it doesn't shift the address to the left by 1 so I tried it with no luck. I believe what is not commented out is correct. Someone could maybe confirm that. I then set the control register to transmit the SLA+W, wait for the hardware to set the TWINT flag and print the registers once more. At this point I should expect 0x18 to be in the status register however this is where I'm receiving the 0x20. The next step after it is kind of pointless at this point because I am stuck on trying to receive an ACK isntead of a NACK.

 

Does anyone have any ideas what could be going wrong here? I've been stuck for a few days and would love some help.

 

Thank you in advance.

This topic has a solution.
Last Edited: Mon. Jul 10, 2017 - 04:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Double check your slave address. It is always a source of confusion as some companies express it as a 7 bit address that gets shifted left and others as an 8 bit number that needs no shifting.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Every project I've seen has used 0x68 for the MPU-6050 slave address. Looking at documentation it depends on the AD0 line. Changing this line selects the address to be either 0x68 or 0x69 allowing you to have two MPUs. I don't think this will be it considering all the projects I've seen use 0x68 but it's definitely something I will try.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Trying 0x69 vs 0x68 did not work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have an ATmega2560. So you could just build and run regular Arduino code on your hardware.
This will verify that your I2C devices are working ok.
.
Then I would test it with the Fleury library that is written in C.
.
The Fleury library code is very easy to follow. You can wrap the basic structure in a C++ class. Job done.
.
If you attached a buildable project, someone might debug it for you.
But I have no intention of guessing about your VOID methods from tiny snippets.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you missed the point that Jim was making:

 

It is always a source of confusion as some companies express it as a 7 bit address that gets shifted left and others as an 8 bit number that needs no shifting

It is, indeed, a very common inconsistency and, thus, a cause for confusion: https://www.avrfreaks.net/comment...

 

So the question is not whether it is 0x69 or 0x68 - but whether that value needs to be shifted ...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

He has shifted it correctly in message #1.

People have an overwhelming desire to work blindfolded i.e. with void functions.
.
Whatever language you prefer, making errors go away by closing your eyes is not wise.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I threw this together, in the future I can change it from void but I'm not exactly going about this blind. I'm printing out the value of the 3 registers so I know where my error is. I'm getting a NACK.

 

With this I've found something I thought the hardware was doing one way but actually is not. My confusion is with the TWINT bit in the control register. From printing the control register each step I've come to this understanding. Is this correct?

To send the start condition we write TWINT to 1 (clearing it). I'm assuming as soon as I write a 1 in that bit the hardware changes it to 0. Then the next time it is 1 I know an action needs to be taken. For the next step, even though TWINT is already 1 writing a 1 to it will cause the next step to trigger?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

He has shifted it correctly in message #1.

​I'm skeptical. Take this library for example 

Procyon AVRlib

 

In the i2ctest.c they have the target address as 0xA0. Now if they did not hardcode the address to be bitshifted in that line (as in the address is really 0x50) then I can't find anywhere in the code where they shift the address to left 1 bit. 

 

I followed the interrupt driven example code. Here's the relevant parts

#define TARGET_ADDR	0xA0

int main()
{
    ...
    i2cMasterSend(TARGET_ADDR, localBufferLength, localBuffer);
}


void i2cMasterSend(u08 deviceAddr, u08 length, u08* data)
{
	u08 i;
	// wait for interface to be ready
	while(I2cState);
	// set state
	I2cState = I2C_MASTER_TX;
	// save data
	I2cDeviceAddrRW = (deviceAddr & 0xFE);	// RW cleared: write operation
	for(i=0; i<length; i++)
		I2cSendData[i] = *data++;
	I2cSendDataIndex = 0;
	I2cSendDataLength = length;
	// send start condition
	i2cSendStart();
}


inline void i2cSendStart(void)
{
	// send start condition
	outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
}

After the i2cSendStart() they wait for the interrupt. Again, I don't see anywhere where they shift the address unless they hardcoded the address shifted already.

 

Is that the case here? The slave address really is 0x50??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's another example of a code that I can't find a place where they shift the address. And this project is the same chip I'm using. Atmega 2560 with the MPU-6050. He has the slave address as 0x68! And yet I'm unable to find a spot where he shifts the address. He even has a video claiming the code works. This is why I'm skeptical that my initial code example is correct. The only thing that is telling me I should be shifting my address is through documentation and what you guys are saying.

 

Quadcopter-Homemade

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you attached a buildable project, someone might debug it for you.

In the past I used Atmel Studios to debug but now prefer not to boot into Windows. Is there a way to use my AVR Dragon and JTAG to debug in linux?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If someone did want to give my code a try here's the link :). The TWI debugging I'm doing is not on the master branch. It's on the mpu branch.

 

link

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jkleve wrote:
APM 2.6 board

You mean this: http://www.ardupilot.co.uk/ ?

 

So they must already have working code to do this - have you looked at it?

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

From the MPU6050 datasheet:

The slave address of the MPU-60X0 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is determined by the logic level on pin AD0.

So the 7-bit address is 0x68 or 0x69.

The equivalent 8-bit addresses are 0xD0/0xD1 (W/R) or 0xD2/0xD3.

 

Regarding the code in your link.   It is clear that no one has a clue!

 

I2C is very straightforward.   But only if you follow proven code.

 

David.

Last Edited: Fri. Mar 3, 2017 - 10:55 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ended up being a faulty MPU 6050.