PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] I2C-Kommunikation läuft nicht über längere Zeit (Atmega644PA)



addi241
30.07.2014, 22:39
Hallo Community!

Ich bin schon seit längerer Zeit hier im Forum Mitleser und habe bereits einige nützliche Informationen erhalten, auch für mein aktuelles Projekt (einen Quadrocopter zu bauen :) ). Doch nun stehe ich vor einem Problem, das mich seit Tagen verwirrt. :confused:

Ich habe einen zentralen Controller (Atmega644PA), der den Sensor ausliest (MPU6050), alle Berechnungen durchführt und die Daten für die Geschwindigkeit an die 4 Motorregler (Atmega88A) überträgt.
Die gesamte Kommunikation zwischen den Bauteilen läuft über I2C (mit der fleury-lib) , jedoch nur etwa 0-30sec, danach funktioniert der I2C-Part nicht mehr. Doch nun ein wenig genauer:

Was ich bisher getestet habe:
- Auslesen des Sensors + Berechnung (Winkel, PID (aktuell nur P)) und Ausgabe auf einem LCD Display (es werden die Werte genauso angezeigt wie sie zu einem Motorregler gesendet werden würden) Das funktioniert wunderbar, auch beliebig lange.

- Kommunikation mit den Reglern (Atmega88A als Slave) und Motorsteuerung. Die Regler (+Software) sind ein Nachbau der Regler von Ulrich Radig. Die Motoren fahren langsam hoch und starten wieder von vorne, das Ganze ebenfalls beliebig lange.

Hier die main für die Augabe aufm LCD:


while(1){
while(a!=1); // wait until timer1 overflow (sets a=1) -> constant sample rate (500Hz)
a=0;

get_gyro_data(gyro_data); //gyro_data -> uint16_t , reads raw data from MPU6050
get_acc_data(acc_data);

//complementary filter + angle calculation in degrees
angle_calculation(acc_data,gyro_data,gyro_data_off set,&pitch,&roll); //gyro_data_offset -> uint16_t, pitch/roll -> float

pid_calculation(0,&pitch,&pid_pitch); //pid_pitch -> int16_t
motora+=pid_pitch; //motora -> uint8_t

i2c_send_data_to_lcd(motora); //sends data to lcd display
}

Und hier für die Motoren:


while(1){
while(a!=1); // wait until timer1 overflow (sets a=1) -> 10Hz
a=0;

motora++;
motorb++;
i2c_start(MOTORA+I2C_WRITE); //MOTORA: adress of motor A
i2c_write(motora);
i2c_stop();
i2c_start(MOTORB+I2C_WRITE); //MOTORB: adress of motor B
i2c_write(motorb);
i2c_stop();
}

Da beides so sehr gut funktioniert kann ich eigentlich ein Hardware-Fehler ausschließen. Trotzdem hier noch kurz: Pull-ups für I2C sind 4,7k, der MPU6050 ist über einen Pegelwandler, bestehend aus 2 Transistoren (siehe AN10441), angeschlossen. Stromversorgung ist ein 3S Lipo (mit 78S05 für die 5V). Elkos müssten genug 1mF low-esr drauf sein). Wenn ein Schaltplan hilfreich ist, stell ich ihn auch noch rein.

Tja nur zusammen läuft es nicht wirklich. Wenn ich die berechneten Werte, statt an das Display, an die Regler schicke, dann läuft es maximal 30 Sekunden, dann bricht die Kommunikation zusammen. Innerhalb dieser 30 sec stabilisiert sich aber mein quad 1achsig auf einer Holzkonstruktion (und schwingt dabei, ist aktuell ja nur ein P-Regler). Immerhin etwas :D

Bei den Reglern kommt es zu einem i2c-timeout. d.h. es werden keine neuen Werte mehr vom Atmega644PA versendet und die Motoren gehen aus.
Der Atmega644PA hängt sich aber nicht vollkommen auf. In der ISR für den Timer1 habe ich einen Zähler eingebaut, der eine LED blinken lässt. Gehen die Motoren aus, blinkt diese weiter.
-> d.h. die while(1) schleife hängt irgendwo nach 30sec fest.
Dann habe ich eine Variable drin, die überprüft ob sie von der while(1) schleife zurückgesetzt wurde (d.h. die schleife noch aktiv ist), wenn sie in der ISR abgefragt wird.
Wenn sie nicht zurückgesetzt wurde dann resete ich das TWI und übertrage neue Daten an den Motor (langsames hochfahren), dann läuft er wieder!

Hier die ISR:


