PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : TWI kommunikation, Slave läuft nicht



hosti
19.06.2009, 15:44
Guten Tag,

Ich bin zur Zeit dabei einen Atmega644(Master) mit einem Atmega8 (Slave) kommunizieren zu lassen.
Der Mastercode funktioniert soweit(glaube ich), auch die verbindung mit dem Slave funktioniert(Interupt wird aufgerufen).

Dort möchte ich einen bestimmten Wert den ich vom Master erhalte in einer Variable speichern.
Nur leider passiert nichts.
Ausschnitte aus meinem Slavecode:

unsigned volatile char adresse, daten;

void twi_slave(void)
{
TWAR = 0x40;
TWRC = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
TWSR = 0;
};

ISR(TWI_vect)
{
daten = TWDR;
TWRC |= (1<<TWINT);
TWRC = (1<<TWEA) | (1<<TWEN) | (1<<TWSTO);
if (TWSR &0b11111000 == 0x80)
{
if (daten == 0x04)
{PORTB |= (1<<PORTB1);}
else
{PORTB &= ~(1<<PORTB1);}
}
TWRC |= 0b1000000;
};

int main(void)
{
sei();
twi_slave();
DDRB |= (1<<PORTB1);
PORTB &= ~(PORTB1);

while(1)
{}
}

Und mein Mastercode(Alles etwas unübersichtlich, und Beta):
#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros

#define F_CPU 16000000UL //CPU Tackt

unsigned volatile char daten, error;
uint8_t adresse;

void twi_init(void);
int twi_senden(uint8_t adresse, char daten);


int main(void)
{

twi_init();
adresse = 0x40;
daten = 0x04;

while(1)
{
twi_senden(adresse, daten);

};

}

void twi_init(void)
{
DDRC &= !((1<<DD0) | (DD1));
PORTC = (1<<DD0) | (1<<DD1);
TWSR = 0x00; //Prescaler
TWBR = 12; //TWI 400khz
};

int twi_senden(uint8_t adresse, char daten)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); //TWI aktivieren und Start condition auslösen
while(!(TWCR & (1<<TWINT))); //Warten auf Start condition
if((TWSR & 0xF8) != 0x08) return 0;

TWDR = adresse & (0xFE); //Adresse mit Schreibbit(xxxxxxx0) in Register
TWCR = (1<<TWINT) | (1<<TWEN); //senden
if((TWSR & 0xF8) != 0x18) return 0;

while(!(TWCR & (1<<TWINT))); //warten auf ACK oder NACK

TWDR = daten; //Byte in Datenregister laden
TWCR = (1<<TWINT) | (1<<TWEN); //senden
while (!(TWCR & (1<<TWINT))); //warten auf ACK oder NACK
if ((TWSR & 0xF8) != 0x28) return 0;

TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); //STOP Conditions auslösen
return 1;
};




Ivh versuche folgendes zu lösen
Ich habe 3 Variablen die ich durch den Master im Slave mit bestimmten Werten setzten möchte.
Mit TWI wird ja immer ein Byte gesendet soweit ich das verstanden habe.
Ich möchte aber einen wert von 1000-2000 (also 10Bit) übertragen.
Desweitern muss ich ja entscheiden welche Variable im Slave angepasst wird.


Hat jemand eine Idee wie man das lösen könnte?

Ich denke das auswählen der Variable läst sich mit switch lösen.
Sprich ich gebe jeder Variable eine Nummer und übermittle diese um zu bestimmen welche Variable gesetzt werden soll.
Nur wie übermittle ich den eigentlichen Wert, so das er auch in die richtige Variable geschrieben wird. Mit der zusätzlichen Schwierigkeit das der Wert grösser als 8Bit ist.

Ich bin für jede Hilfe sehr dankbar

uwegw
19.06.2009, 16:12
Schau dir erstmal die defines für die Statuscodes in der avr-libc (<util/twi.h>)
an.
if (TWSR &0b11111000 == 0x80)
versteht keiner.
if (TW_STATUS == TW_SR_DATA_ACK)
ist da schon deutlich besser lesbar.

Lass dir mal in der ISR den aktuellen Statuscode ausgeben. Dazu solltest du beim Master ne gewisse Wartezeit zwischen zwei Übertragungen einfügen.

hosti
20.06.2009, 15:55
Ursprünglich hatte ich meinen Code mit der AVR-lib erstellt.
Da dies aber nicht ging habe ich aus "verzweiflung" fragmente von anderen Codes übernommen. Deshalb ist der Code so verbastelt.

