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
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
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
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
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
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
@ 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
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
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?
@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
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
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
>...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
@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 ?
>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.
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
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
@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
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
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.
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:
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
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
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
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
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:
1
uint8_tTWIM_Write(uint8_tbyte){
2
3
//ramius: zur Verbesserung der Übersichtlichkeit fehlen die ersten Codezeilen hier im Post
4
5
twst=TWSR&0xF8;
6
if(twst!=TWI_MTX_DATA_ACK)return1;//ramius: hier FALSE
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
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"
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
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
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
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
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
@ 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
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
@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
@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":
1
caseTWIS_WriteBytes:
2
for(i=0;i<8;i++)
3
{
4
TWIS_Write(i++);
5
printf("Byte sent: %d\n",i-1);
6
}
7
TWIS_Stop();
8
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:
@ 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
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
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
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/doc2565.pdf) 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!
Hallo Manni,
vielen Dank für die Libraries!
Eine Sache klappt leider bei mir nicht, und zwar eine Nachricht an alle
Slaves zu schicken.
1
/*
2
** Write byte(s) to ALL slaves.
3
** It is implicitely assumed, that the slave will
4
** accepts 8 bytes
5
*/
6
if(!TWIM_Start(0x00,TWIM_WRITE))// send to all slaves
7
{
8
TWIM_Stop();
9
printf("Could not start TWI Bus for WRITE\n");
10
}
11
else
12
{
13
for(i=0;i<8;i++)
14
{
15
DataSent[i]=i*3+j;
16
TWIM_Write(DataSent[i]);
17
printf("Byte %d sent: %d\n",i,DataSent[i]);
18
}
19
TWIM_Stop();
20
PORTD^=_BV(PD4);// toggle LED
21
Delay_ms(2000);
22
}
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!
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
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 ?
......
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.
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
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 !
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]);
}
}
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
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
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
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
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:
1
/*
2
** Set baud rate
3
*/
4
UBRRH=(uint8_t)((SYSCLOCK/(BAUD_RATE*16L)-1)>>8);
5
UBRRL=(uint8_t)(SYSCLOCK/(BAUD_RATE*16L)-1);
Dabei ist dann:
- BAUDE_RATE = 9600
- SYSCLOCK = 8000000
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.
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?
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
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:
1
switch (I2C_ResponseType)
2
{
3
// I2C requests to write a byte to the master.
4
case I2C_WriteBytes:
5
SendI2CRequestAnswer();
6
break;
7
8
// I2C requests to read a byte from the master.
9
case I2C_ReadBytes:
10
data[0] = I2C_ReadNack(); // Hier kommt das AVR_CMD_GET_DUMMY an
11
I2C_STATUS = data[0];
12
I2C_Stop();
13
break;
14
}
15
16
void SendI2CRequestAnswer(void)
17
{
18
switch(I2C_STATUS)
19
{
20
case AVR_CMD_GET_DUMMY:
21
x = I2C_Write(0x01); // 1 byte will follow...
22
x = I2C_Write(0x2A); // dec 42, die antwort auf die frage des lebens ;)
23
break;
24
}
25
26
I2C_STATUS = 0;
27
I2C_Stop();
28
}
29
30
uint8_t I2C_Write (uint8_t byte)
31
{
32
uint8_t twst;
33
34
TWDR = byte;
35
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
36
37
while (!(TWCR & (1<<TWINT)));
38
39
twst = TWSR & 0xF8;
40
if ((twst != TWI_STX_DATA_ACK) || (twst != TWI_STX_DATA_NACK)) return -1;
41
42
return 1;
43
}
Auszug aus dem Master:
1
uint8_t I2C_Start (uint8_t Address, uint8_t Type)
2
{
3
uint8_t twst;
4
5
while (1)
6
{
7
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
8
while (!(TWCR & (1<<TWINT)));
9
10
twst = TWSR & 0xF8;
11
if ((twst != TWI_START) && (twst != TWI_REP_START)) continue;
12
break;
13
}
14
15
TWDR = (Address + Type);
16
TWCR = (1<<TWINT)|(1<<TWEN);
17
18
while (!(TWCR & (1<<TWINT)));
19
twst = TWSR & 0xF8;
20
21
return twst;
22
}
23
24
25
void I2C_Stop (void)
26
{
27
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)|(1<<TWEA);
28
while (TWCR & (1<<TWINT));
29
}
30
31
32
33
uint8_t I2C_ReadAck (void)
34
{
35
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
36
while (!(TWCR & (1<<TWINT)));
37
38
return TWDR;
39
}
40
41
42
uint8_t I2C_ReadNack (void)
43
{
44
TWCR = (1<<TWINT)|(1<<TWEN);
45
while(!(TWCR & (1<<TWINT)));
46
47
return TWDR;
48
}
49
50
51
Hauptprogramm:
52
I2C_Start((SlaveAddress << 1), I2C_WRITE);
53
I2C_Write(AVR_CMD_GET_DUMMY);
54
I2C_Stop();
55
56
I2C_Start((SlaveAddress << 1), I2C_READ);
57
data[0] = I2C_ReadAck(); // Bis hierhin ist alles OK
58
data[1] = I2C_ReadNack();
59
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
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
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
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
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?
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!
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
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
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
1
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
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?
1
/* I2C-Master */
2
3
#include<avr/io.h>
4
#include<stdio.h>
5
#include<inttypes.h>
6
#include<avr/io.h>
7
#include<avr/interrupt.h>
8
#include<avr/sleep.h>
9
#include<string.h>
10
#include<avr/pgmspace.h>
11
#include<util/delay.h>
12
#include<avr/wdt.h>
13
#include"uart.h"
14
//#include "twi.h"
15
#include<util/twi.h>
16
17
//EMPTY_INTERRUPT(BADISR_vect)
18
19
voidi2c_init(){
20
cli();// Sperren aller IRQ's
21
22
DDRC|=((_BV(PC4))|(_BV(PC5));// PC4 (SDA) + PC5 (SCL) als Ausgang
23
PORTC|=(_BV(PC4)|_BV(PC5));// Port C Pin4+5 Pullups aktiviert
24
25
TWBR=22;// Setzen der TWBR Bits = 22
26
TWSR=0;// Prescaler = 1
27
28
TWCR|=(_BV(TWEN));// TWI Enable
29
30
sei();// Erlauben aller IRQ's
31
}
32
33
intmain(){
34
35
i2c_init();
36
37
while(1){
38
39
}
40
return0;
41
}
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".
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!?
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
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...
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!!
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
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 ;)
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:
1
#define F_CPU 20000000UL
2
#define adres 0x2f
3
4
#include<avr/io.h>
5
#include<util/delay.h>
6
#include"uart.h"
7
#include"TWI_Master.h"
8
9
intmain()
10
{
11
sei();
12
uart_init();
13
_delay_ms(500);
14
DDRD|=(1<<DDD7);
15
DDRC|=(1<<DDC2)|(1<<DDC5);
16
17
if(TWIM_Init(100000))
18
{
19
uart_puts("TWIM_Init..\n");
20
PORTD|=(1<<PD7);
21
}
22
23
if(TWIM_Start(adres,TWIM_WRITE))
24
{
25
uart_puts("start\n");
26
if(TWIM_Write('*'))
27
{
28
PORTC|=(1<<PC5);
29
}
30
}
31
TWIM_Stop();
32
33
_delay_ms(1000);
34
35
if(TWIM_Start(adres,TWIM_READ))
36
{
37
uart_puts("Start read");
38
unsignedchara=TWIM_ReadNack();
39
PORTC|=(1<<PC2);
40
uart_putc((char)a);
41
}
42
TWIM_Stop();
43
44
while(1)
45
{
46
asm("nop");
47
}
48
49
return0;
50
}
Slave:
1
#define F_CPU 8000000UL
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/interrupt.h>
6
#include<util/twi.h>
7
#include"TWI_Slave.h"
8
9
unsignedcharTWIS_ResponseType;
10
volatileunsignedchara;
11
12
intmain()
13
{
14
sei();
15
_delay_ms(10);
16
TWIS_Init(0x2F,100000);
17
TWCR|=(1<<TWIE);
18
19
while(1)
20
{
21
asm("nop");
22
}
23
}
24
25
26
ISR(TWI_vect)
27
{
28
if(TWIS_ResponseRequired(&TWIS_ResponseType))
29
{
30
switch(TWIS_ResponseType)
31
{
32
caseTWIS_ReadBytes:
33
a=TWIS_ReadNack();
34
TWIS_Stop();
35
DDRB|=(1<<1);
36
PORTB|=(1<<1);
37
break;
38
39
caseTWIS_WriteBytes:
40
TWIS_Write(a);
41
TWIS_Stop();
42
break;
43
}
44
}
45
}
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
Manni schrieb:> 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
Hallo zusammen,
ich muss diesen alten Thread noch mal rauskramen, da ich gerade ein paar
Stunden an dem Adressierungsproblem hing.
Ich nutze von deinem Code (vielen Dank Manni BTW) nur den Slave. Mein
Master nutzt die Lib von Peter Fleury. Und der interpretiert die vollen
8 Bit als Adresse (das letzte Bit der "Adresse" ist das R/W Bit und
damit 0). So scheint es auch zB bei I2C Flash Chips üblich zu sein. Auch
auf vielen Boards findet man in der Jumper-Beschreibung bei der
Adressvergabe die auf 8-bit bezogene Adresse (zB Pollin LCD-I2C Modul)
Das hat gereicht um mich zu verwirren :-)
==> Aufgemerkt: Adresse für den eigenen Slave noch einmal nach rechts
shiften damit's der Addressieung bei Fleury entspricht!
TWIS_Init(MY_SLAVEADDRESS_AS_USED_BY_FLEURY >> 1, 100000)
.. oder die TWIS_Init Routine anpassen und den Linksshift entfernen...
Grüße,
Pusaken
Hallo Manni und auch alle anderen,
gibt es irgendwelche Neuigkeiten zum Thema "General Call", der letzte
Post ist vom 24.11.2009.
Ich selber habe es schon mehrfach versucht, bin aber bisher noch nicht
zum Ziel gekommen.
Marc
Hallo,
ich habe den Slave von Manni modifiziert, sodass er
jetzt auch auf "General Call" antwortet. Wenn ein 16 MHz Quarz verwendet
wird kann auch der Fast Mode mit 400 KHz genutzt werden.
Auf Mannis Anregung, setze ich diesen Post fort und betrachte den von
mir neu eröffneten Beitrag "Re: TWI-Slave mit General Call"
hiermit als beendet.
Manni schrieb:> 2) Jetzt stehe ICH auf dem Schlauch und verstehe Deine Implementierung> nicht. Soweit ich das sehe, hast Du nur den Code:> ------------------------------------------------------------------------> /*> ** Slave is requests to read bytes from the master with GeneralCall.> ** It is expliciltely assumed, that the master sends 8 bytes> */> case TWIS_ReadBytes_GC:> for (i=0;i<7;i++)> {> byte[i] = TWIS_ReadAck ();> }> byte[7] = TWIS_ReadNack ();> TWIS_Stop ();> break;> ------------------------------------------------------------------------> eingebaut, neben der Definition von "TWIS_ReadBytes_GC".>> Soll das heißen, dass der Master dann "TWIS_ReadBytes_GC" senden soll,> damit der Slave die Anweisung ausführt ?>> Nach meinem Verständnis ist das eine falsche Interpretation des> physikalisch implementierten GC in den I2C's.
Leider bin ich kein Experte und habe mir das Ganze nur über Analogien
hergeleitet. Nach meinem Verständnis ist die Empfangsroutine die
gleiche, egal ob GC oder direkte Adressierung. Warum und wieso das Ganze
klappt kann ich nicht genau sagen. Ich stelle nur fest, dass es klappt,
wenigstens mit zwei Slaves.
Gruß Marc
Hallo,
ich habe mal eine Frage zur obigen Software:
Muss ich die Datenrichtungsregister irgendwie setzen, oder macht das die
TWI Hardware selber?
Ich habe nämlich den Code 1:1 genommen, aber irgendwie klappt es noch
nicht. SCL bleibt ständig auf H und SDA wird nach ca. 1 sek auf L
gezogen und bleibt da. Wenn ich den Master µc rausnehme, dann bleiben
beide auf H, so wie es sein soll.
Der Master hängt dann bei mir in der ersten while Schleife:
Ich möchte nur nochmal kurz eine Rückmeldung geben. Und zwar beherzigt
ruhig die Aussagen zum Pullup, die oben immer mal wieder kommen. Wenn
das Start - Signal nicht mal gesendet werden kann, wie es bei mir war.
Da ich mir sicher war, das die Pullups eingelötet waren, habe ich da
nicht weiter nachgeschaut. Allerdings hatte sich im Board bei mir ein
Fehler eingeschlichen, es war unter dem SMD-Widerstand aus Versehen die
Leiterbahn durchgezogen (kommt wohl davon wenn man den ERC nicht
beachtet). Naja zufälligerweise ist es mir aufgefallen, das der Pin
damit direkt an VCC hing und dann absolut unerklärliches Verhalten
zustande kam. Jetzt funktioniert alles wunderbar!
Aller besten Dank, das du den Code reingestellt hast, Manni!!
Hallo alle zusammen,
ich brauche mal Eure Hilfe, vorab aber ein großes Dankeschön an Manni
für die UART/TWI-Lib's!!!
Bevor ich mich an mein I2C-Display mache wollte ich mal eben schnell die
Uart oder rs232 funktionen von Manni testen. Mußte dazu erstmal einiges
umschreiben, da der Mege324a zwei Uart - Schnittstellen hat. Gesagt -
getan. Dann mal eben eine Ausgabe wie im Beispiel rs232.c probieren.
Leider bekomme ich keine Ausgabe in Putty. (Jumper Rx-->Tx liefert ein
Loop-back :))Das Programm läuft artig in der Schleife. Sehe ich an einer
LED die kurz am Ende blinkt. Könnte einer mal eben sich durch meinen
Code wühlen und mir 'n Tip geben was ich falsch mache? Das wäre fein und
wünsche allen Lesern/Helfern einen schönen Abend
MfG
Eddy
Marc Höner schrieb:> Hallo,>> ich habe den Slave von Manni modifiziert, sodass er> jetzt auch auf "General Call" antwortet. Wenn ein 16 MHz Quarz verwendet> wird kann auch der Fast Mode mit 400 KHz genutzt werden.
wieso 16 MHz Quarz ?
wenn doch 16x CPU clock genügt rechne ich 16x 400kHz = 6,4 MHz und das
ist weniger als der intern genutzte clock von 8MHz und sollte also
funktionieren, auch ohne 16 Mhz Quarz
Warum und wieso das ganze nur mit 16 MHz funktioniert kann ich nicht
sagen. Ich habe nur die Erfahrung gemacht, das es so ist. Vielleicht ist
der interne Quarz nicht genau genug und es geht auch mit externen 8th.
Habe ich nicht getestet.
Hallo,
ich habe versucht, die Bibliothek von Manni auf meinen Atmega32 zu laden
um einen PCF8474A anzusteuern.
Aus den Mitteilungen über RS232 scheitert es bereits beim Ansprechen des
PCF und es wird diese IF Bedingung ausgelöst:
if (!TWIM_Start (SlaveAddress, TWIM_WRITE))
{
TWIM_Stop ();
printf ("Could not start TWI Bus for WRITE\n");
Als SlaveAdrress habe ich 116 (also 0x74) verwendet, wie aus dem
Datenblatt. Kann jemand hier den Fehler erkennen?
Hier das vollständige Programm:
#include <stdio.h>
#include <avr/interrupt.h>
#include "General.h"
#include "RS232.h"
#include "Delay.h"
#include "TWI_Master.h"
/*
** This main programm demonstrates how to use the
** implemented TWI master functions. These are:
** TWIM_Init
** TWIM_ReadAck
** TWIM_ReadNack
** TWIM_Write
** TWIM_Stop
**
** For testing this program, use the program
** TWI_Slave_main.c in the slave uC and connect the
** two TWI lines properly (don't forget to also
** connect GND between Master and Slave!)
**
** Used uC for Master is ATMega32
*/
int main (void)
{
uint8_t i;
uint8_t j=0;
uint8_t Data[8];
uint8_t SlaveAddress = 116;//1116; //0x74;
uint8_t PCF3Byte; // Soll an PCF3 gesendet werden
PCF3Byte = 0; //0x02; //B00000010
DDRA |= (1 << PD6); // Am PortD wird PD0 auf Ausgang geschaltet für
LEDI2cwbyte &B00000010
//I2cwbyte &B00000010
/*
** Clear any interrupt
*/
cli ();
/*
** Wait 1 second for POR
*/
Delay_ms (1000);
/*
** Initiate RS232
*/
RS232_Init ();
printf ("Hello world...\n");
/*
** Initiate TWI Master Interface with bitrate of 100000 Hz
*/
if (!TWIM_Init (100000))
{
printf ("Error in initiating TWI interface\n");
while (1);
}
/*
** Endless loop
*/
while (1)
{
Delay_ms (500);
// Anmerkung: Um folgende If - Zeile handelt es sich, er schreibt also
// jedesmal "Could not start TWI Bus for WRITE"
if (!TWIM_Start (SlaveAddress, TWIM_WRITE))
{
TWIM_Stop ();
printf ("Could not start TWI Bus for WRITE\n");
}
else
{
for (i=0;i<1;i++)
{
TWIM_Write (PCF3Byte);
printf ("Byte %d sent: %d\n", i, PCF3Byte);
}
TWIM_Stop ();
Delay_ms (1000);
}
}
return 0;
}
Das Programm ist ansonsten unverändert. Weiss jemand, warum der I2C
nicht startet? Ich arbeite mit 8Mhz als Taktfrequenz mit Atmel Studio 7.
Danke und Gruß,
Klaus
klaus k. schrieb:> ich denke ja, da die Schreibadresse 0x74 und die Leseadresse des PCF> 0x75 ist, das sollte das niederwertige Bit sein.
Die Frage aber ist, wie die Library es gerne hätte. Die Tatsache, dass
da neben der Adresse noch ein weiterer Parameter für Lesen/Schreiben
ist, legt doch irgendwie die Vermutung nahe, dass sie die Adresse eben
ohne das R/W-Bit haben möchte.
Hallo,
update:
Funktioniert nun alles prima, danke für den Hinweis.
Ich musste bei BASCOM sonst immer Hex74 angeben, da hier das komplette
Byte als Adresse übergeben wurde.
Bei dieser Library muss man aber tatsächlich das LSB weglassen, also die
reinen 7 Adressbits in Dezimal umwandeln.
Funktioniert nun echt super.
Danke und Gruss,
Klaus
Hallo,
ja, ich lebe noch und es freut mich, dass mein AVR TWI Post immer noch
lebt und gern besucht wird :-)
Ich möchte mich noch bei Thorsten K. Hinweis vom 5.10.2008 für den Tip
bedanken, wie man das Ganze in eine interrupt-gesteuert ISR packt. Sein
Hinweis mit dem Wiedersetzen des TWIE flags in:
void TWIS_Stop (void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)|(1<<TWEA)|(1<<TWIE);
}
ist Gold wert und hat nun jetzt auch bei mir auf Anhieb funktioniert.
--> Danke Thorsten !!
Gruß
Manni
Hallo,
jetzt muss ich den ollen Thread doch noch mal ausbuddeln.
Ich als Author der TWI source codes benutze diese in vielen Anwendungen
und sie funktionieren hervorragend.
In der Funktion:
1
uint8_tTWIM_Init(uint32_tTWI_Bitrate)
steht zur Berechnung des TWBR Registers das Statement:
1
TWBR=((F_CPU/TWI_Bitrate)-16)/8
mit:
- F_CPU in Hertz
- TWI_Bitrate in Bit/s
Jetzt bin ich auf den folgenden Link gestossen:
--> [[https://rn-wissen.de/wiki/index.php/TWI]]
In diesem Link etwas weiter unten steht zur Berechnung des TWBR
Registers:
1
TWBR=((F_CPU/TWI_Bitrate)-16)/2;
Somit bin ich stutzig geworden, was denn nun richtig ist, denn das würde
bedeuten, dass bei meinen Routinen die TWI Bitrate 4 mal geringer ist
als ich eigentlich angenommen habe.
Also habe ich nochmals genau die AVR (Microchip) Dokumentation gelesen,
die da sagt:
--> TWPS = Value of the prescaler bits in the TWI Status Register
siehe auch attached Bild der Dokumentation.
Nach meinem Verständnis heißt das:
- Value of prescaler bits = 00 (=0) --> Prescaler Value = 1
- Value of prescaler bits = 01 (=1) --> Prescaler Value = 4
- Value of prescaler bits = 10 (=2) --> Prescaler Value = 16
- Value of prescaler bits = 11 (=3) --> Prescaler Value = 64
Da ich den Prescaler auf 00 (=0) gelassen habe, heißt das:
Der Autor des o.g. Links hat aber folgendes verwendet:
Ich habe dies dann nochmals mit Echtzeit running software getestet,
Bitrate = 100.000 Bit/s.
Bei meiner Implementierung gibt es keine TWI Übertragungsfehler.
Wenn ich aber die Implementierung aus dem o.g. Link verwende,
funktioniert der TWI link nicht mehr, weil die Übertragungsrate
offensichtlich viel zu hoch ist.
Ich gehe deshalb davon aus, dass meine Implementierung richtig ist.
Oder wie seht ihr das ?
Gruß
Manni
Manfred L. schrieb:> Ich gehe deshalb davon aus, dass meine Implementierung richtig ist.> Oder wie seht ihr das ?
ist mir gerade zu hoch, ich teste auf die üblichen 400kHz und wenn das
nicht klappt gehe ich runter auf 100kHz.
BTW, soll gerade der AVR nicht Probleme mit Clockstretching haben?
Also müsste eh ein Port nur auf dem TWI lauschen mit timeout ob der
Slave Clockstretching macht?
Hi Joachim,
kann ich gut verstehen, dass die das zu dieser Stunde zu hoch ist.
Ist aber auch wirklich trockene Materie.
Vom Thema "Clockstretching" habe ich noch nie was gehört.
Was hat es denn damit auf sich ?
Manfred L. schrieb:> Hi Joachim,> kann ich gut verstehen, dass die das zu dieser Stunde zu hoch ist.> Ist aber auch wirklich trockene Materie.>> Vom Thema "Clockstretching" habe ich noch nie was gehört.> Was hat es denn damit auf sich ?
langsame TWI halten den Clock auf low solange sie "nachdenken" bevor sie
Clock wieder auf high setzen, der AVR wartet dann ewig wenn er sich
nicht vom Bus befreit, besonders wenn der Andere abgestürzt ist und nie
frei meldet!
https://rn-wissen.de/wiki/index.php/Clock_Stretching
uvam.
Manfred L. schrieb:>> steht zur Berechnung des TWBR Registers das Statement:>
1
TWBR=((F_CPU/TWI_Bitrate)-16)/8
> mit:> - F_CPU in Hertz> - TWI_Bitrate in Bit/s>> Jetzt bin ich auf den folgenden Link gestossen:> --> [[https://rn-wissen.de/wiki/index.php/TWI]]>> In diesem Link etwas weiter unten steht zur Berechnung des TWBR> Registers:>>
1
TWBR=((F_CPU/TWI_Bitrate)-16)/2;
>
Ui,
das ist ja schon länger her, das ich den Artikel im Roboternetz verfasst
habe.
Aber in der zitierten Formel fehlt ja dann noch das "* 4^1",
dann ergibt 2*4 = 8 und dann ist es gleich !
Oder hab ich was anderes übersehen ?
mfG
Hi John,
toll, dass ich gleich den Roboternetz Author hier an der Leine habe.
Deine Berechnungsschritte sind schon in Ordnung:
100kHz = 8.0MHz / (16 + 2 x 4^0 ) / 1 fällt weg
100kHz = 8.0MHz / (16 + 2 * x ) / *(16 + 2 * x )
100kHz * (16 + 2 * x ) = 8000kHz kürzen : 100
16 + 2 * x = 80 / -16
2 * x = 64 / :2
x = 32
Hier speziell: 4^0 im ersten Schritt. Nur die daraus resultierende
Formel:
1
TWBR=((F_CPU/TWI_Bitrate)-16)/2;
ist meines Erachtens falsch. Sollte jedenfalls lauten:
1
TWBR=((F_CPU/TWI_Bitrate)-16)/8;
Vielleicht kannst Du da nochmals drüber sinnieren und die Formel
entsprechend ändern.
Gruß
Manni
Manfred L. schrieb:> Hier speziell: 4^0 im ersten Schritt. Nur die daraus resultierende> Formel:>>
1
TWBR=((F_CPU/TWI_Bitrate)-16)/2;
>> ist meines Erachtens falsch. Sollte jedenfalls lauten:>>
1
TWBR=((F_CPU/TWI_Bitrate)-16)/8;
Aber,
ein 4^0 gibt es ja nicht, der kleinste Wert is 1, welchen man erreicht
wenn man die Bits auf 00 setzt, was als Rechenwert eine 4^1 = 4 ergibt.
Ich hab mir die Formel IIRC nicht selbst ausgedacht, sondern aus dem AVR
PDF von 2006 genommen.
mfG
John S. schrieb:> Aber,> ein 4^0 gibt es ja nicht, der kleinste Wert is 1, welchen man erreicht> wenn man die Bits auf 00 setzt, was als Rechenwert eine 4^1 = 4 ergibt.
Jetzt geht's wohl etwas durcheinander.
Also nochmals back to the roots.
Die Formel aus dem Atmel (Microchip) Datenblatt lautet:
SCL_frequ. = (CPU clock) / (16 + (2*TWBR * 4^TWPS))
Daraus folgt:
TWBR = (F_CPU/SCL_frequ.-16) / (2 * 4^TWPS)
Die Frage die sich jetzt stellt ist: Was ist TWPS ?
Im Datenblatt steht:
--> "TWPS = Value of the prescaler bits in the TWI Status Register"
Variante A) --> das ist die von https://rn-wissen.de/wiki/index.php/TWI
Wenn TWPS wirklich die Bits sind und nicht der Prescaler Wert selber,
dann gilt mit TWPS0 und TWPS1:
00 (TWPS=0) --> Prescaler = 1
01 (TWPS=1) --> Prescaler = 4
10 (TWPS=2) --> Prescaler = 16
11 (TWPS=3) --> Prescaler = 64
Für den Fall von Prescaler = 1 gilt dann:
TWBR = (F_CPU/SCL_frequ.-16) / (2 * 4^0) = TWBR = (F_CPU/SCL_frequ.-16)
/ 2
Variante B)
Wenn TWPS aber der Prescaler Wert selber ist, dann gilt:
00 --> Prescaler = 1 --> TWPS
01 --> Prescaler = 4 --> TWPS
10 --> Prescaler = 16 --> TWPS
11 --> Prescaler = 64 --> TWPS
Für den Fall von Prescaler = 1 gilt dann:
TWBR = (F_CPU/SCL_frequ.-16) / (2 * 4^1) = TWBR = (F_CPU/SCL_frequ.-16)
/ 8
Summary:
========
Nach nochmaligem "Drüber Schlafen" komme ich zu dem Ergebnis, dass Deine
Variante A) wohl doch die Richtige ist und meine Variante B) ein
Denkfehler ist.
Das Merkwürdige ist nur, dass ich mit der Variante A) die beiden
Controller nicht zum Laufen kriege. Das Environment ist:
Master: ATmega8 mit 16 MHz und einer Bitrate von 100.000 Bit/s
Slave: ATmega32 mit 12 MHz
Und die 100.000 Bit/s sind ja nicht das obere Ende, denn es sind laut
Datenblatt damit:
- 1.0 MBit/s bei 16 MHz und
- 0.625 MBit/s bei 12 MHz
erlaubt.
Mit der Variante B) funktioniert es aber hervorragend.
Die Frage ist also jetzt nur: Was ist Richtig und was ist Falsch ?
Gruß
Manni
Manfred L. schrieb:> John S. schrieb:>> Aber,>> ein 4^0 gibt es ja nicht, der kleinste Wert is 1, welchen man erreicht>> wenn man die Bits auf 00 setzt, was als Rechenwert eine 4^1 = 4 ergibt.> Mit der Variante B) funktioniert es aber hervorragend.> Die Frage ist also jetzt nur: Was ist Richtig und was ist Falsch ?>> Gruß> Manni
Hmm,
in meiner Beispielrechnung steht ja auch 4^0 ... das passt ja eigentlich
nicht zusammen !
d.H. die umgestellte Formel bei mir stimmt nicht.
es muss mit
1
/2*4
enden.
Wenn man das Ergebnis (32) in die original Formel einsetzt, kommt man
auf ca. 29kHz bei 8MHz. (lt. Libreoffice-Calc :) )
Das stand da jetzt 14 Jahre ohne dass es jemanden aufgefallen ist, dann
hat das noch ein paar Tage Zeit bis ich über den Artikel gehe.
Nun überlege ich schon die ganze Zeit warum ich diese Formel so
vorgerechnet und umgestellt habe, aber das ist schon zu lange her.
uiui ;)
John S. schrieb:> Das stand da jetzt 14 Jahre ohne dass es jemanden aufgefallen ist, dann> hat das noch ein paar Tage Zeit bis ich über den Artikel gehe.
Bitte warte noch mit der Überarbeitung Deines Artikels.
Ich werde mir das jetzt mal auf meinem Digital Ossi (Siglent SDS1202X-E)
anschauen, der ein I2C Signal dekodieren können soll. Das habe ich zwar
bisher noch nicht gemacht, werde es aber wohl hin bekommen.
Danke Dir erst mal für Deine Rückmeldungen.
Gruß
Manni
Hi,
ich benutze immer eine Funktion zum Baudrate setzen, die die gewünschten
Settings so berechnet, dass der Fehler am kleinsten ist.
Funktioniert ähnlich auch bei UART.
Gruß
Olaf
Olaf D. schrieb:> ich benutze immer eine Funktion zum Baudrate setzen, die die gewünschten> Settings so berechnet, dass der Fehler am kleinsten ist.
Das klingt sehr verlockend, denn bei I2C ist die realisierte Kabellänge
bzw. Leiterbahnlänge ja schon ein Faktor, der die maximale Bitrate
erheblich reduziert.
Bin mal über Deinen Source Code geflogen, aber kann nicht so richtig
nachvollziehen, welcher Algorithmus dahinter steckt. Ein paar comments
oder Hifesprüche im Source Code würden da schon hilfreich sein.
Kannst Du hier die Philosophie zur Findung des kleinsten Fehlers bei der
Übertragung mal kurz anskizieren ?
Gruß
Manni
Na klar.
Bei I2C ist das ja nicht so schlimm, der der Master ja den Clock
vorgibt.
Bei UART ergibt sich dabei durchaus ein Problem.
Da mein AVR vorher nicht weiß, welche Settings einzustellen sind,
muss er es halt selber berechnen.
Das nur zu Motivation.
In der Datei habe ich die Error-Funktion vergessen gehabt.
Daher noch mal ein Update.
Nun zur Erklärung:
Hier berechne ich vier verschiedene twbr Werte für die
gewünschte Baudrate, jeweils mit unterschiedlichen Prescaler (0 .. 3):
uint32_t twbr0 = i2cGetTWBR(uBaudrate, 0);
uint32_t twbr1 = i2cGetTWBR(uBaudrate, 1);
uint32_t twbr2 = i2cGetTWBR(uBaudrate, 2);
uint32_t twbr3 = i2cGetTWBR(uBaudrate, 3);
Dann berechne ich daraus die sich ergebenden Baudraten:
uint32_t f0 = i2cBaudrate(twbr0, 0);
uint32_t f1 = i2cBaudrate(twbr1, 1);
uint32_t f2 = i2cBaudrate(twbr2, 2);
uint32_t f3 = i2cBaudrate(twbr3, 3);
Aus den Sollwerten (uBaudrate) und Istwerten (f0 .. f3)
berechne ich die jeweiligen Fehler:
uint32_t error0 = error(f0, uBaudrate, 100);
uint32_t error1 = error(f1, uBaudrate, 100);
uint32_t error2 = error(f2, uBaudrate, 100);
uint32_t error3 = error(f3, uBaudrate, 100);
Und wähle die Kombination mit dem kleinsten Fehler (minError in %):
uint8_t br;
uint8_t ps;
uint32_t f;
uint32_t minError = 100;
if((twbr3 >= 10) && (twbr3 <= 255) && (error3 <= minError))
{
f = f3;
minError = error3;
br = twbr3;
ps = 3;
bRet = true;
}
if((twbr2 >= 10) && (twbr2 <= 255) && (error2 <= minError))
{
f = f2;
minError = error2;
br = twbr2;
ps = 2;
bRet = true;
}
if((twbr1 >= 10) && (twbr1 <= 255) && (error1 <= minError))
{
f = f1;
minError = error1;
br = twbr1;
ps = 1;
bRet = true;
}
if((twbr0 >= 10) && (twbr0 <= 255) && (error0 <= minError))
{
f = f0;
minError = error0;
br = twbr0;
ps = 0;
bRet = true;
}
Ich hoffe nun ist es deutlicher.
Die Datei wird so nicht kompilieren, da sie nur Teile des eigentlichen
C++ Codes enthält.
Werde jetzt Feierabend machen.
Weitere Hilfe wird also etwas dauern.
Gruß
Olaf
Manfred L. schrieb:> John S. schrieb:>> Das stand da jetzt 14 Jahre ohne dass es jemanden aufgefallen ist, dann>> hat das noch ein paar Tage Zeit bis ich über den Artikel gehe.>> Bitte warte noch mit der Überarbeitung Deines Artikels.
ausgewartet ;)
https://rn-wissen.de/wiki/index.php?title=TWI
Die Beispielrechnung hab ich ganz raus.
Hi John,
habe den überarbeiteten Text auf der Seite gelesen. Der ist OK !
Vielleicht solltest Du noch den Vermerk anbringen, dass TWPS ein Element
von {0, 1, 2, 3} ist und nicht {1, 4, 16, 64}.
Ich habe mir das ganze jetzt auf dem Ossi angesehen. Dazu habe ich
folgendes Environment:
F_CPU = 16 MHz
f-SCL Soll = 100.000 Hz
Prescaler = 1 (also TWPS = 0)
Daraus folgt mit Deiner Formel: TWBR = 72
Und siehe da: die 100.000 Hz für die gewünschte f-SCL werden perfekt
erreicht, siehe attached Bild.
Also vergiss all meine Formeln und Einlassungen, denn Deine ist richtig.
Nochmals vielen Dank für Deine Mühen und Kommentare.
Gruß
Manni
Olaf D. schrieb:> Na klar.
Vielen Dank für die zusätzlichen Erläuterungen !
Macht schon Sinn, so etwas einzubauen, wenn man die Bit Rate frei
definieren kann, denn das TWBR sollte ja nicht kleiner als 10 sein.
Anfangs aber dachte ich, dass Du die Bit Rate auf der Basis der Güte der
Kanalübertragung definierst, also wie hoch darf ich mit der Bit Rate
gehen, bis sich die ersten Übertragungsfehler einschleichen.
Wenn man vieeeeeeel Zeit hat, kann man das ja auch mal angehen :-)
Nochmals Dank für den schnellen Response !