ISR(TIMER1_COMPA_vect){
if(b==1){ //d=1 if while(1) in main does nothing -> reset TWI + send new datas to motor
TWCR &= ~((1 << TWSTO) | (1 << TWEN));
TWCR |= (1 << TWEN);
for(int b=0;b<50;b++){ //shows that I2C works again
i2c_start(MOTORC+I2C_WRITE);
i2c_write(b);
i2c_stop();
_delay_ms(100);
}
}

a=1; //for while(1)
DDRB|=(1<<PB1);
if(c==250){
PORTB^=(1<<PB1); //toggle green led (=ISR works)
c=0;
}
c++;
}

Der Fehler, wo es nun hängt liegt offenbar in der Funktion i2c_start(), genauer hier:
Die LED bleibt an, der Controller hängt in dieser Schleife. Anscheinend kommt kein ACK/NACK mehr zurück. Überprüfen kann ich das leider nicht, meine Soundkarte als Oszi ist nicht schnell genug ;) btw die i2c-Frequenz zu senken bringt leider nichts, im Gegensatz selbst TWBR=5; (~760kHz) funktioniert.



unsigned char i2c_start(unsigned char address)
{
//....... other code
PORTB|=(1<<PB3); //red led = on

// wail until transmission completed and ACK/NACK has been received
while(!(TWCR & (1<<TWINT)));

PORTB&=~(1<<PB3); //red led = off
//....... other code
}


Eine Lösung wäre in der ISR TWI zu resetten und dann an den Anfang der Schleife zu springen, aber das ist, soweit ich weiß, ein nogo... Ein Watchdog kommt eher nicht in Frage da ich am Anfang vom Programm ja den Gyro-Offset bestimmen muss und zudem noch andere Einstellungen vornehmen muss. Außerdem suche ich nach einem Weg das Hängenbleiben zu vermeiden. Ständige Resets in einem Quadrocopter sind nicht das wahre ;)

28775

28783 28781 28780 28779 28778 28776 28782 28777

Vielen Dank im Voraus! :D

mfg adrian

oberallgeier
30.07.2014, 23:27
... Kommunikation ... läuft über I2C ... nur etwa 0-30sec, danach funktioniert der I2C-Part nicht mehr. Doch nun ein wenig genauer:
...

...
i2c_start(MOTORA+I2C_WRITE); //MOTORA: adress of motor A
i2c_write(motora);
i2c_stop();
i2c_start(MOTORB+I2C_WRITE); //MOTORB: adress of motor B
i2c_write(motorb);
i2c_stop();
}Hallo Adrian,

willkommen im Forum.

Leider hast Du den I²C-Takt in Deiner Lösung nicht genannt. Aber das ist wohl nicht so wichtig. Ich kenne ähnliche Fehler die ich so lange hatte, bis ich mich mal mit dem I²C-Timing beschäftigt hatte. Der Code, den ich oben blau markiert hatte, scheint mir verdächtig.