Ein konkreter Fehler fählt dir so nicht auf?

uwegw
20.06.2009, 16:06
Hast du jetzt mal ne Version mit den Codes? Dann lässt sich das viel schneller prüfen... und deine Fehlerbeschreibung ist auch noch relativ nichtssagend...

hosti
20.06.2009, 16:59
So, die Masterdatei

#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros
#include <util/twi.h>

#define F_CPU 16000000UL //CPU Tackt

unsigned volatile char daten, error;
uint8_t adresse;

void twi_init(void);
int twi_senden(uint8_t adresse, char daten);


int main(void)
{

twi_init();
adresse = 0x40;
daten = 0x04;

while(1)
{
twi_senden(adresse, daten);

};

}

void twi_init(void)
{
DDRC &= !((1<<DD0) | (DD1));
PORTC = (1<<DD0) | (1<<DD1);
TWSR = 0x00; //Prescaler
TWBR = 12; //TWI 400khz
};

int twi_senden(uint8_t adresse, char daten)
{
uint8_t twst;

while(1)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); //TWI aktivieren und Start condition auslösen
while(!(TWCR & (1<<TWINT))); //Warten auf Start condition
twst = TW_STATUS & 0xF8;
//if((TWSR & 0xF8) != 0x08);
if ((twst != TW_START) && (twst != TW_REP_START))continue;

TWDR = adresse & (0xFE); //Adresse mit Schreibbit(xxxxxxx0) in Register
TWCR = (1<<TWINT) | (1<<TWEN); //senden
//if((TWSR & 0xF8) != 0x18);
while(!(TWCR & (1<<TWINT))); //warten auf ACK oder NACK
twst = TW_STATUS & 0xF8;
if ((twst == TW_MT_SLA_NACK) || (twst == TW_MR_DATA_NACK))
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
while(TWCR & (1<<TWSTO));
continue;
}

TWDR = daten; //Byte in Datenregister laden
TWCR = (1<<TWINT) | (1<<TWEN); //senden
while (!(TWCR & (1<<TWINT))); //warten auf ACK oder NACK
//if ((TWSR & 0xF8) != 0x28);

TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); //STOP Conditions auslösen
return 1;
}
};



Und hier die Slavedatei:

#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros
#include <util/twi.h>
#define F_CPU 16000000UL //CPU Tackt
#define timer 236

unsigned volatile char adresse, daten;

void twi_slave(void)
{
TWAR = 0x40;
TWCR &= ~(1<<TWSTA) | (1<<TWSTO);
TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE); //ack, TWI enable, Interupt
TWSR = 0;
};


ISR(TWI_vect)
{

switch(TW_STATUS)
{
case TW_SR_SLA_ACK:
//Wen ich hier den PORT1 Highschalte klappts.
TWCR = (1<<TWEN) | (1<<TWIE) | (1<<TWINT) | (1<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWWC);
break;
case TW_SR_DATA_ACK:
//Hier klappts leider nicht mehr, heisst das abfragen dieses Status schlägt fehl
PORTB |= (1<<PORTB1);
break;
}

/*daten = TWDR;
TWCR |= (1<<TWINT);
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
if (TW_STATUS == TW_SR_SLA_ACK)
{
daten = TWDR;
//if (daten == 0x04)
{PORTB |= (1<<PORTB1);}
else
{PORTB &= ~(1<<PORTB1);}
}*/
TWCR |= 0b10000000;
};
int main(void)
{

sei(); //Globale Interupts zulassen
twi_slave();

DDRB |= (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3); //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low

while(1)
{


}

}


So, ich habe den Status stück für Stück abgefragt. Und bei TW_STATUS_DATA_ACK
kriege ich keine Rückmeldung.
Also das heisst für mich das Ansprechen des Slaves funktioniert(TW_STATUS_SLA_ACK) und im weiteren Ablauf ist ein Fehler.
Nur habe ich keine Ahnung wo, noch nicht einmal ob Master oder Slave. Aber ich tendiere auf Master aufgrund der scheiternden Status abfrage im Slave.

Ich habe noch kein warten für das senden drin.
Würdest du das über delay machen oder einen Timer laufen lassen?

uwegw
20.06.2009, 18:37
Du hast dich beim Master ja anscheinend größtenteils bei P. Fleury bedient. Warum übernimmst du seine lib nicht komplett? Es reicht, wenn du den Slave als offene Baustelle hast...

