Datum:
Angehängte Dateien:Hallo, anbei relativ einfache AVR TWI Funktionen in C für die Kommunikation zwischen zwei uC. Für den Master habe ich den ATMega32 und den Slave ATMega8 verwendet. Sollte aber auch auf anderen ATMegas laufen, sofern das TWI implementiert ist. Hab's aus diversen Sourcen aus diesem Forum abgeleitet. Da die Funktionen entsprechend dokumentiert sind, sollte die Nutzung relativ einfach sein. Die Funktionen können als Basis verwendet werden, um ein entsprechendes Protokoll zwischen den beiden uC aufzubauen. Vielleicht kann's jemand brauchen, da ich dies in der Form noch nicht im Forum gefunden habe. Als AddOn gibt's noch eine Funktionssammlung für das serielle Interface und Delay-Routinen (Delay_ms(x) und Delay_us (x)). Gruss Manni
Datum:
Hallo Manni, das scheint das Gegenstück zu meinem Assembler - Beispiel zu sein ;) Beitrag "TWI / I2C einf. MASTER SLAVE Beispiel(Assembler) ATmega8" Bernhard
Datum:
Mag sein. Hab's aber aus diversen C Quellen zusammengesetzt. Die Master Quelle kommt von Peter Fleury <pfleury@gmx.ch>. Da es die C Source für den Slave bei Fleury nicht gibt (er steuert I2C ICs an) habe ich sie in ähnlicherweise aufgebaut. Dein Assembler Beispiel habe ich gesehen, wollte aber nicht wieder auf ASM zurückfallen --> Gewohnheitsfaktor. Manni
Datum:
Hallo Manni, ich hab mir deine TWI_Functions mal runtergeladen und ausgetestet. Für den TWI_Master gibt es ja jede Menge an Vorlagen, aber beim TWI_Slave für den AVR sieht es ja eher bescheiden aus (zumindest in C). Da ich aber nun mehrere AVR miteinander verbinden muß, ist das genau das richtige. Bei mir läuft die Kommunikation problemlos und stabil, ich werde jetzt mal auf der Basis ein Protokoll aufsetzen, um entsprechende Befehle zwichen den Controllern austauschen zu können. Viele Grüsse Torsten
Datum:
ist das ein schreibfehler in deiner datei oder dauern 500ms bei dir im programm tatsächlich 1 sec. /* ** Wait 1 second for POR */ Delay_ms (500);
Datum:
2 bascom routinen die man leicht in winavr-c umsetzen kann.
kurz-funktionsfähig und einfach im händling.
-twi-master send-empfang
-twi-slave empfang-send
twi-master send-empfang-------------------------------------------------
$regfile = "m32def.dat"
$crystal = 8000000
$hwstack = 32
$swstack = 32
$framesize = 64
$baud = 19200
Declare Sub Twi_send_byte(byval Slave As Byte , Zeichen As Byte)
Declare Function Twi_read_byte(byval Slave As Byte) As Byte
Dim Twi_control As Byte
Dim Twi_status As Byte
Dim Twi_data_em As Byte
Dim Twi_data_se As Byte
Enable Interrupts
Twcr = &B00000100 ' erstmal nur TWI aktivieren
Twsr = 0 ' Status und Prescaler Register
Twbr = 72 ' Bit Rate Register, 100kHz
Waitms 500
Twi_data_se = 145
Do
Call Twi_send_byte(&H50 , Twi_data_se)
Waitms 250
Twi_data_em = Twi_read_byte(&H50)
Waitms 250
Print Twi_data_em
Loop
End
Function Twi_read_byte(byval Slave As Byte) As Byte
Twi_read_byte = 0 ' Wert vorbelegen
' Startbedingung
Twcr = &B10100100 ' TWINT
' warten bis TWINT gesetzt ist
Gosub Twi_wait_int
' wenn Zugriff auf den Bus erlaubt, Slaveadresse ausgeben
If Twi_status = &H08 Or Twi_status = &H10 Then
Twdr = Slave Or &H01 ' slave adresse + Read
Twcr = &B10000100 ' TWINT löschen, Byte senden
' warten bis TWINT gesetzt ist
Gosub Twi_wait_int
' Slave hat sich gemeldet
If Twi_status = &H40 Then
Twcr = &B10000100 ' TWINT löschen, Byte senden
Gosub Twi_wait_int
' ein Byte wurde empfangen
If Twi_status = &H58 Or Twi_status = &H50 Then
Twi_read_byte = Twdr ' Daten lesen
End If
End If
Else
' Bus belegt, wird er wieder freigegeben
Twcr = &B10000100 ' TWINT löschen, Bus freigeben
End If
End Function
Sub Twi_send_byte(byval Slave As Byte , Zeichen As Byte)
' Startbedingung
Twcr = &B10100100 ' TWINT
' warten bis TWINT gesetzt ist
Gosub Twi_wait_int
' wenn Zugriff auf den Bus erlaubt, Slaveadresse ausgeben
If Twi_status = &H08 Or Twi_status = &H10 Then
Twdr = Slave And &HFE ' slave adresse + Write
Twcr = &B10000100 ' TWINT löschen, Byte senden
' warten bis TWINT gesetzt ist
Gosub Twi_wait_int
' Slave hat sich gemeldet
If Twi_status = &H18 Or Twi_status = &H20 Then
Twdr = Zeichen ' Daten
Twcr = &B10000100 ' TWINT löschen, Byte senden
' warten bis TWINT gesetzt ist
Gosub Twi_wait_int
End If
Else
' Bus belegt, wird er wieder freigegeben
Twcr = &B10000100 ' TWINT löschen, Bus freigeben
End If
End Sub
Twi_wait_int:
Do
Twi_control = Twcr And &H80
Loop Until Twi_control = &H80
Twi_status = Twsr And &HF8
Return
twi-slave
empfang-send---------------------------------------------------
$regfile = "m32def.dat"
$crystal = 8000000
$hwstack = 32
$swstack = 32
$framesize = 64
$baud = 19200
Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 ,
Db7 = Portc.5 , E = Portc.6 , Rs = Portc.7
Config Lcd = 20 * 2
Dim Twi_control As Byte
Dim Twi_status As Byte
Dim Twi_data_em As Byte
Dim Twi_data_se As Byte
Declare Sub Twi_sla_se_em()
Declare Sub Twi_init_slave()
Enable Interrupts
Twi_data_em = 0
Twi_data_se = 77
Call Twi_init_slave
Cls
Do
Waitms 300
Call Twi_sla_se_em()
Cls
Lcd Twi_data_em
Loop
End
Sub Twi_sla_se_em()
' schauen ob TWINT gesetzt ist
Twi_control = Twcr And &H80 ' Bit7 von Controlregister
If Twi_control = &H80 Then
Twi_status = Twsr And &HF8 ' Status
' wurde ein Byte geschickt
If Twi_status = &H80 Or Twi_status = &H88 Then
Twi_data_em = Twdr ' neue Daten merken
End If
' will der Master ein Byte haben
If Twi_status = &HA8 Or Twi_status = &HB8 Then
Twdr = Twi_data_se ' neue Daten ausgeben
End If
' TWINT muss immer gelöscht werden, damit es auf dem Bus weiter
geht
Twcr = &B11000100 ' TWINT löschen, erzeugt ACK
End If
End Sub
Sub Twi_init_slave
Twsr = 0 ' status und Prescaler auf 0
Twdr = &HFF ' default
Twar = &H50 ' Slaveadresse setzen
Twcr = &B01000100 ' TWI aktivieren, ACK
einschalten
End Sub
Datum:
hallo manni! ich versuche in den letzten tagen dein programm zum laufen zu bringen! ich benutzte einen atmega16 als slave und einen atmega 2561 als master! zum deguggen lasse ich mir über printf fktionen texte ausgeben! allerdings streikt bei mir die printfunktion sobald ich werte ausgeben möchte zbsp: printf ("jetzt kommt die zahl: %d \n", test1); mein programm bzw meine terminals für die com schnittstelle hängen sich dann immer auf! ich verwende die aktuelle avr studio 4.14 version und das im moment neuesete winavr. in anderen foren habe ich gelesen dass ich über die configurtion options noch die bibliotheken lbprintf_flt.a und libprintf_min.a und bei den custom options beim linker die -Wl,-u,vfprintf und -lprintf_flt miteinfügen soll. allerdings alles käsekuchen, keine verbesserung oder verschlechterung kennst du oder jemand anderes den Fehler? vilen dank
Datum:
Angehängte Dateien:Na Hoppla, habe ganz vergessen, dass ich die TWI S/W ins Forum gestellt hatte. Deshalb antworte ich jetzt hier erst so spät. Trotzdem freut es mich, dass jemand die S/W verwenden kann. @ neuer( Gast) Das ist ein reiner Schreibfehler. Die Funktion Delay_ms macht das was sie soll: Parameter sind die Millisekunden. Zur BAS software kann ich nichts hinzufügen. BAS habe ich vor 20 Jahren verlassen :-) als K&R mit C DER Standard wurde. Wiess bis heute nicht, warum es immer noch Menschen gibt, die mit BAS rumspielen !?! @ hans-jakob Ich habe die S/W mit ATmega32 und -8 getestet. Sollte also auch mit ATmega16 gehen. Zum atmega 2561 kann ich leider keine Aussagen machen, da ich mit dem noch nicht gearbeitet habe. S/W-mäßig sollten aber alle Atmel uC funktionieren, da alles H/W unabhängig ist, wenn die Baudrate und das transmission protokoll richtig eingestellt ist mit: /* ** Set transmission type, Asynchron 8N1 */ UCSRC |= (1 << URSEL)|(3<<UCSZ0); Bei mir läuft alles mit 8MHz Clock. Ich habe drüber hinaus keine zusätzlichen Bibliotheken wie lbprintf_flt.a und libprintf_min.a gelinkt. Optimisation ist -O0. Anbei zur Hilfe noch mein Makefile, welches das Default ist, welches Winavr generiert (habe nichts dran geändert). Was nicht funktioniert sind printf(s) von float/double Zahlen, wie in den Comments in der Datei RS232.c dargestellt. Grüße Manni
Datum:
@ Torsten K. Wenn du da was zusammengestellt hast, d.h. Protokoll, würde mich das Ergebnis wirklich interessieren, da ich für meine Anwendung was im HauRuck-Verfahren zusammengestellt habe mit Tausend if und else Anweisungen, also nicht professionelles, sollte nur schnell funktionieren. Gruß Manni
Datum:
Hi ich benutze auch diese Funktionen. Senden von Daten vom Master zum Slave funktioniert einwandfrei! Allerdings bleiben beide µC beim Lesen des Slaves irgendwie hängen! Habe die Funktion in main ganz leicht abgeändert! Hab aber eigentlich nur printf rausgeworfen, die For schleife und delay_ms. So sieht es nun beim Slave aus
case TWIS_WriteBytes: uint8_t Wert = 123; if ((PIND & _BV(PD6)) == 0) { Wert = 123; TWIS_Write (Wert); } else { Wert = 1; TWIS_Write (Wert); } TWIS_Stop (); break; } |
und so beim Master
case 4 : if (!TWIM_Start (SlaveAddress, TWIM_READ)) { TWIM_Stop (); } else { Data[0] = TWIM_ReadAck (); Data[7] = TWIM_ReadNack (); TWIM_Stop (); //Delay_ms (1000); } if (!PC_Com(4,Data[0],Data[7])) { } break; |
Hat jemand ne ahnung woran das liegen kann?
Datum:
hab jetzt herausgefunden dass die Controller nicht wirklich hängen. Es ist so dass der Master ohne PRobleme Senden kann. Es ist auch möglich daten vom Slave zu empfangen. Allerdings hängt sich der Master Controller auf wenn im nachhinein wieder senden ausgeführt wird. Und zwar weil im Slave gar nicht mehr empfangen ausgeführt wird sondern nur noch senden. Ich hoffe das ist soweit verständlich! Aber trotzdem warum geht es nicht so wie es gehen sollte?
Datum:
@Thomas Jooooo, das "Hängenbleiben" hatte ich auch schon mal. Ist aber leicht verständlich, wenn nicht darauf geachtet wird, dass ZUERST der Slave eingeschaltet wird (d.h. power on mit reset). Und erst dann darf der Master eingeschaltet werden. Wenn diese Regel nicht eingehalten wird, hängt der Slave, weil er nicht richtig das Master command empfangen konnte. Du hast in deiner Antwort eigentlich schon die Antwort zur Lösung gegeben. D.h. du hast die Anweisung delay_ms rausgeworfen. Und genau diese ist es, die benötigt wird. Bei meinen Versuchen habe ich beide Controller mit der gleichen Stromversorgung verdrahtet. D.h. beide Controller laufen zur gleichen Zeit hoch, inkl. interner Reset-Prozedur. Im Master habe ich dann einen Delay von 1 sec eigebaut und im Slave 0.5 sec. Damit ist IMMER sicher gestellt, dass der Slave eher empfangsbereit ist, als dass der Master senden kann. In wie weit man diesen delay "runterdrehen" kann, habe ich noch nicht ausprobiert. Aber ich vermute, dass man z.B. mit 2 ms im Master und 1 ms im Slave auch leben kann. Ich hoffe das hilft. Gruß Manni
Datum:
Hallo, warum wird denn die Adresse des Device nach links geschoben? Damit ist erst einmal ein Test mit anderen I2C Bausteinen ausgeschlossen, wenn man es nicht sieht in der Anweisung!! /* ** Send device address */ TWDR = (Address<<1) + TWIM_Type; TWCR = (1<<TWINT)|(1<<TWEN); /* Gruss Bernd
Datum:
Angehängte Dateien:Wieso sollen damit andere I2C Bausteine nicht angesteuert werden? Das I2C Protokoll sagt doch eindeutig, dass bei einer START Operation die ersten 7 rausgeschriebenen Bits die Adresse definieren und das 8. besagt, ob es eine READ oder WRITE Operation ist. Du mußt halt die Adresse bei A0 beginnen zu zählen, und nicht das R/W bit in die Adresse einbeziehen. Siehe attached Beispiel vom LM75 Temperatursensor. Gruß Manni
Datum:
>...das "Hängenbleiben" hatte ich auch schon mal. Ist aber leicht >verständlich, wenn nicht darauf geachtet wird, dass ZUERST der Slave >eingeschaltet wird (d.h. power on mit reset). Ich habe das Problem so gelöst, dass permanent der Bus, also SDA & SCL überwacht wird. Sollte der Bus "hängen" dann resetet sich der Slave nach einigen Sekunden selber. Sollte der Bus weiterhin hängen, dann resetet sich nach weiteren Sekunden der Master. Läuft seit Jahren problemlos ;-) Bernhard
Datum:
@Bernhard Kannst du mir mal ein paar Tips geben, wie du das implementiert hast ? Läuft das mit Watchdog oder wertest du das Status Register vom TWI näher aus ?
Datum:
>Kannst du mir mal ein paar Tips geben, wie du das implementiert hast ? >Läuft das mit Watchdog oder wertest du das Status Register vom TWI näher >aus ? Ich hab's so gelöst (gibt bestimmt noch bessere Löungen): Soll per TWI ein Byte bzw. mehrere Bytes gesendet werden wird durch den Master einige ms lang geprüft, ob SDA und SCL auf high sind. Ist ein PIN permanent auf LOW oder findet ein Pegelwechsel bei SDA oder SCL statt, dann kann nicht gesendet werden, dann erfolgt nach einigen ms eine erneute Abfrage der Pins, bis dann irgendwan eine Error-Routine die Fehlversuche stoppt. Nach einigen Minuten wird sogar der Master per Watchdog geresetet. Desweiteren werden im Sekundentakt (z.B. Timer oder in der Hauptschleife) SDL und SDL überwacht. Ist sekundenlang ein PIN permanent auf LOW (also kein Pegelwechsel) dann wird zuerst TWI gestoppt und abgeschaltet, wenn das nicht hilf erfolgt ein Reset über den Watchdog beim Master und beim Slave.
Datum:
Danke dir für die Tips. Damit ist die Kommunikation so ziemlich wasserdicht, auch wenn's mal "klemmt". Das produziert natürlich 'ne Menge Overhead in der Gesamt-Kommunikation, speziell wenn kleine, aber viele Datenpakete übertragen werden sollen. Ich werd mal sehen, wie ich das implementieren kann. Besten Dank und Gruß Manni
Datum:
Hallo,
nach langer Zeit melde ich mich mal wieder zu Wort.
Ich habe ja auch diese Routinen verwendet, nur mit dem Unterschied das
der Slave bei mir nicht gepollt wird sondern per ISR das Ganze erledigt.
Dieses Hängenbleiben hatte ich auch bzw. konnte der Slave einmalig eine
Anfrage vom Master beantworten und danach ging bei der Kommunikation
nichts mehr. Die Controller ansich waren aber noch arbeitsfähig und
andere Funktionen gingen noch. Nach langer Suche bin ich über
Debugausgaben dann hinter das Problem gekommen. Der Slavecontroller
setzt nach dem ersten Aufrufen der ISR das TWI-Interrupt-Enable-Bit
wieder zurück. Damit ist dann im nächsten Zyklus der Slave "taub" und
beantwortet keine TWI-Anfragen mehr. Warum dieses Bit zurückgesetzt wird
ist mir noch unerklärlich. Ich habe noch nichts darüber gefunden. Als
Abhilfe habe ich einfach beim Aufruf der Stop-Condition das Bit wieder
gesetzt und damit funktioniert die Kommunikation schon seit längerem
ohne Probleme.
void TWIS_Stop (void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)|(1<<TWEA)|(1<<TWIE);
}
Delay's gibt es nur beim Systemstart des Slaves. Diese sind aber
eigentlich gar nicht relevant weil der Slave auch ohne Master sauber
hochläuft und ja sowieso selbst nichts aktiv auf den Bus sendet. Als
Master habe ich den Webserver von Ulrich Radig verwendet und die TWI
hinzugefügt.
Gruß Torsten
Datum:
@Torsten Ich danke dir für den wertvollen Tipp. Da ich das Programm bisher noch nicht im ISR Modus ausprobiert, ist mir diese "Eigenart" des TWI Interfaces noch aufgefallen. Ich werde es auch mal ausprobieren. Frage: Hast du mittlereile das "angekündigte" Protokoll etabliert ? Wenn Ja, kannst du das mal hier posten ? Gruß Manni
Datum:
Manni wrote: > Wieso sollen damit andere I2C Bausteine nicht angesteuert werden? > > Das I2C Protokoll sagt doch eindeutig, dass bei einer START Operation > die ersten 7 rausgeschriebenen Bits die Adresse definieren und das 8. > besagt, ob es eine READ oder WRITE Operation ist. Du mußt halt die > Adresse bei A0 beginnen zu zählen, und nicht das R/W bit in die Adresse > einbeziehen. Hallo, entschuldige die späte Antwort. Dann ist es doch so, wenn ich mit dem Oszi ein Adressbyte lese (z.B. 0x30), dann ist die Adresse also eignetlich 0x18. Ist ja dann logisch, man muss es nur wissen. Ich benutzte die Libary von Procyon(AVRLIB), da wird nichts nach links geschoben. Ist eben wichtig, wie man es benutzt, denke ich. Ich muss mich auf ein vorhandenes System einklinken, un da kann ich eben nur messen mir dem Oszi. Mal sehen , ob ich die Kommunication hinbekomme dieses WE. Gruss Bernd
Datum:
Hi, im Datenblatt des Atmega8 steht auf Seite 190 unter Miscellaneous States, dass wenn ein Busfehler aufgetreten ist, der Fehler erst wieder gelöscht werden muss, damit neue Daten angenommen werden. Deswegen funktioniert es TWIS_Stop auszuführen, da dann genau der Fehler gelöscht wird.
Miscellaneous States There are two status codes that do not correspond to a defined TWI state, see Table 70. Status 0xF8 indicates that no relevant information is available because the TWINT Flag is not set. This occurs between other states, and when the TWI is not involved in a serial transfer. Status 0x00 indicates that a bus error has occurred during a Two-wire Serial Bus transfer. A bus error occurs when a START or STOP condition occurs at an illegal position in the format frame. Examples of such illegal positions are during the serial transfer of an address byte, a data byte, or an acknowledge bit. When a bus error occurs, TWINT is set. To recover from a bus error, the TWSTO Flag must set and TWINT must be cleared by writing a logic one to it. This causes the TWI to enter the not addressed Slave mode and to clear the TWSTO Flag (no other bits in TWCR are affected). The SDA and SCL lines are released, and no STOP condition is transmitted. |
Datum:
Hallo, erstmals vielen Dank an Manni für seine Arbeit und dafür, dass er andere daran Teil haben lässt !!! Hier eine kurze Frage zu Deiner Slave Init Funktion:
*******************************************************/
uint8_t TWIS_Init (uint8_t Address, uint32_t Bitrate)
{
TWBR = ((F_CPU/Bitrate)-16)/2;
if (TWBR < 11)
return FALSE;
TWAR = (Address << 1);
TWCR = (1<<TWEN)|(1<<TWEA);
return TRUE;
}
|
Brauchts die Baudrateninitialisierung für den Slave eigentlich? Der sollte doch den "Clock" vom Master bekommen, oder? Oder ist das nur für die Baudratenprüfung, ob der Slave den Takt überhaupt schafft ? Grüße, Michael
Datum:
Hallo, ich habe mir die Slave Routinen von Manni runtergeladen und bei mir ausprobiert. Leider habe ich dabei keinen Erfolg. Trotzdem erstmal vielen Dank an dieser Stelle. Das Slave-Device (ein Atmega168) sendet kein ACK (SDA bleibt high) obwohl ich die richtige Adresse auf den Bus lege. Als Slave verwende ich ein komerzielles RS232->I2C Modem, welches in vielen Anwendungsbereichen ohne Probleme funktioniert hat und will ich daher als Fehlerquelle ausschließen. Das die richtige SlaveID versendet wird kann ich kontrolliern, da ich die Pegel des Buses via Oszi nachverfolgen kann. Ich habe den Code von Manni 1:1 genommen (ein paar Änderungen an RS232.h, so dass es mit einem ATmega168 compiliert) Gibt es etwas auf das man sonst noch achten muss? Fusebits, .. ? Grüße, Thorsten
Datum:
Hallo zusammen, erst mal danke an Manni für die Arbeit. Ich den Code malausprobiert(1:1), leider klappts bei mir nicht so ganz. Ich benutze die selben AVR´s wie Manni. Es scheint als ob gar keine Signale auf den Bus kommne, denn SCL und SDA bleiben high. Hab ich vielleicht vergessen den PORT zu Konfigurieren? Sollten doch beim Master die Pin´s C.0 und C.1 für SCL/SDA oder? Im voraus danke für euere hilfe. Gruß Dennis
Datum:
Uuuuups, da kommen ja weitere Anfragen zu den TWI Funktionen von mir. Sorry dass ich jetzt dazu kommen, mal wieder ins Forum reinzuschauen, da ich leider anderweitig busy war --> Umzug und Haus renovieren. Damit lag das Hobby am Boden, bzw. in den Umzugskisten. Ich werde mir die Kommentare/Fragen von Michael, Thorsten und Dennis mal etwas genauer zu Gemüte führen und mal schauen, was da ggf. nicht stimmt, denn all diese Probleme hatte ich bis jetzt --> alles läuft auch über längere Zeit konstant und ohne zu mucken. I come back !!!! Manni
Datum:
Hallo zusammen, mir ist da etwas im Code aufgefallen was mich leicht stutzig machte. Es handelt sich um die Schreibroutine vom Master, genauer gesagt um die Returnwerte. Die sind doch vertauscht worden oder irre ich mich da? orginaler Auszug aus "TWI_Master.c" meine Komentare beginnen mit //ramius:
uint8_t TWIM_Write (uint8_t byte) {
//ramius: zur Verbesserung der Übersichtlichkeit fehlen die ersten Codezeilen hier im Post
twst = TWSR & 0xF8;
if (twst != TWI_MTX_DATA_ACK) return 1; //ramius: hier FALSE
return 0; //ramius: hier TRUE
}
|
mfg ramius
Datum:
Es ist üblich, daß 0 für "alles in Butter" steht und jeder andere Wert für "Fehler". Z.B. auch Windowsprogramme machen das so. Eine Routine oder ein Programm kann ja aus mehreren Gründen scheitern und dann kann man mit dem Wert die konkrete Fehlerquelle kennzeichnen. Peter
Datum:
Da will ich dir nicht wiedersprechen, jedoch stimmt in dem Fall nicht die Funktionsdokumentation mit dem Returnwert überein und das war eigentlich der Knackpunkt auf den ich hinaus wollte. ((twst != TWI_MTX_DATA_ACK) ist ja wohl nicht "OK, Byte sent") Hatte ich wohl vergessen zu posten. Wer nun nur die Doku liest und bei einem Schreiberfolg auf 1 hofft, wird ja demnach ewig drauf warten können. Auszug aus "TWI_Master.c"
/*******************************************************
Public Function: TWIM_Write
Purpose: Write a byte to the slave
Input Parameter:
- uint8_t Byte to be sent
Return Value: uint8_t
- TRUE: OK, Byte sent
- FALSE: Error in byte transmission
*******************************************************/
|
gruß ramius
Datum:
Hallo zusammen, ich muss meine Aussage verbessern, das Prog. läuft doch. Schreiben mit dem slave und master empfangen klappt ohne probleme.War wohl mein Versuchsaufbau. Was nicht nicht so richtig funzen will, ist das Schreiben vom master bzw. empfangen mit dem slave. Master legt an daten adresse+schreiben auf den Bus, dann passiert allerdings nichts mehr. Grüsse Dennis
Datum:
@Dennis Du könntest ja mal etwas von deinem code posten, dann kann dir vll einer dazu mehr sagen. gruß ramius
Datum:
Angehängte Dateien:Hallo ramius, hier mal der Code. Wie schon gesagt, das Auslesen des Slave funktioniert. Nur das Schreiben mit dem Master klappt nicht. Im voraus danke. Gruß Dennis
Datum:
aus deiner TWI_Slave_main.c Zeile 76
/*case*/ TWIS_ReadBytes; |
ersetze das mal hiermit:
case TWIS_ReadBytes: |
Das war das erste was mir auffiel, nach weiteren Fehlern hatte ich nicht mehr geschaut, kann also sein, dass es noch Probleme geben kann. gruß ramuis
Datum:
Und nun doch noch meine Antworten / Kommentare: @ Michael K. (mmike) vom 24.05.2009 =================================== Wie auf Seite 167 vom ATmega32 Handbuch beschrieben, kann das TWI interface eine Datenrate von bis zu 400KHz ab und diese kann dann noch durch das TWI Bit Rate Rescaler Regsiter (siehe Seite 177) eingestellt werden. Die Formel hierzu findest du auf Seite 173. Wie in meiner Main routine dargestellt, habe ich 100KHz als Übergabewert in der TWIM_Init Routine eingestellt. Ich kann mir nicht vorstellen, dass der Slave automatisch die Bitrate (SCL) erkennt. Das ist auch nirgendwo im Manual beschrieben. Das entsprechende Register (TWSR) muß also genau so eingestllt werden wie das im Master. @ ramius (Gast), 22.09.2009 ========================== Das ist eine typische Übergabekovention, wie Peter schon richtig beantwortet hat. Damit kann man den Funktionsaufruf und die Auswertung direkt kombinieren, wie z.B. if (!TWIM_Write (xxx)) ErrorFunction (); Was 1 und 0 ist, siehe hierzu General.h mit #define TRUE 1 #define FALSE 0 @Dennis P. (dennis_), 29.09.2009 ================================ Dein code kann nicht funktionieren, oder er läuft nur aus versehen, weil du die case Anweisung "misshandelt" hast: /*case*/ TWIS_ReadBytes; anstatt case TWIS_ReadBytes: bzw. falsch implementiert hast. Ich kann mir garnicht vorstellen, dass der Compiler das hat durchgehen lassen. Aber das hat dir ja ramius schon gepostet. Ich hoffe, dass es jetzt funktioniert. Gruss Manni
Datum:
Hallo zusammen, das Programm läuft. Mein Fehler, war die "vergewaltigung" der case Anweisung. Hätte ich selber drauf kommen müssen. Seltsam das der Compiler das ohne zu meckern geschluckt hat. Danke an Manni und ramius für die Unterstützung und die Arbeit. Gruss Dennis P.S. Tolles Forum
Datum:
Na also, es geht doch. Und merke: Niemals zu viel rumfummeln, am Code meine ich ;-) Gruß Manni
Datum:
Hallo Manni, ich hätte da auch noch eine Frage (ich hoffe du hast zeit und lust zum beantworten)... ich benutze dein Programm für 1 Master und 2 Slaves. Das funktioniert auch ganz gut (manchmal muss ich reseten)...jetzt hab ich einen dritten Slave (natürlich auch mit anderer Addresse) dazugeschlossen. Das funktioniert jetzt manchmal, manchmal rührt sich gar nichts mehr. Meine Fragen nun: zur Hardware: ich verbinde Ground,SDA,SCL aller MCUs, habe einen Pull-up 5,5kOhm für je SDA und SCL und eine eigene Spannungsversorgung jedes MCUs. Ist das richtig so? zum Code: Kannst du mir mehr zum Ablauf der Kommunikation erklären bei mehreren Slaves? Wieso muss ich da öfter reseten? Ich habe nur die Adresse im Slave-Code geändert und natürlich dem Master den Befehl für senden an jeden Slave. Danke im Voraus, bob
Datum:
@ Bob 1) 5.5 KOhm ist Okay. Ich verwende 3.3 KOhm. Weniger als 1.5 KOhm sollten es aber laut Datenblatt nicht sein. 2) Falls du für jeden uC eine eigene Stromversorgung hast, solltest du auf die Verdrahtung der Masse-Leitung achten. Am besten alle Slaves sternförmig mit der Masse vom Master verbinden (bloß nicht die Slaves hintereinander verbinden) 3) Der code ist mehr als gut dokumentiert, nahezu jede Zeile - ist auch'n hobby von mir ;-) - daraus solltest du in Verbindung mit dem ATMEL Datenblatt eigentlich alles verstehen. 4) Zu deinem letzten Para: ich gehe mal davon aus, dass du nicht nur im Slave, sondern auch im Master die Adresse des anzusprechenden uC geändert hast. 5) Die Kommunikation mit mehreren Slaves kann zweifach geschehen. 5a) Wenn die Adresse 0h ist, dann ist es ein "General Call", und wird von allen angeschlossenen Slaves gelesen. Hierbei ist zu beachten, dass nicht alle Slaves gleichzeitig eine Antwort senden. Dafür ist der "Generall Call" sowieso nicht vorgesehen. Typische Anwendung ist z.B. alle Slaves "resetten". 5b) Bei allen anderen Adressen wird immer nur der Slave mit der entsprechend im Master gesendeten Adresse angesprochen. Aber bitte immer nur eine Adresse pro Slave. 6) Dein Problem mit dem "Resetten" kommt bei mir nicht vor. Der häufigste Grund hierfür ist, dass der Master noch Daten vom Slave erwartet, aber der Slave sendet keine mehr. Bitte genau darauf achten, sonst kann's nicht funktionieren. Gruß Manni
Datum:
Moin moin! Ich hoffe nichts doppelt zu posten, aber mir ist ein Fehler aufgefallen, der mich nerven gekostet hat: Der Beispiel Code in TWI_Slave_main.c funktioniert. Leider stimmt dieser NICHT mit dem auskommentierten Header aus TWI_Slave.c überein. Darin wird nämlich beim read nicht über die Laufvariable eingelesen, sondern immer in byte[0]. Beim schreiben steht dort in der for-Schleife leider auch nicht immer j. Nicht Weltbewegend, aber hat mich grade 20min gekostet. ... :D Um sie jemand anderem zu ersparen! Viele Grüße, Johannes
Datum:
@manni danke für die beantwortung meiner fragen! zu 4) ja auch im Master.. zu 5) hab Variante 5b gewählt. Funktioniert optimal:)
Datum:
Angehängte Dateien:@Johannes Wo er recht hat, hat er recht. Sorry dass ich dir 20 Minuten deines kostbaren Daseins vermasselt habe, war nicht meine Absicht ;-) Ich habe den Fehler im Quellcode im attached zip file behoben. Ich danke dir und Gruß Manni
Datum:
@Manni Vielen dank für den Code. Ich habe es mal ausprobiert und leider sendet die Slave nur 4 bytes. Auszug aus "TWI_Slave_main.c":
case TWIS_WriteBytes: for (i=0;i<8;i++) { TWIS_Write (i++); printf ("Byte sent: %d\n", i-1); } TWIS_Stop (); break; |
In den for-Schleife wird i Zweimal erhöht mit 1. Wegen i<8 werden dann nur 4 Bytes gesendet und wartet der Master auf den anderen 4 Bytes. Ich habe den Code so geändert und dann funktioniert es:
case TWIS_WriteBytes: for (i=0;i<8;i++) { TWIS_Write (i); printf ("Byte sent: %d\n", i); } TWIS_Stop (); break; |
Viele Grüße, Geert-Jon Jepkes
Datum:
@ Geert-Jon Du hast auch recht. Leider hat mich der Kommentar von Johannes Kreuzer dazu verleitet, den ursprünglichen Quellcode zu verändern, wobei er doch nur den "auskommentierten" Einleitungstext bemängelte. Da war ich halt zu schnell mit meinen Änderungen, denn der ursprüngliche Code vom 4.1.2008 war schon Okay. Also an alle: Bitte das gepostete zip file vom 15.10.2009 nicht verwenden. Es gilt weiterhin der Code vom 4.1.2008. Dank an Geert-Jon !! Gruss Manni
Datum:
Heihei. Clock ist beim Slave IMHO unwichtig da der Master den Clock vorgibt. Der Slave zieht falls nötig den Clock auf Low und senkt somit künstlich die Frequenz. Stretching nennt man das glaubich. I2C/TWI-ICs haben ja auch keinen Clock... Grüsse Yvo
Datum:
Und wieder was dazu gelernt :) Denn auf Seite 173 unten des ATmega32 Manuals steht eindeutig: "Slave operation does not depend on Bit Rate or Prescaler settings, but the CPU clock frequency in the slave must be at least 16 times higher than the SCL frequency. Note that slaves may prolong the SCL low period, thereby reducing the average TWI bus clock period." Scheint ja auch logisch, hab's aber noch nie ausprobiert. Bei dem nächsten S/W Update werde ich das mal berücksichtigen. Dank an Yvo für den Hinweis. Gruß Manni
Datum:
Super Manni, vielen Dank für die Bibliotheken! Klappt prima! Wollte dir einfach mal einen Dank aussprechen. Ich hatte es erst mit den Atmel-Bibliotheken aus den AVR311 Application Notes ausprobiert (Code: http://www.atmel.com/dyn/resources/prod_documents/AVR311.zip, PDF: http://www.atmel.com/dyn/resources/prod_documents/...) um mit einem Atmega8 als Slave in Abhängigkeit der Anfrage verschiedene Daten zu senden. Mein Slave ist nach einer unbestimmten Zeit, meistens nach wenigen Sekunden, "abgestürzt". Ein Watchdog konnte da weiter helfen. Ist aber auch nicht ganz Sinn der Sache. Ich schätze, das der Atmega in den Waitstates aus den Atmel-Bibliotheken hängen geblieben ist. Habe ein bisschen dran rumgedoktort und dann auf deine Bibliotheken angepasst, läuft zuverlässig ohne Abstürze. Danke!
Datum:
Joooo, so soll's auch sein und nicht wie bei AEG: Auspacken, Einschalten, Geht nicht :( Gruss Manni
Datum:
Hallo Manni, vielen Dank für die Libraries! Eine Sache klappt leider bei mir nicht, und zwar eine Nachricht an alle Slaves zu schicken.
/* ** Write byte(s) to ALL slaves. ** It is implicitely assumed, that the slave will ** accepts 8 bytes */ if (!TWIM_Start (0x00, TWIM_WRITE)) // send to all slaves { TWIM_Stop (); printf ("Could not start TWI Bus for WRITE\n"); } else { for (i=0;i<8;i++) { DataSent[i] = i*3 + j; TWIM_Write (DataSent[i]); printf ("Byte %d sent: %d\n", i, DataSent[i]); } TWIM_Stop (); PORTD ^= _BV(PD4); // toggle LED Delay_ms (2000); } |
Der TWI Bus kann dann nicht gestartet werden. Ich habe übrigens nirgendwo im Code gefunden, wo das Bit TWGCE des TWAR gesetzt wird. Wenn ich aber es explizit in TWI_Init der Slaves setze, bleibt das ganze System hängen. Weisst ihr vielleicht, woran das liegt? Vielen Dank im Voraus!
Datum:
Hallo Slog, vielen Dank für deinen Kommentar. Den "General Call" Fall hatte ich bisher noch nicht ausprobiert. Da ich derzeit keine zwei uCs auf meinem Tisch rum liegen habe, kann ich das TWI hinsichtlich des "General Call" leider nicht ausprobieren. Ich habe aber nochmal in der Literatur geforscht und komme zu dem Ergebnis, dass das TWI für diese Funktion erst aktiviert werden muss, d.h. im TWAR Register muss das entsprechende TWGCE Bit enabled werden. Um dies zu aktivieren, ist nur die Slave Funktion TWIS_Init am Ende zu erweitern. Siehe code below. Reporte mal, ob es dann funktioniert. Beste Grüße Manni
uint8_t TWIS_Init (uint8_t Address, uint32_t Bitrate)
{
/*
** Set the TWI bitrate
** If TWBR is less 11, then error
*/
TWBR = ((F_CPU/Bitrate)-16)/2;
if (TWBR < 11) return FALSE;
/*
** Set the TWI slave address
*/
TWAR = (Address << 1);
/*
** Activate TWI interface
*/
TWCR = (1<<TWEN)|(1<<TWEA);
/*
** Enable recognition of a General Call
*/
TWAR = (1<<TWGCE);
return TRUE;
}
|
Datum:
Ich habe das Problem, das ich immer die gleichen Daten vom Slave auslese. Beim ersten mal lesen stehen die korrekten daten drin und beim 2., 3., 4.,... mal stehen dann anscheinend immer die gleichen Werte in TWDR. Die Werte die der Write Funktion übergeben werden ändern sich aber definitiv. Jemand eine Idee, woran das liegen kann ?
Datum:
Schwierig, so das Problem zu identifizieren. Schick mal den Code. Gruß Manni
Datum:
......
TWIS_Init (25);
transmit[0] = pulse01;
transmit[1] = pulse02;
transmit[2] = 0x03;
transmit[3] = 0x04;
transmit[4] = 0x05;
transmit[5] = 0x06;
transmit[6] = 0x07;
transmit[7] = 0x08;
if (TWIS_ResonseRequired (&TWIS_ResonseType))
{
if( TWIS_ResonseType == 0x68 )
{
for (i=0;i<7;i++)
{
byte[i] = TWIS_ReadAck ();
}
byte[7] = TWIS_ReadNack ();
}
else
{
for (i=0;i<8;i++)
{
TWIS_Write (transmit[i]);
}
}
}
.......
TWI_STOP habe ich raus genommen, da es damit nichtmal beim ersten mal
funktioniert. pulse01 und pulse02 sind Lüfterdrehzahlen in rps, kleiner
als 200. Drehen sich die Lüfter während ich den uC einschalte, so lese
ich plausible Werte aus. Halte ich die Lüfter während des Betriebes an,
so ändern sich die ausgewählten Werte nicht.
Datum:
Das kann auch nicht funktionieren, da du den TWIS_ResonseType (sollte
eigentlich "TWIS_ResponseType" heißen, ist halt ein Tippfehler von mir
gewesen) falsch interpretierst.
Es muß heißen:
if( TWIS_ResonseType == TWIS_ReadBytes )
und nicht:
if( TWIS_ResonseType == 0x68 )
denn der return value ist definiert in TWI_Slave.h als:
#define TWIS_ReadBytes 0x60
#define TWIS_WriteBytes 0xA8
Ausserdem solltest du nicht:
else
{
for (i=0;i<8;i++)
verwenden, denn TWIS_ResonseType kann noch viele andere Statuswerte
annehmen, wie in TWI_Slave.c definiert. Verwende besser:
if( TWIS_ResonseType == TWIS_WriteBytes )
Ich hoffe das hilft weiter.
Gruß
Manni
Datum:
Genauso habe ich es schon probiert. Hat leider nicht funktioniert. Möglicherweise habe ich auch noch ein Problem mit meinem Master. Werde das erstmal genauer untersuchen !
Datum:
So, eigentlich funktioniert jetzt alles, was funktionieren soll. Das
einzige Problem was ich noch habe, ist, dass meine ISR´s nach dem ersten
auslesen des TWDR nicht mehr funktionieren. Kriege die Interrups auch
nicht mehr aktiviert. Das auslesen der verschiedenen Daten klappt jedoch
weiterhin einwandfrei.
code:
transmit[0] = 0x01;
transmit[1] = 0x02;
transmit[2] = 0x03;
transmit[3] = 0x04;
transmit[4] = 0x05;
transmit[5] = 0x06;
transmit[6] = 0x07;
transmit[7] = 0x08;
transmit[8] = 0x09;
transmit[9] = 0x10;
if (TWIS_ResonseRequired (&TWIS_ResonseType))
{
if( TWIS_ResonseType == 0x68 || TWIS_ResonseType == 0x60 )
{
byte = TWIS_ReadAck ();
}
else
{
TWIS_Write (transmit[byte]);
}
}
Datum:
Ich habe den Eindruck, dass du den Example Code in TWI_Slave_main.c
nicht verstehst. Da steht doch:
[c]
/*
** Check wether something is to do for the TWI slave interface
*/
if (TWIS_ResonseRequired (&TWIS_ResonseType))
{
switch (TWIS_ResonseType)
{
/*
** Slave is requests to read bytes from the master.
** It is expliciltely assumed, that the master sends 8 bytes
*/
case TWIS_ReadBytes:
// jetzt 7 bytes lesen mit TWIS_ReadAck()
// dann 1 byte lesen mit TWIS_ReadNack()
TWIS_Stop ();
break;
/*
** Slave is requested to send bytes to the master.
** It is expliciltely assumed, that the master expects 8 bytes
*/
case TWIS_WriteBytes:
// jetzt 8 bytes schreiben mit TWIS_Write()
TWIS_Stop ();
break;
}
}
[c/]
Dein Muster-Code sieht da ganz anders aus und du fragst TWIS_ResonseType
weiterhin total falsch ab. Ausserdem sind die stati weitehin falsch mit
0x68 und 0x60. Schau in TWI_Slave.h
Wenn du diese Logik nicht einhälst, kann alles Mögliche passieren, dass
ich aber hier nicht nachprüfen kann.
Versuch's doch einfach mal mit meinem Sample-Code und du wirst sehen, es
geht ohne wenn und aber. Danach kannst du den Code ja für deine
Applikation abändern
Gruß
Manni
Datum:
hallo leute ich habe eine frage ich möchte meinen atmega8 als i2c slave verwenden sie steuern im ende über ihre ports leds ein zentraler master (ebenfalls atmega8) soll alle intelligenz bei sich behalten um ein debugging zu erübrigen (die slaves gibts nachher 3x->somit wirds komplizierter wenn der slave auch fehler enthalten sollte) sprich kurz gesagt: mein master soll nur auf den port schreiben. hat jemand mit ähnlichen progs erfahrung bzw gibt es tipps, die zu beachten sind da ich ein unerfahrener experimentierer bin, bitte ich um hilfe. cheers andy
Datum:
Angehängte Dateien:Hier ist die Lösung für dein Problem. Gruß Manni
Datum:
Hallo Manni,
ich habe noch eine Frage zu deinem Programm.
Und zwar kann ich mir nich ganz erklären was das i++ am schluß von
deinem Programm im Master macht. Wäre nett wenn du mir weiterhelfen
könntest.
/*
** Do something else
*/
i++;
}
return 0;
}
lg monte
Datum:
Ganz einfach: garnichts! Das soll bloß ein Platzhalter sein für "Do something else", also alles andere, was dein Programm so machen soll........ Gruß Manni
Datum:
So jetzt hab ich noch eine Frage. Mit welcher Freuquenz muss der µC laufen. Bekomme nur kommische Zeichen über RS232 heraus. Bin Anfänger! lg monte
Datum:
Bei mir ist die Quarzfrequenz 8 MHz. Aber dies ist egal, weil die Werte für das Baudrate register (UBRRH, UBRRL) mit dem folgenden code aus dem file RS232.c berechnet wird:
/* ** Set baud rate */ UBRRH = (uint8_t) ((SYSCLOCK / (BAUD_RATE * 16L) - 1)>>8); UBRRL = (uint8_t) (SYSCLOCK / (BAUD_RATE * 16L) - 1); |
Dabei ist dann: - BAUDE_RATE = 9600 - SYSCLOCK = 8000000
Datum:
Vielen Dank für Deine Antwort! Ich abe deine Vorlage zum Laufen gebracht. Echt super von Dir das du deinen Code online stellst. Ich hätte da noch ein Frage: Hab jetzt noch versucht deine Variante mit den 3Slaves zu probieren. Dieses läuft aber nicht stabil. Meistens kann die das Programm nicht auf den Bus schreiben. Hast du dieses Programm mal bei dir laufen lassen? Die erste Variante von dir läuft bei mir ohne Probleme.
Datum:
Mir ist gerade noch etwas aufgefallen. Ich habe die zwei Widerstände vom TWI Bus zufällig berührt, beide haben 3,3kOhm, und plötzlich hatte ich eine verbindung. Beide µC haben auch die selbe Spannungsversorgung und dadurch die selbe Masse. Was kann das sein?
Datum:
Na super, wenn es schon mal mit 1 Slave bei dir läuft. Zu deiner Frage mit 3 Slaves verstehe ich dein Frage nicht: "Meistens kann die das Programm nicht auf den Bus schreiben." Ich weiss nicht was du damit meinst. Ausserdem muss ich gestehen, dass ich 3 Slaves noch nie ausprobiert habe. Werde ich wohl nochmal machen müssen. Zum Thema Widerstände und deinem "Berühren" schließe ich, dass anscheindend die Masseverbindungen zwischen dem Master und den Slaves nicht ganz in Ordnung ist. Bitte prüfe dass nochmal nach. Am besten dicke CuL Leitung verwenden und nicht so'n dünnes Drähtchen. Ganz wichtig: Sternleitung von Master zu den Slaves und keine Ringleitung. Gruß Manni
Datum:
Hallo, ich hoffe mir kann Jemand helfen, ich habe gestern den ganzen Tag damit verbracht die Kommunikation zwischen einem AtMega168 als Master und einem AtMega8 als Slave zum laufen zu bekommen... Es läuft sagen wir mal so, halbwegs. Ich kann vom Master ein Byte an den Slave problemlos übertragen, sowie auch ein Byte vom Slave zum Master als Antwort zurück, alles ok soweit. Nun möchte ich aber statt einem Byte, 2 Bytes vom Slave zum Master zurücksenden, das erste Byte makiert die Anzahl folgender Bytes, die der Master anschliessend lesen soll. Auszug aus dem Slave:
switch (I2C_ResponseType)
{
// I2C requests to write a byte to the master.
case I2C_WriteBytes:
SendI2CRequestAnswer();
break;
// I2C requests to read a byte from the master.
case I2C_ReadBytes:
data[0] = I2C_ReadNack(); // Hier kommt das AVR_CMD_GET_DUMMY an
I2C_STATUS = data[0];
I2C_Stop();
break;
}
void SendI2CRequestAnswer(void)
{
switch(I2C_STATUS)
{
case AVR_CMD_GET_DUMMY:
x = I2C_Write(0x01); // 1 byte will follow...
x = I2C_Write(0x2A); // dec 42, die antwort auf die frage des lebens ;)
break;
}
I2C_STATUS = 0;
I2C_Stop();
}
uint8_t I2C_Write (uint8_t byte)
{
uint8_t twst;
TWDR = byte;
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
while (!(TWCR & (1<<TWINT)));
twst = TWSR & 0xF8;
if ((twst != TWI_STX_DATA_ACK) || (twst != TWI_STX_DATA_NACK)) return -1;
return 1;
}
|
Auszug aus dem Master:
uint8_t I2C_Start (uint8_t Address, uint8_t Type)
{
uint8_t twst;
while (1)
{
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)));
twst = TWSR & 0xF8;
if ((twst != TWI_START) && (twst != TWI_REP_START)) continue;
break;
}
TWDR = (Address + Type);
TWCR = (1<<TWINT)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)));
twst = TWSR & 0xF8;
return twst;
}
void I2C_Stop (void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)|(1<<TWEA);
while (TWCR & (1<<TWINT));
}
uint8_t I2C_ReadAck (void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
while (!(TWCR & (1<<TWINT)));
return TWDR;
}
uint8_t I2C_ReadNack (void)
{
TWCR = (1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
return TWDR;
}
Hauptprogramm:
I2C_Start((SlaveAddress << 1), I2C_WRITE);
I2C_Write(AVR_CMD_GET_DUMMY);
I2C_Stop();
I2C_Start((SlaveAddress << 1), I2C_READ);
data[0] = I2C_ReadAck(); // Bis hierhin ist alles OK
data[1] = I2C_ReadNack();
I2C_Stop();
|
Der Master sendet erfolgreich das AVR_CMD_GET_DUMMY, der Slave empfängt und verarbeitet es korrekt, und sendet dann ebenso erfolgreich, das erste Byte=1 (TWSR=b8) und das zweite Byte=42 (twsr=c0) (Ausgabe auf LCD am Slave) Nur leider kommt das zweite Byte nicht richtig am Master an, wenn ich es mit itoa wandel und anzeige (Ausgabe auf LCD am Master) habe ich nur einen Datenmüll, während er das erste Byte korrekt ausgibt, ebenso wenn ich es z.B auf 99 änder. Ich hoffe Jemand kann mich mal auf den richtigen Weg schubsen. Gruss, Gilbert
Datum:
Warum machst du es dir so schwer ? Nimm doch einfach die SW aus dem TWI_Master-Slave_C_Functions.zip file, dann läuft es ohne Klimmzüge. Hinsichtsich deiner SW Auszüge: vergleich doch einfach 1:1 deinen Code mit dem von mir, irgenwo wirst du wohl noch ein Böckchen drin haben. Beste Grüße Manni
Datum:
Danke für den Code!! läuft problemlos! Harry Ach so...SYSCLOCK durch F_CPU ersetzen! ;)
Datum:
Danke dir für den Tip ... es gibt halt Fehler, die man erst nach 10 Jahren bemerkt. Liegt halt daran, dass ich 'nen 100er Pack 8 MHz Quarze in der Kiste habe........... Gruß Manni
Datum:
Angehängte Dateien:Hi Manni,
du bist ja hier der experte auf dem Thema TWI slave,
ich hab deinen Code genutzt nur leider geht es nicht wirklich, hier das
Problem:
Ich hab als master nen AT Mega 2561 mit vielen Slaves, jetzt will ich
auf deinen Slave Daten senden, dafür nutze ich eine Routine die bei den
Anderen gut Funktioniert (Ist auf meinen mist gewachsen) Das sieht wie
folgt aus:
lcd_clear();
print("ok 00",4,1); // print(Text,Zeile,Pos)
// Test mit nem ATMega 8 als Slave
TWI_start(0x0F,Write); // Adresse 15 + das Bit auf 0 zum Schreiben
print("ok 01",4,1); // Daten Schicken Byte 1
// HIER hängt der 2561 und macht nicht weiter Warum?
TWI_write(0x05); // Daten Schicken Byte 2
print("ok 02",4,1);
TWI_write(0x01); // Daten Schicken Byte 2
print("ok 03",4,1);
TWI_write(0x02); // Daten Schicken Byte 2
..... // usw bis 6 Byte versch. wurden
TWI_stop(); // Busverbindung abschalten
print("ok 08",4,1);
Auf dem Slave (ATMega8 mit 8MHz) Programm im anhang kommt er immer bis
er ir auf dem LCD "Init OK" gedruckt hat. Somit springt der nicht in
deine Case Anweisung... Was ist falsch muss ich noch irgendwas beachten?
Bin irgendwie grade mal voll ratlos für hilfe wäre ich super dankbar
MFG Christoph
Datum:
Habe mal den TWSR auf dem Mega8 ausgelesen der sagt mir 0xF8 - Keine Relevanten Informationen, also denke ich das ich das Teil nicht richtig ansteuere oder?
Datum:
Habs schon gefunden! Trotzdem danke der mühen! War nen Fehler in der Adressierung mit dem Verschieben in deiner Software und dem Aufbau in meiner! MFG Schönen Abend noch!
Datum:
Wie du schon sagst, habe ich als Experte dein Problem mal wieder gelöst :-; Nichts für ungut, freut mich, wenn der Code bei dir funktioniert. Scheint ein generelles Verständnis-Problem mit dem Verschieben der Daten-Bits und dem OR'en der Read/Write Anweisung auf Bit 0 zu sein. Da bist du nicht der Erste, der dabei stolpert. Grüße Manni
Datum:
Moin moin^^ Für alle mit Startschwierigkeiten: Denkt an die Pull-Up Widerstände an den Leitungen (SCL + SDA)! Weil ich die vergessen hatte, habe ich mir 3 Nächte um die Ohren geschlagen. Jetzt funktioniert's perfekt! Besten Dank nochmal an Manni für den leicht verständlichen C-Code!^^ Damit sollte jeder klarkommen ;) lg David
Datum:
Moin zusammen, Manni dein SC ist einfach spitze und durch Einfachheit und Übersichtlichkeit kaum zu toppen. Sowohl das Senden als auch das Empfangen klappt bei mir problemlos :-) (Nutze deine Slave-Lib) Doch habe ich Probleme mit dem Repeatet Start, den der Master schickt. Da scheint deine Slave-Lib iwie nicht bei mir zu gehen und SCL verweilt dann immer dauerhaft auf Low. Wenn man die ganze Prozedur mal debuggt, dann stelle ich bei mir folgendes fest: (Zuerst werden Daten gesendet, um anschließend Daten vom Slave per Read zu bekommen) Das Senden geht ohne Probleme, doch wenn dann der Master ein RS schickt, dann wird dass leider nicht erkannt. Auch vom Status-Register (Wert muss 0xA0 sein) nicht. Ganz im Gegenteil. Das Statusregister steht noch auf 0xF8, sprich ich habe gerade das letzte Byte empfangen und kein Acknoledge zurückgesendet. Nach der Funktion
TWIS_Stop (); |
geht er dann in den Status "Nothing to do", obwohl er anscheinend die SCL Leitung untenhält... Ich kann das Repeatet Start aber klar auf meinem Oszi erkennen. :-( Hat jemand vielleicht damit schon Erfahrungen, oder hat jemand nen Code-Schnipsel für mich für die Repeatet Start Bedingung? Oder von mir aus auch nen anderen SC, wo Repeatet Start geht, sodass ich das auf Mannis SC umschreiben kann und und wenns geht natürlich hochlade :-) Danke im Vorraus! Maik
Datum:
Ein Rep. Start ist doch einfach nur eine Startbedingung ohne ein Stop vorher. Also z.B. so:
uint8_t TWIM_Start (uint8_t Address, uint8_t TWIM_Type)
{
uint8_t twst;
/*
** Send START condition
*/
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
/*
** Wait until transmission completed
*/
while (!(TWCR & (1<<TWINT)));
/*
** Check value of TWI Status Register. Mask prescaler bits.
*/
twst = TWSR & 0xF8;
if ((twst != TWI_START) && (twst != TWI_REP_START)) return FALSE;
return TRUE;
}
|
Gruß Fabian
Datum:
Hallo ein Newbie stellt sich zu doof an, ich versuche nun schon seit 2 Tagen den I2C auf einem Atmega8 (3,6864 MHz) zum laufen zu bekommen... Ohne Erfolg. Anscheinend scheitert es schon an der Initialisierung. Am SCL-Ausgang kann ich mit dem Oszi keinen Takt messen. Die Masterplatine ist nicht mit der Slavepaltine verbunden, trotzdem müßte doch ein Takt am Master messbar sein?
/* I2C-Master */ #include <avr/io.h> #include <stdio.h> #include <inttypes.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> #include <string.h> #include <avr/pgmspace.h> #include <util/delay.h> #include <avr/wdt.h> #include "uart.h" //#include "twi.h" #include <util/twi.h> //EMPTY_INTERRUPT(BADISR_vect) void i2c_init(){ cli(); // Sperren aller IRQ's DDRC |= ((_BV(PC4)) | (_BV(PC5)); // PC4 (SDA) + PC5 (SCL) als Ausgang PORTC |= (_BV(PC4) | _BV(PC5)); // Port C Pin4+5 Pullups aktiviert TWBR = 22; // Setzen der TWBR Bits = 22 TWSR = 0; // Prescaler = 1 TWCR |= (_BV(TWEN)); // TWI Enable sei(); // Erlauben aller IRQ's } int main() { i2c_init(); while (1) { } return 0; } |
Manni seine Version (vom 15.10.2009) habe ich ohne Änderung hochgeladen, funktioniert aber auch nicht. Ich habe auch nicht so wirklich einen Plan was ich ggf. anpassen muss. Hat einer von Euch vielleicht einen Tip? Edit: Habe "SYSCLOCK" durch "F_CPU" in Mannis Programm ersetzt... Bekomme jetzt schon mal eine Ausgabe auf dem UART. Also das Hello World wird ausgespuckt und ebenfalls ein "Error in initiating TWI interface".
Datum:
Erstmal danke für die Antwort. Die Sache mit den Fuses kann ich gar nicht sagen, muß ich morgen mal gucken ob ich das ausgelesen kriege... Ich weiß zwar nicht genau was diese machen, aber vielleicht ist die Frage schon beantwortet, das ich den Atmega mit anderen Sachen programmiert bekomme. Sprich Comperator, UART, Gipo, ADC. Wenn nicht versuche ich den Controller morgen mal auszulesen ob/wie die Fusebits gesetzt sind. Aber mit der Annahme liege ich richtig, dass wenn ich den uC wie in meinem Codebeispiel programmiere, zumindest der Takt auf dem Oszi sichtbar sein müsste!?
Datum:
Hallo, über die Fuses wählst Du z.B., ob der interne Oszillator oder der externe Quarz den Takt vorgeben. Bei externem Quarz noch einige andere Details dazu. Das sollte man sich mal ansehen und auch verstehen. Wenn der UART mit der Baudrate funktioniert, die man auf Grund des Takts berechnet hat, würde ich erst mal sagen, das es ok ist. (Aber manchmal steckt der Teufel im Detail) ATmega8 ca. Seite 223
Datum:
Hallo zusammen, ich bin neu hier im Forum und habe mir mit Interesse schon diverse Einträge durchgelesen. Oft konnte ich mit deren Hilfe meine Probleme lösen. Bei meinem aktuellen Projekt möchte ich eine TWI Schnitstelle implementieren und habe den Code von Manni verwendet. Allerdings kommen bei mir schon beim kompelieren folgende Fehlermeldungen. Was bedeuten die?? Schon mal vielen Dank!! Grüße Simon Build started 21.12.2010 at 18:37:23 avr-gcc -mmcu=atmega88 -Wl,-Map=Master.map Master.o -o Master.elf Master.o: In function `main': C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:46: undefined reference to `Delayloop32' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:50: undefined reference to `RS232_Init' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:55: undefined reference to `TWIM_Init' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:70: undefined reference to `TWIM_Start' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:72: undefined reference to `TWIM_Stop' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:79: undefined reference to `TWIM_ReadAck' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:82: undefined reference to `TWIM_ReadNack' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:84: undefined reference to `TWIM_Stop' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:85: undefined reference to `Delayloop32' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:92: undefined reference to `TWIM_Start' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:94: undefined reference to `TWIM_Stop' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:101: undefined reference to `TWIM_Write' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:104: undefined reference to `TWIM_Stop' C:\Users\Simon\Desktop\Master\TWI_Master\default/../Master.c:105: undefined reference to `Delayloop32' make: *** [Master.elf] Error 1 Build failed with 14 errors and 0 warnings...
Datum:
Das bedeutet, daß der Compiler die Funktionen nicht finden kann. Pack doch bitte mal deine main.c dazu, damit man sehen kann, was du geschrieben hast.
Datum:
Angehängte Dateien:Vielen Dank für die zügige Antwort. Ich hab jetzt mal im Anhang alle Dateien reingepackt, die ich im meinem Ordner für das aktuelle Projekt habe. Diese werden ja im "TWI_Master_main.c" inkludiert. Im Endeffekt ist das 1:1 der Master-Quellcode von Manni aus diesem Thread. Das einzige was ich veränder hab, war im "General.h" die Definition der CPU Taktfrequenz (Sysclock durch F_CPU ersetzt). Bin schon gespannt, was ich falsch gemacht hab. Vielen Dank für die Hilfe!!
Datum:
...nachdem ich grad die ganze RS232 Kommunikation rausgeschmissen hab funktioniert die Kompilation. An was liegt das? Ich verwend nen Atmega88.
Datum:
Ich habe ein Problem mit dem Programm. So wie es hier steht funktioniert es einwandfrei. Allerdings versuche ich jetzt eine LED am Slave mit Hilfe des Masters zu toggeln. Der Master sendet mir eine "1", wartet 1 sek und sendet mir eine "0", wartet wieder 1 sek usw. Gleichzeitig gibt er mir über den Uart "LED einschalten" bzw "LED ausschalten" aus. Probleme macht mir das empfangen des Bytes. Theoretisch würde das ganze ja so aussehen: if(byte[i] == 0b00000000) { PORTB = 0x00; } if(byte[i] == 0b00000001) { PORTB = 0xFF; } ->funktioniert nicht... wäre ja auch zu einfach gewesen :D Habe momentan echt keine Ahnung was ich machen kann wäre nett wenn mir jemand helfen würde. PS: seit wann hängt hier captcha drin? Chriss
Datum:
Hi Chriss, hast du das Problem schon gelöst? Wenn nicht, vielleicht liegt es am Datenausgangsregister (DDRB)! Ist das richtig gesetzt? Gruß pc-fan
Datum:
Nein habe das Problem nicht gelöst... das Register ist richtig gesetzt (kann eine LED blinken lassen) Ich werde im Laufe des Tages mal mein Programm hochladen sitze nur gerade am falschen PC ;)
Datum:
Hi Chriss, stellst du den Code noch online oder hat das Problem sich geklärt? Wenn ja, an was lag es denn? Gruß pc-fan
Datum:
Moin, ich hätte da auch noch ein Problem. Slave = Atmega8, Master = Atmega1284p Beim Slave habe ich die Response Abfrage in ein Interrupt gepackt. Hier der Code: Master:
#define F_CPU 20000000UL #define adres 0x2f #include <avr/io.h> #include <util/delay.h> #include "uart.h" #include "TWI_Master.h" int main() { sei(); uart_init(); _delay_ms(500); DDRD |= (1<<DDD7); DDRC |= (1<<DDC2)|(1<<DDC5); if(TWIM_Init(100000)) { uart_puts("TWIM_Init..\n"); PORTD |= (1<<PD7); } if(TWIM_Start(adres,TWIM_WRITE)) { uart_puts("start\n"); if(TWIM_Write('*')) { PORTC |= (1<<PC5); } } TWIM_Stop(); _delay_ms(1000); if(TWIM_Start(adres,TWIM_READ)) { uart_puts("Start read"); unsigned char a = TWIM_ReadNack(); PORTC |= (1<<PC2); uart_putc((char)a); } TWIM_Stop(); while(1) { asm("nop"); } return 0; } |
Slave:
#define F_CPU 8000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <util/twi.h> #include "TWI_Slave.h" unsigned char TWIS_ResponseType; volatile unsigned char a; int main() { sei(); _delay_ms(10); TWIS_Init(0x2F,100000); TWCR |= (1<<TWIE); while(1) { asm("nop"); } } ISR(TWI_vect) { if (TWIS_ResponseRequired (&TWIS_ResponseType)) { switch (TWIS_ResponseType) { case TWIS_ReadBytes: a = TWIS_ReadNack (); TWIS_Stop (); DDRB |= (1<<1); PORTB |= (1<<1); break; case TWIS_WriteBytes: TWIS_Write(a); TWIS_Stop (); break; } } } |
Die Uart Sachen sind nur zum Debugging. Das Prolem ist, dass ich das Byte senden kann, und der Slave empfängt es auch, weil die Led angeht. (DDRB |= (1<<1);PORTB |= (1<<1);) Wenn ich versuche ein Byte zulesen komme ich in die Abfrage if(TWIM_Start(...TWIM_READ)) rein, aber an unsigned char a = TWIM_ReadNack(); scheitert das ganze dann, obwohl er eigentlich ein Byte vom Slave bekommen sollte. Hab ich vielleicht einen Fehler im Programm, oder muss ich in dem Interrupt beim Slave was ändern? Ich habe auchnoch festgestellt, dass beim Master, zwischen der Funktion TWIM_Stop() und TWIM_Start(...TWIM_READ) mindestens 1ms gewartet werden muss, sonst gehts nicht. Ist das richtig? MfG




