www.mikrocontroller.net

Forum: Codesammlung AVR TWI Master und Slave Funtionen in C

Autor: Manni (Gast)
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
Autor: Bernhard S. (bernhard)
Datum:

Hallo Manni,

das scheint das Gegenstück zu meinem Assembler - Beispiel zu sein ;)

Beitrag "TWI / I2C einf. MASTER SLAVE Beispiel(Assembler) ATmega8"


Bernhard
Autor: Manni (Gast)
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
Autor: Torsten K. (avr_fan)
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
Autor: neuer (Gast)
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);
Autor: neuer (Gast)
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
Autor: hans-jakob (Gast)
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
Autor: Manni (Gast)
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
Autor: Manni (Gast)
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
Autor: Thomas Frosch (Gast)
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?
Autor: Thomas Frosch (Gast)
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?
Autor: Manni (Gast)
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
Autor: Bernd Bömer (behbeh)
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
Autor: Manni (Gast)
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
Autor: Bernhard S. (bernhard)
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
Autor: Manni (Gast)
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 ?
Autor: Bernhard S. (bernhard)
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.
Autor: Manni (Gast)
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
Autor: Torsten K. (avr_fan)
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
Autor: Manni (Gast)
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
Autor: Bernd Bömer (behbeh)
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
Autor: Thorsten (Gast)
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. 
Autor: Michael K. (mmike)
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
Autor: Thorsten G. (Gast)
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
Autor: Dennis P. (dennis_)
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
Autor: Manni (Gast)
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
Autor: ramius (Gast)
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
Autor: Peter Dannegger (peda)
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
Autor: ramius (Gast)
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
Autor: Dennis P. (dennis_)
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
Autor: ramius (Gast)
Datum:

@Dennis Du könntest ja mal etwas von deinem code posten, dann kann dir
vll einer dazu mehr sagen.

gruß ramius
Autor: Dennis P. (dennis_)
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
Autor: ramius (Gast)
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
Autor: Manni (Gast)
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
Autor: Dennis P. (dennis_)
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
Autor: Manni (Gast)
Datum:

Na also, es geht doch. Und merke: Niemals zu viel rumfummeln, am Code
meine ich ;-)

Gruß
Manni
Autor: Bob (Gast)
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
Autor: Manni (Gast)
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
Autor: Johannes Kreuzer (andun)
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
Autor: Bob (Gast)
Datum:

@manni

danke für die beantwortung meiner fragen!

zu 4) ja auch im Master..
zu 5) hab Variante 5b gewählt.

Funktioniert optimal:)
Autor: Manni (Gast)
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
Autor: Geert-Jon (Gast)
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
Autor: Manni (Gast)
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
Autor: Yvo Comte (Firma: www.twipnet.ch/) (majortwip) Benutzerseite
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
Autor: Manni (Gast)
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
Autor: Veit H. (veith)
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!
Autor: Manni (Gast)
Datum:

Joooo, so soll's auch sein und nicht wie bei AEG:
Auspacken, Einschalten, Geht nicht :(

Gruss Manni
Autor: slog (Gast)
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!
Autor: Manni (Gast)
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;
  }
Autor: Sören Dierking (tweety955)
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 ?
Autor: Manni (Gast)
Datum:

Schwierig, so das Problem zu identifizieren. Schick mal den Code.

Gruß
Manni
Autor: Sören Dierking (tweety955)
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.
Autor: Manni (Gast)
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
Autor: Sören Dierking (tweety955)
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 !
Autor: Sören Dierking (tweety955)
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]);
        }

    }
Autor: Manni (Gast)
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
Autor: andy b. (Gast)
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
Autor: Manni (Gast)
Datum:
Angehängte Dateien:

Hier ist die Lösung für dein Problem.

Gruß
Manni
Autor: Monte (Gast)
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
Autor: Manni (Gast)
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
Autor: Monte (Gast)
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
Autor: Manni (Gast)
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
Autor: Monte (Gast)
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.
Autor: Monte (Gast)
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?
Autor: Manni (Gast)
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
Autor: Gilbi (Gast)
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
Autor: Manni (Gast)
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
Autor: Harald L. (mysth)
Datum:

Danke für den Code!!

läuft problemlos!

Harry

Ach so...SYSCLOCK durch F_CPU ersetzen! ;)
Autor: Manni (Gast)
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
Autor: Christoph (Gast)
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
Autor: Christoph (Gast)
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?
Autor: Christoph (Gast)
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!
Autor: Manni (Gast)
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
Autor: David F.r. (miyu)
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
Autor: Maik (Gast)
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
Autor: Fabian B. (fabs)
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
Autor: Daniel S. (nasenschleim)
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".
Autor: Sicherungshalter (Gast)
Datum:

sind die Fuses richtig gesetzt?
Autor: Daniel S. (nasenschleim)
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!?
Autor: Sicherungshalter (Gast)
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
Autor: Simon Hi (himmsi)
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...
Autor: Chris (Gast)
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.
Autor: Simon Hi (himmsi)
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!!
Autor: Simon Hi (himmsi)
Datum:

...nachdem ich grad die ganze RS232 Kommunikation rausgeschmissen hab
funktioniert die Kompilation. An was liegt das? Ich verwend nen
Atmega88.
Autor: Chriss (Gast)
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
Autor: Chriss (Gast)
Datum:

niemand?
Autor: André M. (pc-fan)
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
Autor: Chriss (Gast)
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 ;)
Autor: André M. (pc-fan)
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
Autor: Philipp Maricek (lord-maricek)
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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder GIF-Format hochladen.
Siehe Bildformate
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net