Forum: Mikrocontroller und Digitale Elektronik MPU6050 mit Mikrocontroller TWI


von Michael A. (michael_a108)


Lesenswert?

Hallo Community,

bin neu hier und habe gleich mal eine Frage. Ich versuche über TWI eine
Verbindung zwischen meinen uC (ATmega8) und meinen MPU6050 herzustellen.
Ich versuche die Daten aus einem der vielen Register zu lesen, doch das
funktioniert leider nicht wie gewollt. Es wird nur einmal ein Wert
zurückgegeben und der ist 0. Habe herausgefunden, dass er beim erneuten
Auslesen in der Funktion "twi_start()" hängen bleibt. Vielleicht könnt
ihr mir hier weiterhelfen. Mir wurde gesagt, dass hier viele Leute sind
die sich sehr gut mit den AVRs auskennen. Hoffe ihr könnt mir helfen.
Die Daten gebe ich übrigens über die UART Schnittstelle am Computer aus,
die funktioniert einwandfrei ;)

#include <avr/io.h>
#include <avr/delay.h>
#include "uart.h"
#define MPU6050_ADDRESS 0b11010000
#define REG 0x46
unsigned char twi_read_byte(void);
unsigned char twi_read(uint16_t address);
void twi_write_byte(uint16_t byte);
void twi_write(uint16_t address, unsigned char data);
void iniz_twi(void);
void twi_start(void);
void twi_stopp(void);

void iniz_twi(void)
{
    TWBR = 10;
    TWSR = 1;
}
void twi_start(void)
{
    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
    while(!(TWCR & (1 << TWINT)));
}
void twi_stopp(void)
{
    TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
}
unsigned char twi_read(uint16_t address)
{
    unsigned char data;
    unsigned char _register;

    twi_start();
    twi_write_byte(address);
    twi_write_byte(REG);

    address |= (1 << 0); //LSB auf write setzen

    twi_start();
    twi_write_byte(address);
    TWCR = (1 << TWINT) | (1 << TWEN);
    while(!(TWCR & (1 << TWINT)));
    data = twi_read_byte();
    twi_stopp();

    return data;
}
unsigned char twi_read_byte(void)
{
    unsigned char data;
    data = TWDR;
    TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN);
    while(!(TWCR & (1 << TWINT)));
    return data;
}
void twi_write(uint16_t address, unsigned char data)
{
    twi_start();
    twi_write_byte(address);
    twi_write_byte(REG);
    twi_write_byte(data);
    twi_stopp();
}
void twi_write_byte(uint16_t byte)
{
    TWDR = byte;
    TWCR = (1 << TWINT) | (1 << TWEN);
    while(!(TWCR & (1 << TWINT)));
}
int main(void)
{
    unsigned char _str[255] = "";
    char value;
    uart_iniz();
    iniz_twi();
    while(1)
    {
        value = twi_read(MPU6050_ADDRESS);
        itoa(value, _str, 10);
        write_string(_str); //Uart
        _delay_ms(200);
    }
    return 0;
}

von S. Landolt (Gast)


Lesenswert?

Auch mit geänderter Betreffreihenfolge ist mir, als I²C-Laien, seit 
gestern nicht viel eingefallen. Außer, dass ich mir die Status- bzw. 
Errorcodes in TWSR anschauen würde. Und dass das Programm im start 
hängenbleibt, ist merkwürdig, als würde der Slave den Bus nicht 
freigeben.

von Kai (Gast)


Lesenswert?

Da Du keine Interrupt-Routine verwendest, musst Du vor einem neuen 
Befehl das Interrupt-Register selbst zurücksetzen, sonst überrennst Du 
den I2C im AVR, da Deine Warteschleifen sofort zurückkehren.

von Wolfgang (Gast)


Lesenswert?

Michael A. schrieb:
> address |= (1 << 0); //LSB auf write setzen
Was soll das eigentlich für ein Konstrukt sein. Eine 0-malige 
Verschiebung eines Bitmusters ist relativ wirkungslos.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Wolfgang schrieb:
> Michael A. schrieb:
>> address |= (1 << 0); //LSB auf write setzen
> Was soll das eigentlich für ein Konstrukt sein. Eine 0-malige
> Verschiebung eines Bitmusters ist relativ wirkungslos.

Das kann man schon machen, aber es ist genau verkehrt herum im Programm. 
Ein gesetztes Bit ist eine I²C Read Adresse und ein gelöschtes Bit ist 
eine I²C Write Adresse.
Wenn der MPU6050 also die Grundadresse 0xD0 hat, ist diese die Write 
Adresse und 0xD1 die zum Lesen.

von S. Landolt (Gast)


Lesenswert?

Wie schon gesagt, ich bin Laie. Frage mich aber, weshalb in 
twi_read_byte nach dem 'Single-Byte Read' (data=TWDR) ein weiterer 
Transfer, diesmal mit ACK, gestartet wird - ich hätte gedacht, da käme 
nur noch ein stop.

von Michael A. (michael_a108)


Lesenswert?

Danke S. Landolt für den Hinweis, das schaue ich mir gleich nochmal 
genauer an.

von Michael A. (michael_a108)


Lesenswert?

Kai danke für deine Antwort, allerdings glaube würde das nicht die 
unklaren Werte erklären, welche ich bekomme oder?

von Michael A. (michael_a108)


Lesenswert?

Hallo Wolfgang, bei dieser Operation wir ein Bit in das LSB der 8-Bit 
Variable addresse geschrieben. Aus 0b11010000 wird 0b11010001. Hoffe, 
dass erklärt die kleine Unklarheit ;)