hosti
20.06.2009, 19:43
Es läuft, ich habe vergessen am Schluss TWCR_NACK aufzurufen.

Jetzt versuche ich folgendes zu lösen.
Ich habe 3 Variablen die ich durch den Master im Slave mit bestimmten Werten setzten möchte.
Mit TWI wird ja immer ein Byte gesendet soweit ich das verstanden habe.
Ich möchte aber einen wert von 1000-2000 (also 10Bit) übertragen.
Desweitern muss ich ja entscheiden welche Variable im Slave angepasst wird.


Hat jemand eine Idee wie man das lösen könnte?

Ich denke das auswählen der Variable läst sich mit switch lösen.
Sprich ich gebe jeder Variable eine Nummer und übermittle diese um zu bestimmen welche Variable gesetzt werden soll.
Nur wie übermittle ich den eigentlichen Wert, so das er auch in die richtige Variable geschrieben wird. Mit der zusätzlichen Schwierigkeit das der Wert grösser als 8Bit ist.

Ich bin für jede Hilfe sehr dankbar

uwegw
21.06.2009, 17:17
Schau mal ins Wiki, da findest du meinen Code für nen Slave:
http://www.rn-wissen.de/index.php/TWI_Slave_mit_avr-gcc
Ich hab mich dabei an fertigen I2C-Speicherbausteinen orientiert. Das erste Byte einer Übertragung wählt die Speicherstelle im Slave aus, und dann kann man beliebig viele Daten hintereinander schreiben, die dann fortlaufend abgelegt werden.

hosti
21.06.2009, 17:39
Wen ich den Code richtig verstehe muss ich erst die Position im Buffer übermittel. Z.B. eine 1 um die erste Postition im Buffer zu bestimmen.

Anschliessend kann ich den Wert übermitteln für Position 1 des Buffers.
Der nächste Wert kommt dann in Position 2.... usw.

Ist das so Richtig?

Was mach ich wen ich nicht(Oder nicht immer) alle Positionen des Buffers brauche?
einfach einen Wert in die letzte Position schreiben?

Übrigens ein sehr schön dokumentierter Wiki Eintrag von dir,
er hat mir schon viel geholfen.

uwegw
21.06.2009, 17:58
Wenn du nicht alle Positionen brauchst, hörst du einfach mit der Übertragung auf. Beim nächsten Mal musst du dann aber wieder erst die Startposition senden.

hosti
21.06.2009, 18:10
Jetzt geht mir langsam ein Licht auf.

Das heisst ich muss meine Twi funktion aufteilen.
Ich habe ja jetzt nur twi_init und twi_senden(diese beinhaltet start, adresse, senden und stop)

Dann werde ich diese aufteilen und wie folgt vorgehen.

erstens initialisieren
starten
bufferplatz senden
senden 1
senden 2
senden 3
und anschliessend stop.

Bei neuen werten einfach bei starten wieder vorfahren, aussert die slave adresse ändert; dann neu initialisieren

Ist das so richtig wen ich 3 Werte in den buffer schreiben möchte?

uwegw
22.06.2009, 09:13
Ja das ist die richtige Reihenfolge (wenn du die Adressierung zum Start dazurechnest).

Wenn du von Slave lesen willst, geht das so:
initialisieren
starten
Adresse+Write
bufferplatz senden
repeated start
Adresse+Read
lesen 1
lesen 2
lesen 3
und anschliessend stop.

hosti
22.06.2009, 20:55
Ich habe versucht die Sache umzuschreiben. Aber leider läuft jetzt garnix mehr. Und ich weis einfach nicht warum.
MASTER:

#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros
#include <util/twi.h>
#include <util/delay.h>

#define F_CPU 16000000UL //CPU Tackt

unsigned volatile char adresse;
unsigned volatile char daten;

void twi_init(void);
void twi_start(char adresse);
int twi_senden( char daten);
void twi_stop(void);

int main(void)
{

twi_init();
adresse = 0x40;

while(1)
{
twi_start(adresse);
daten = 1;
twi_senden( daten);
_delay_ms(100);
daten = 100;
twi_senden(daten);
_delay_ms(100);
daten = 200;
twi_senden( daten);
_delay_ms(250);
twi_stop();

};

}