Nach dem Stop braucht das System ne Weile bis per I²C wieder geschrieben -- oder gelesen -- werden kann. Wenn man das sofort angeht KANN es funktionieren, muss aber nicht. Und wenns nicht sofort klappt ,dann kann die Kommunikation hängen bleiben. Daher hatte ich bei i2c_start nach einem i2c_stop -- neee, genaugenommen jetzt immer -- eine Abfrage eingefügt, ob der Bus überhaupt bereit ist. Beispiel hier (klick). (https://www.roboternetz.de/community/threads/59388-I%C2%B2C-mit-Bitrate-100-kHz-nur-mit-Treiber?p=601914&viewfull=1#post601914)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Lesen (read back) vom Slave ... mit der Routine aus fleury´s lib
i2c_start_wait(SLAVE_MoCo+I2C_WRITE); // set device address and write mode
i2c_write( laddr ); // write address = laddr, das angepeilte Byte
i2c_stop(); //

while ((i2c_rep_start(SLAVE_MoCo+I2C_READ))) {}// Slave bereit zum Lesen?
//i2c_rep_start(SLAVE_MoCo+I2C_READ); // set device address and read mode
ipwm12 = i2c_readAck(); // Bytes lesen... ab laddr = 0x33/51
soll12 = i2c_readAck(); //
ipwm34 = i2c_readAck(); //
soll34 = i2c_readNak(); // letztes Byte lesen, NAK
i2c_stop(); //

Das Zauberwort heißt bei mir "while" - und dann wartet der Befehl ab, bis der Bus frei ist. Und schon liefs (bei mir). Nun könnte ich Dir das viele Zeugs in den Herstellerschriften zum TWI/I²C bzw. dessen Timing schreiben - kannst Du in den entsprechenden Schriften nachlesen. Vorschlag: probiers mit "while" - vielleicht klappts und dann kannst Du Dir vielleicht das Lesen sparen. Übrigens könnte man bei erfolglosem Warten noch ne Fehlermeldung einbauen. Wenn man möchte.

Viel Erfolg.

addi241
31.07.2014, 21:36
Ja, die Frequenz habe ich vergessen (würden aber in der datei twimaster.c stehen), sie liegt bei 400kHz. Aber wie gesagt, das Problem tritt frequenzunabhängig auf.

Leider funktioniert dein Lösungsansatz bei mir nicht. Ich glaube auch zu wissen warum: nach i2c_stop() wird ja trotzdem im Endeffekt i2c_start() (über den Umweg while() und i2c_rep_start() ) direkt aufgerufen und kann deswegen dort hängen. Ich habe es auch mit kurzen delays zwischen jeder i2c-Operation versucht, brachte recht wenig.

Um mal zu sehen, wo genau das Programm stehen bleibt, habe ich jeweils vor den einzelnen Funktionen eine Led angeschalten, am Ende wieder ausgeschalten. Abwechselnd blieb es bei i2c_start() und i2c_readAck() an.
D.h. es kann nur in folgender Schleife hängen: while(!(TWCR & (1<<TWINT))); (je nachdem was ihr für eine lib benützt, ich verwende die von fleury)

Um diese zu verlassen muss TWINT == 1 sein. Deswegen habe ich in der ISR, statt wie oben das TWI zu reseten und die Motoren neu ansteuern einfach TWCR |= (1<<TWINT); eingegeben. Sozusagen die brachiale Lösung dass das Programm weiterläuft. Und es scheint zu funktionieren!

Was es allerdings genau bewirkt und welche Auswirkungen es genau hat kann ich leider nicht sagen, dazu fehlt mir das Wissen (und die Zeit mich dazu ordentlich einzulesen^^)

Zwar eigentlich keine schöne Lösung und es packt das Problem nicht an der Wurzel, aber es läuft... Es sei denn ich hatte in der letzten halben Stunde einfach nur Glück und es lief auch so :D Sobald ich es mal längere Zeit laufen lassen kann melde ich mich wieder!

addi241
01.08.2014, 23:08
Ich hatte wohl doch nur Glück. :(
Das setzten von TWCR |= (1<<TWINT); in der ISR bringt ein paar mal etwas und das Programm läuft weiter (eine LED an der Stelle, wo ich TWCR |= (1<<TWINT) setze, schaltet um) aber nur für weitere 10 Sekunden... danach bleibt das Programm wieder stehen...
SDA und SCL sind beide High! (dauerhaft)

Da ich mir nun beim besten Willen nicht vorstellen kann, wo das Problem nun liegt werde ich für die Ansteuerung auf PPM o.ä. umsteigen.

Es ist etwas komisch, da das ansprechen der Regler ja bereits funktioniert, ich kann nacheinander z.B. immer größere Werte übergeben, sodass der Motor hochfährt. Nur zusammen mit dem MPU6050 nicht. Aber ich kann wiederum von diesem Sensor die Werte auf einem LCD-Display ausgeben lassen.

Es wäre natürlich weiterhin praktisch wenn jemand eine Lösung hätte I2C zu resetten bzw. wenn die ISR ausgeführt wird, die Schleife zu unterbrechen und an anderen bestimmen Stelle weiterzumachen.

mfg

oberallgeier
01.08.2014, 23:49
... wenn jemand eine Lösung hätte I2C zu resetten ...Versuch mal die Methode von Klebwax (klick hier) (https://www.roboternetz.de/community/threads/59388-I%C2%B2C-mit-Bitrate-100-kHz-nur-mit-Treiber?p=601928&viewfull=1#post601928). Habs (noch) nicht getestet - derzeit ne andere Baustelle.

addi241
05.08.2014, 20:31
Hat leider auch nicht funktioniert :( Mittlerweile glaube ich, dass der Sensor die ganzen Probleme macht und den Bus blockiert.
Habe es jetzt so gelöst, dass ich den Sensor mit Hardware-I2C auslese und die 4 Motorregler mit Software-I2C ansteuere (getrennte Leitungen). Funktioniert bestens!

fulltime
06.08.2014, 21:06
Kein Plan ob dass hilfreich ist, aber solange ds TWINT-Bit gesetzt ist kann keine Altion auf den TWI gestartet werden. TWINT wird ja nach jeder Aktion am I2C gesetzt und anschließend gelöscht werden. Vielleicht wird es bei deinem Problem irgendwann nicht gelöscht und haltet somit den TWI auf.

Bin selbst leider kein Mikrokontroller-Guru, beschäftige mich erst seit einigen Monaten intensiv damit. Wird wohl zu meinem neuen Hobby ^^

Mfg
fulltime