-         

Ergebnis 1 bis 8 von 8

Thema: TWI-Bussystem

  1. #1
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    24
    Beiträge
    601

    TWI-Bussystem

    Anzeige

    Hallo allerseits und einen schönen Samstag,

    ich möchte gerne etwa 8 Controller an ein TWI/I²C - Bussystem hängen, um Daten zwischen denselben austauschen zu können. Vorerst - zu Testzwecken und Einarbeitung - habe ich den Aufbau auf 2 Controller reduziert. Ein RN-Control und ein Mega32 auf einem Steckbrett sollen miteinandern ins Gespräch kommen. Ich habe SCL und SDA der beiden µC's verbunden und mit 10k Pull-Up-Widerständen versehen. Die Leitungslänge des Busses beträgt etwas mehr als 40cm.

    Nun habe ich mich im Wiki schlau gemacht, welche Register usw. man braucht, um die nötigen Einstellungen vorzunehmen. Hier findet man ja eine sehr schöne Zusammenstellung. Bei der Umsetzung in C habe ich mich an diesen Programmbeispielen, am Datenblatt sowie an den ApplicationNotes 311 und 315 orientiert und mal einige Zeilen Code geschrieben.

    Leider gibts noch irgendwo einen Haken, den ich bis jetzt nicht entdecken konnte. Es scheint (für mich) so, als würde der Slave zwar angesprochen werden (kurzes Aufblitzen der LEDs an den Busleitungen), aber keine sinnvollen Daten empfangen können. Zu Testzwecken habe ich mir das empfangene Byte an einem Port ausgeben lassen, an dem ich aber stets den Wert Null messe. Nach stundenlangem Ausprobieren, kontrollieren und ausbessern kann ich keine Fehler mehr entdecken. Aber meist ist es ja so, dass einem die eigenen Fehler am wenigsten auffallen, und drum möchte ich euch bitten, vielleicht mal einen kurzen Blick auf meinen Code zu werfen, vielleicht fällt euch ja was auf.

    Hier die Zeilen für den Master:
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <inttypes.h>
    #include <util/twi.h>
    
    void i2c_init (void);
    uint8_t i2c_sendbyte (uint8_t Adress, uint8_t Data);
    uint8_t i2c_status (void);
    
    volatile uint8_t TWIStatus = 0;
    
    int main (void)
    {
    	DDRD = 0xFF;
    	uint8_t Status = 0;
    
    	i2c_init();
    
    	Status = i2c_sendbyte (26,15);
    
    	PORTD = Status;		//Ausgabe des Status zu Debugzwecken
    
    	while(1);
    
    	return(0);
    }
    
    void i2c_init (void)
    {
    	//Einstellen des Bustaktes auf 400kHz:
    	TWSR = 0;
    	TWBR = 12;
    }
    
    uint8_t i2c_sendbyte (uint8_t Adress, uint8_t Data)
    {
    	uint8_t TWIStatus = 0;	//Variable für Fehlercode
    
    	//Senden der Startbedingung und zurücksetzen des Flags:
    	TWCR = (1 << TWSTA) | (1 << TWINT) | (1 << TWEN);
    
    	//Warten auf Abschluss der Aktion:
    	TWIStatus = i2c_status();
    
    	/* -------------------------------------------------------------------------- */
    	/* Falls der Bus nicht gerade als "busy" gesetzt ist, wurde das TWI-Interrupt */
    	/* Bit gesetzt, der Statuswert kann nun ausgelesen werden.					  */
    	/* Ist kein Fehler aufgetreten, kann die Adresse mit gesetztem Write-Bit      */
    	/* ausgegeben werden.														  */
    	/* -------------------------------------------------------------------------- */
    
    	if ((TWIStatus == 0x08) || (TWIStatus == 0x10))
    	{
    		//Zugriff auf Bus erlaubt, nun wird die Slaveadresse mit gesetztem Write-Bit ausgegeben:
    		TWDR = (Adress << 1) & 0xFE;
    		TWCR = (1 << TWINT) | (1 << TWEN);		//TWINT-Bit zurücksetzen, um nächste Aktion auszulösen
    
    		//Warten auf Abschluss der Aktion:
    		TWIStatus = i2c_status();
    
    		if ((TWIStatus == 0x18) || (TWIStatus == 0x20))
    		{
    			//Slaveadresse wurde erfolgreich gesendet, nun können Daten verschickt werden:
    			TWDR = Data;
    			TWCR = (1 << TWINT) | (1 << TWEN);		//TWINT-Bit zurücksetzen, um nächste Aktion auszulösen
    
    			//Warten auf Abschluss der Aktion:
    			TWIStatus = i2c_status();
    
    			if ((TWIStatus == 0x28) || (TWIStatus == 0x30))
    			{
    				TWIStatus = 0;	//kein Fehler
    			}
    
    		}
    	}
    
    	//Senden der Stoppbedingung:
    	TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
    	return(TWIStatus);
    }
    
    uint8_t i2c_status()
    {
    	//Warten, bis das Interrupt-Bit gesetzt und so das erfolgreiche Ende einer Busaktion angekündigt wurde
    	while(!(TWCR & (1 << TWINT)));
    
    	//Ausmaskieren der Prescaler-Bits und Rückgabe des Statuswertes
    	return(TWSR & 0xF8);
    }
    ... und für den Slave:

    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <inttypes.h>
    #include <util/twi.h>
    
    uint8_t i2c_getbyte(void);
    void i2c_init(uint8_t Adress);
    uint8_t i2c_status(void);
    
    int main (void)
    {
    	DDRD = 0xFF;
    
    	uint8_t buffer = 0;
    	i2c_init(26);
    
    	buffer = i2c_getbyte();		//Empfangen des Bytes
    	PORTD = buffer;		//Ausgabe zur Kontrolle
    
    	while(1);
    
    	return(0);
    }
    
    void i2c_init(uint8_t Adress)
    {
    	TWSR = 0;
    	TWAR = (Adress << 1);
    	TWCR = (1 << TWEA) | (1 << TWEN);
    	TWDR = 0xFF;
    }
    
    uint8_t i2c_getbyte(void)
    {
    	uint8_t Status = 0;
    	uint8_t Byte = 0;
    
    	//Warten, bis der Slave angesprochen wurde
    	Status = i2c_status();
    
    	Byte = Status;
    
    	TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN); 		//TWINT-Bit zurücksetzen, um nächste Aktion auszulösen
    
    	return(Byte);
    }
    
    uint8_t i2c_status()
    {
    	//Warten, bis das Interrupt-Bit gesetzt und so das erfolgreiche Ende einer Busaktion angekündigt wurde
    	while(!(TWCR & (1 << TWINT)));
    
    	//Ausmaskieren der Prescaler-Bits und Rückgabe des Statuswertes
    	return(TWSR & 0xF8);
    }
    Herzlichen Dank fürs Lesen; vielleicht hat ja einer eine Idee.

    Schönes Wochenende,
    ikarus_177

  2. #2
    Super-Moderator Robotik Visionär Avatar von PicNick
    Registriert seit
    23.11.2004
    Ort
    Wien
    Beiträge
    6.836
    Bevor ich die Story der ganzen Bits nachlese:
    Du solltest auf jeden Fall beim Slave das Empfangen INNERHALB der Schleife haben. So macht er ja nur einen einzigen Versuch, dann musst du wieder resetten.
    Also etwa so :
    Code:
       while(1)
      {
           buffer = i2c_getbyte();      //Empfangen des Bytes
           PORTD = buffer;      //Ausgabe zur Kontrolle
       }
       return(0);
    Beim Empfangen vernisse ich auch das Lesen von den Daten selbst (TWDR)
    mfg robert
    Wer glaubt zu wissen, muß wissen, er glaubt.

  3. #3
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    24
    Beiträge
    601
    Hi PicNick,

    ich hab' mal die Endlosschleife beim Slave ergänzt (das fehlende TWDR war zu Testzwecken, er sollte mir den Statuscode zurückgeben); so schauts momentan aus:

    Slave:
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <inttypes.h>
    #include <util/twi.h>
    
    uint8_t i2c_getbyte(void);
    void i2c_init(uint8_t Adress);
    uint8_t i2c_status(void);
    
    int main (void)
    {
    	DDRD = 0xFF;
    
    	uint8_t buffer = 0;
    	i2c_init(26);
    
    	while(1)
    	{
    		buffer = i2c_getbyte();		//Empfangen des Bytes
    		PORTD = buffer;		//Ausgabe zur Kontrolle
    	}
    
    	return(0);
    
    
    void i2c_init(uint8_t Adress)
    {
    	TWSR = 0;
    	TWAR = (Adress << 1);
    	TWCR = (1 << TWEA) | (1 << TWEN) | (0 << TWSTA) | (0 << TWSTO);
    }
    
    uint8_t i2c_getbyte(void)
    {
    	uint8_t Status = 0;
    	uint8_t Byte = 0;
    
    	//Warten, bis der Slave angesprochen wurde
    	Status = i2c_status();
    
    	Byte = TWDR;
    
    	TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (0 << TWSTA) | (0 << TWSTO); 		//TWINT-Bit zurücksetzen, um nächste Aktion auszulösen
    
    	return(Byte);
    }
    
    uint8_t i2c_status()
    {
    	//Warten, bis das Interrupt-Bit gesetzt und so das erfolgreiche Ende einer Busaktion angekündigt wurde
    	while(!(TWCR & (1 << TWINT)));
    
    	//Ausmaskieren der Prescaler-Bits und Rückgabe des Statuswertes
    	return(TWSR & 0xF8);
    }
    Master:
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <inttypes.h>
    #include <util/twi.h>
    
    void i2c_init (void);
    uint8_t i2c_sendbyte (uint8_t Adress, uint8_t Data);
    uint8_t i2c_status (void);
    
    volatile uint8_t TWIStatus = 0;
    
    int main (void)
    {
    	DDRD = 0xFF;
    	uint8_t Status = 0;
    
    	i2c_init();
    
    	Status = i2c_sendbyte (26,15);
    
    	PORTD = Status;		//Ausgabe des Status zu Debugzwecken
    
    	while(1);
    
    	return(0);
    }
    
    void i2c_init (void)
    {
    	//Einstellen des Bustaktes auf 400kHz:
    	TWSR = 0;
    	TWBR = 12;
    }
    
    uint8_t i2c_sendbyte (uint8_t Adress, uint8_t Data)
    {
    	uint8_t TWIStatus = 0;	//Variable für Fehlercode
    
    	//Senden der Startbedingung und zurücksetzen des Flags:
    	TWCR = (1 << TWSTA) | (1 << TWEN);
    
    	//Warten auf Abschluss der Aktion:
    	TWIStatus = i2c_status();
    
    	/* -------------------------------------------------------------------------- */
    	/* Falls der Bus nicht gerade als "busy" gesetzt ist, wurde das TWI-Interrupt */
    	/* Bit gesetzt, der Statuswert kann nun ausgelesen werden.					  */
    	/* Ist kein Fehler aufgetreten, kann die Adresse mit gesetztem Write-Bit      */
    	/* ausgegeben werden.														  */
    	/* -------------------------------------------------------------------------- */
    
    	PORTD = TWIStatus;
    
    	if ((TWIStatus == 0x08) || (TWIStatus == 0x10))
    	{
    		//Zugriff auf Bus erlaubt, nun wird die Slaveadresse mit gesetztem Write-Bit ausgegeben:
    		TWDR = (Adress << 1) & 0xFE;
    		TWCR = (1 << TWINT) | (1 << TWEN) | (0 << TWSTA);		//TWINT-Bit zurücksetzen, um nächste Aktion auszulösen
    
    		//Warten auf Abschluss der Aktion:
    		TWIStatus = i2c_status();
    
    		PORTD = TWIStatus;
    
    
    		if (TWIStatus == 0x18)
    		{
    			//Slaveadresse wurde erfolgreich gesendet, nun können Daten verschickt werden:
    			TWDR = Data;
    			TWCR = (1 << TWINT) | (1 << TWEN);		//TWINT-Bit zurücksetzen, um nächste Aktion auszulösen
    
    			//Warten auf Abschluss der Aktion:
    			TWIStatus = i2c_status();
    
    			PORTD = TWIStatus;
    
    			if ((TWIStatus == 0x28) || (TWIStatus == 0x30))
    			{
    				TWIStatus = 255;	//kein Fehler
    			}
    
    		}
    	}
    
    	//Senden der Stoppbedingung:
    	TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
    	return(TWIStatus);
    }
    
    uint8_t i2c_status()
    {
    	//Warten, bis das Interrupt-Bit gesetzt und so das erfolgreiche Ende einer Busaktion angekündigt wurde
    	while(!(TWCR & (1 << TWINT)));
    
    	//Ausmaskieren der Prescaler-Bits und Rückgabe des Statuswertes
    	return(TWSR & 0xF8);
    }
    Ich habe mir die Statuscodes beim Master mal angesehen, da schauts so aus, als ob er kein ACK vom Slave bekommt, obwohl bei diesem das TWEA-Bit gesetzt ist.

    Viele Grüße

  4. #4
    Super-Moderator Robotik Visionär Avatar von PicNick
    Registriert seit
    23.11.2004
    Ort
    Wien
    Beiträge
    6.836
    Verstehe, den Status auf das Port auszugeben ist keine schlechte Idee, denn wenn der Status passt, kannst du das Byte immer noch lesen.

    In der Mega-PDF ist ja bei TWI eine ganze Speisekarte von Zuständen.
    Sie dort mal nach, was du denn kriegst.

    (ich bin aber für heute weg, ausserdem muss ich mich auch erstmal schlauer machen, da ich das TWI zeugs schon eine Weile nicht mehr programmiert habe)
    mfg robert
    Wer glaubt zu wissen, muß wissen, er glaubt.

  5. #5
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    24
    Beiträge
    601
    Hi,

    laut ATMega-Datenblatt und Statuscodes schauts so aus, als ob der Master kein ACK vom Slave bekommt (Statuscode 0x20), der Slave gibt mir stets den Code 0 aus, was eine "illegal bus operation" bedeuten soll, also START bzw. STOP an den falschen Stellen.

    Viele Grüße

  6. #6
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    31
    Beiträge
    388
    hast du dir meinen code angesehen?

  7. #7
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    24
    Beiträge
    601
    Ja, hab ich gemacht. Vom Aufbau her ist der Teil für den Master auch ganz ähnlich wie meiner. Das Empfangen hast du ja mit den Interrupts gemacht, das würde ich bei mir gerne vermeiden.

    Noch eine kleine Frage: muss der Slave eigentlich verbindlich ein ACK senden? Oder nur, wenn ich das so vorsehe, bzw. braucht die TWI-Hardware dieses Signal, um korrekt weitermachen zu können?

    Viele Grüße

  8. #8
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    24
    Beiträge
    601
    Hallo,

    ich hab in den letzten Tagen nicht recht viel Zeit gehabt, da ich momentan ein Ferialpraktikum mache. Heute hab ich mich aber nochmals hingesetzt, und statt den 10k nun zwei 1k Widerstände als Pull-up's verwendet.

    Mit dem selben Code hab ich nun schon was erreicht, das man ansatzweise als gelungene Übertragung bezeichnen könnte
    Der Slave empfängt zwar Daten, die empfangenen Bytes sind jedoch genau das doppelte dessen, das der Master weggeschickt hat (bzw. um 1 nach links geshiftet). Sehr seltsam das ganze

    Gibt es dafür eine "natürliche" Erklärung?

    Viele Grüße

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •