Hallo, ich weiß, dass das Thema hier schon häufiger bearbeitet wurde, allerdings komme ich mit den Threads nicht ganz klar und habe hier noch einige grundlegende Probleme. Ich will Daten von einem ATmega16 zum anderen senden und umgekehrt. Wo muss ich Pullup-Wiederstände ein bzw. abschalten? Wie baut mann die Ereignissenke ein um z.B. Daten zu empfangen? Wie aktiviert bzw. deaktiviert mann das Hardware-SPI? Wie müssen die Ports eingestellt werden? Vielleicht hatt mir jemand ein ganz einfaches Beispiel. Danke euch!
Hi, also ich habe jetzt ein Bsp. gefunden und auch die darin vorkommenden Befehle habe ich des Öffteren gesehen. Leider funktionieren diese bei mir nicht. Fehlermeldung : TVEN undeclared (first use in this function) und so geht es mit allen anderen weiter. Diese Dateien habe ich included #include <avr/i2cmaster.h> #include <avr/twimaster.c> Und dann noch ne Frage: Muss eine Funktion generell vorab deklariert werden ? z.B. eine Funktion die ich benutze sieht so aus: void delay_ms(unsigned int ms) { ... } hierfür benötige ich die deklaration - void delay_ms(unsigned int ms); Ist das immer so? hier mal mein source: int sendByte(unsigned char datenbyte, unsigned char adresse){ DDRC &= !((1<<DD0)|(1<<DD1)); // SDA und SCL lesen ... PORTC= (1<<DD0)|(1<<DD1); // ... und Pullups aktivieren // TWBR =0x00; // Schnellser Bitratenwert // TWSR =0x00; Schalte Prescaler aus TWCR= ((1<<TWINT)|(1<<TWSTA)|(1<<TVEN)); // TWI aktivieren und Start-Condition auslösen while(!(TWCR & (1<<TWINT))); // warte auf Start-Condition if((TWSR & 0xF8)!= TW_Start) return 0; // Wenn keine Start-Condition gesendet wurde, abbrechen TWDR=adresse & (0xFE); // Adresse mit Schreibbit (XXXXXXX0) in Datenregister laden TWCR= ((1<<TWINT)|(1<<TWEN)); // ... und senden while(!(TWCR & (1<<TWINT))); // warte auf ACK oder NACK if((TWSR & 0xF8) != TW_MT_SAL_ACK) return twiStop(); // Wenn kein Slave reagiert abbrechen TWDR=datenbyte; // Byte in Datenregister laden... TWCR= ((1<<TWINT)|(1<<TWEN)); // ... und senden while(!(TWCR & (1<<TWINT))); // warten auf ACK oder NACK if((TWSR & 9xF8) != TW_MT_DATA_ACK) return twiStop(); // Wenn nicht von Slave akzeptiert, abbrechen TWCR= ((1<<TWINT)|(1<<TWSTO)|(1<<TVEN)); STOP-Condition auslösen return 1; }
1. Wenn Du die richtigen Headerdateien einfügst, sollten auch alle Register- und Bitnamen bekannt sein. Da Du keinen kompletten Code geschickt hast, kann ich nicht sehen, was Du noch alles eingefügt hast (auf jeden Fall muss die avr/io.h drin sein und im Makefile bzw. AVRStudio muss der richtige Controllertyp eingestellt werden). 2. Eine Funktion muss zum Zeitpunkt ihres ersten Aufrufes bekannt sein. Wenn eine Funktion vor ihrem ersten Aufruf definiert ist (also <typ> Funktionsname(<parameter1>, <parameter2>, ...) {//Code}), dann ist kein Funktionsprototyp erforderlich. Wenn Du allerdings Funktionen im Programm aufrufst, die erst weiter unten definiert werden, dann muss am Programmbeginn ein Prototyp angegeben werden, der dann noch keinen Code enthält. Der Compiler muss zum Zeitpunkt des Aufrufs wissen, dass eine Funktion des Namens existiert, welchen Typ der Rückgabewert hat und welche Typen die Parameter haben. Das musst Du ihm notfalls durch eine Prototypendeklaration mitteilen.
Tausend Dank erstmal. Ich habe jetzt ein besseres Beispiel gefunden, zu welchem ich auch die Headerdateien kenne. Die io.h ist drinn. hier der Source. #include <twi.h> void i2cStart() { // TWINT = Enables the TWI Interrupt Flag // TWSTA = Writes the TWI Start Condition Bit into the Bus (Master) // TWEN = Enables the TWI Hardware TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); while (!(TWCR & (1<<TWINT))); //wait until the Bus is not ready } void i2cStop() { // TWINT = Enables the TWI Interrupt Flag // TWSTO = Writes the TWI Stop Condition Bit into the Bus (nolonger Master) // TWEN = Enables the TWI Hardware TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); //Send Stop } void i2cSendData(unsigned char address,unsigned char data) { TWDR = address << 1; //load Slave Address + Write in TWDR TWCR = (1<<TWINT)|(1<<TWEN); //clear Bits to start transmission while (! (TWCR & (1<<TWINT))); //wait until the Bus is not ready TWDR = data; //load data in TWDR TWCR = (1<<TWINT)|(1<<TWEN); //clear Bits to start transmission while (! (TWCR & (1<<TWINT))); //wait until the Bus is not ready } //???????????????????????????????????????????? unsigned char i2cReadData(unsigned char address) { TWDR = (address << 1) + 1; //load Slave Address + Read in TWDR TWCR = (1<<TWINT)|(1<<TWEN); //clear Bits to start transmission while (! (TWCR & (1<<TWINT))); //wait until the Bus is not ready TWCR = (1<<TWINT)|(1<<TWEN); //clear Bits to start transmission while (! (TWCR & (1<<TWINT))); //wait until the Bus is not ready return TWDR; //return TWDR } Jetzt noch eine Frage. Welche Adresse muss ich angeben, wenn ich die Daten an den anderen ATmega16 senden will? (ich nehme mal an, dass die Daten an den Datenspeicher gesendet werden. Auf dem slave soll lediglich ein Programm zur Abfrage der Daten laufen, über welches beim Empfangen eine LED an einem Pin aktiviert werden soll. )
TWI != SPI Beim TWI gibst du dem Slave eine feste Adresse (im Programm, per EEPROM oder per DIP-Schalter). Ist aber schon "cool", 2-3 Leitungen zu benutzen, um eine LED zu schalten...
Versuche mal, den Code und auch die Funktion des SPI zu verstehen. Den ganzen Adresskram brauchst Du nur dann, wenn Du mehrere Slaves ansprechen willst, ohne für jeden Slave ein eigenes Chip-Select-Signal verdrahten zu müssen. Da Du ja Deinen Angaben zufolge nur einen einzigen Slave hast, brauchst Du auch keine Adressen und demzufolge kannst Du das Codebeispiel oben erheblich zusammenkürzen.
Ach ja, das sehe ich jetzt auch... Er redet von SPI und hat Code mit TWI... Da war ich mal schön blind... Autsch!
@Rahul: Danke, dass Du mich aufgeweckt hast. > Ist aber schon "cool", 2-3 Leitungen zu benutzen, um eine LED zu > schalten... Naja, wenn er dabei was lernt... Später kann er dann ja vielleicht auf zwei LEDs (oder gar mehr) umsteigen.
Also, vergiss mein Posting von 14:12... Zum Thema Adresse: Du kannst jedem Slave in Deinem System eine beliebige Adresse im zulässigen Adressbereich (0...127) geben (wobei natürlich jede Adresse nur einmal vorkommen darf). Wenn Du dem Slave-Mega16 die Adresse 100 gibst, muss der Master eben diese Adresse senden, um den Slave anzusprechen...
Danke euch. <<Ist aber schon "cool", 2-3 Leitungen zu benutzen, um eine LED zu schalten Das soll ja nur erst mal ein Test sein. Später würde ich gerne Messdaten darüber senden, oder den 2.Coltroller dazu veranlassen etwas zu messen, ein Display ansteuern etc. Mit den Grundlagen beschäftige ich mich gerade, aber komme nicht unbedingt klar, denn der Source sieht überall anderst aus und die Beschreibungen sind meist so gehalten, dass mann als newbi auf dem Schlauch steht. Habe mir das Microcontrollenkochbuch besorgt, der Source sieht aber eher wie ein Gemisch aus C und Assembler aus und bringt mich nicht wirklich weiter. SPI - TWI ? TWI benutzt doch SPI oder teusche ich mich da? Dann werden doch auch die vier Leitungen verkabelt oder? MASI, MISO, Takt und die Steuerleitung?
TWI ist die Atmel-Variante des I²C-Bus von Philips (2 Leitungsbus). SPI braucht zwar auch mindestens zwei Leitungen (SCK und MOSI), verwendet aber zum Synchronisieren der Controller (quasi als Startsequenz) und zur Auswahl des Zielcontrollers noch die Chip- bzw Slave-Select-Leitung. Als Rückweg wird dann die MISO-Leitung benutzt. Bei TWI passiert der gesamte Datenaustausch über eine Leitung (SDA), weswegen SPI schneller sein müsste/kann.
SPI überträgt in beiden Richtungen synchron, d.h. Master und Slave senden gleichzeitig. SPI benötigt in der Vollversion 3 Leitungen (MOSI, MISO und SCK) plus die Chip-Select-Signale für jeden einzelnen Slave. Diese CS-Signale sorgen dafür, dass der Verdrahtungsaufwand bei steigender Anzahl der Slaves erheblich zunimmt. Man kann die CS zwar auch multiplexen, aber dann benötigt jeder Slave wiederum einen Demultiplexer, was erheblich mehr Hardware-Aufwand nach sich zieht. I²C (oder TWI) benötigt unabhängig von der Anzahl der Slaves immer nur zwei Leitungen. Dafür muss aber die doppelte Datenmenge übertragen werden (erst Adresse und dann Daten) und der Bus kann nicht in beiden Richtungen gleichzeitig verwendet werden.
Hi, Ich habe noch ein Problem. Und zwar kommt das Programm in der Funktion void i2cStart() bei while (!(TWCR & (1<<TWINT))); aus der Schleife nicht mehr herraus (2ter Source). Habe es bereits mit Pullups (4,7 K) versucht, bringt aber leider nichts. An allen Pinns der Ports habe ich Leichtdioden angebracht. Seltsamerweise leuchten die von PC2(TCK), PC1(SDA) und PC0(SCL) sowiso immer, egal was für ein Programm ich aufspiele (oder komplett lösche). Kann mir das jemand erklähren (auch bei einem ersatz (ATMega16)? Ach ja, und wenn wohl so PORTC= (1<<DD0)|(1<<DD1); // ... und Pullups aktivieren die internen Pullups aktiviert werden, wie schaltet mann die dann wieder ab?
> TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); > while (!(TWCR & (1<<TWINT))); Das macht so anscheinend in Deinem Anwendungsfall auch keinen Sinn. Die erste Anweisung löscht das Interrupt-Flag und dann wird wieder auf einen Interrupt gewartet. Wenn aber niemand sendet, dann tritt dieser Interrupt auch nie auf. Ich vermute mal, dass dieses Codebeispiel (wo auch immer Du es herhast) für eine Anwendung geschrieben wurde, bei der es Sinn macht, an der Stelle zu warten, bis was kommt. Die Kommentare sind übrigens auch irreführend bis falsch.
1 | while (!(TWCR & (1<<TWINT))); //wait until the Bus is not ready |
ist natürlich völliger Blödsinn. Es soll ja nicht gewartet werden, bis ("until") der Bus nicht bereit ist, sonder solange ("while") der Bus nicht bereit ist... Wer auch immer das geschrieben hat, er hätte sich da ein wenig mehr Mühe geben können. Auch
1 | // TWINT = Enables the TWI Interrupt Flag
|
ist Unfug. TWINT ist das Interrupt Flag und "enabelt" gar nichts. Es wird gesetzt, sobald eine Übertragung abgeschlossen ist. Tu Dir selbst den Gefallen und versuche, u.a. anhand des Datenblattes, Schritt für Schritt zu verstehen, was der Code überhaupt macht. Sonst wird das so nichts. Einfach Codeschnipsel von einer fremden Anwendung zu kopieren und hoffen, dass es irgendwie klappt, ist keine sinnvolle Vorgehensweise, wie Du vermutlich so nach und nach festgestellt haben wirst...
Nochmals danke!!! Ja, da hast Du recht. Werd mir morgen das Datenblatt mal anschauen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.