void twi_init(void)
{
DDRC &= !((1<<DD0) | (DD1));
PORTC = (1<<DD0) | (1<<DD1);
TWSR = 0x00; //Prescaler
TWBR = 12; //TWI 400khz
};
void twi_start(char adresse)
{
uint8_t twst;
while(1)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); //TWI aktivieren und Start condition auslösen
while(!(TWCR & (1<<TWINT))); //Warten auf Start condition
twst = TW_STATUS & 0xF8;
//if((TWSR & 0xF8) != 0x08);
if ((twst != TW_START) && (twst != TW_REP_START))continue;

TWDR = adresse /*& (0xFE)*/; //Adresse mit Schreibbit(xxxxxxx0) in Register
TWCR = (1<<TWINT) | (1<<TWEN); //senden
//if((TWSR & 0xF8) != 0x18);
while(!(TWCR & (1<<TWINT))); //warten auf ACK oder NACK
twst = TW_STATUS & 0xF8;
if ((twst == TW_MT_SLA_NACK) || (twst == TW_MR_DATA_NACK))
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
while(TWCR & (1<<TWSTO));
continue;
}
break;
}
}

int twi_senden(char daten)
{


TWDR = daten; //Byte in Datenregister laden
TWCR = (1<<TWINT) | (1<<TWEN); //senden
while (!(TWCR & (1<<TWINT))); //warten auf ACK oder NACK
//if ((TWSR & 0xF8) != 0x28);

//TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); //STOP Conditions auslösen
return 0;

};

void twi_stop(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
while(TWCR & (1<<TWSTO));
}


SLAVE:

#include <avr/io.h> //I/O Port definitions
#include <avr/interrupt.h> //Interrupt macros
#include <util/twi.h> //TWI STATUS

#define F_CPU 16000000UL //CPU Tackt
#define buffer_size 3

unsigned volatile char adresse, daten; //Slaveadresse, Daten
volatile uint8_t buffer_adr;
volatile uint8_t rxbuffer[buffer_size];

//1000 = 1ms(links), 1500 = 1,5ms(mitte), 2000 = 2ms(rechts)
volatile int schulter= 0;
volatile int huefte = 0;
volatile int knie = 0;

void twi_slave(char adresse) //TWI initialisieren
{
TWAR = adresse; //Slaveadresse
TWCR &= ~(1<<TWSTA) | (1<<TWSTO);
TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE); //ack, TWI enable, Interupt
TWSR = 0;
buffer_adr=0xFF;
};

//ACK nach empfangenen Daten senden/erwarten
#define TWCR_ACK TWCR = (1<<TWEN) | (1<<TWIE) | (1<<TWINT) | (1<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWWC)
//NACK nach empfangenen Daten senden/erwarten
#define TWCR_NACK TWCR =(1<<TWEN) | (1<<TWIE) | (1<<TWINT) | (0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWWC)

ISR(TWI_vect) //TWI Interupt (Ausgelöst bei Bus Ereignis)
{

switch(TW_STATUS) //TWI-Statusregister prüfen und agieren
{
case TW_SR_SLA_ACK: //0x60, Slave Receiver wurde adressiert
TWCR_ACK;
buffer_adr = 0xFF; //ACK
break;

case TW_SR_DATA_ACK: //0x80, Slave Receiver Daten empfangen
daten = TWDR; //Daten auslesen
TWCR_ACK;
if (buffer_adr == 0xFF)
{
if(daten<=buffer_size)
{
buffer_adr = daten;
}
else
{
buffer_adr=0;
}
TWCR_ACK;
}
else
{
rxbuffer[buffer_adr] = daten;
buffer_adr++;
if (buffer_adr<(buffer_size-1))
{
TWCR_ACK;
}
else
{
TWCR_NACK;
}
}

break;

TWCR_NACK; //Ende
}

TWCR |= 0b10000000;
};



int main(void)
{

sei(); //Globale Interupts zulassen
adresse = 0x40; //Slave 1
twi_slave(adresse); //TWI initialisieren

DDRB |= (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3); //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low

while(1)
{

schulter = rxbuffer[1];
huefte = rxbuffer[2];
knie = rxbuffer[3];
if(schulter == 100)
PORTB |= (1<<PORTB1);
if (huefte == 200)
PORTB |= (1<<PORTB2);


}

}



Ich hoffe es hat jemand(du?) eine Idee

hosti
24.06.2009, 13:23
Hat keiner eine Idee warum das nicht läuft?