von Michael A. (michael_a108)


Angehängte Dateien:

Lesenswert?

Hallo Matthias, könntest du mir genau sagen, wo mein Fehler liegt 
(möglicherweise habe ich etwas vertauscht :) ), denn so weit ich mein 
Programm beurteilen kann, ist die Addresse zunächst auf write (11010000) 
und wird anschließend auf read (11010001) gesetzt.
Freue mich auf deine Antwort ;)

PS. Ich habe im Anhang ein Bild der Single Byte Read Sequence angehängt

von S. Landolt (Gast)


Lesenswert?

> Hallo Matthias, könntest du mir genau sagen, wo mein Fehler liegt

In Vertretung von Matthias S.: das Programm ist an dieser Stelle 
richtig, aber der Kommentar ist falsch:
1
    address |= (1 << 0); //LSB auf write setzen

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

S. Landolt schrieb:
> aber der Kommentar ist falsch

So isses. Falls es immer noch nicht funktioniert, empfehle ich Peter 
Fleurys Lib für I²C. Damit hatte ich bis jetzt immer Erfolg:
http://www.peterfleury.epizy.com/avr-software.html

von Michael A. (michael_a108)


Lesenswert?

Danke für den Vorschlag,aber ich würde gerne wissen, warum MEIN Code 
nicht wie gewollt funktioniert.

von Wolfgang (Gast)


Lesenswert?

Michael A. schrieb:
> Danke für den Vorschlag,aber ich würde gerne wissen, warum MEIN Code
> nicht wie gewollt funktioniert.
Dann guck dir mit dem LA an, an welcher Stelle nicht das erwartete 
Signal auf dem Bus liegt.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michael A. schrieb:
> warum MEIN Code
> nicht wie gewollt funktioniert.

Z.B. ignorierst du völlig das Status Register, das dir schon mal sagen 
könnte, ob da irgendwelchen grundlegenden Komm. Probleme sind.
Hier mal auszugsweise aus der o.a. Lib:
1
// nach der Startcondition
2
  twst = TW_STATUS & 0xF8;
3
  if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;
4
// nach dem Senden eines Bytes
5
  twst = TW_STATUS & 0xF8;
6
  if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
Warum man das Rad allerdings neu erfinden will, ist mir nicht klar.

: Bearbeitet durch User
von Michael A. (michael_a108)


Lesenswert?

Hallo Matthias,
bin grundsätzlich ein neugieriger Mensch, der wissen möchte, warum etwas 
so ist, wie es ist. Programmiere später auch in Assemler, weshalb das 
richtige Verständnis der Dinge spätestens dann sehr wichtig ist..Danke 
für den Hinweis mit dem Status Register;)

von S. Landolt (Gast)


Lesenswert?

> ... Hinweis mit dem Status Register ...

Pardon, aber so weit waren wir schon vor anderthalb Tagen.

von Michael A. (michael_a108)


Lesenswert?

Hallo zusammen, konnte das Problem lösen und somit passt das 
mitlerweile. Danke für die Hilfe

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michael A. schrieb:
> Danke für die Hilfe

Es wäre sicher mal sinnvoll, die Lösung zu posten - kann ja sein, das du 
nicht der einzige bist.

von Michael A. (michael_a108)


Lesenswert?

Naja es gab Probleme beim wiederstarten der TWI, weshalb ich diesmal 
einfach die Stopbedingung weggelassen habe und nur mit Restarts arbeite, 
also ganz gelöst ist es nicht und ist auch nicht sehr professionell, 
aber ein Schritt in die richtige Richtung, werde in den nächsten Tagen 
versuchen auch mit der Stop-Bedingung zu arbeiten ;) . Wenn man aber nur 
EINMAL diesen Code ausführt
    twi_start();
    twi_write_byte(address);
    twi_write_byte(REG);
und man anschließend den folgenden Code in einer Schleife ausführt 
bekommt man eine Reihe von brauchbaren Werten zurück. ACHTUNG: Keine 
Stop-Bedingung schicken, da es (aus noch ungeklärten Gründen) ein 
Problem beim Starten des TWI nach einer Stop-Bedingung gibt.
    address |= (1 << 0);
    twi_start();
    twi_write_byte(address);
    TWCR = (1 << TWINT) | (1 << TWEN);
    while(!(TWCR & (1 << TWINT)));
    data = twi_read_byte();


z.B.

void main(void)
{
    twi_iniz();
    twi_start();
    twi_write_byte(MPU6050_ADDRESS);
    twi_write_byte(REG);
    address |= (1 << 0);
    while(1)
    {
      twi_start();
      twi_write_byte(address);
      TWCR = (1 << TWINT) | (1 << TWEN);
      while(!(TWCR & (1 << TWINT)));
      data = twi_read_byte();
    }
}
Man erkennt, dass ich keine Stopbedingung verwende, sondern immer wieder 
mit dem Restart arbeite. Habe jeden einzelnen TWI-Befehl mittels 
TWSR-Register überprüft, bin aber zu keinem Ergebnis gekommen.

Noch etwas ist wichtig zu wissen. Die ersten paar Werte des MPU6050 sind 
immer "0", anschließend gibt er brauchbare Werte aus. Daher, dass ich 
anfangs nur einen Wert zurückbekam ("0"), kam mir das sehr seltsam vor 
und ich nahm an, dass dies ein Fehler ist. Jetzt bin ich aber beruhigt 
und kann sagen, dass dies kein Fehler ist.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.